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 // ---------------------------------------------------------------------------- 13 // 14 // This file is part of Echo Digital Audio's generic driver library. 15 // Copyright Echo Digital Audio Corporation (c) 1998 - 2005 16 // All rights reserved 17 // www.echoaudio.com 18 // 19 // This library is free software; you can redistribute it and/or 20 // modify it under the terms of the GNU Lesser General Public 21 // License as published by the Free Software Foundation; either 22 // version 2.1 of the License, or (at your option) any later version. 23 // 24 // This library is distributed in the hope that it will be useful, 25 // but WITHOUT ANY WARRANTY; without even the implied warranty of 26 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 27 // Lesser General Public License for more details. 28 // 29 // You should have received a copy of the GNU Lesser General Public 30 // License along with this library; if not, write to the Free Software 31 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 32 // 33 // **************************************************************************** 34 35 #include "CEchoGals.h" 36 37 38 /**************************************************************************** 39 40 Construction and destruction, clean up and init 41 42 ****************************************************************************/ 43 44 //============================================================================ 45 // 46 // Construction & destructor 47 // 48 //============================================================================ 49 50 CMidiInQ::CMidiInQ() 51 { 52 53 m_pBuffer = NULL; 54 m_ullLastActivityTime = 0; 55 56 m_wMtcState = MIDI_IN_STATE_NORMAL; 57 58 m_pEG = NULL; 59 m_pMtcSync = NULL; 60 61 } // CMidiInQ::CMidiInQ() 62 63 64 CMidiInQ::~CMidiInQ() 65 { 66 67 Cleanup(); 68 69 } // CMidiInQ::~CMidiInQ() 70 71 72 //============================================================================ 73 // 74 // Init 75 // 76 //============================================================================ 77 78 ECHOSTATUS CMidiInQ::Init( CEchoGals *pEG ) 79 { 80 ECHO_DEBUGPRINTF(("CMidiInQ::Init\n")); 81 82 m_pEG = pEG; 83 84 m_dwFill = 0; 85 m_dwBufferSizeMask = MIDI_IN_Q_SIZE - 1; // MIDI_IN_Q_SIZE must be a power of 2 86 87 return ECHOSTATUS_OK; 88 89 } // Init 90 91 92 //============================================================================ 93 // 94 // Cleanup 95 // 96 //============================================================================ 97 98 void CMidiInQ::Cleanup() 99 { 100 // 101 // Free the MIDI input buffer 102 // 103 if (m_pBuffer) 104 { 105 OsFreeNonPaged( m_pBuffer ); 106 m_pBuffer = NULL; 107 } 108 109 // 110 // Free the MTC sync object if it exists 111 // 112 if (m_pMtcSync) 113 { 114 delete m_pMtcSync; 115 m_pMtcSync = NULL; 116 } 117 } 118 119 120 121 122 /**************************************************************************** 123 124 Queue management - add and remove MIDI data 125 126 ****************************************************************************/ 127 128 //============================================================================ 129 // 130 // GetMidi - get oldest MIDI input byte from the Q 131 // 132 //============================================================================ 133 134 ECHOSTATUS CMidiInQ::GetMidi 135 ( 136 ECHOGALS_MIDI_IN_CONTEXT *pContext, 137 DWORD &dwMidiByte, 138 LONGLONG &llTimestamp 139 ) 140 { 141 DWORD dwDrain; 142 143 if (NULL == m_pBuffer) 144 return ECHOSTATUS_CHANNEL_NOT_OPEN; 145 146 dwDrain = pContext->dwDrain; 147 if ( m_dwFill == dwDrain) 148 return ECHOSTATUS_NO_DATA; 149 150 dwMidiByte = m_pBuffer[ dwDrain ].dwMidi; 151 llTimestamp = m_pBuffer[ dwDrain ].llTimestamp; 152 153 //ECHO_DEBUGPRINTF( ("CMidiInQ::GetMidi 0x%lx\n", dwMidiByte) ); 154 155 dwDrain++; 156 dwDrain &= m_dwBufferSizeMask; 157 pContext->dwDrain = dwDrain; 158 159 return ECHOSTATUS_OK; 160 161 } // ECHOSTATUS CMidiInQ::GetMidi 162 163 164 //============================================================================ 165 // 166 // AddMidi - add new MIDI input byte to the Q 167 // 168 //============================================================================ 169 170 ECHOSTATUS CMidiInQ::AddMidi 171 ( 172 DWORD dwMidiByte, 173 LONGLONG llTimestamp 174 ) 175 { 176 DWORD dwFill; 177 178 //ECHO_DEBUGPRINTF( ("CMidiInQ::AddMidi 0x%lx\n", dwMidiByte) ); 179 180 dwFill = m_dwFill; 181 m_pBuffer[ dwFill ].dwMidi = dwMidiByte; 182 m_pBuffer[ dwFill ].llTimestamp = llTimestamp; 183 184 dwFill++; 185 dwFill &= m_dwBufferSizeMask; 186 m_dwFill = dwFill; 187 188 return ECHOSTATUS_OK; 189 190 } // ECHOSTATUS CMidiInQ::AddMidi 191 192 193 //============================================================================ 194 // 195 // Reset 196 // 197 //============================================================================ 198 199 void CMidiInQ::Reset(ECHOGALS_MIDI_IN_CONTEXT *pContext) 200 { 201 pContext->dwDrain = m_dwFill; 202 203 } // void CMidiInQ::Reset() 204 205 206 207 208 /**************************************************************************** 209 210 Arm and disarm 211 212 ****************************************************************************/ 213 214 215 //============================================================================ 216 // 217 // Arm - resets the Q and enables the Q to start receiving MIDI input data 218 // 219 //============================================================================ 220 221 ECHOSTATUS CMidiInQ::Arm(ECHOGALS_MIDI_IN_CONTEXT *pContext) 222 { 223 ECHOSTATUS Status; 224 225 pContext->fOpen = FALSE; 226 227 // 228 // Return if there is no Echogals pointer 229 // 230 if (NULL == m_pEG) 231 { 232 return ECHOSTATUS_CANT_OPEN; 233 } 234 235 //------------------------------------------------------------------------- 236 // 237 // Set up the MIDI input buffer 238 // 239 //------------------------------------------------------------------------- 240 241 if (NULL == m_pBuffer) 242 { 243 // 244 // Reset the buffer pointers 245 // 246 m_dwFill = 0; 247 248 // 249 // Allocate 250 // 251 Status = OsAllocateNonPaged( MIDI_IN_Q_SIZE * sizeof(MIDI_DATA), 252 (PPVOID) &m_pBuffer); 253 if (Status != ECHOSTATUS_OK) 254 { 255 ECHO_DEBUGPRINTF(("CMidiInQ::Arm - Could not allocate MIDI input buffer\n")); 256 return Status; 257 } 258 } 259 260 Reset(pContext); 261 m_dwNumClients++; 262 263 //------------------------------------------------------------------------- 264 // 265 // Enable MIDI input 266 // 267 //------------------------------------------------------------------------- 268 269 m_pEG->GetDspCommObject()->SetMidiOn( TRUE ); 270 271 pContext->fOpen = TRUE; 272 return ECHOSTATUS_OK; 273 274 } // Arm 275 276 277 //============================================================================ 278 // 279 // Disarm - surprisingly, does the opposite of Arm 280 // 281 //============================================================================ 282 283 ECHOSTATUS CMidiInQ::Disarm(ECHOGALS_MIDI_IN_CONTEXT *pContext) 284 { 285 // 286 // Did this client open the MIDI input? 287 // 288 if (FALSE == pContext->fOpen) 289 { 290 ECHO_DEBUGPRINTF(("CMidiInQ::Disarm - trying to disarm with client that isn't open\n")); 291 return ECHOSTATUS_CHANNEL_NOT_OPEN; 292 } 293 294 pContext->fOpen = FALSE; 295 296 // 297 // Last client? 298 // 299 if (0 == m_dwNumClients) 300 return ECHOSTATUS_OK; 301 302 m_dwNumClients--; 303 304 if (0 == m_dwNumClients) 305 { 306 // 307 // Tell the DSP to disable MIDI input 308 // 309 if (m_pEG) 310 m_pEG->GetDspCommObject()->SetMidiOn( FALSE ); 311 312 // 313 // Free the MIDI input buffer 314 // 315 if (m_pBuffer) 316 { 317 OsFreeNonPaged( m_pBuffer ); 318 m_pBuffer = NULL; 319 } 320 } 321 322 return ECHOSTATUS_OK; 323 324 } // Disarm 325 326 327 //============================================================================ 328 // 329 // ArmMtcSync - turns on MIDI time code sync 330 // 331 //============================================================================ 332 333 ECHOSTATUS CMidiInQ::ArmMtcSync() 334 { 335 if (NULL == m_pEG) 336 return ECHOSTATUS_DSP_DEAD; 337 338 if (NULL != m_pMtcSync) 339 return ECHOSTATUS_OK; 340 341 // 342 // Create the MTC sync object 343 // 344 m_pMtcSync = new CMtcSync( m_pEG ); 345 if (NULL == m_pMtcSync) 346 return ECHOSTATUS_NO_MEM; 347 348 // 349 // Tell the DSP to enable MIDI input 350 // 351 m_pEG->GetDspCommObject()->SetMidiOn( TRUE ); 352 353 return ECHOSTATUS_OK; 354 355 } // ArmMtcSync 356 357 358 //============================================================================ 359 // 360 // DisarmMtcSync - turn off MIDI time code sync 361 // 362 //============================================================================ 363 364 ECHOSTATUS CMidiInQ::DisarmMtcSync() 365 { 366 if (NULL == m_pMtcSync) 367 return ECHOSTATUS_OK; 368 369 if (NULL == m_pEG) 370 return ECHOSTATUS_DSP_DEAD; 371 372 // 373 // Tell the DSP to disable MIDI input 374 // 375 m_pEG->GetDspCommObject()->SetMidiOn( FALSE ); 376 377 // 378 // Free m_pMtcSync 379 // 380 CMtcSync *pTemp; // Use temp variable to avoid killing the object 381 // while the ISR is using it 382 383 pTemp = m_pMtcSync; 384 m_pMtcSync = NULL; 385 delete pTemp; 386 387 return ECHOSTATUS_OK; 388 389 } // DisarmMtcSync 390 391 392 393 394 /**************************************************************************** 395 396 Detect MIDI input activity - see if the driver has recently received 397 any MIDI input 398 399 ****************************************************************************/ 400 401 BOOL CMidiInQ::IsActive() 402 { 403 ULONGLONG ullCurTime,ullDelta; 404 405 // 406 // See if anything has happened recently 407 // 408 m_pEG->m_pOsSupport->OsGetSystemTime( &ullCurTime ); 409 ullDelta = ullCurTime - m_ullLastActivityTime; 410 411 if (ullDelta > MIDI_ACTIVITY_TIMEOUT_USEC) 412 return FALSE; 413 414 return TRUE; 415 416 } // IsActive 417 418 419 420 421 /**************************************************************************** 422 423 MIDI time code 424 425 ****************************************************************************/ 426 427 //=========================================================================== 428 // 429 // Get and set the base MTC sample rate 430 // 431 //=========================================================================== 432 433 434 ECHOSTATUS CMidiInQ::GetMtcBaseRate(DWORD *pdwBaseRate) 435 { 436 ECHOSTATUS Status; 437 438 if (m_pMtcSync) 439 { 440 *pdwBaseRate = m_pMtcSync->m_dwBaseSampleRate; 441 Status = ECHOSTATUS_OK; 442 } 443 else 444 { 445 *pdwBaseRate = 0; 446 Status = ECHOSTATUS_NO_DATA; 447 } 448 449 return Status; 450 451 } // GetMtcBaseRate 452 453 454 ECHOSTATUS CMidiInQ::SetMtcBaseRate(DWORD dwBaseRate) 455 { 456 ECHOSTATUS Status; 457 458 if (m_pMtcSync) 459 { 460 m_pMtcSync->m_dwBaseSampleRate = dwBaseRate; 461 Status = ECHOSTATUS_OK; 462 } 463 else 464 { 465 Status = ECHOSTATUS_NO_DATA; 466 } 467 468 return Status; 469 470 } // SetMtcBaseRate 471 472 473 //=========================================================================== 474 // 475 // Run the state machine for MIDI input data 476 // 477 // You need this state machine to parse the incoming 478 // MIDI data stream. Every time the DSP sees a 0xF1 byte come in, 479 // it adds the DSP sample position to the MIDI data stream. 480 // The DSP sample position is represented as a 32 bit unsigned 481 // value, with the high 16 bits first, followed by the low 16 bits. 482 // 483 // The following logic parses the incoming MIDI data. 484 // 485 //=========================================================================== 486 487 DWORD CMidiInQ::MtcProcessData( DWORD dwMidiData ) 488 { 489 DWORD dwRval; 490 491 dwRval = 0; 492 493 switch ( m_wMtcState ) 494 { 495 496 case MIDI_IN_STATE_NORMAL : 497 498 if ( dwMidiData == 0xF1L ) 499 { 500 m_wMtcState = MIDI_IN_STATE_TS_HIGH; 501 } 502 break; 503 504 505 case MIDI_IN_STATE_TS_HIGH : 506 507 if (m_pMtcSync) 508 m_pMtcSync->StoreTimestampHigh( dwMidiData ); 509 510 m_wMtcState = MIDI_IN_STATE_TS_LOW; 511 dwRval = MIDI_IN_SKIP_DATA; 512 break; 513 514 515 case MIDI_IN_STATE_TS_LOW : 516 517 if (m_pMtcSync) 518 m_pMtcSync->StoreTimestampLow( dwMidiData ); 519 520 m_wMtcState = MIDI_IN_STATE_F1_DATA; 521 dwRval = MIDI_IN_SKIP_DATA; 522 break; 523 524 525 case MIDI_IN_STATE_F1_DATA : 526 527 if (m_pMtcSync) 528 m_pMtcSync->StoreMtcData( dwMidiData ); 529 530 m_wMtcState = MIDI_IN_STATE_NORMAL; 531 break; 532 533 } 534 535 return dwRval; 536 537 } // DWORD CMidiInQ::MtcProcessData 538 539 540 //=========================================================================== 541 // 542 // ServiceMtcSync 543 // 544 //=========================================================================== 545 546 void CMidiInQ::ServiceMtcSync() 547 { 548 if (m_pMtcSync) 549 m_pMtcSync->Sync(); 550 551 } // ServiceMtcSync 552 553 554 555 556 /**************************************************************************** 557 558 Interrupt service 559 560 ****************************************************************************/ 561 562 ECHOSTATUS CMidiInQ::ServiceIrq() 563 { 564 DWORD dwMidiCount; 565 WORD wIndex; 566 ECHOSTATUS Status; 567 CDspCommObject *pDCO; 568 LONGLONG llTimestamp; 569 570 // 571 // Store the time for the activity detector 572 // 573 m_pEG->m_pOsSupport->OsGetSystemTime( &m_ullLastActivityTime ); 574 575 // 576 // Get the MIDI count 577 // 578 pDCO = m_pEG->GetDspCommObject(); 579 pDCO->ReadMidi( 0, dwMidiCount ); // The count is at index 0 580 581 #ifdef ECHO_DEBUG 582 //ECHO_DEBUGPRINTF( ("\tMIDI interrupt (%ld MIDI bytes)\n", dwMidiCount) ); 583 if ( dwMidiCount == 0 ) 584 { 585 ECHO_DEBUGBREAK(); 586 } 587 #endif 588 589 // 590 // Get the timestamp 591 // 592 llTimestamp = m_pEG->m_pOsSupport->GetMidiInTimestamp(); 593 594 // 595 // Get the MIDI data from the comm page 596 // 597 wIndex = 1; // First MIDI byte is at index 1 598 while ( dwMidiCount-- > 0 ) 599 { 600 DWORD dwMidiByte; 601 602 // 603 // Get the MIDI byte 604 // 605 Status = pDCO->ReadMidi( wIndex++, dwMidiByte ); 606 if ( ECHOSTATUS_OK != Status ) 607 { 608 ECHO_DEBUGPRINTF(("Failed on ReadMidi\n")); 609 ECHO_DEBUGBREAK(); // should never happen... 610 break; 611 } 612 613 // 614 // Parse the incoming MIDI stream. The incoming MIDI data consists 615 // of MIDI bytes and timestamps for the MIDI time code 0xF1 bytes. 616 // MtcProcessData is a little state machine that parses the stream. 617 // 618 // If you get MIDI_IN_SKIP_DATA back, then this is a timestamp byte, 619 // not a MIDI byte, so don't store it in the MIDI input buffer. 620 // 621 if ( MIDI_IN_SKIP_DATA == MtcProcessData( dwMidiByte ) ) 622 continue; 623 624 // 625 // Only store the MIDI data if there is at least one 626 // client 627 // 628 if (0 != m_dwNumClients) 629 { 630 // 631 // Stash the MIDI data and check for overflow 632 // 633 if ( ECHOSTATUS_BUFFER_OVERFLOW == AddMidi( dwMidiByte, llTimestamp ) ) 634 { 635 break; 636 } 637 638 } 639 } // while there is more MIDI data to read 640 641 return ECHOSTATUS_OK; 642 643 } // ServiceIrq 644 645 646 // *** CMidiInQ.cpp *** 647