1 // **************************************************************************** 2 // 3 // CEchoGals_transport.cpp 4 // 5 // Audio transport methods for the CEchoGals driver class. 6 // Set editor tabs to 3 for your viewing pleasure. 7 // 8 // Copyright Echo Digital Audio Corporation (c) 1998 - 2002 9 // All rights reserved 10 // www.echoaudio.com 11 // 12 // Permission is hereby granted, free of charge, to any person obtaining a 13 // copy of this software and associated documentation files (the 14 // "Software"), to deal with the Software without restriction, including 15 // without limitation the rights to use, copy, modify, merge, publish, 16 // distribute, sublicense, and/or sell copies of the Software, and to 17 // permit persons to whom the Software is furnished to do so, subject to 18 // the following conditions: 19 // 20 // - Redistributions of source code must retain the above copyright 21 // notice, this list of conditions and the following disclaimers. 22 // 23 // - Redistributions in binary form must reproduce the above copyright 24 // notice, this list of conditions and the following disclaimers in the 25 // documentation and/or other materials provided with the distribution. 26 // 27 // - Neither the name of Echo Digital Audio, nor the names of its 28 // contributors may be used to endorse or promote products derived from 29 // this Software without specific prior written permission. 30 // 31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 32 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 33 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 34 // IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR 35 // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 36 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 37 // SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. 38 // 39 // **************************************************************************** 40 41 #include "CEchoGals.h" 42 43 #pragma optimize("",off) 44 45 46 /****************************************************************************** 47 48 Functions for opening and closing pipes 49 50 ******************************************************************************/ 51 52 //=========================================================================== 53 // 54 // OpenAudio is used to reserve audio pipes for your exclusive use. The call 55 // will fail if someone else has already opened the pipes. Calling OpenAudio 56 // is the first step if you want to play or record. 57 // 58 //=========================================================================== 59 60 ECHOSTATUS CEchoGals::OpenAudio 61 ( 62 PECHOGALS_OPENAUDIOPARAMETERS pOpenParameters, // Info on pipe 63 PWORD pwPipeIndex // Pipe index ptr 64 ) 65 { 66 CChannelMask cmMask; 67 WORD wPipeMax, wPipe, wPipeIndex, i, wWidth; 68 69 ECHO_DEBUGPRINTF( ("CEchoGals::OpenAudio: %s %u " 70 "PipeWidth %d " 71 "Cyclic %u \n", 72 ( pOpenParameters->Pipe.bIsInput ) ? "Input" : "Output", 73 pOpenParameters->Pipe.nPipe, 74 pOpenParameters->Pipe.wInterleave, 75 pOpenParameters->bIsCyclic) ); 76 77 *pwPipeIndex = (WORD) -1; // So it's never undefined 78 79 // 80 // Make sure the hardware is OK 81 // 82 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 83 { 84 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") ); 85 return ECHOSTATUS_DSP_DEAD; 86 } 87 88 // 89 // Make sure the DSP & ASIC are up and running 90 // 91 ECHOSTATUS Status = GetDspCommObject()->LoadFirmware(); 92 if ( ECHOSTATUS_OK != Status ) 93 return Status; 94 95 // 96 // Validate the pipe number 97 // 98 wPipe = pOpenParameters->Pipe.nPipe; 99 wWidth = pOpenParameters->Pipe.wInterleave; 100 101 if ( pOpenParameters->Pipe.bIsInput ) 102 { 103 wPipeIndex = wPipe + GetNumPipesOut(); 104 wPipeMax = GetNumPipesIn(); 105 } 106 else 107 { 108 wPipeIndex = wPipe; 109 wPipeMax = GetNumPipesOut(); 110 } 111 112 if ( ( wPipe + wWidth ) > wPipeMax ) 113 { 114 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_INVALID_CHANNEL\n") ); 115 return ECHOSTATUS_INVALID_CHANNEL; 116 } 117 118 // 119 // If the width is more than two, make sure that this card 120 // can handle super interleave 121 // 122 if ( (0 == (m_wFlags & ECHOGALS_ROFLAG_SUPER_INTERLEAVE_OK)) && 123 (wWidth > 2) 124 ) 125 { 126 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_NO_SUPER_INTERLEAVE\n") ); 127 return ECHOSTATUS_NO_SUPER_INTERLEAVE; 128 } 129 130 // 131 // See if the specified pipes are already open 132 // 133 for ( i = 0; i < pOpenParameters->Pipe.wInterleave; i++ ) 134 { 135 cmMask.SetIndexInMask( wPipeIndex + i ); 136 } 137 138 if ( m_cmAudioOpen.Test( &cmMask ) ) 139 { 140 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_CHANNEL_ALREADY_OPEN\n") ); 141 return( ECHOSTATUS_CHANNEL_ALREADY_OPEN ); 142 } 143 144 // 145 // Make a daffy duck 146 // 147 Status = MakeDaffyDuck(wPipeIndex); 148 if (ECHOSTATUS_OK != Status) 149 return Status; 150 151 // 152 // Reset the 64-bit DMA position 153 // 154 ResetDmaPos(wPipeIndex); 155 GetDspCommObject()->ResetPipePosition(wPipeIndex); 156 157 // 158 // Prep stuff 159 // 160 m_cmAudioOpen += cmMask; 161 if ( pOpenParameters->bIsCyclic ) 162 m_cmAudioCyclic += cmMask; 163 m_Pipes[ wPipeIndex ] = pOpenParameters->Pipe; 164 *pwPipeIndex = wPipeIndex; 165 m_ProcessId[ wPipeIndex ] = pOpenParameters->ProcessId; 166 Reset( wPipeIndex ); 167 168 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_OK\n") ); 169 return ECHOSTATUS_OK; 170 171 } // ECHOSTATUS CEchoGals::OpenAudio 172 173 174 //=========================================================================== 175 // 176 // CloseAudio is, naturally, the inverse of OpenAudio. 177 // 178 //=========================================================================== 179 180 ECHOSTATUS CEchoGals::CloseAudio 181 ( 182 PECHOGALS_CLOSEAUDIOPARAMETERS pCloseParameters 183 ) 184 { 185 CChannelMask cmMask; 186 ECHOSTATUS Status; 187 WORD i; 188 WORD wPipeIndex; 189 190 ECHO_DEBUGPRINTF( ("CEchoGals::CloseAudio: Pipe %u ", 191 pCloseParameters->wPipeIndex) ); 192 193 Status = VerifyAudioOpen( pCloseParameters->wPipeIndex ); 194 if ( ECHOSTATUS_OK != Status ) 195 return Status; 196 197 for ( i = 0; 198 i < m_Pipes[ pCloseParameters->wPipeIndex ].wInterleave; 199 i++ ) 200 { 201 cmMask.SetIndexInMask( pCloseParameters->wPipeIndex + i ); 202 } 203 Reset( pCloseParameters->wPipeIndex ); 204 205 KillDaffyDuck( pCloseParameters->wPipeIndex ); 206 207 m_cmAudioOpen -= cmMask; 208 m_cmAudioCyclic -= cmMask; 209 210 wPipeIndex = pCloseParameters->wPipeIndex; 211 m_ProcessId[ wPipeIndex ] = NULL; 212 m_Pipes[ wPipeIndex ].wInterleave = 0; 213 214 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_OK\n") ); 215 return ECHOSTATUS_OK; 216 217 } // ECHOSTATUS CEchoGals::CloseAudio 218 219 220 //=========================================================================== 221 // 222 // VerifyAudioOpen is a utility function; it tells you if 223 // a pipe is open or not. 224 // 225 //=========================================================================== 226 227 ECHOSTATUS CEchoGals::VerifyAudioOpen 228 ( 229 WORD wPipeIndex 230 ) 231 { 232 CChannelMask cmMask; 233 234 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 235 { 236 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") ); 237 return ECHOSTATUS_DSP_DEAD; 238 } 239 240 cmMask.SetIndexInMask( wPipeIndex ); 241 if ( !( m_cmAudioOpen.Test( &cmMask ) ) ) 242 { 243 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_CHANNEL_NOT_OPEN\n") ); 244 return ECHOSTATUS_CHANNEL_NOT_OPEN; 245 } 246 247 return ECHOSTATUS_OK; 248 249 } // ECHOSTATUS CEchoGals::VerifyAudioOpen 250 251 252 //=========================================================================== 253 // 254 // GetActivePipes tells you which pipes are currently active; that is, which 255 // pipes are currently playing or recording. 256 // 257 //=========================================================================== 258 259 ECHOSTATUS CEchoGals::GetActivePipes 260 ( 261 PCChannelMask pChannelMask 262 ) 263 { 264 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 265 return ECHOSTATUS_DSP_DEAD; 266 267 GetDspCommObject()->GetActivePipes( pChannelMask ); 268 return ECHOSTATUS_OK; 269 } // void CEchoGals::GetActivePipes() 270 271 272 //=========================================================================== 273 // 274 // Just like GetActivePipes, but this one tells you which pipes are currently 275 // open. 276 // 277 //=========================================================================== 278 279 ECHOSTATUS CEchoGals::GetOpenPipes 280 ( 281 PCChannelMask pChannelMask 282 ) 283 { 284 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 285 return ECHOSTATUS_DSP_DEAD; 286 287 *pChannelMask = m_cmAudioOpen; 288 return ECHOSTATUS_OK; 289 290 } // void CEchoGals::GetOpenPipes() 291 292 293 294 295 /****************************************************************************** 296 297 Functions for setting audio formats and the sample rate 298 299 ******************************************************************************/ 300 301 //=========================================================================== 302 // 303 // Validate an audio format. 304 // 305 // For comments on audio formats, refer to the definition of 306 // ECHOGALS_AUDIOFORMAT. 307 // 308 //=========================================================================== 309 310 ECHOSTATUS CEchoGals::QueryAudioFormat 311 ( 312 WORD wPipeIndex, 313 PECHOGALS_AUDIOFORMAT pAudioFormat 314 ) 315 { 316 ECHOSTATUS Status = ECHOSTATUS_OK; 317 318 ECHO_DEBUGPRINTF( ("CEchoGals::QueryAudioFormat:\n") ); 319 320 // 321 // If this pipe is open, make sure that this audio format 322 // does not exceed the stored pipe width 323 // 324 WORD wInterleave = pAudioFormat->wDataInterleave; 325 WORD wStoredPipeWidth = m_Pipes[ wPipeIndex ].wInterleave; 326 327 if (0 != wStoredPipeWidth) 328 { 329 if (wInterleave > wStoredPipeWidth) 330 { 331 ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - pipe was opened " 332 "with a width of %d; interleave of %d invalid.\n", 333 wStoredPipeWidth, 334 pAudioFormat->wDataInterleave)); 335 return ECHOSTATUS_BAD_FORMAT; 336 } 337 } 338 339 // 340 // Check for super interleave (i.e. interleave > 2) 341 // 342 if (wInterleave > 2) 343 { 344 // 345 // Make sure the card is capable of super interleave 346 // 347 if (0 == (m_wFlags & ECHOGALS_ROFLAG_SUPER_INTERLEAVE_OK)) 348 return ECHOSTATUS_NO_SUPER_INTERLEAVE; 349 350 // 351 // Allow super interleave for 32 bit little endian data; 352 // the interleave factor must be even. 353 // 354 if ( (32 != pAudioFormat->wBitsPerSample) || 355 (0 != pAudioFormat->byDataAreBigEndian) || 356 (0 != (wInterleave & 1) ) 357 ) 358 return ECHOSTATUS_BAD_FORMAT; 359 360 // 361 // Make sure that this interleave factor on this pipe 362 // does not exceed the number of pipes for the card 363 // 364 WORD wMaxPipe; 365 366 if (wPipeIndex >= GetNumPipesOut()) 367 { 368 wMaxPipe = GetNumPipesIn(); 369 wPipeIndex -= GetNumPipesOut(); 370 } 371 else 372 { 373 wMaxPipe = GetNumPipesOut(); 374 } 375 376 if ( (wPipeIndex + wInterleave) > wMaxPipe) 377 return ECHOSTATUS_BAD_FORMAT; 378 379 return ECHOSTATUS_OK; 380 } 381 382 // 383 // Check the interleave 384 // 385 if ( (1 != pAudioFormat->wDataInterleave) && 386 (2 != pAudioFormat->wDataInterleave) ) 387 388 { 389 ECHO_DEBUGPRINTF( ("CEchoGals::QueryAudioFormat - interleave %d not allowed\n", 390 pAudioFormat->wDataInterleave)); 391 return ECHOSTATUS_BAD_FORMAT; 392 } 393 394 // 395 // If the big endian flag is set, the data must be mono or stereo interleave, 396 // 32 bits wide, left justified data. Only the upper 24 bits are used. 397 // 398 if (pAudioFormat->byDataAreBigEndian) 399 { 400 // 401 // Must have 32 bits per sample 402 // 403 if (pAudioFormat->wBitsPerSample != 32) 404 { 405 ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - Only 32 bits per" 406 " sample supported for big-endian data\n")); 407 return ECHOSTATUS_BAD_FORMAT; 408 } 409 410 // 411 // Mono or stereo only 412 // 413 switch (pAudioFormat->wDataInterleave) 414 { 415 416 #ifdef STEREO_BIG_ENDIAN32_SUPPORT 417 418 case 1 : 419 case 2 : 420 break; 421 #else 422 423 case 1 : 424 break; 425 426 #endif 427 428 default : 429 ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - Interleave of %d" 430 " not allowed for big-endian data\n", 431 pAudioFormat->wDataInterleave)); 432 return ECHOSTATUS_BAD_FORMAT; 433 } 434 435 return ECHOSTATUS_OK; 436 } 437 438 // 439 // Check bits per sample 440 // 441 switch ( pAudioFormat->wBitsPerSample ) 442 { 443 case 8 : 444 case 16 : 445 case 32 : 446 break; 447 448 default : 449 ECHO_DEBUGPRINTF( 450 ("CEchoGals::QueryAudioFormat: No valid format " 451 "specified, bits per sample %d\n", 452 pAudioFormat->wBitsPerSample) ); 453 Status = ECHOSTATUS_BAD_FORMAT; 454 break; 455 } 456 457 return Status; 458 459 } // ECHOSTATUS CEchoGals::QueryAudioFormat 460 461 462 //=========================================================================== 463 // 464 // SetAudioFormat sets the format of the audio data in host memory 465 // for this pipe. 466 // 467 //=========================================================================== 468 469 ECHOSTATUS CEchoGals::SetAudioFormat 470 ( 471 WORD wPipeIndex, 472 PECHOGALS_AUDIOFORMAT pAudioFormat 473 ) 474 { 475 ECHOSTATUS Status; 476 477 ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: " 478 "for pipe %d\n", 479 wPipeIndex) ); 480 481 // 482 // Make sure this pipe is open 483 // 484 Status = VerifyAudioOpen( wPipeIndex ); 485 if ( ECHOSTATUS_OK != Status ) 486 return Status; 487 488 // 489 // Check the format 490 // 491 Status = QueryAudioFormat( wPipeIndex, pAudioFormat ); 492 if ( ECHOSTATUS_OK != Status ) 493 return Status; 494 495 // 496 // Set the format 497 // 498 BOOL fDitherDigitalInputs; 499 500 fDitherDigitalInputs = 0 != (m_wFlags & ECHOGALS_FLAG_SPDIF_NODITHER); 501 Status = 502 GetDspCommObject()->SetAudioFormat( wPipeIndex, 503 pAudioFormat, 504 fDitherDigitalInputs ); 505 return Status; 506 507 } // ECHOSTATUS CEchoGals::SetAudioFormat - single pipe 508 509 510 //=========================================================================== 511 // 512 // This call lets you set the audio format for several pipes at once. 513 // 514 //=========================================================================== 515 516 ECHOSTATUS CEchoGals::SetAudioFormat 517 ( 518 PCChannelMask pChannelMask, 519 PECHOGALS_AUDIOFORMAT pAudioFormat 520 ) 521 { 522 WORD wPipeIndex = 0xffff; 523 ECHOSTATUS Status; 524 BOOL fDitherDigitalInputs; 525 526 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 527 { 528 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") ); 529 return ECHOSTATUS_DSP_DEAD; 530 } 531 532 fDitherDigitalInputs = 0 != (m_wFlags & ECHOGALS_FLAG_SPDIF_NODITHER); 533 for ( ; ; ) 534 { 535 wPipeIndex = pChannelMask->GetIndexFromMask( ++wPipeIndex ); 536 if ( (WORD) ECHO_INVALID_CHANNEL == wPipeIndex ) 537 break; // We be done! 538 539 // 540 // See if this pipe is open 541 // 542 if ( !( m_cmAudioOpen.TestIndexInMask( wPipeIndex ) ) ) 543 { 544 ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: " 545 "for pipe %d failed, pipe not open\n", 546 wPipeIndex) ); 547 return ECHOSTATUS_CHANNEL_NOT_OPEN; 548 } 549 550 // 551 // See if the format is OK 552 // 553 ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: " 554 "for pipe %d\n", 555 wPipeIndex) ); 556 Status = QueryAudioFormat( wPipeIndex, pAudioFormat ); 557 if ( ECHOSTATUS_OK != Status ) 558 return Status; 559 560 // 561 // Set the format for this pipe 562 // 563 Status = 564 GetDspCommObject()->SetAudioFormat( wPipeIndex, 565 pAudioFormat, 566 fDitherDigitalInputs ); 567 if ( ECHOSTATUS_OK != Status ) 568 return Status; 569 570 m_wBytesPerSample[ wPipeIndex ] = pAudioFormat->wBitsPerSample / 8; 571 m_wBytesPerSample[ wPipeIndex ] *= pAudioFormat->wDataInterleave; 572 } 573 574 return ECHOSTATUS_OK; 575 576 } // ECHOSTATUS CEchoGals::SetAudioFormat - multiple pipes 577 578 579 //=========================================================================== 580 // 581 // GetAudioFormat returns the current audio format for a pipe. 582 // 583 //=========================================================================== 584 585 ECHOSTATUS CEchoGals::GetAudioFormat 586 ( 587 WORD wPipeIndex, 588 PECHOGALS_AUDIOFORMAT pAudioFormat 589 ) 590 { 591 ECHO_DEBUGPRINTF( ("CEchoGals::GetAudioFormat: " 592 "for pipe %d\n", 593 wPipeIndex) ); 594 595 GetDspCommObject()->GetAudioFormat( wPipeIndex, pAudioFormat ); 596 597 return ECHOSTATUS_OK; 598 599 } // ECHOSTATUS CEchoGals::GetAudioFormat 600 601 602 //=========================================================================== 603 // 604 // This function does exactly what you think it does. 605 // 606 // Note that if the card is not set to internal clock (that is, the hardware 607 // is synced to word clock or some such), this call has no effect. 608 // 609 // Note that all of the inputs and outputs on a single card share the same 610 // clock. 611 // 612 //=========================================================================== 613 614 ECHOSTATUS CEchoGals::SetAudioSampleRate 615 ( 616 DWORD dwSampleRate 617 ) 618 { 619 ECHOSTATUS Status; 620 621 ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioSampleRate: " 622 "to %ld Hz\n", 623 dwSampleRate) ); 624 625 // 626 // Check to see if the sample rate is locked 627 // 628 if ( 0 != (m_wFlags & ECHOGALS_FLAG_SAMPLE_RATE_LOCKED)) 629 { 630 return ECHOSTATUS_OK; 631 } 632 else 633 { 634 Status = QueryAudioSampleRate( dwSampleRate ); 635 if ( ECHOSTATUS_OK != Status ) 636 return Status; 637 638 if ( dwSampleRate == GetDspCommObject()->SetSampleRate( dwSampleRate ) ) 639 { 640 m_dwSampleRate = dwSampleRate; 641 return ECHOSTATUS_OK; 642 } 643 } 644 return ECHOSTATUS_BAD_FORMAT; 645 646 } // ECHOSTATUS CEchoGals::SetAudioSampleRate 647 648 649 //=========================================================================== 650 // 651 // GetAudioSampleRate - retrieves the current sample rate for the hardware 652 // 653 //=========================================================================== 654 655 ECHOSTATUS CEchoGals::GetAudioSampleRate 656 ( 657 PDWORD pdwSampleRate 658 ) 659 { 660 ECHO_DEBUGPRINTF( ("CEchoGals::GetAudioSampleRate\n")); 661 662 *pdwSampleRate = m_dwSampleRate; 663 664 return ECHOSTATUS_OK; 665 666 } // ECHOSTATUS CEchoGals::GetAudioSampleRate 667 668 669 670 671 /****************************************************************************** 672 673 Functions related to the scatter-gather list 674 675 ******************************************************************************/ 676 677 //=========================================================================== 678 // 679 // This protected method is used to create a CDaffyDuck object to 680 // manage a scatter-gather list for a newly opened pipe. 681 // 682 //=========================================================================== 683 684 ECHOSTATUS CEchoGals::MakeDaffyDuck(WORD wPipeIndex) 685 { 686 ECHOSTATUS Status = ECHOSTATUS_OK; 687 688 //--------------------------------------------------------- 689 // 690 // Allocate the main daffy duck for this pipe 691 // 692 //--------------------------------------------------------- 693 694 m_DaffyDucks[wPipeIndex] = new CDaffyDuck(m_pOsSupport, 695 GetDspCommObject(), 696 wPipeIndex ); 697 if (NULL == m_DaffyDucks[wPipeIndex]) 698 { 699 return ECHOSTATUS_NO_MEM; 700 } 701 702 // 703 // Check the daffy duck 704 // 705 Status = m_DaffyDucks[wPipeIndex]->InitCheck(); 706 if (ECHOSTATUS_OK != Status) 707 { 708 return Status; 709 } 710 711 // 712 // Put the daffy duck in the comm page 713 // 714 GetDspCommObject()-> 715 SetAudioDuckListPhys(wPipeIndex, 716 m_DaffyDucks[wPipeIndex]->GetPhysStartAddr() ); 717 718 ECHO_DEBUGPRINTF(("CEchoGals::MakeDaffyDuck - 0x%lx for pipe index %d\n", 719 (DWORD) m_DaffyDucks[wPipeIndex],wPipeIndex)); 720 721 return Status; 722 723 } // MakeDaffyDuck 724 725 726 //=========================================================================== 727 // 728 // Daffy duck cleanup 729 // 730 //=========================================================================== 731 732 void CEchoGals::KillDaffyDuck(WORD wPipeIndex) 733 { 734 if (NULL != m_DaffyDucks[wPipeIndex]) 735 { 736 delete m_DaffyDucks[wPipeIndex]; 737 m_DaffyDucks[wPipeIndex] = NULL; 738 } 739 740 } // KillDaffyDuck 741 742 743 //=========================================================================== 744 // 745 // This method returns a pointer to the daffy duck for a pipe; the caller 746 // can then have direct access to the daffy duck object. 747 // 748 //=========================================================================== 749 750 CDaffyDuck *CEchoGals::GetDaffyDuck(WORD wPipeIndex) 751 { 752 ECHO_DEBUGPRINTF(("CEchoGals::GetDaffyDuck for pipe index %d\n",wPipeIndex)); 753 754 if (wPipeIndex >= GetNumPipes()) 755 return NULL; 756 757 return m_DaffyDucks[wPipeIndex]; 758 } 759 760 761 762 /****************************************************************************** 763 764 Functions for starting and stopping transport 765 766 ******************************************************************************/ 767 768 //=========================================================================== 769 // 770 // Start transport for a single pipe 771 // 772 //=========================================================================== 773 774 ECHOSTATUS CEchoGals::Start 775 ( 776 WORD wPipeIndex 777 ) 778 { 779 CChannelMask cmMask; 780 781 cmMask.SetIndexInMask( wPipeIndex ); 782 return Start( &cmMask ); 783 784 } // ECHOSTATUS CEchoGals::Start 785 786 787 //=========================================================================== 788 // 789 // Start transport for a group of pipes 790 // 791 // This function includes logic to sync-start several pipes at once, 792 // according to the process ID specified when the pipe was opened. This is 793 // included to work around a limitation of the Windows wave API so that 794 // programs could use multiple inputs and outputs and have them start at the 795 // same time. 796 // 797 // If you don't want to use this feature, call CEchoGals::ClearFlags 798 // with ECHOGALS_FLAG_SYNCH_WAVE and the pipes will start immediately. 799 // 800 //=========================================================================== 801 802 ECHOSTATUS CEchoGals::Start 803 ( 804 PCChannelMask pChannelMask 805 ) 806 { 807 WORD wPipe; 808 CChannelMask cmStart; 809 PVOID ProcessId = NULL; 810 811 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 812 return ECHOSTATUS_DSP_DEAD; 813 814 // 815 // See if we are dealing with synchronized wave pipes. If the sync 816 // flag is set, get the process ID for this pipe to compare with 817 // other pipes. 818 // 819 if ( GetFlags() & ECHOGALS_FLAG_SYNCH_WAVE ) 820 { 821 wPipe = pChannelMask->GetIndexFromMask( 0 ); 822 ProcessId = m_ProcessId[ wPipe ]; 823 } 824 825 //-------------------------------------------------------- 826 // Process each pipe in the mask 827 //-------------------------------------------------------- 828 829 for (wPipe = 0; wPipe < GetNumPipes(); wPipe++) 830 { 831 PDWORD pdwDspCommPositions; 832 833 // 834 // Skip this pipe if it's not in the mask 835 // 836 if (!pChannelMask->TestIndexInMask(wPipe)) 837 continue; 838 839 // 840 // This pipe must have a CDaffyDuck object 841 // 842 if (NULL == m_DaffyDucks[ wPipe ]) 843 { 844 ECHO_DEBUGPRINTF(("CDaffyDuck::Start - trying to start pipe index %d " 845 "but there is no CDaffyDuck!\n",wPipe)); 846 return ECHOSTATUS_CHANNEL_NOT_OPEN; 847 } 848 849 // 850 // If this pipe was opened in cyclic mode, make sure that the corresponding 851 // CDaffyDuck has been wrapped 852 // 853 if ( (0 != m_cmAudioCyclic.TestIndexInMask( wPipe ) ) && 854 (FALSE == m_DaffyDucks[wPipe]->Wrapped()) 855 ) 856 { 857 ECHO_DEBUGPRINTF(("CEchoGals::Start called for pipe index %d - " 858 "pipe was opened in cyclic mode, but the duck " 859 "has not been wrapped\n",wPipe)); 860 return ECHOSTATUS_DUCK_NOT_WRAPPED; 861 } 862 863 864 // 865 // Do different things to this pipe depending on the 866 // state 867 // 868 switch (m_byPipeState[wPipe]) 869 { 870 case PIPE_STATE_RESET : 871 // 872 // Clean up the DMA position stuff 873 // 874 pdwDspCommPositions = GetDspCommObject()->GetAudioPositionPtr(); 875 pdwDspCommPositions[ wPipe ] = 0; 876 877 // 878 // If this pipe isn't synced or is in a reset state, 879 // start it up 880 // 881 if (NULL == ProcessId) 882 { 883 m_byPipeState[ wPipe ] = PIPE_STATE_STARTED; 884 cmStart.SetIndexInMask( wPipe ); 885 } 886 else 887 { 888 // 889 // This pipe is synced; upgrade to PENDING 890 // 891 m_byPipeState[ wPipe ] = PIPE_STATE_PENDING; 892 } 893 break; 894 895 896 case PIPE_STATE_STOPPED : 897 898 if (NULL == ProcessId) 899 { 900 // 901 // Non-synced pipe; start 'er up! 902 // 903 m_byPipeState[ wPipe ] = PIPE_STATE_STARTED; 904 cmStart.SetIndexInMask( wPipe ); 905 } 906 else 907 { 908 // 909 // Synced pipe; if this pipe is in STOP mode, 910 // upgrade it to PENDING status 911 // 912 m_byPipeState[ wPipe ] = PIPE_STATE_PENDING; 913 } 914 break; 915 916 917 case PIPE_STATE_PENDING : 918 case PIPE_STATE_STARTED : 919 break; 920 } 921 } 922 923 //----------------------------------------------------------------- 924 // Start the pipes 925 //----------------------------------------------------------------- 926 927 // 928 // Don't go if all the synced pipes are not yet pending 929 // 930 BOOL fAllReady = TRUE; 931 for ( wPipe = 0; wPipe < GetNumPipes(); wPipe++ ) 932 { 933 if ( ( ProcessId == m_ProcessId[ wPipe ] ) && 934 ( PIPE_STATE_STOPPED == m_byPipeState[wPipe])) 935 { 936 ECHO_DEBUGPRINTF(("CEchoGals::Start - can't start; pipe %d " 937 "still set to state %d\n", 938 wPipe, 939 m_byPipeState[wPipe])); 940 fAllReady = FALSE; 941 } 942 } 943 944 // 945 // All synced pipes are pending; time to go! 946 // 947 if (fAllReady) 948 { 949 for (wPipe = 0; wPipe < GetNumPipes(); wPipe++) 950 { 951 if ( (ProcessId == m_ProcessId[ wPipe ]) && 952 (PIPE_STATE_PENDING == m_byPipeState[ wPipe ])) 953 { 954 m_byPipeState[wPipe] = PIPE_STATE_STARTED; 955 cmStart.SetIndexInMask( wPipe ); 956 ECHO_DEBUGPRINTF(("CEchoGals::Start - setting pipe %d to start\n", 957 wPipe)); 958 } 959 } 960 } 961 962 //----------------------------------------------------------------- 963 // Send the result of all this to the DSP 964 //----------------------------------------------------------------- 965 966 if ( cmStart.IsEmpty() ) 967 return ECHOSTATUS_OK; 968 969 return GetDspCommObject()->StartTransport( &cmStart, &m_cmAudioCyclic ); 970 971 } // ECHOSTATUS CEchoGals::Start 972 973 974 975 //=========================================================================== 976 // 977 // Stop a single pipe 978 // 979 //=========================================================================== 980 981 ECHOSTATUS CEchoGals::Stop 982 ( 983 WORD wPipeIndex 984 ) 985 { 986 CChannelMask cmMask; 987 988 cmMask.SetIndexInMask( wPipeIndex ); 989 return( Stop( &cmMask ) ); 990 991 } // ECHOSTATUS CEchoGals::Stop 992 993 994 //=========================================================================== 995 // 996 // Stop several pipes simultaneously 997 // 998 //=========================================================================== 999 1000 ECHOSTATUS CEchoGals::Stop 1001 ( 1002 PCChannelMask pChannelMask 1003 ) 1004 { 1005 int i; 1006 ECHOSTATUS Status; 1007 1008 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 1009 return ECHOSTATUS_DSP_DEAD; 1010 1011 Status = GetDspCommObject()->StopTransport( pChannelMask ); 1012 if ( ECHOSTATUS_OK != Status ) 1013 return Status; 1014 1015 for ( i = 0; i < GetNumPipes(); i++ ) 1016 { 1017 // 1018 // Skip channel if not in mask 1019 // 1020 if ( !pChannelMask->TestIndexInMask( (WORD) i ) ) 1021 continue; 1022 1023 1024 // 1025 // Don't bother if it's stopped already 1026 // 1027 if ( PIPE_STATE_STOPPED == m_byPipeState[ i ] ) 1028 continue; 1029 1030 // 1031 // Muck with the DMA position 1032 // 1033 UpdateDmaPos( (WORD) i ); 1034 1035 m_byPipeState[ i ] = PIPE_STATE_STOPPED; 1036 } 1037 1038 return Status; 1039 1040 } // ECHOSTATUS CEchoGals::Stop 1041 1042 1043 //=========================================================================== 1044 // 1045 // Reset transport for a single pipe 1046 // 1047 //=========================================================================== 1048 1049 ECHOSTATUS CEchoGals::Reset 1050 ( 1051 WORD wPipeIndex 1052 ) 1053 { 1054 CChannelMask cmMask; 1055 1056 cmMask.SetIndexInMask( wPipeIndex ); 1057 return Reset( &cmMask ); 1058 1059 } // ECHOSTATUS CEchoGals::Reset 1060 1061 1062 //=========================================================================== 1063 // 1064 // Reset transport for a group of pipes simultaneously 1065 // 1066 //=========================================================================== 1067 1068 ECHOSTATUS CEchoGals::Reset 1069 ( 1070 PCChannelMask pChannelMask 1071 ) 1072 { 1073 WORD i; 1074 ECHOSTATUS Status; 1075 1076 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 1077 return ECHOSTATUS_DSP_DEAD; 1078 1079 Status = GetDspCommObject()->ResetTransport( pChannelMask ); 1080 if ( ECHOSTATUS_OK != Status ) 1081 return Status; 1082 1083 PDWORD pdwDspCommPositions = 1084 GetDspCommObject()->GetAudioPositionPtr(); 1085 1086 for ( i = 0; i < GetNumPipes(); i++ ) 1087 { 1088 // 1089 // Skip channel if not in mask 1090 // 1091 if ( !pChannelMask->TestIndexInMask( (WORD) i ) ) 1092 continue; 1093 1094 if ( PIPE_STATE_RESET == m_byPipeState[ i ] ) 1095 continue; 1096 1097 // 1098 // Muck with the DMA position 1099 // 1100 UpdateDmaPos( i ); 1101 m_dwLastDspPos[ i ] = 0; 1102 GetDspCommObject()->ResetPipePosition(i); 1103 1104 pdwDspCommPositions[ i ] = 0; 1105 1106 m_byPipeState[ i ] = PIPE_STATE_RESET; 1107 } 1108 1109 return Status; 1110 1111 } // ECHOSTATUS CEchoGals::Reset 1112 1113 1114 1115 1116 /****************************************************************************** 1117 1118 Functions for handling the current DMA position for pipes; the DMA position 1119 is the number of bytes transported by a pipe. 1120 1121 ******************************************************************************/ 1122 1123 //=========================================================================== 1124 // 1125 // The DSP sends back a 32 bit DMA counter for each pipe of the number of bytes 1126 // transported; this count is written by the DSP to the comm page without 1127 // the driver doing anything. 1128 // 1129 // The driver then maintains a 64 bit counter based off of the DSP's counter. 1130 // 1131 // Call UpdateDmaPos to cause the driver to update the internal 64 bit DMA 1132 // counter. 1133 // 1134 // The 64 bit DMA counter is in units of bytes, not samples. 1135 // 1136 //=========================================================================== 1137 1138 void CEchoGals::UpdateDmaPos( WORD wPipeIndex ) 1139 { 1140 DWORD dwDspPos; 1141 DWORD dwDelta; 1142 1143 // 1144 // Get the current DSP position and find out how much it 1145 // has moved since last time. This is necessary to avoid 1146 // the 32 bit counter wrapping around. 1147 // 1148 dwDspPos = GetDspCommObject()->GetAudioPosition( wPipeIndex ); 1149 dwDelta = dwDspPos - m_dwLastDspPos[ wPipeIndex ]; 1150 1151 // 1152 // Adjust the 64 bit position 1153 // 1154 m_ullDmaPos[ wPipeIndex ] += dwDelta; 1155 m_dwLastDspPos[ wPipeIndex ] = dwDspPos; 1156 1157 } // UpdateDmaPos 1158 1159 1160 //=========================================================================== 1161 // 1162 // ResetDmaPos resets the 64 bit DMA counter. 1163 // 1164 //=========================================================================== 1165 1166 void CEchoGals::ResetDmaPos(WORD wPipe) 1167 { 1168 m_ullDmaPos[ wPipe ] = 0; 1169 m_dwLastDspPos[ wPipe ] = 0; 1170 1171 // 1172 // There may still be mappings in the daffy duck; if so, 1173 // tell them to reset their DMA positions starting at zero 1174 // 1175 if (NULL != m_DaffyDucks[wPipe]) 1176 m_DaffyDucks[wPipe]->ResetStartPos(); 1177 } 1178 1179 1180 //=========================================================================== 1181 // 1182 // This is a very powerful feature; calling this function gives you a pointer 1183 // to the memory location where the DSP writes the 32 bit DMA position for 1184 // a pipe. The DSP is constantly updating this value as it moves data. 1185 // 1186 // Since the DSP is constantly updating it, you can dereference this pointer 1187 // from anywhere and read the DMA position without calling the generic driver. 1188 // This means that with some adroit mapping, you could read the DMA position 1189 // from user mode without calling the kernel. 1190 // 1191 // Remember, Peter - with great power comes great responsibility; you should 1192 // only read this pointer and never write to it or to anywhere around it. You 1193 // could easily lock up your computer. 1194 // 1195 // Note that the DSP writes the position in little endian format; if you are 1196 // on a big endian machine, you will need to byte swap the postion 1197 // before you use it. 1198 // 1199 //=========================================================================== 1200 1201 ECHOSTATUS CEchoGals::GetAudioPositionPtr 1202 ( 1203 WORD wPipeIndex, 1204 PDWORD & pdwPosition 1205 ) 1206 { 1207 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 1208 return ECHOSTATUS_DSP_DEAD; 1209 1210 if (wPipeIndex >= GetNumPipes()) 1211 { 1212 pdwPosition = NULL; 1213 return ECHOSTATUS_INVALID_CHANNEL; 1214 } 1215 1216 PDWORD pdwDspCommPos = GetDspCommObject()->GetAudioPositionPtr(); 1217 1218 pdwPosition = pdwDspCommPos + wPipeIndex; 1219 1220 return ECHOSTATUS_OK; 1221 1222 } // ECHOSTATUS CEchoGals::GetAudioPositionPtr 1223 1224 1225 1226