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