1 // **************************************************************************** 2 // 3 // CMidiInQ.cpp 4 // 5 // Implementation file for the CMidiInQ class. 6 // Use a simple fixed size queue for managing MIDI data. 7 // Fill & drain pointers are maintained automatically whenever 8 // an Add or Get function succeeds. 9 // 10 // Set editor tabs to 3 for your viewing pleasure. 11 // 12 // Copyright Echo Digital Audio Corporation (c) 1998 - 2002 13 // All rights reserved 14 // www.echoaudio.com 15 // 16 // Permission is hereby granted, free of charge, to any person obtaining a 17 // copy of this software and associated documentation files (the 18 // "Software"), to deal with the Software without restriction, including 19 // without limitation the rights to use, copy, modify, merge, publish, 20 // distribute, sublicense, and/or sell copies of the Software, and to 21 // permit persons to whom the Software is furnished to do so, subject to 22 // the following conditions: 23 // 24 // - Redistributions of source code must retain the above copyright 25 // notice, this list of conditions and the following disclaimers. 26 // 27 // - Redistributions in binary form must reproduce the above copyright 28 // notice, this list of conditions and the following disclaimers in the 29 // documentation and/or other materials provided with the distribution. 30 // 31 // - Neither the name of Echo Digital Audio, nor the names of its 32 // contributors may be used to endorse or promote products derived from 33 // this Software without specific prior written permission. 34 // 35 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 36 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 37 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 38 // IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR 39 // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 40 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 41 // SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. 42 // 43 // **************************************************************************** 44 45 #include "CEchoGals.h" 46 47 48 /**************************************************************************** 49 50 Construction and destruction, clean up and init 51 52 ****************************************************************************/ 53 54 //============================================================================ 55 // 56 // Construction & destructor 57 // 58 //============================================================================ 59 60 CMidiInQ::CMidiInQ() 61 { 62 63 Reset(); 64 65 m_pBuffer = NULL; 66 m_ullLastActivityTime = 0; 67 68 m_wMtcState = MTC_STATE_NORMAL; 69 70 m_pEG = NULL; 71 72 m_fArmed = FALSE; 73 74 } // CMidiInQ::CMidiInQ() 75 76 77 CMidiInQ::~CMidiInQ() 78 { 79 80 Cleanup(); 81 82 } // CMidiInQ::~CMidiInQ() 83 84 85 //============================================================================ 86 // 87 // Init 88 // 89 //============================================================================ 90 91 ECHOSTATUS CMidiInQ::Init( CEchoGals *pEG ) 92 { 93 ECHOSTATUS Status; 94 95 ECHO_DEBUGPRINTF(("CMidiInQ::Init\n")); 96 97 m_pEG = pEG; 98 99 Status = OsAllocateNonPaged( sizeof(MIDI_DATA) * ECHO_MIDI_QUEUE_SZ, 100 (PPVOID) &m_pBuffer); 101 102 if (ECHOSTATUS_OK == Status) 103 Reset(); 104 105 return Status; 106 107 } // Init 108 109 110 //============================================================================ 111 // 112 // Cleanup 113 // 114 //============================================================================ 115 116 void CMidiInQ::Cleanup() 117 { 118 // 119 // Free the MIDI input buffer 120 // 121 if (NULL != m_pBuffer) 122 { 123 if (ECHOSTATUS_OK != OsFreeNonPaged( m_pBuffer )) 124 { 125 ECHO_DEBUGPRINTF(("CMidiInQ::Cleanup - Failed to free MIDI input bufer\n")); 126 } 127 } 128 129 m_pBuffer = NULL; 130 } 131 132 133 134 135 /**************************************************************************** 136 137 Queue management - add and remove MIDI data 138 139 ****************************************************************************/ 140 141 //============================================================================ 142 // 143 // GetMidi - get oldest MIDI input byte from the Q 144 // 145 //============================================================================ 146 147 ECHOSTATUS CMidiInQ::GetMidi 148 ( 149 MIDI_DATA &Midi 150 ) 151 { 152 if (NULL == m_pBuffer) 153 return ECHOSTATUS_CHANNEL_NOT_OPEN; 154 155 if ( m_pFill == m_pDrain ) 156 return( ECHOSTATUS_NO_DATA ); 157 158 Midi = *m_pDrain; 159 160 ECHO_DEBUGPRINTF( ("CMidiInQ::GetMidi 0x%x\n", Midi) ); 161 162 m_pDrain = ComputeNext( m_pDrain ); 163 164 return ECHOSTATUS_OK; 165 166 } // ECHOSTATUS CMidiInQ::GetMidi 167 168 169 //============================================================================ 170 // 171 // AddMidi - add new MIDI input byte to the Q 172 // 173 //============================================================================ 174 175 ECHOSTATUS CMidiInQ::AddMidi 176 ( 177 MIDI_DATA Midi 178 ) 179 { 180 if ( ComputeNext( m_pFill ) == m_pDrain ) 181 { 182 ECHO_DEBUGPRINTF( ("CMidiInQ::AddMidi buffer overflow\n") ); 183 ECHO_DEBUGBREAK(); 184 return ECHOSTATUS_BUFFER_OVERFLOW; 185 } 186 187 ECHO_DEBUGPRINTF( ("CMidiInQ::AddMidi 0x%x\n", Midi) ); 188 189 *m_pFill = Midi; 190 m_pFill = ComputeNext( m_pFill ); 191 192 return ECHOSTATUS_OK; 193 194 } // ECHOSTATUS CMidiInQ::AddMidi 195 196 197 //============================================================================ 198 // 199 // ComputeNext - protected routine used to wrap the read and write pointers 200 // around the circular buffer 201 // 202 //============================================================================ 203 204 PMIDI_DATA CMidiInQ::ComputeNext( PMIDI_DATA pCur ) 205 { 206 if ( ++pCur >= m_pEnd ) 207 pCur = m_pBuffer; 208 209 return pCur; 210 211 } // PMIDI_DATA CMidiInQ::ComputeNext( PMIDI_DATA pCur ) 212 213 214 //============================================================================ 215 // 216 // Reset 217 // 218 //============================================================================ 219 220 void CMidiInQ::Reset() 221 { 222 // 223 // Midi buffer pointer initialization 224 // 225 m_pEnd = m_pBuffer + ECHO_MIDI_QUEUE_SZ; 226 m_pFill = m_pBuffer; 227 m_pDrain = m_pBuffer; 228 229 } // void CMidiInQ::Reset() 230 231 232 233 234 /**************************************************************************** 235 236 Arm and disarm 237 238 ****************************************************************************/ 239 240 241 //============================================================================ 242 // 243 // Arm - resets the Q and enables the Q to start receiving MIDI input data 244 // 245 //============================================================================ 246 247 ECHOSTATUS CMidiInQ::Arm() 248 { 249 // 250 // Return if the MIDI input buffer is not allocated 251 // 252 if ( (NULL == m_pBuffer) || 253 (NULL == m_pEG) 254 ) 255 return ECHOSTATUS_CANT_OPEN; 256 257 // 258 // Reset the buffer pointers 259 // 260 Reset(); 261 262 // 263 // Tell the DSP to enable MIDI input 264 // 265 m_pEG->GetDspCommObject()->SetMidiOn( TRUE ); 266 267 // 268 // Set the flag 269 // 270 m_fArmed = TRUE; 271 272 return ECHOSTATUS_OK; 273 274 } // Arm 275 276 277 //============================================================================ 278 // 279 // Disarm - surprisingly, does the opposite of Arm 280 // 281 //============================================================================ 282 283 void CMidiInQ::Disarm() 284 { 285 // 286 // Tell the DSP to disable MIDI input 287 // 288 if (m_pEG) 289 m_pEG->GetDspCommObject()->SetMidiOn( FALSE ); 290 291 // 292 // Clear the flag 293 // 294 m_fArmed = FALSE; 295 296 } // Disarm 297 298 299 300 301 /**************************************************************************** 302 303 Detect MIDI input activity - see if the driver has recently received 304 any MIDI input 305 306 ****************************************************************************/ 307 308 BOOL CMidiInQ::IsActive() 309 { 310 ULONGLONG ullCurTime,ullDelta; 311 312 // 313 // See if anything has happened recently 314 // 315 m_pEG->m_pOsSupport->OsGetSystemTime( &ullCurTime ); 316 ullDelta = ullCurTime - m_ullLastActivityTime; 317 318 if (ullDelta > MIDI_ACTIVITY_TIMEOUT_USEC) 319 return FALSE; 320 321 return TRUE; 322 323 } // IsActive 324 325 326 327 328 /**************************************************************************** 329 330 MIDI time code 331 332 ****************************************************************************/ 333 334 335 //=========================================================================== 336 // 337 // Run the state machine for MIDI input data 338 // 339 // MIDI time code sync isn't supported by this code right now, 340 // but you still need this state machine to parse the incoming 341 // MIDI data stream. Every time the DSP sees a 0xF1 byte come in, 342 // it adds the DSP sample position to the MIDI data stream. 343 // The DSP sample position is represented as a 32 bit unsigned 344 // value, with the high 16 bits first, followed by the low 16 bits. 345 // 346 // Since these aren't real MIDI bytes, the following logic is 347 // needed to skip them. 348 // 349 //=========================================================================== 350 351 DWORD CMidiInQ::MtcProcessData( DWORD dwMidiData ) 352 { 353 switch ( m_wMtcState ) 354 { 355 case MTC_STATE_NORMAL : 356 if ( dwMidiData == 0xF1L ) 357 m_wMtcState = MTC_STATE_TS_HIGH; 358 break; 359 case MTC_STATE_TS_HIGH : 360 m_wMtcState = MTC_STATE_TS_LOW; 361 return MTC_SKIP_DATA; 362 break; 363 case MTC_STATE_TS_LOW : 364 m_wMtcState = MTC_STATE_F1_DATA; 365 return MTC_SKIP_DATA; 366 break; 367 case MTC_STATE_F1_DATA : 368 m_wMtcState = MTC_STATE_NORMAL; 369 break; 370 } 371 return 0; 372 373 } // DWORD CMidiInQ::MtcProcessData 374 375 376 377 378 /**************************************************************************** 379 380 Interrupt service 381 382 ****************************************************************************/ 383 384 ECHOSTATUS CMidiInQ::ServiceIrq() 385 { 386 DWORD dwMidiCount; 387 WORD wIndex; 388 ECHOSTATUS Status; 389 CDspCommObject *pDCO; 390 391 // 392 // Store the time for the activity detector 393 // 394 m_pEG->m_pOsSupport->OsGetSystemTime( &m_ullLastActivityTime ); 395 396 // 397 // Get the MIDI count 398 // 399 pDCO = m_pEG->GetDspCommObject(); 400 pDCO->ReadMidi( 0, dwMidiCount ); // The count is at index 0 401 402 #ifdef ECHO_DEBUG 403 ECHO_DEBUGPRINTF( ("\tMIDI interrupt (%ld MIDI bytes)\n", dwMidiCount) ); 404 if ( dwMidiCount == 0 ) 405 { 406 ECHO_DEBUGBREAK(); 407 } 408 #endif 409 410 // 411 // Get the MIDI data from the comm page 412 // 413 wIndex = 1; // First MIDI byte is at index 1 414 while ( dwMidiCount-- > 0 ) 415 { 416 DWORD dwMidiByte; 417 418 // 419 // Get the MIDI byte 420 // 421 Status = pDCO->ReadMidi( wIndex++, dwMidiByte ); 422 if ( ECHOSTATUS_OK != Status ) 423 { 424 ECHO_DEBUGPRINTF(("Failed on ReadMidi\n")); 425 ECHO_DEBUGBREAK(); // should never happen... 426 break; 427 } 428 429 // 430 // Parse the incoming MIDI stream. The incoming MIDI data consists 431 // of MIDI bytes and timestamps for the MIDI time code 0xF1 bytes. 432 // MtcProcessData is a little state machine that parses the strem. 433 // 434 // If you get MTC_SKIP_DATA back, then this is a timestamp byte, 435 // not a MIDI byte, so don't store it in the MIDI input buffer. 436 // 437 if ( MTC_SKIP_DATA == MtcProcessData( dwMidiByte ) ) 438 continue; 439 440 // 441 // Only store the MIDI data if MIDI input is armed 442 // 443 if (m_fArmed) 444 { 445 // 446 // Stash the MIDI and timestamp data and check for overflow 447 // 448 if ( ECHOSTATUS_BUFFER_OVERFLOW == AddMidi( (MIDI_DATA) dwMidiByte ) ) 449 { 450 break; 451 } 452 453 } 454 } // while there is more MIDI data to read 455 456 return ECHOSTATUS_OK; 457 458 } // ServiceIrq 459 460 461 // *** CMidiInQ.cpp *** 462