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