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