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 34 /****************************************************************************** 35 36 Functions for opening and closing pipes 37 38 ******************************************************************************/ 39 40 //=========================================================================== 41 // 42 // OpenAudio is used to reserve audio pipes for your exclusive use. The call 43 // will fail if someone else has already opened the pipes. Calling OpenAudio 44 // is the first step if you want to play or record. 45 // 46 // If the fCheckHardware flag is true, then the open will fail 47 // if the DSP and ASIC firmware have not been loaded (usually means 48 // your external box is turned off). 49 // 50 //=========================================================================== 51 52 ECHOSTATUS CEchoGals::OpenAudio 53 ( 54 PECHOGALS_OPENAUDIOPARAMETERS pOpenParameters, // Info on pipe 55 PWORD pwPipeIndex, // Pipe index ptr 56 BOOL fCheckHardware, 57 CDaffyDuck *pDuck 58 ) 59 { 60 CChannelMask cmMask; 61 WORD wPipeMax, wPipe, wPipeIndex, i, wWidth; 62 ECHOSTATUS Status; 63 64 ECHO_DEBUGPRINTF( ("CEchoGals::OpenAudio: %s %u " 65 "PipeWidth %d " 66 "Cyclic %u \n", 67 ( pOpenParameters->Pipe.bIsInput ) ? "Input" : "Output", 68 pOpenParameters->Pipe.nPipe, 69 pOpenParameters->Pipe.wInterleave, 70 pOpenParameters->bIsCyclic) ); 71 72 *pwPipeIndex = (WORD) -1; // So it's never undefined 73 74 // 75 // Make sure the hardware is OK 76 // 77 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 78 { 79 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") ); 80 return ECHOSTATUS_DSP_DEAD; 81 } 82 83 // 84 // Make sure the DSP & ASIC are up and running 85 // - only if fCheckHardware is true 86 // 87 if (fCheckHardware) 88 { 89 Status = GetDspCommObject()->LoadFirmware(); 90 91 if ( ECHOSTATUS_OK != Status ) 92 return Status; 93 } 94 95 // 96 // Validate the pipe number 97 // 98 wPipe = pOpenParameters->Pipe.nPipe; 99 wWidth = pOpenParameters->Pipe.wInterleave; 100 101 if ( pOpenParameters->Pipe.bIsInput ) 102 { 103 wPipeIndex = wPipe + GetNumPipesOut(); 104 wPipeMax = GetNumPipesIn(); 105 } 106 else 107 { 108 wPipeIndex = wPipe; 109 wPipeMax = GetNumPipesOut(); 110 } 111 112 if ( ( wPipe + wWidth ) > wPipeMax ) 113 { 114 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_INVALID_CHANNEL\n") ); 115 return ECHOSTATUS_INVALID_CHANNEL; 116 } 117 118 // 119 // If the width is more than two, make sure that this card 120 // can handle super interleave 121 // 122 if ( (0 == (m_wFlags & ECHOGALS_ROFLAG_SUPER_INTERLEAVE_OK)) && 123 (wWidth > 2) 124 ) 125 { 126 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_NO_SUPER_INTERLEAVE\n") ); 127 return ECHOSTATUS_NO_SUPER_INTERLEAVE; 128 } 129 130 // 131 // See if the specified pipes are already open 132 // 133 for ( i = 0; i < pOpenParameters->Pipe.wInterleave; i++ ) 134 { 135 cmMask.SetIndexInMask( wPipeIndex + i ); 136 } 137 138 if ( m_cmAudioOpen.Test( &cmMask ) ) 139 { 140 ECHO_DEBUGPRINTF( ("OpenAudio - ECHOSTATUS_CHANNEL_ALREADY_OPEN - m_cmAudioOpen 0x%x cmMask 0x%x\n", 141 m_cmAudioOpen.GetMask(),cmMask.GetMask()) ); 142 return ECHOSTATUS_CHANNEL_ALREADY_OPEN; 143 } 144 145 #ifdef AUTO_DUCK_ALLOCATE 146 // 147 // Make a daffy duck 148 // 149 if (NULL == pDuck) 150 { 151 pDuck = CDaffyDuck::MakeDaffyDuck(m_pOsSupport); 152 153 if (NULL == pDuck) 154 return ECHOSTATUS_NO_MEM; 155 } 156 157 SetDaffyDuck( wPipeIndex, pDuck ); 158 159 #else 160 161 // 162 // Use the specified duck if one was passed in 163 // 164 if (NULL != pDuck) 165 SetDaffyDuck( wPipeIndex, pDuck ); 166 167 #endif 168 169 170 // 171 // Reset the 64-bit DMA position 172 // 173 ResetDmaPos(wPipeIndex); 174 GetDspCommObject()->ResetPipePosition(wPipeIndex); 175 176 // 177 // Prep stuff 178 // 179 m_cmAudioOpen += cmMask; 180 if ( pOpenParameters->bIsCyclic ) 181 m_cmAudioCyclic += cmMask; 182 m_Pipes[ wPipeIndex ] = pOpenParameters->Pipe; 183 *pwPipeIndex = wPipeIndex; 184 m_ProcessId[ wPipeIndex ] = pOpenParameters->ProcessId; 185 Reset( wPipeIndex ); 186 187 ECHO_DEBUGPRINTF( ("OpenAudio - ECHOSTATUS_OK - m_cmAudioOpen 0x%x\n",m_cmAudioOpen.GetMask()) ); 188 return ECHOSTATUS_OK; 189 190 } // ECHOSTATUS CEchoGals::OpenAudio 191 192 193 //=========================================================================== 194 // 195 // CloseAudio is, naturally, the inverse of OpenAudio. 196 // 197 //=========================================================================== 198 199 ECHOSTATUS CEchoGals::CloseAudio 200 ( 201 PECHOGALS_CLOSEAUDIOPARAMETERS pCloseParameters, 202 BOOL fFreeDuck 203 ) 204 { 205 CChannelMask cmMask; 206 ECHOSTATUS Status; 207 WORD i; 208 WORD wPipeIndex; 209 210 wPipeIndex = pCloseParameters->wPipeIndex; 211 212 ECHO_DEBUGPRINTF( ("CEchoGals::CloseAudio: Pipe %u ", 213 wPipeIndex) ); 214 215 Status = VerifyAudioOpen( wPipeIndex ); 216 if ( ECHOSTATUS_OK != Status ) 217 return Status; 218 219 for ( i = 0; 220 i < m_Pipes[ wPipeIndex ].wInterleave; 221 i++ ) 222 { 223 cmMask.SetIndexInMask( wPipeIndex + i ); 224 } 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 m_ProcessId[ wPipeIndex ] = NULL; 243 m_Pipes[ wPipeIndex ].wInterleave = 0; 244 245 ECHO_DEBUGPRINTF( ("CloseAudio - ECHOSTATUS_OK - m_cmAudioOpen 0x%x\n",m_cmAudioOpen.GetMask()) ); 246 return ECHOSTATUS_OK; 247 248 } // ECHOSTATUS CEchoGals::CloseAudio 249 250 251 //=========================================================================== 252 // 253 // VerifyAudioOpen is a utility function; it tells you if 254 // a pipe is open or not. 255 // 256 //=========================================================================== 257 258 ECHOSTATUS CEchoGals::VerifyAudioOpen 259 ( 260 WORD wPipeIndex 261 ) 262 { 263 CChannelMask cmMask; 264 265 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 266 { 267 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") ); 268 return ECHOSTATUS_DSP_DEAD; 269 } 270 271 cmMask.SetIndexInMask( wPipeIndex ); 272 if ( !( m_cmAudioOpen.Test( &cmMask ) ) ) 273 { 274 ECHO_DEBUGPRINTF( ("VerifyAudioOpen - ECHOSTATUS_CHANNEL_NOT_OPEN - wPipeIndex %d - m_cmAudioOpen 0x%x - cmMask 0x%x\n", 275 wPipeIndex,m_cmAudioOpen.GetMask(),cmMask.GetMask()) ); 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 540 return Status; 541 542 } // ECHOSTATUS CEchoGals::SetAudioFormat - single pipe 543 544 545 //=========================================================================== 546 // 547 // This call lets you set the audio format for several pipes at once. 548 // 549 //=========================================================================== 550 551 ECHOSTATUS CEchoGals::SetAudioFormat 552 ( 553 PCChannelMask pChannelMask, 554 PECHOGALS_AUDIOFORMAT pAudioFormat 555 ) 556 { 557 WORD wPipeIndex = 0xffff; 558 ECHOSTATUS Status; 559 560 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 561 { 562 ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") ); 563 return ECHOSTATUS_DSP_DEAD; 564 } 565 566 for ( ; ; ) 567 { 568 wPipeIndex = pChannelMask->GetIndexFromMask( ++wPipeIndex ); 569 if ( (WORD) ECHO_INVALID_CHANNEL == wPipeIndex ) 570 break; // We be done! 571 572 // 573 // See if this pipe is open 574 // 575 if ( !( m_cmAudioOpen.TestIndexInMask( wPipeIndex ) ) ) 576 { 577 ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: " 578 "for pipe %d failed, pipe not open\n", 579 wPipeIndex) ); 580 return ECHOSTATUS_CHANNEL_NOT_OPEN; 581 } 582 583 // 584 // See if the format is OK 585 // 586 ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: " 587 "for pipe %d\n", 588 wPipeIndex) ); 589 Status = QueryAudioFormat( wPipeIndex, pAudioFormat ); 590 if ( ECHOSTATUS_OK != Status ) 591 return Status; 592 593 // 594 // Set the format for this pipe 595 // 596 Status = GetDspCommObject()->SetAudioFormat( wPipeIndex, pAudioFormat ); 597 if ( ECHOSTATUS_OK != Status ) 598 return Status; 599 } 600 601 return ECHOSTATUS_OK; 602 603 } // ECHOSTATUS CEchoGals::SetAudioFormat - multiple pipes 604 605 606 607 //=========================================================================== 608 // 609 // GetAudioFormat returns the current audio format for a pipe. 610 // 611 //=========================================================================== 612 613 ECHOSTATUS CEchoGals::GetAudioFormat 614 ( 615 WORD wPipeIndex, 616 PECHOGALS_AUDIOFORMAT pAudioFormat 617 ) 618 { 619 ECHO_DEBUGPRINTF( ("CEchoGals::GetAudioFormat: " 620 "for pipe %d\n", 621 wPipeIndex) ); 622 623 GetDspCommObject()->GetAudioFormat( wPipeIndex, pAudioFormat ); 624 625 return ECHOSTATUS_OK; 626 627 } // ECHOSTATUS CEchoGals::GetAudioFormat 628 629 630 //=========================================================================== 631 // 632 // This function does exactly what you think it does. 633 // 634 // Note that if the card is not set to internal clock (that is, the hardware 635 // is synced to word clock or some such), this call has no effect. 636 // 637 // Note that all of the inputs and outputs on a single card share the same 638 // clock. 639 // 640 //=========================================================================== 641 642 ECHOSTATUS CEchoGals::SetAudioSampleRate 643 ( 644 DWORD dwSampleRate 645 ) 646 { 647 ECHOSTATUS Status; 648 649 ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioSampleRate: " 650 "to %ld Hz\n", 651 dwSampleRate) ); 652 653 // 654 // Check to see if the sample rate is locked 655 // 656 if ( 0 != (m_wFlags & ECHOGALS_FLAG_SAMPLE_RATE_LOCKED)) 657 { 658 return ECHOSTATUS_OK; 659 } 660 else 661 { 662 Status = QueryAudioSampleRate( dwSampleRate ); 663 if ( ECHOSTATUS_OK != Status ) 664 return Status; 665 666 if ( dwSampleRate == GetDspCommObject()->SetSampleRate( dwSampleRate ) ) 667 { 668 m_dwSampleRate = dwSampleRate; 669 return ECHOSTATUS_OK; 670 } 671 } 672 return ECHOSTATUS_BAD_FORMAT; 673 674 } // ECHOSTATUS CEchoGals::SetAudioSampleRate 675 676 677 //=========================================================================== 678 // 679 // GetAudioSampleRate - retrieves the current sample rate for the hardware 680 // 681 //=========================================================================== 682 683 ECHOSTATUS CEchoGals::GetAudioSampleRate 684 ( 685 PDWORD pdwSampleRate 686 ) 687 { 688 ECHO_DEBUGPRINTF( ("CEchoGals::GetAudioSampleRate\n")); 689 690 *pdwSampleRate = m_dwSampleRate; 691 692 return ECHOSTATUS_OK; 693 694 } // ECHOSTATUS CEchoGals::GetAudioSampleRate 695 696 697 698 699 /****************************************************************************** 700 701 Functions related to the scatter-gather list 702 703 ******************************************************************************/ 704 705 706 //=========================================================================== 707 // 708 // Use the given CDaffyDuck object as the scatter-gather list for this pipe 709 // 710 //=========================================================================== 711 712 ECHOSTATUS CEchoGals::SetDaffyDuck(WORD wPipeIndex, CDaffyDuck *pDuck) 713 { 714 m_DaffyDucks[wPipeIndex] = pDuck; 715 716 return ECHOSTATUS_OK; 717 718 } // SetDaffyDuck 719 720 721 722 723 //=========================================================================== 724 // 725 // This method returns a pointer to the daffy duck for a pipe; the caller 726 // can then have direct access to the daffy duck object. 727 // 728 //=========================================================================== 729 730 CDaffyDuck *CEchoGals::GetDaffyDuck(WORD wPipeIndex) 731 { 732 ECHO_DEBUGPRINTF(("CEchoGals::GetDaffyDuck for pipe index %d\n",wPipeIndex)); 733 734 if (wPipeIndex >= GetNumPipes()) 735 return NULL; 736 737 return m_DaffyDucks[wPipeIndex]; 738 } 739 740 741 742 /****************************************************************************** 743 744 Functions for starting and stopping transport 745 746 ******************************************************************************/ 747 748 //=========================================================================== 749 // 750 // Start transport for a single pipe 751 // 752 //=========================================================================== 753 754 ECHOSTATUS CEchoGals::Start 755 ( 756 WORD wPipeIndex 757 ) 758 { 759 CChannelMask cmMask; 760 761 cmMask.SetIndexInMask( wPipeIndex ); 762 return Start( &cmMask ); 763 764 } // ECHOSTATUS CEchoGals::Start 765 766 767 //=========================================================================== 768 // 769 // Start transport for a group of pipes 770 // 771 // This function includes logic to sync-start several pipes at once, 772 // according to the process ID specified when the pipe was opened. This is 773 // included to work around a limitation of the Windows wave API so that 774 // programs could use multiple inputs and outputs and have them start at the 775 // same time. 776 // 777 // If you don't want to use this feature, call CEchoGals::ClearFlags 778 // with ECHOGALS_FLAG_SYNCH_WAVE and the pipes will start immediately. 779 // 780 //=========================================================================== 781 782 ECHOSTATUS CEchoGals::Start 783 ( 784 PCChannelMask pChannelMask 785 ) 786 { 787 WORD wPipe; 788 DWORD dwPhysStartAddr; 789 CChannelMask cmStart; 790 PVOID ProcessId = NULL; 791 CDspCommObject *pDCO; 792 793 pDCO = GetDspCommObject(); 794 if ( NULL == pDCO || pDCO->IsBoardBad() ) 795 return ECHOSTATUS_DSP_DEAD; 796 797 // 798 // See if we are dealing with synchronized wave pipes. If the sync 799 // flag is set, get the process ID for this pipe to compare with 800 // other pipes. 801 // 802 if ( GetFlags() & ECHOGALS_FLAG_SYNCH_WAVE ) 803 { 804 wPipe = pChannelMask->GetIndexFromMask( 0 ); 805 ProcessId = m_ProcessId[ wPipe ]; 806 } 807 808 //-------------------------------------------------------- 809 // Process each pipe in the mask 810 //-------------------------------------------------------- 811 812 for (wPipe = 0; wPipe < GetNumPipes(); wPipe++) 813 { 814 PDWORD pdwDspCommPositions; 815 816 // 817 // Skip this pipe if it's not in the mask 818 // 819 if (!pChannelMask->TestIndexInMask(wPipe)) 820 continue; 821 822 // 823 // This pipe must have a CDaffyDuck object 824 // 825 if (NULL == m_DaffyDucks[ wPipe ]) 826 { 827 ECHO_DEBUGPRINTF(("CDaffyDuck::Start - trying to start pipe index %d " 828 "but there is no CDaffyDuck!\n",wPipe)); 829 return ECHOSTATUS_CHANNEL_NOT_OPEN; 830 } 831 832 // 833 // If this pipe was opened in cyclic mode, make sure that the corresponding 834 // CDaffyDuck has been wrapped 835 // 836 if ( (0 != m_cmAudioCyclic.TestIndexInMask( wPipe ) ) && 837 (FALSE == m_DaffyDucks[wPipe]->Wrapped()) 838 ) 839 { 840 ECHO_DEBUGPRINTF(("CEchoGals::Start called for pipe index %d - " 841 "pipe was opened in cyclic mode, but the duck " 842 "has not been wrapped\n",wPipe)); 843 return ECHOSTATUS_DUCK_NOT_WRAPPED; 844 } 845 846 // 847 // Set the physical start address for the duck for this pipe 848 // 849 dwPhysStartAddr = m_DaffyDucks[wPipe]->GetPhysStartAddr(); 850 pDCO->SetAudioDuckListPhys( wPipe, dwPhysStartAddr ); 851 852 853 // 854 // Do different things to this pipe depending on the 855 // state 856 // 857 switch (m_byPipeState[wPipe]) 858 { 859 case PIPE_STATE_RESET : 860 // 861 // Clean up the DMA position stuff 862 // 863 pdwDspCommPositions = pDCO->GetAudioPositionPtr(); 864 pdwDspCommPositions[ wPipe ] = 0; 865 866 // 867 // If this pipe isn't synced or is in a reset state, 868 // start it up 869 // 870 if (NULL == ProcessId) 871 { 872 m_byPipeState[ wPipe ] = PIPE_STATE_STARTED; 873 cmStart.SetIndexInMask( wPipe ); 874 } 875 else 876 { 877 // 878 // This pipe is synced; upgrade to PENDING 879 // 880 m_byPipeState[ wPipe ] = PIPE_STATE_PENDING; 881 } 882 break; 883 884 885 case PIPE_STATE_STOPPED : 886 887 if (NULL == ProcessId) 888 { 889 // 890 // Non-synced pipe; start 'er up! 891 // 892 m_byPipeState[ wPipe ] = PIPE_STATE_STARTED; 893 cmStart.SetIndexInMask( wPipe ); 894 } 895 else 896 { 897 // 898 // Synced pipe; if this pipe is in STOP mode, 899 // upgrade it to PENDING status 900 // 901 m_byPipeState[ wPipe ] = PIPE_STATE_PENDING; 902 } 903 break; 904 905 906 case PIPE_STATE_PENDING : 907 case PIPE_STATE_STARTED : 908 break; 909 } 910 } 911 912 //----------------------------------------------------------------- 913 // Start the pipes 914 //----------------------------------------------------------------- 915 916 // 917 // Don't go if all the synced pipes are not yet pending 918 // 919 BOOL fAllReady = TRUE; 920 for ( wPipe = 0; wPipe < GetNumPipes(); wPipe++ ) 921 { 922 if ( ( ProcessId == m_ProcessId[ wPipe ] ) && 923 ( PIPE_STATE_STOPPED == m_byPipeState[wPipe])) 924 { 925 ECHO_DEBUGPRINTF(("CEchoGals::Start - can't start; pipe %d " 926 "still set to state %d\n", 927 wPipe, 928 m_byPipeState[wPipe])); 929 fAllReady = FALSE; 930 } 931 } 932 933 // 934 // All synced pipes are pending; time to go! 935 // 936 if (fAllReady) 937 { 938 for (wPipe = 0; wPipe < GetNumPipes(); wPipe++) 939 { 940 if ( (ProcessId == m_ProcessId[ wPipe ]) && 941 (PIPE_STATE_PENDING == m_byPipeState[ wPipe ])) 942 { 943 m_byPipeState[wPipe] = PIPE_STATE_STARTED; 944 cmStart.SetIndexInMask( wPipe ); 945 ECHO_DEBUGPRINTF(("CEchoGals::Start - setting pipe %d to start\n", 946 wPipe)); 947 } 948 } 949 } 950 951 if ( cmStart.IsEmpty() ) 952 return ECHOSTATUS_OK; 953 954 955 //----------------------------------------------------------------- 956 // Time to go 957 //----------------------------------------------------------------- 958 959 return pDCO->StartTransport( &cmStart ); 960 961 } // ECHOSTATUS CEchoGals::Start 962 963 964 965 //=========================================================================== 966 // 967 // Stop a single pipe 968 // 969 //=========================================================================== 970 971 ECHOSTATUS CEchoGals::Stop 972 ( 973 WORD wPipeIndex 974 ) 975 { 976 CChannelMask cmMask; 977 978 cmMask.SetIndexInMask( wPipeIndex ); 979 return( Stop( &cmMask ) ); 980 981 } // ECHOSTATUS CEchoGals::Stop 982 983 984 //=========================================================================== 985 // 986 // Stop several pipes simultaneously 987 // 988 //=========================================================================== 989 990 ECHOSTATUS CEchoGals::Stop 991 ( 992 PCChannelMask pChannelMask 993 ) 994 { 995 INT32 i; 996 ECHOSTATUS Status; 997 998 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 999 return ECHOSTATUS_DSP_DEAD; 1000 1001 Status = GetDspCommObject()->StopTransport( pChannelMask ); 1002 if ( ECHOSTATUS_OK != Status ) 1003 return Status; 1004 1005 for ( i = 0; i < GetNumPipes(); i++ ) 1006 { 1007 // 1008 // Skip channel if not in mask 1009 // 1010 if ( !pChannelMask->TestIndexInMask( (WORD) i ) ) 1011 continue; 1012 1013 1014 // 1015 // Don't bother if it's stopped already 1016 // 1017 if ( PIPE_STATE_STOPPED == m_byPipeState[ i ] ) 1018 continue; 1019 1020 // 1021 // Muck with the DMA position 1022 // 1023 UpdateDmaPos( (WORD) i ); 1024 1025 m_byPipeState[ i ] = PIPE_STATE_STOPPED; 1026 } 1027 1028 return Status; 1029 1030 } // ECHOSTATUS CEchoGals::Stop 1031 1032 1033 //=========================================================================== 1034 // 1035 // Reset transport for a single pipe 1036 // 1037 //=========================================================================== 1038 1039 ECHOSTATUS CEchoGals::Reset 1040 ( 1041 WORD wPipeIndex 1042 ) 1043 { 1044 CChannelMask cmMask; 1045 1046 cmMask.SetIndexInMask( wPipeIndex ); 1047 return Reset( &cmMask ); 1048 1049 } // ECHOSTATUS CEchoGals::Reset 1050 1051 1052 //=========================================================================== 1053 // 1054 // Reset transport for a group of pipes simultaneously 1055 // 1056 //=========================================================================== 1057 1058 ECHOSTATUS CEchoGals::Reset 1059 ( 1060 PCChannelMask pChannelMask 1061 ) 1062 { 1063 WORD i; 1064 ECHOSTATUS Status; 1065 1066 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 1067 return ECHOSTATUS_DSP_DEAD; 1068 1069 Status = GetDspCommObject()->ResetTransport( pChannelMask ); 1070 if ( ECHOSTATUS_OK != Status ) 1071 return Status; 1072 1073 for ( i = 0; i < GetNumPipes(); i++ ) 1074 { 1075 // 1076 // Skip channel if not in mask 1077 // 1078 if ( !pChannelMask->TestIndexInMask( (WORD) i ) ) 1079 continue; 1080 1081 if ( PIPE_STATE_RESET == m_byPipeState[ i ] ) 1082 continue; 1083 1084 // 1085 // Muck with the DMA position 1086 // 1087 UpdateDmaPos( i ); 1088 m_dwLastDspPos[ i ] = 0; 1089 GetDspCommObject()->ResetPipePosition(i); 1090 1091 m_byPipeState[ i ] = PIPE_STATE_RESET; 1092 } 1093 1094 return Status; 1095 1096 } // ECHOSTATUS CEchoGals::Reset 1097 1098 1099 1100 1101 /****************************************************************************** 1102 1103 Functions for handling the current DMA position for pipes; the DMA position 1104 is the number of bytes transported by a pipe. 1105 1106 ******************************************************************************/ 1107 1108 //=========================================================================== 1109 // 1110 // The DSP sends back a 32 bit DMA counter for each pipe of the number of bytes 1111 // transported; this count is written by the DSP to the comm page without 1112 // the driver doing anything. 1113 // 1114 // The driver then maintains a 64 bit counter based off of the DSP's counter. 1115 // 1116 // Call UpdateDmaPos to cause the driver to update the internal 64 bit DMA 1117 // counter. 1118 // 1119 // The 64 bit DMA counter is in units of bytes, not samples. 1120 // 1121 //=========================================================================== 1122 1123 void CEchoGals::UpdateDmaPos( WORD wPipeIndex ) 1124 { 1125 DWORD dwDspPos; 1126 DWORD dwDelta; 1127 1128 // 1129 // Get the current DSP position and find out how much it 1130 // has moved since last time. This is necessary to avoid 1131 // the 32 bit counter wrapping around. 1132 // 1133 dwDspPos = GetDspCommObject()->GetAudioPosition( wPipeIndex ); 1134 dwDelta = dwDspPos - m_dwLastDspPos[ wPipeIndex ]; 1135 1136 // 1137 // Adjust the 64 bit position 1138 // 1139 m_ullDmaPos[ wPipeIndex ] += dwDelta; 1140 m_dwLastDspPos[ wPipeIndex ] = dwDspPos; 1141 1142 } // UpdateDmaPos 1143 1144 1145 //=========================================================================== 1146 // 1147 // ResetDmaPos resets the 64 bit DMA counter. 1148 // 1149 //=========================================================================== 1150 1151 void CEchoGals::ResetDmaPos(WORD wPipe) 1152 { 1153 m_ullDmaPos[ wPipe ] = 0; 1154 m_dwLastDspPos[ wPipe ] = 0; 1155 1156 // 1157 // There may still be mappings in the daffy duck; if so, 1158 // tell them to reset their DMA positions starting at zero 1159 // 1160 if (NULL != m_DaffyDucks[wPipe]) 1161 m_DaffyDucks[wPipe]->ResetStartPos(); 1162 } 1163 1164 1165 //=========================================================================== 1166 // 1167 // This is a very powerful feature; calling this function gives you a pointer 1168 // to the memory location where the DSP writes the 32 bit DMA position for 1169 // a pipe. The DSP is constantly updating this value as it moves data. 1170 // 1171 // Since the DSP is constantly updating it, you can dereference this pointer 1172 // from anywhere and read the DMA position without calling the generic driver. 1173 // This means that with some adroit mapping, you could read the DMA position 1174 // from user mode without calling the kernel. 1175 // 1176 // Remember, Peter - with great power comes great responsibility; you should 1177 // only read this pointer and never write to it or to anywhere around it. You 1178 // could easily lock up your computer. 1179 // 1180 // Note that the DSP writes the position in little endian format; if you are 1181 // on a big endian machine, you will need to byte swap the postion 1182 // before you use it. 1183 // 1184 //=========================================================================== 1185 1186 ECHOSTATUS CEchoGals::GetAudioPositionPtr 1187 ( 1188 WORD wPipeIndex, 1189 PDWORD & pdwPosition 1190 ) 1191 { 1192 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 1193 return ECHOSTATUS_DSP_DEAD; 1194 1195 if (wPipeIndex >= GetNumPipes()) 1196 { 1197 pdwPosition = NULL; 1198 return ECHOSTATUS_INVALID_CHANNEL; 1199 } 1200 1201 PDWORD pdwDspCommPos = GetDspCommObject()->GetAudioPositionPtr(); 1202 1203 pdwPosition = pdwDspCommPos + wPipeIndex; 1204 1205 return ECHOSTATUS_OK; 1206 1207 } // ECHOSTATUS CEchoGals::GetAudioPositionPtr 1208 1209 1210