1// **************************************************************************** 2// 3// CEchoGals_mixer.cpp 4// 5// Implementation file for the CEchoGals driver class (mixer functions). 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#undef ECHO_DEBUGPRINTF 34#define ECHO_DEBUGPRINTF(x) 35 36/**************************************************************************** 37 38 CEchoGals mixer client management 39 40 ****************************************************************************/ 41 42//=========================================================================== 43// 44// Open the mixer - create a mixer client structure for this client and 45// return the cookie. The cookie uniquely identifies this client to the 46// mixer driver. 47// 48// Valid cookies are non-zero. If you get a zero cookie, the open failed 49// somehow. 50// 51// Clients can change mixer controls without calling OpenMixer first; it just 52// means that they can't track control changes made by other clients. 53// 54//=========================================================================== 55 56ECHOSTATUS CEchoGals::OpenMixer(NUINT &Cookie) 57{ 58 ECHO_MIXER_CLIENT *pTemp; 59 60 if (m_fMixerDisabled) 61 return ECHOSTATUS_MIXER_DISABLED; 62 63 // 64 // If the cookie is non-zero, then use the specified value 65 // 66 if (0 != Cookie) 67 { 68 pTemp = m_pMixerClients; 69 while (pTemp != NULL) 70 { 71 if (Cookie == pTemp->Cookie) 72 { 73 ECHO_DEBUGPRINTF(("CEchoGals::OpenMixer - cookie 0x%lx already in use\n", 74 Cookie)); 75 return ECHOSTATUS_BAD_COOKIE; 76 } 77 78 pTemp = pTemp->pNext; 79 } 80 } 81 else 82 { 83 // 84 // Make a new cookie 85 // 86 ULONGLONG ullTime; 87 88 m_pOsSupport->OsGetSystemTime(&ullTime); 89 Cookie = (NUINT) ullTime; 90 if (0 == Cookie) 91 Cookie = 1; 92 93 // 94 // Look through the existing mixer client list to ensure that this 95 // cookie is unique. 96 // 97 pTemp = m_pMixerClients; 98 while (pTemp != NULL) 99 { 100 if (Cookie == pTemp->Cookie) 101 { 102 // 103 // Oops, someone is already using this cookie. Increment 104 // the new cookie and try again. 105 // 106 Cookie++; 107 pTemp = m_pMixerClients; 108 } 109 110 pTemp = pTemp->pNext; 111 } 112 113 } 114 115 116 // 117 // Allocate the mixer client structure 118 // 119 ECHO_MIXER_CLIENT *pClient = NULL; 120 ECHOSTATUS Status; 121 122 Status = OsAllocateNonPaged(sizeof(ECHO_MIXER_CLIENT),(void **) &pClient); 123 if (NULL == pClient) 124 { 125 Cookie = 0; 126 return Status; 127 } 128 129 130 // 131 // Store the new cookie and the new mixer client 132 // 133 pClient->Cookie = Cookie; 134 pClient->pNext = m_pMixerClients; 135 m_pMixerClients = pClient; 136 137 return ECHOSTATUS_OK; 138 139} // OpenMixer 140 141 142//=========================================================================== 143// 144// Find a mixer client that matches a cookie 145// 146//=========================================================================== 147 148ECHO_MIXER_CLIENT *CEchoGals::GetMixerClient(NUINT Cookie) 149{ 150 ECHO_MIXER_CLIENT *pTemp; 151 152 pTemp = m_pMixerClients; 153 while (NULL != pTemp) 154 { 155 if (Cookie == pTemp->Cookie) 156 break; 157 158 pTemp = pTemp->pNext; 159 } 160 161 return pTemp; 162 163} // GetMixerClient 164 165 166//=========================================================================== 167// 168// Close the mixer - free the mixer client structure 169// 170//=========================================================================== 171 172ECHOSTATUS CEchoGals::CloseMixer(NUINT Cookie) 173{ 174 // 175 // Search the linked list and remove the client that matches the cookie 176 // 177 ECHO_MIXER_CLIENT *pTemp; 178 179 pTemp = m_pMixerClients; 180 if (NULL == pTemp) 181 return ECHOSTATUS_BAD_COOKIE; 182 183 // 184 // Head of the list? 185 // 186 if (pTemp->Cookie == Cookie) 187 { 188 m_pMixerClients = pTemp->pNext; 189 OsFreeNonPaged(pTemp); 190 191 return ECHOSTATUS_OK; 192 } 193 194 // 195 // Not the head of the list; search the list 196 // 197 while (NULL != pTemp->pNext) 198 { 199 if (Cookie == pTemp->pNext->Cookie) 200 { 201 ECHO_MIXER_CLIENT *pDeadClient; 202 203 pDeadClient = pTemp->pNext; 204 pTemp->pNext = pDeadClient->pNext; 205 OsFreeNonPaged(pDeadClient); 206 207 return ECHOSTATUS_OK; 208 } 209 210 pTemp = pTemp->pNext; 211 } 212 213 // 214 // No soup for you! 215 // 216 return ECHOSTATUS_BAD_COOKIE; 217 218} // CloseMixer 219 220 221//=========================================================================== 222// 223// IsMixerOpen - returns true if at least one client has the mixer open 224// 225//=========================================================================== 226 227BOOL CEchoGals::IsMixerOpen() 228{ 229 if (NULL == m_pMixerClients) 230 return FALSE; 231 232 return TRUE; 233 234} // IsMixerOpen 235 236 237//=========================================================================== 238// 239// This function is called when a mixer control changes; add the change 240// to the queue for each client. 241// 242// Here's what the wCh1 and wCh2 parameters represent, based on the wType 243// parameter: 244// 245// wType wCh1 wCh2 246// ----- ---- ---- 247// ECHO_BUS_OUT Output bus Ignored 248// ECHO_BUS_IN Input bus Ignored 249// ECHO_PIPE_OUT Output pipe Output bus 250// ECHO_MONITOR Input bus Output bus 251// 252// ECHO_PIPE_IN is not used right now. 253// 254//=========================================================================== 255 256ECHOSTATUS CEchoGals::MixerControlChanged 257( 258 WORD wType, // One of the ECHO_CHANNEL_TYPES 259 WORD wParameter, // One of the MXN_* values 260 WORD wCh1, // Depends on the wType value 261 WORD wCh2 // Also depends on wType value 262) 263{ 264 ECHO_MIXER_CLIENT *pClient = m_pMixerClients; 265 PMIXER_NOTIFY pNotify; 266 267 if (m_fMixerDisabled) 268 return ECHOSTATUS_MIXER_DISABLED; 269 270 // 271 // Go through all the clients and store this control change 272 // 273 while (NULL != pClient) 274 { 275 // 276 // Search the circular buffer for this client and see if 277 // this control change is already stored 278 // 279 DWORD dwIndex,dwCount; 280 BOOL fFound; 281 282 dwCount = pClient->dwCount; 283 dwIndex = pClient->dwTail; 284 fFound = FALSE; 285 while (dwCount > 0) 286 { 287 pNotify = pClient->Notifies + dwIndex; 288 if ( (pNotify->wType == wType) && 289 (pNotify->wParameter == wParameter) && 290 (pNotify->u.wPipeOut == wCh1) && // can use any union member her 291 (pNotify->wBusOut == wCh2)) 292 { 293 // 294 // Found this notify already in the circular buffer 295 // 296 fFound = TRUE; 297 break; 298 } 299 dwIndex++; 300 dwIndex &= MAX_MIXER_NOTIFIES - 1; 301 dwCount--; 302 } 303 304 // 305 // If the notify was not found, add this notify to the circular buffer if 306 // there is enough room. 307 // 308 if ( (FALSE == fFound) && 309 (pClient->dwCount != MAX_MIXER_NOTIFIES)) 310 { 311 pNotify = pClient->Notifies + pClient->dwHead; 312 313 pNotify->wType = wType; 314 pNotify->wParameter = wParameter; 315 316 if (ECHO_BUS_OUT == wType) 317 { 318 pNotify->u.wPipeOut = ECHO_CHANNEL_UNUSED; 319 pNotify->wBusOut = wCh1; 320 } 321 else 322 { 323 pNotify->u.wPipeOut = wCh1; // can use any union member here also 324 pNotify->wBusOut = wCh2; 325 } 326 327 pClient->dwCount += 1; 328 pClient->dwHead = (pClient->dwHead + 1) & (MAX_MIXER_NOTIFIES - 1); 329 } 330 331 pClient = pClient->pNext; 332 } 333 334 return ECHOSTATUS_OK; 335 336} // MixerControlChanged 337 338 339//=========================================================================== 340// 341// This method is called when a client wants to know what controls have 342// changed. 343// 344//=========================================================================== 345 346ECHOSTATUS CEchoGals::GetControlChanges 347( 348 PMIXER_MULTI_NOTIFY pNotifies, 349 NUINT MixerCookie 350) 351{ 352 // 353 // Match the cookie 354 // 355 ECHO_MIXER_CLIENT *pClient = GetMixerClient(MixerCookie); 356 357 if (NULL == pClient) 358 { 359 pNotifies->dwCount = 0; 360 return ECHOSTATUS_BAD_COOKIE; 361 } 362 363 // 364 // Copy mixer notifies 365 // 366 PMIXER_NOTIFY pDest,pSrc; 367 DWORD dwNumClientNotifies,dwNumReturned; 368 369 dwNumClientNotifies = pNotifies->dwCount; 370 pDest = pNotifies->Notifies; 371 dwNumReturned = 0; 372 while ( (dwNumClientNotifies > 0) && (pClient->dwCount > 0)) 373 { 374 pSrc = pClient->Notifies + pClient->dwTail; 375 376 OsCopyMemory(pDest,pSrc,sizeof(MIXER_NOTIFY)); 377 378 pDest++; 379 380 pClient->dwTail = (pClient->dwTail + 1) & (MAX_MIXER_NOTIFIES - 1); 381 pClient->dwCount -= 1; 382 383 dwNumClientNotifies--; 384 385 dwNumReturned++; 386 } 387 388 pNotifies->dwCount = dwNumReturned; 389 390 return ECHOSTATUS_OK; 391 392} // GetControlChanges 393 394 395 396 397/**************************************************************************** 398 399 CEchoGals mixer control 400 401 ****************************************************************************/ 402 403//=========================================================================== 404// 405// Process mixer function - service a single mixer function 406// 407//=========================================================================== 408 409ECHOSTATUS CEchoGals::ProcessMixerFunction 410( 411 PMIXER_FUNCTION pMixerFunction, 412 INT32 & iRtnDataSz 413) 414{ 415 ECHOSTATUS Status = ECHOSTATUS_OK; 416 417 if (m_fMixerDisabled) 418 return ECHOSTATUS_MIXER_DISABLED; 419 420 switch ( pMixerFunction->iFunction ) 421 { 422 case MXF_GET_CAPS : 423 Status = GetCapabilities( &pMixerFunction->Data.Capabilities ); 424 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 425 "MXF_GET_CAPS Status %ld\n", Status) ); 426 break; 427 428 case MXF_GET_LEVEL : 429 Status = GetAudioLineLevel( pMixerFunction); 430 /* 431 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 432 "MXF_GET_LEVEL Status %ld\n", Status) ); 433 */ 434 break; 435 436 case MXF_SET_LEVEL : 437 Status = SetAudioLineLevel( pMixerFunction); 438 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 439 "MXF_SET_LEVEL Status %ld\n", Status) ); 440 break; 441 442 case MXF_GET_NOMINAL : 443 Status = GetAudioNominal( pMixerFunction); 444 /* 445 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 446 "MXF_GET_NOMINAL Status %ld\n", Status) ); 447 */ 448 break; 449 450 case MXF_SET_NOMINAL : 451 Status = SetAudioNominal( pMixerFunction); 452 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 453 "MXF_SET_NOMINAL Status %ld\n", Status) ); 454 break; 455 456 case MXF_GET_MONITOR : 457 Status = GetAudioMonitor( pMixerFunction->Channel.wChannel, 458 pMixerFunction->Data.Monitor.wBusOut, 459 pMixerFunction->Data.Monitor.Data.iLevel ); 460 /* 461 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 462 "MXF_GET_MONITOR Status %ld\n", Status) ); 463 */ 464 break; 465 466 case MXF_SET_MONITOR : 467 Status = SetAudioMonitor( pMixerFunction->Channel.wChannel, 468 pMixerFunction->Data.Monitor.wBusOut, 469 pMixerFunction->Data.Monitor.Data.iLevel ); 470 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 471 "MXF_SET_MONITOR Status %ld\n", Status) ); 472 break; 473 474 case MXF_GET_CLOCK_DETECT : 475 Status = GetInputClockDetect( pMixerFunction->Data.dwClockDetectBits ); 476 break; 477 478 case MXF_GET_INPUT_CLOCK : 479 Status = GetInputClock( pMixerFunction->Data.wClock ); 480 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 481 "MXF_GET_INPUT_CLOCK Status %ld\n", Status) ); 482 break; 483 484 case MXF_SET_INPUT_CLOCK : 485 Status = SetInputClock( pMixerFunction->Data.wClock ); 486 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 487 "MXF_SET_INPUT_CLOCK Status %ld\n", Status) ); 488 break; 489 490 491 case MXF_GET_OUTPUT_CLOCK : 492 Status = GetOutputClock( pMixerFunction->Data.wClock ); 493 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 494 "MXF_GET_OUTPUT_CLOCK Status %ld\n", Status) ); 495 break; 496 497 case MXF_SET_OUTPUT_CLOCK : 498 Status = SetOutputClock( pMixerFunction->Data.wClock ); 499 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 500 "MXF_SET_OUTPUT_CLOCK Status %ld\n", Status) ); 501 break; 502 503 504 case MXF_GET_METERS : 505 506 if (NULL != GetDspCommObject()) 507 { 508 Status = GetDspCommObject()-> 509 GetAudioMeters( &pMixerFunction->Data.Meters ); 510 } 511 else 512 { 513 Status = ECHOSTATUS_DSP_DEAD; 514 } 515 516 //ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 517 // "MXF_GET_METERS Status %ld\n", Status) ); 518 break; 519 520 case MXF_GET_METERS_ON : 521 Status = GetMetersOn( pMixerFunction->Data.bMetersOn ); 522 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 523 "MXF_SET_METERS Status %ld\n", Status) ); 524 break; 525 526 case MXF_SET_METERS_ON : 527 Status = SetMetersOn( pMixerFunction->Data.bMetersOn ); 528 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 529 "MXF_SET_METERS_ON Status %ld\n", Status) ); 530 break; 531 532 case MXF_GET_PROF_SPDIF : 533 if ( NULL == GetDspCommObject() ) 534 { 535 Status = ECHOSTATUS_DSP_DEAD; 536 } 537 else 538 { 539 pMixerFunction->Data.bProfSpdif = IsProfessionalSpdif(); 540 } 541 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 542 "MXF_GET_PROF_SPDIF Pro S/PDIF: 0x%x Status %ld\n", 543 pMixerFunction->Data.bProfSpdif, 544 Status) ); 545 break; 546 547 case MXF_SET_PROF_SPDIF : 548 SetProfessionalSpdif( pMixerFunction->Data.bProfSpdif ); 549 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 550 "MXF_SET_PROF_SPDIF Pro S/PDIF: 0x%x Status %ld\n", 551 pMixerFunction->Data.bProfSpdif, 552 Status) ); 553 break; 554 555 case MXF_GET_MUTE : 556 Status = GetAudioMute(pMixerFunction); 557 /* 558 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 559 "MXF_GET_MUTE Status %ld\n", Status) ); 560 */ 561 break; 562 563 case MXF_SET_MUTE : 564 Status = SetAudioMute(pMixerFunction); 565 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 566 "MXF_SET_MUTE Status %ld\n", Status) ); 567 break; 568 569 case MXF_GET_MONITOR_MUTE : 570 Status = 571 GetAudioMonitorMute( pMixerFunction->Channel.wChannel, 572 pMixerFunction->Data.Monitor.wBusOut, 573 pMixerFunction->Data.Monitor.Data.bMuteOn ); 574 /* 575 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 576 "MXF_GET_MONITOR_MUTE Status %ld\n", Status) ); 577 */ 578 break; 579 580 case MXF_SET_MONITOR_MUTE : 581 Status = 582 SetAudioMonitorMute( pMixerFunction->Channel.wChannel, 583 pMixerFunction->Data.Monitor.wBusOut, 584 pMixerFunction->Data.Monitor.Data.bMuteOn ); 585 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 586 "MXF_SET_MONITOR_MUTE Status %ld\n", Status) ); 587 break; 588 589 case MXF_GET_MONITOR_PAN : 590 Status = 591 GetAudioMonitorPan( pMixerFunction->Channel.wChannel, 592 pMixerFunction->Data.Monitor.wBusOut, 593 pMixerFunction->Data.Monitor.Data.iPan); 594 /* 595 596 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 597 "MXF_GET_MONITOR_PAN Status %ld\n", Status) ); 598 */ 599 600 break; 601 case MXF_SET_MONITOR_PAN : 602 Status = 603 SetAudioMonitorPan( pMixerFunction->Channel.wChannel, 604 pMixerFunction->Data.Monitor.wBusOut, 605 pMixerFunction->Data.Monitor.Data.iPan ); 606 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 607 "MXF_SET_MONITOR_PAN Status %ld\n", Status) ); 608 break; 609 610 case MXF_GET_FLAGS : 611 pMixerFunction->Data.wFlags = GetFlags(); 612 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 613 "MXF_GET_FLAGS 0x%x Status %ld\n", 614 pMixerFunction->Data.wFlags, 615 Status) ); 616 break; 617 case MXF_SET_FLAGS : 618 SetFlags( pMixerFunction->Data.wFlags ); 619 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 620 "MXF_SET_FLAGS 0x%x Status %ld\n", 621 pMixerFunction->Data.wFlags, 622 Status) ); 623 break; 624 case MXF_CLEAR_FLAGS : 625 ClearFlags( pMixerFunction->Data.wFlags ); 626 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 627 "MXF_CLEAR_FLAGS 0x%x Status %ld\n", 628 pMixerFunction->Data.wFlags, 629 Status) ); 630 break; 631 632 case MXF_GET_SAMPLERATE_LOCK : 633 GetAudioLockedSampleRate( pMixerFunction->Data.dwLockedSampleRate ); 634 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 635 "MXF_GET_SAMPLERATE_LOCK 0x%lx Status %ld\n", 636 pMixerFunction->Data.dwLockedSampleRate, 637 Status) ); 638 break; 639 640 case MXF_SET_SAMPLERATE_LOCK : 641 SetAudioLockedSampleRate( pMixerFunction->Data.dwLockedSampleRate ); 642 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 643 "MXF_SET_SAMPLERATE_LOCK 0x%lx Status %ld\n", 644 pMixerFunction->Data.dwLockedSampleRate, 645 Status) ); 646 break; 647 648 case MXF_GET_SAMPLERATE : 649 650 GetAudioSampleRate( &pMixerFunction->Data.dwSampleRate ); 651 652 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 653 "MXF_GET_SAMPLERATE 0x%lx Status %ld\n", 654 pMixerFunction->Data.dwSampleRate, 655 Status) ); 656 break; 657 658#ifdef MIDI_SUPPORT 659 660 case MXF_GET_MIDI_IN_ACTIVITY : 661 pMixerFunction->Data.bMidiActive = m_MidiIn.IsActive(); 662 663 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 664 "MXF_GET_MIDI_IN_ACTIVITY %s " 665 "Status %ld\n", 666 ( pMixerFunction->Data.bMidiActive ) 667 ? "ACTIVE" : "INACTIVE", 668 Status) ); 669 break; 670 671 672 case MXF_GET_MIDI_OUT_ACTIVITY : 673 pMixerFunction->Data.bMidiActive = 674 GetDspCommObject()->IsMidiOutActive(); 675 676 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 677 "MXF_GET_MIDI_OUT_ACTIVITY %s " 678 "Status %ld\n", 679 ( pMixerFunction->Data.bMidiActive ) 680 ? "ACTIVE" : "INACTIVE", 681 Status) ); 682 break; 683 684#endif // MIDI_SUPPORT 685 686 687 case MXF_GET_DIGITAL_MODE : 688 Status = ECHOSTATUS_OK; 689 pMixerFunction->Data.iDigMode = GetDigitalMode(); 690 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 691 "MXF_GET_DIGITAL_MODE %s " 692 "Status %ld\n", 693 ( DIGITAL_MODE_SPDIF_RCA == 694 pMixerFunction->Data.iDigMode ) 695 ? "S/PDIF RCA" 696 : ( DIGITAL_MODE_SPDIF_OPTICAL == 697 pMixerFunction->Data.iDigMode ) 698 ? "S/PDIF Optical" : "ADAT", 699 Status) ); 700 break; 701 702 703 case MXF_SET_DIGITAL_MODE : 704 Status = SetDigitalMode( (BYTE) pMixerFunction->Data.iDigMode ); 705 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 706 "MXF_SET_DIGITAL_MODE %s " 707 "Status %ld\n", 708 ( DIGITAL_MODE_SPDIF_RCA == 709 pMixerFunction->Data.iDigMode ) 710 ? "S/PDIF RCA" 711 : ( DIGITAL_MODE_SPDIF_OPTICAL == 712 pMixerFunction->Data.iDigMode ) 713 ? "S/PDIF Optical" : "ADAT", 714 Status) ); 715 break; 716 717 718 case MXF_GET_PAN : 719 Status = GetAudioPan( pMixerFunction); 720 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 721 "MXF_GET_PAN Status %ld\n", Status) ); 722 break; 723 724 case MXF_SET_PAN : 725 Status = SetAudioPan( pMixerFunction); 726 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 727 "MXF_SET_PAN Status %ld\n", Status) ); 728 break; 729 730#ifdef DIGITAL_INPUT_AUTO_MUTE_SUPPORT 731 732 case MXF_GET_DIG_IN_AUTO_MUTE : 733 Status = GetDigitalInAutoMute( pMixerFunction ); 734 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 735 "MXF_GET_DIG_IN_AUTO_MUTE Status %ld\n", Status) ); 736 break; 737 738 739 case MXF_SET_DIG_IN_AUTO_MUTE : 740 Status = SetDigitalInAutoMute( pMixerFunction ); 741 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 742 "MXF_SET_DIG_IN_AUTO_MUTE Status %ld\n", Status) ); 743 break; 744 745#endif // DIGITAL_INPUT_AUTO_MUTE_SUPPORT 746 747 case MXF_GET_AUDIO_LATENCY : 748 GetAudioLatency( &(pMixerFunction->Data.Latency) ); 749 break; 750 751#ifdef PHANTOM_POWER_CONTROL 752 753 case MXF_GET_PHANTOM_POWER : 754 GetPhantomPower( &(pMixerFunction->Data.fPhantomPower) ); 755 break; 756 757 case MXF_SET_PHANTOM_POWER : 758 SetPhantomPower( pMixerFunction->Data.fPhantomPower ); 759 break; 760 761#endif 762 763 default : 764 iRtnDataSz = 0; 765 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: " 766 "Function %ld not supported\n", 767 pMixerFunction->iFunction) ); 768 return ECHOSTATUS_NOT_SUPPORTED; 769 } 770 771 pMixerFunction->RtnStatus = Status; 772 iRtnDataSz = sizeof( MIXER_FUNCTION ); 773 774 return Status; 775 776} // ECHOSTATUS CEchoGals::ProcessMixerFunction 777 778 779 780 781//=========================================================================== 782// 783// Process multiple mixer functions 784// 785//=========================================================================== 786 787ECHOSTATUS CEchoGals::ProcessMixerMultiFunction 788( 789 PMIXER_MULTI_FUNCTION pMixerFunctions, // Request from mixer 790 INT32 & iRtnDataSz // # bytes returned (if any) 791) 792{ 793 ECHOSTATUS Status = ECHOSTATUS_NOT_SUPPORTED; 794 PMIXER_FUNCTION pMixerFunction; 795 INT32 iRtn, nCard, i; 796 797 if (m_fMixerDisabled) 798 return ECHOSTATUS_MIXER_DISABLED; 799 800 iRtnDataSz = sizeof( MIXER_MULTI_FUNCTION ) - sizeof( MIXER_FUNCTION ); 801 pMixerFunction = &pMixerFunctions->MixerFunction[ 0 ]; 802 nCard = pMixerFunction->Channel.wCardId; 803 for ( i = 0; i < pMixerFunctions->iCount; i++ ) 804 { 805 pMixerFunction = &pMixerFunctions->MixerFunction[ i ]; 806 if ( nCard != pMixerFunction->Channel.wCardId ) 807 { 808 ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerMultiFunction: " 809 "All functions MUST be for the same card " 810 "exp %ld act %d!\n", 811 nCard, 812 pMixerFunction->Channel.wCardId) ); 813 return ECHOSTATUS_NOT_SUPPORTED; 814 } 815 816 Status = ProcessMixerFunction(pMixerFunction,iRtn); 817 iRtnDataSz += iRtn; 818 } 819 820 return Status; 821 822} // ECHOSTATUS CEchoGals::ProcessMixerMultiFunction 823 824 825 826 827//=========================================================================== 828// 829// Typically, if you are writing a console, you will be polling the driver 830// to get the current peak and VU meters, clock detect bits, and 831// control changes. GetPolledStuff will fill out an ECHO_POLLED_STUFF 832// structure with all of those things. 833// 834//=========================================================================== 835 836ECHOSTATUS CEchoGals::GetPolledStuff 837( 838 ECHO_POLLED_STUFF *pPolledStuff, 839 NUINT MixerCookie 840) 841{ 842 ECHO_MIXER_CLIENT *pClient; 843 CDspCommObject *pDCO = GetDspCommObject(); 844 845 if (m_fMixerDisabled) 846 return ECHOSTATUS_MIXER_DISABLED; 847 848 if (NULL == pDCO) 849 return ECHOSTATUS_DSP_DEAD; 850 851 // 852 // Fill out the non-client-specific portions of the struct 853 // 854 pDCO->GetAudioMeters(&(pPolledStuff->Meters)); 855 GetInputClockDetect(pPolledStuff->dwClockDetectBits); 856 857 // 858 // If there is a matching client, fill out the number 859 // of notifies 860 // 861 pClient = GetMixerClient(MixerCookie); 862 if (NULL == pClient) 863 pPolledStuff->dwNumPendingNotifies = 0; 864 else 865 pPolledStuff->dwNumPendingNotifies = pClient->dwCount; 866 867 return ECHOSTATUS_OK; 868 869} // GetPolledStuff 870 871 872//=========================================================================== 873// 874// Get the pan setting for an output pipe (virtual outputs only) 875// 876//=========================================================================== 877 878ECHOSTATUS CEchoGals::GetAudioPan 879( 880 PMIXER_FUNCTION pMF 881) 882{ 883 WORD wPipe; 884 WORD wBus; 885 ECHOSTATUS Status; 886 887 if ( (pMF->Channel.dwType != ECHO_PIPE_OUT) || 888 ( !HasVmixer() ) ) 889 return ECHOSTATUS_INVALID_CHANNEL; 890 891 wPipe = pMF->Channel.wChannel; 892 wBus = pMF->Data.PipeOut.wBusOut; 893 Status = m_PipeOutCtrl.GetPan(wPipe, 894 wBus, 895 pMF->Data.PipeOut.Data.iPan); 896 897 return Status; 898 899} // ECHOSTATUS CEchoGals::GetAudioPan 900 901 902//=========================================================================== 903// 904// Set the pan for an output pipe (virtual outputs only) 905// 906//=========================================================================== 907 908ECHOSTATUS CEchoGals::SetAudioPan 909( 910 PMIXER_FUNCTION pMF 911) 912{ 913 WORD wPipe; 914 WORD wBus; 915 ECHOSTATUS Status; 916 917 if ( (pMF->Channel.dwType != ECHO_PIPE_OUT) || 918 ( !HasVmixer() ) ) 919 return ECHOSTATUS_INVALID_CHANNEL; 920 921 wPipe = pMF->Channel.wChannel; 922 wBus = pMF->Data.PipeOut.wBusOut; 923 Status = m_PipeOutCtrl.SetPan(wPipe, 924 wBus, 925 pMF->Data.PipeOut.Data.iPan); 926 927 return Status; 928 929} // ECHOSTATUS CEchoGals::SetAudioPan 930 931 932 933 934/**************************************************************************** 935 936 CEchoGals clock control 937 938 The input clock is the sync source - is the audio for this card running 939 off of the internal clock, synced to word clock, etc. 940 941 Output clock is only supported on Layla20 - Layla20 can transmit either 942 word or super clock. 943 944 ****************************************************************************/ 945 946//=========================================================================== 947// 948// Get input and output clocks - just return the stored value 949// 950//=========================================================================== 951 952ECHOSTATUS CEchoGals::GetInputClock(WORD &wClock) 953{ 954 if ( NULL == GetDspCommObject() ) 955 return ECHOSTATUS_DSP_DEAD; 956 957 wClock = GetDspCommObject()->GetInputClock(); 958 959 return ECHOSTATUS_OK; 960} 961 962 963ECHOSTATUS CEchoGals::GetOutputClock(WORD &wClock) 964{ 965 if ( NULL == GetDspCommObject() ) 966 return ECHOSTATUS_DSP_DEAD; 967 968 wClock = GetDspCommObject()->GetOutputClock(); 969 970 return ECHOSTATUS_OK; 971} 972 973 974//=========================================================================== 975// 976// Set input and output clocks - pass it down to the comm page and 977// store the control change. 978// 979//=========================================================================== 980 981ECHOSTATUS CEchoGals::SetInputClock(WORD wClock) 982{ 983 ECHOSTATUS Status; 984 985 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 986 return ECHOSTATUS_DSP_DEAD; 987 988 ECHO_DEBUGPRINTF( ("CEchoGals::SetInputClock: ") ); 989 990 Status = GetDspCommObject()->SetInputClock( wClock ); 991 992 if (ECHOSTATUS_OK == Status) 993 { 994 MixerControlChanged( ECHO_NO_CHANNEL_TYPE, 995 MXN_INPUT_CLOCK); 996 } 997 return Status; 998 999} // SetInputClock 1000 1001 1002ECHOSTATUS CEchoGals::SetOutputClock(WORD wClock) 1003{ 1004 ECHOSTATUS Status; 1005 1006 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 1007 return ECHOSTATUS_DSP_DEAD; 1008 1009 ECHO_DEBUGPRINTF( ("CEchoGals::SetOutputClock: ") ); 1010 1011 Status = GetDspCommObject()->SetOutputClock( wClock ); 1012 1013 if (ECHOSTATUS_OK == Status) 1014 { 1015 MixerControlChanged( ECHO_NO_CHANNEL_TYPE, 1016 MXN_OUTPUT_CLOCK); 1017 } 1018 return Status; 1019 1020} 1021 1022 1023//=========================================================================== 1024// 1025// Get the currently detected clock bits - default method. Overridden by 1026// derived card classes. 1027// 1028//=========================================================================== 1029 1030ECHOSTATUS CEchoGals::GetInputClockDetect(DWORD &dwClockDetectBits) 1031{ 1032 dwClockDetectBits = ECHO_CLOCK_INTERNAL; 1033 1034 return ECHOSTATUS_OK; 1035} 1036 1037 1038//=========================================================================== 1039// 1040// Set the locked sample rate 1041// 1042//=========================================================================== 1043 1044ECHOSTATUS CEchoGals::SetAudioLockedSampleRate 1045( 1046 DWORD dwSampleRate 1047) 1048{ 1049 ECHOSTATUS Status; 1050 1051 Status = QueryAudioSampleRate( dwSampleRate ); 1052 if ( ECHOSTATUS_OK != Status ) 1053 return Status; 1054 1055 if (0 != (ECHOGALS_FLAG_SAMPLE_RATE_LOCKED & GetFlags())) 1056 { 1057 GetDspCommObject()->SetSampleRate( dwSampleRate ); 1058 m_dwSampleRate = dwSampleRate; 1059 } 1060 1061 m_dwLockedSampleRate = dwSampleRate; 1062 1063 return ECHOSTATUS_OK; 1064 1065} // ECHOSTATUS CEchoGals::SetAudioLockedSampleRate 1066 1067 1068//=========================================================================== 1069// 1070// Get the locked sample rate 1071// 1072//=========================================================================== 1073 1074ECHOSTATUS CEchoGals::GetAudioLockedSampleRate 1075( 1076 DWORD &dwSampleRate 1077) 1078{ 1079 dwSampleRate = m_dwLockedSampleRate; 1080 1081 return ECHOSTATUS_OK; 1082 1083} // ECHOSTATUS CEchoGals::GetAudioLockedSampleRate 1084 1085 1086 1087#ifdef DIGITAL_INPUT_AUTO_MUTE_SUPPORT 1088 1089//=========================================================================== 1090// 1091// Get the digital input auto mute flag from the comm page 1092// 1093//=========================================================================== 1094 1095ECHOSTATUS CEchoGals::GetDigitalInAutoMute(PMIXER_FUNCTION pMixerFunction) 1096{ 1097 BOOL fAutoMute; 1098 1099 if (0 == (m_wFlags & ECHOGALS_ROFLAG_DIGITAL_IN_AUTOMUTE)) 1100 { 1101 pMixerFunction->Data.fDigitalInAutoMute = FALSE; 1102 return ECHOSTATUS_NOT_SUPPORTED; 1103 } 1104 1105 GetDspCommObject()->GetDigitalInputAutoMute( fAutoMute ); 1106 pMixerFunction->Data.fDigitalInAutoMute = fAutoMute; 1107 1108 return ECHOSTATUS_OK; 1109 1110} // GetDigitalInAutoMute 1111 1112 1113//=========================================================================== 1114// 1115// Set the digital input auto mute flag 1116// 1117//=========================================================================== 1118 1119ECHOSTATUS CEchoGals::SetDigitalInAutoMute(PMIXER_FUNCTION pMixerFunction) 1120{ 1121 BOOL fAutoMute; 1122 1123 if (0 == (m_wFlags & ECHOGALS_ROFLAG_DIGITAL_IN_AUTOMUTE)) 1124 return ECHOSTATUS_NOT_SUPPORTED; 1125 1126 fAutoMute = pMixerFunction->Data.fDigitalInAutoMute; 1127 GetDspCommObject()->SetDigitalInputAutoMute( fAutoMute ); 1128 1129 return ECHOSTATUS_OK; 1130 1131} // SetDigitalInAutoMute 1132 1133#endif // DIGITAL_INPUT_AUTO_MUTE_SUPPORT 1134 1135 1136//=========================================================================== 1137// 1138// Get the gain for an output bus, input bus, or output pipe. 1139// 1140// Gain levels are in units of dB * 256. 1141// 1142//=========================================================================== 1143 1144ECHOSTATUS CEchoGals::GetAudioLineLevel 1145( 1146 PMIXER_FUNCTION pMF 1147) 1148{ 1149 WORD wPipe; 1150 WORD wBus; 1151 ECHOSTATUS Status; 1152 1153 switch (pMF->Channel.dwType) 1154 { 1155 case ECHO_BUS_OUT : 1156 1157 wBus = pMF->Channel.wChannel; 1158 1159 if (wBus < GetNumBussesOut()) 1160 { 1161 pMF->Data.iLevel = m_BusOutLineLevels[wBus].GetGain(); 1162 Status = ECHOSTATUS_OK; 1163 } 1164 else 1165 { 1166 Status = ECHOSTATUS_INVALID_CHANNEL; 1167 } 1168 1169 break; 1170 1171 case ECHO_BUS_IN : 1172 1173 wBus = pMF->Channel.wChannel; 1174 if (wBus < GetNumBussesIn()) 1175 { 1176 pMF->Data.iLevel = m_BusInLineLevels[wBus].GetGain(); 1177 Status = ECHOSTATUS_OK; 1178 } 1179 else 1180 { 1181 Status = ECHOSTATUS_INVALID_CHANNEL; 1182 } 1183 break; 1184 1185 case ECHO_PIPE_OUT : 1186 1187 wPipe = pMF->Channel.wChannel; 1188 wBus = pMF->Data.PipeOut.wBusOut; 1189 Status = m_PipeOutCtrl.GetGain( wPipe, 1190 wBus, 1191 pMF->Data.PipeOut.Data.iLevel); 1192 break; 1193 1194 default: 1195 Status = ECHOSTATUS_INVALID_PARAM; 1196 break; 1197 } 1198 1199 return Status; 1200 1201} // ECHOSTATUS CEchoGals::GetAudioLineLevel 1202 1203 1204//=========================================================================== 1205// 1206// Utility function to check that a setting is within the correct range. 1207// 1208//=========================================================================== 1209 1210ECHOSTATUS CheckSetting(INT32 iValue,INT32 iMin,INT32 iMax) 1211{ 1212 if ( (iValue > iMax) || (iValue < iMin)) 1213 return ECHOSTATUS_INVALID_PARAM; 1214 1215 return ECHOSTATUS_OK; 1216 1217} // CheckSetting 1218 1219 1220//=========================================================================== 1221// 1222// Set the gain for an output bus, input bus, or output pipe. 1223// 1224// Gain levels are in units of dB * 256. 1225// 1226//=========================================================================== 1227 1228ECHOSTATUS CEchoGals::SetAudioLineLevel 1229( 1230 PMIXER_FUNCTION pMF 1231) 1232{ 1233 WORD wPipe; 1234 WORD wBus; 1235 ECHOSTATUS Status; 1236 INT32 iLevel; 1237 1238 switch (pMF->Channel.dwType) 1239 { 1240 case ECHO_BUS_OUT : 1241 1242 wBus = pMF->Channel.wChannel; 1243 iLevel = pMF->Data.iLevel; 1244 1245 Status = CheckSetting(iLevel,ECHOGAIN_MINOUT,ECHOGAIN_MAXOUT); 1246 if (ECHOSTATUS_OK != Status) 1247 break; 1248 1249 Status = m_BusOutLineLevels[wBus].SetGain(iLevel); 1250 break; 1251 1252 case ECHO_BUS_IN : 1253 1254 wBus = pMF->Channel.wChannel; 1255 iLevel = pMF->Data.iLevel; 1256 1257 Status = CheckSetting(iLevel,ECHOGAIN_MININP,ECHOGAIN_MAXINP); 1258 if (ECHOSTATUS_OK != Status) 1259 break; 1260 1261 Status = m_BusInLineLevels[wBus].SetGain(iLevel); 1262 break; 1263 1264 case ECHO_PIPE_OUT : 1265 1266 wPipe = pMF->Channel.wChannel; 1267 wBus = pMF->Data.PipeOut.wBusOut; 1268 iLevel = pMF->Data.PipeOut.Data.iLevel; 1269 1270 Status = CheckSetting(iLevel,ECHOGAIN_MINOUT,ECHOGAIN_MAXOUT); 1271 if (ECHOSTATUS_OK != Status) 1272 break; 1273 1274 Status = m_PipeOutCtrl.SetGain( wPipe, 1275 wBus, 1276 iLevel); 1277 break; 1278 1279 default: 1280 Status = ECHOSTATUS_INVALID_PARAM; 1281 break; 1282 } 1283 1284 return Status; 1285 1286} // ECHOSTATUS CEchoGals::SetAudioLineLevel 1287 1288 1289//=========================================================================== 1290// 1291// Get the nominal level for an output or input bus. The nominal level is 1292// also referred to as the +4/-10 switch. 1293// 1294//=========================================================================== 1295 1296ECHOSTATUS CEchoGals::GetAudioNominal 1297( 1298 PMIXER_FUNCTION pMF 1299) 1300{ 1301 BYTE byNominal; 1302 ECHOSTATUS Status; 1303 CDspCommObject * pDspCommObj = GetDspCommObject(); 1304 WORD wCh; 1305 1306 if ( NULL == pDspCommObj || pDspCommObj->IsBoardBad() ) 1307 return ECHOSTATUS_DSP_DEAD; 1308 1309 switch (pMF->Channel.dwType) 1310 { 1311 case ECHO_BUS_OUT : 1312 wCh = pMF->Channel.wChannel; 1313 break; 1314 1315 case ECHO_BUS_IN : 1316 wCh = pMF->Channel.wChannel + GetNumBussesOut(); 1317 break; 1318 1319 default : 1320 return ECHOSTATUS_INVALID_CHANNEL; 1321 } 1322 1323 Status = pDspCommObj->GetNominalLevel( wCh, &byNominal ); 1324 1325 if ( ECHOSTATUS_OK != Status ) 1326 return Status; 1327 1328 pMF->Data.iNominal = ( byNominal ) ? -10 : 4; 1329 1330 return ECHOSTATUS_OK; 1331} // ECHOSTATUS CEchoGals::GetAudioNominal 1332 1333 1334//=========================================================================== 1335// 1336// Set the nominal level for an output or input bus. The nominal level is 1337// also referred to as the +4/-10 switch. 1338// 1339//=========================================================================== 1340 1341ECHOSTATUS CEchoGals::SetAudioNominal 1342( 1343 PMIXER_FUNCTION pMF 1344) 1345{ 1346 ECHOSTATUS Status; 1347 WORD wCh; 1348 INT32 iNominal; 1349 1350 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 1351 return ECHOSTATUS_DSP_DEAD; 1352 1353 switch (pMF->Channel.dwType) 1354 { 1355 case ECHO_BUS_OUT : 1356 wCh = pMF->Channel.wChannel; 1357 break; 1358 1359 case ECHO_BUS_IN : 1360 wCh = pMF->Channel.wChannel + GetNumBussesOut(); 1361 break; 1362 1363 default : 1364 return ECHOSTATUS_INVALID_CHANNEL; 1365 } 1366 1367 iNominal = pMF->Data.iNominal; 1368 1369 if ((iNominal!= -10) && (iNominal != 4)) 1370 return ECHOSTATUS_INVALID_PARAM; 1371 1372 Status = 1373 GetDspCommObject()->SetNominalLevel( wCh, 1374 ( iNominal == -10 ) ); 1375 1376 if ( ECHOSTATUS_OK != Status ) 1377 return Status; 1378 1379 Status = MixerControlChanged( (WORD) pMF->Channel.dwType, 1380 MXN_NOMINAL, 1381 pMF->Channel.wChannel); 1382 return Status; 1383 1384} // ECHOSTATUS CEchoGals::SetAudioNominal 1385 1386 1387//=========================================================================== 1388// 1389// Set the mute for an output bus, input bus, or output pipe. 1390// 1391//=========================================================================== 1392 1393ECHOSTATUS CEchoGals::SetAudioMute 1394( 1395 PMIXER_FUNCTION pMF 1396) 1397{ 1398 WORD wPipe; 1399 WORD wBus; 1400 ECHOSTATUS Status; 1401 BOOL bMute; 1402 1403 switch (pMF->Channel.dwType) 1404 { 1405 case ECHO_BUS_OUT : 1406 1407 wBus = pMF->Channel.wChannel; 1408 bMute = pMF->Data.bMuteOn; 1409 Status = m_BusOutLineLevels[wBus].SetMute(bMute); 1410 break; 1411 1412 case ECHO_BUS_IN : 1413 1414 wBus = pMF->Channel.wChannel; 1415 bMute = pMF->Data.bMuteOn; 1416 Status = m_BusInLineLevels[wBus].SetMute(bMute); 1417 break; 1418 1419 case ECHO_PIPE_OUT : 1420 1421 wPipe = pMF->Channel.wChannel; 1422 wBus = pMF->Data.PipeOut.wBusOut; 1423 bMute = pMF->Data.PipeOut.Data.bMuteOn; 1424 Status = m_PipeOutCtrl.SetMute( wPipe, 1425 wBus, 1426 bMute); 1427 break; 1428 1429 default: 1430 Status = ECHOSTATUS_INVALID_PARAM; 1431 break; 1432 } 1433 1434 return Status; 1435} // ECHOSTATUS CEchoGals::SetAudioMute 1436 1437 1438//=========================================================================== 1439// 1440// Get the mute for an output bus, input bus, or output pipe. 1441// 1442//=========================================================================== 1443 1444ECHOSTATUS CEchoGals::GetAudioMute 1445( 1446 PMIXER_FUNCTION pMF 1447) 1448{ 1449 WORD wPipe; 1450 WORD wBus; 1451 ECHOSTATUS Status; 1452 1453 switch (pMF->Channel.dwType) 1454 { 1455 case ECHO_BUS_OUT : 1456 1457 wBus = pMF->Channel.wChannel; 1458 1459 if (wBus < GetNumBussesOut()) 1460 { 1461 pMF->Data.bMuteOn = m_BusOutLineLevels[wBus].IsMuteOn(); 1462 Status = ECHOSTATUS_OK; 1463 } 1464 else 1465 { 1466 Status = ECHOSTATUS_INVALID_CHANNEL; 1467 } 1468 1469 break; 1470 1471 case ECHO_BUS_IN : 1472 1473 wBus = pMF->Channel.wChannel; 1474 1475 if (wBus < GetNumBussesIn()) 1476 { 1477 pMF->Data.bMuteOn = m_BusInLineLevels[wBus].IsMuteOn(); 1478 Status = ECHOSTATUS_OK; 1479 } 1480 else 1481 { 1482 Status = ECHOSTATUS_INVALID_CHANNEL; 1483 } 1484 break; 1485 1486 case ECHO_PIPE_OUT : 1487 1488 wPipe = pMF->Channel.wChannel; 1489 wBus = pMF->Data.PipeOut.wBusOut; 1490 Status = m_PipeOutCtrl.GetMute( wPipe, 1491 wBus, 1492 pMF->Data.PipeOut.Data.bMuteOn); 1493 break; 1494 1495 default: 1496 Status = ECHOSTATUS_INVALID_PARAM; 1497 break; 1498 } 1499 1500 return Status; 1501 1502} // ECHOSTATUS CEchoGals::GetAudioMute 1503 1504 1505//=========================================================================== 1506// 1507// Get the monitor gain for a single input bus mixed to a single output bus. 1508// 1509//=========================================================================== 1510 1511ECHOSTATUS CEchoGals::GetAudioMonitor 1512( 1513 WORD wBusIn, 1514 WORD wBusOut, 1515 INT32 & iGain 1516) 1517{ 1518 if ( wBusIn >= GetNumBussesIn() || 1519 wBusOut >= GetNumBussesOut() ) 1520 { 1521 return ECHOSTATUS_INVALID_INDEX; 1522 } 1523 1524 // 1525 // Get the monitor value 1526 // 1527 m_MonitorCtrl.GetGain(wBusIn,wBusOut,iGain); 1528 1529 return ECHOSTATUS_OK; 1530 1531} // ECHOSTATUS CEchoGals::GetAudioMonitor 1532 1533 1534//=========================================================================== 1535// 1536// Set the monitor gain for a single input bus mixed to a single output bus. 1537// 1538//=========================================================================== 1539 1540ECHOSTATUS CEchoGals::SetAudioMonitor 1541( 1542 WORD wBusIn, 1543 WORD wBusOut, 1544 INT32 iGain 1545) 1546{ 1547 ECHOSTATUS Status; 1548 1549 if ( wBusIn >= GetNumBussesIn() || 1550 wBusOut >= GetNumBussesOut() ) 1551 { 1552 return ECHOSTATUS_INVALID_INDEX; 1553 } 1554 1555 Status = CheckSetting(iGain,ECHOGAIN_MINOUT,ECHOGAIN_MAXOUT); 1556 if (ECHOSTATUS_OK == Status) 1557 { 1558 // 1559 // Set the monitor gain 1560 // 1561 m_MonitorCtrl.SetGain(wBusIn,wBusOut,iGain); 1562 } 1563 1564 return Status; 1565 1566} // ECHOSTATUS CEchoGals::SetAudioMonitor 1567 1568 1569//=========================================================================== 1570// 1571// Helper functions for doing log conversions on pan values 1572// 1573// The parameter iNum is a fixed point 32 bit number in 16.16 format; 1574// that is, 16 bits of integer and 16 bits of decimal 1575// To convert a float number to fixed point, simply multiply by 2^16 and round 1576// 1577// Valid range for iNum is from +1.0 (0x10000) to 0. 1578// 1579//=========================================================================== 1580 1581#define FIXED_BASE 16 // 16 bits of fraction 1582#define FIXED_ONE_HALF ((INT32) 0x00008000) // 0.5 in 16.16 format 1583#define COEFF_A2 ((INT32) 0xffffa9ac) // -.3372223 1584#define COEFF_A1 ((INT32) 0x0000ff8a) // .9981958 1585#define COEFF_A0 ((INT32) 0xffff5661) // -.6626105 1586 1587#define DB_CONVERT 0x60546 // 6.02... in 16.16 1588 1589// Note use of double precision here to prevent overflow 1590static INT32 FixedMult( INT32 iNum1, INT32 iNum2 ) 1591{ 1592 LONGLONG llNum; 1593 1594 llNum = (LONGLONG) iNum1 * (LONGLONG) iNum2; 1595 1596 return (INT32) (llNum >> FIXED_BASE); 1597} // INT32 FixedMult( INT32 iNum1, INT32 iNum2 ) 1598 1599 1600static INT32 log2( INT32 iNum ) 1601{ 1602 INT32 iNumShifts; 1603 INT32 iTemp; 1604 1605 // log2 is undefined for zero, so return -infinity (or close enough) 1606 if ( 0 == iNum ) 1607 return ECHOGAIN_MUTED; 1608 1609 // Step 1 - Normalize and save the number of shifts 1610 // Keep shifting iNum up until iNum > 0.5 1611 iNumShifts = 0; 1612 while ( iNum < FIXED_ONE_HALF ) 1613 { 1614 iNumShifts++; 1615 iNum <<= 1; 1616 } 1617 1618 // Step 2 - Calculate LOG2 by polynomial approximation. 8 bit accuracy. 1619 // 1620 // LOG2(x) = 4.0* (-.3372223 x*x + .9981958 x - .6626105) 1621 // a2 a1 a0 1622 // where 0.5 <= x < 1.0 1623 // 1624 1625 // Compute polynomial sum 1626 iTemp = FixedMult( iNum, iNum ); // iTemp now has iNum squared 1627 iTemp = FixedMult( iTemp, COEFF_A2 ); 1628 iTemp += FixedMult( iNum, COEFF_A1 ); 1629 iTemp += COEFF_A0; 1630 1631 // Multiply by four 1632 iTemp <<= 2; 1633 1634 // Account for the normalize shifts 1635 iTemp -= ( iNumShifts << FIXED_BASE ); 1636 1637 return( iTemp ); 1638} // INT32 log2( INT32 iNum ) 1639 1640 1641// 1642// Convert pan value to Db X 256 1643// Pan value is 0 - MAX_MIXER_PAN 1644// 1645INT32 PanToDb( INT32 iPan ) 1646{ 1647 if ( iPan >= ( MAX_MIXER_PAN - 1 ) ) 1648 return( 0 ); 1649 if ( iPan <= 1 ) 1650 return( ECHOGAIN_MUTED ); 1651 // 1652 // Convert pan to 16.16 1653 // 1654 iPan = ( iPan << 16 ) / MAX_MIXER_PAN; 1655 // 1656 // Take the log 1657 // 1658 iPan = log2( iPan ); 1659 // 1660 // To convert to decibels*256, just multiply by the conversion factor 1661 // 1662 iPan = FixedMult( iPan << 8, DB_CONVERT ); 1663 // 1664 // To round, add one half and then mask off the fractional bits 1665 // 1666 iPan = ( iPan + FIXED_ONE_HALF ) >> FIXED_BASE; 1667 return( iPan ); 1668} // INT32 PanToDb( INT32 iPan ) 1669 1670 1671//=========================================================================== 1672// 1673// Set the monitor pan 1674// 1675// For this to work effectively, both the input and output channels must 1676// both either be odd or even. Thus even channel numbers are for the 1677// left channel and odd channel numbers are for the right channel. 1678// Pan values will be computed for both channels. 1679// 1680// iPan ranges from 0 (hard left) to MAX_MIXER_PAN (hard right) 1681// 1682//=========================================================================== 1683 1684ECHOSTATUS CEchoGals::SetAudioMonitorPan 1685( 1686 WORD wBusIn, 1687 WORD wBusOut, 1688 INT32 iPan // New pan 1689) 1690{ 1691 ECHOSTATUS Status; 1692 1693 if ( wBusIn >= GetNumBussesIn() || 1694 wBusOut >= GetNumBussesOut() ) 1695 { 1696 return ECHOSTATUS_INVALID_INDEX; 1697 } 1698 1699 Status = CheckSetting(iPan,0,MAX_MIXER_PAN); 1700 if (ECHOSTATUS_OK == Status) 1701 { 1702 // 1703 // Set the pan 1704 // 1705 m_MonitorCtrl.SetPan(wBusIn,wBusOut,iPan); 1706 } 1707 1708 return Status; 1709 1710} // ECHOSTATUS CEchoGals::SetAudioMonitorPan 1711 1712 1713//=========================================================================== 1714// 1715// Get the monitor pan 1716// 1717//=========================================================================== 1718 1719ECHOSTATUS CEchoGals::GetAudioMonitorPan 1720( 1721 WORD wBusIn, 1722 WORD wBusOut, 1723 INT32 & iPan // Returns current pan (0 - MAX_MIXER_PAN) 1724) 1725{ 1726 if ( wBusIn >= GetNumBussesIn() || 1727 wBusOut >= GetNumBussesOut() ) 1728 { 1729 return ECHOSTATUS_INVALID_INDEX; 1730 } 1731 1732 // 1733 // Get the pan 1734 // 1735 m_MonitorCtrl.GetPan(wBusIn,wBusOut,iPan); 1736 1737 return ECHOSTATUS_OK; 1738 1739} // ECHOSTATUS CEchoGals::GetAudioMonitorPan 1740 1741 1742//=========================================================================== 1743// 1744// Set the monitor mute 1745// 1746//=========================================================================== 1747 1748ECHOSTATUS CEchoGals::SetAudioMonitorMute 1749( 1750 WORD wBusIn, 1751 WORD wBusOut, 1752 BOOL bMute // New state 1753) 1754{ 1755 if ( wBusIn >= GetNumBussesIn() || 1756 wBusOut >= GetNumBussesOut() ) 1757 { 1758 return ECHOSTATUS_INVALID_INDEX; 1759 } 1760 1761 // 1762 // Set the mute 1763 // 1764 m_MonitorCtrl.SetMute(wBusIn,wBusOut,bMute); 1765 1766 return ECHOSTATUS_OK; 1767 1768} // ECHOSTATUS CEchoGals::SetAudioMonitorMute 1769 1770 1771//=========================================================================== 1772// 1773// Get the monitor mute 1774// 1775//=========================================================================== 1776 1777ECHOSTATUS CEchoGals::GetAudioMonitorMute 1778( 1779 WORD wBusIn, 1780 WORD wBusOut, 1781 BOOL &bMute // Returns current state 1782) 1783{ 1784 if ( wBusIn >= GetNumBussesIn() || 1785 wBusOut >= GetNumBussesOut() ) 1786 { 1787 return ECHOSTATUS_INVALID_INDEX; 1788 } 1789 1790 // 1791 // Get the mute 1792 // 1793 m_MonitorCtrl.GetMute(wBusIn,wBusOut,bMute); 1794 1795 return ECHOSTATUS_OK; 1796 1797} // ECHOSTATUS CEchoGals::GetAudioMonitorMute 1798 1799 1800//=========================================================================== 1801// 1802// Set the S/PDIF output format to professional or consumer 1803// 1804//=========================================================================== 1805 1806void CEchoGals::SetProfessionalSpdif( BOOL bNewStatus ) 1807{ 1808 ECHO_DEBUGPRINTF(("CEchoGals::SetProfessionalSpdif %d\n",bNewStatus)); 1809 1810 if ( NULL != GetDspCommObject() ) 1811 { 1812 GetDspCommObject()->SetProfessionalSpdif( bNewStatus ); 1813 MixerControlChanged( ECHO_NO_CHANNEL_TYPE, 1814 MXN_SPDIF ); 1815 } 1816} // void CEchoGals::SetProfessionalSpdif( BOOL bNewStatus ) 1817 1818 1819//=========================================================================== 1820// 1821// Set driver flags 1822// 1823// See ECHOGALS_FLAG_??? definitions in EchoGalsXface.h 1824// 1825//=========================================================================== 1826 1827ECHOSTATUS CEchoGals::SetFlags 1828( 1829 WORD wFlags 1830) 1831{ 1832 // 1833 // Mask off the read-only flags so they don't change 1834 // 1835 wFlags &= ECHOGALS_FLAG_WRITABLE_MASK; 1836 1837 // 1838 // Set the flags & mark the flags as changed 1839 // 1840 m_wFlags |= wFlags; 1841 1842 MixerControlChanged( ECHO_NO_CHANNEL_TYPE, 1843 MXN_FLAGS ); 1844 1845 return ECHOSTATUS_OK; 1846 1847} // ECHOSTATUS CEchoGals::SetFlags 1848 1849 1850//=========================================================================== 1851// 1852// Clear driver flags 1853// 1854// See ECHOGALS_FLAG_??? definitions in EchoGalsXface.h 1855// 1856//=========================================================================== 1857 1858ECHOSTATUS CEchoGals::ClearFlags 1859( 1860 WORD wFlags 1861) 1862{ 1863 // 1864 // Mask off the read-only flags so they don't change 1865 // 1866 wFlags &= ECHOGALS_FLAG_WRITABLE_MASK; 1867 1868 // 1869 // Clear the flags & mark the flags as changed 1870 // 1871 m_wFlags &= ~wFlags; 1872 1873 MixerControlChanged( ECHO_NO_CHANNEL_TYPE, 1874 MXN_FLAGS ); 1875 1876 return ECHOSTATUS_OK; 1877 1878} // ECHOSTATUS CEchoGals::ClearFlags 1879 1880 1881//=========================================================================== 1882// 1883// Set the digital mode - currently for Gina24, Layla24, and Mona 1884// 1885//=========================================================================== 1886 1887ECHOSTATUS CEchoGals::SetDigitalMode 1888( 1889 BYTE byDigitalMode 1890) 1891{ 1892 ECHOSTATUS Status; 1893 BYTE byPreviousDigitalMode; 1894 1895 if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) 1896 return ECHOSTATUS_DSP_DEAD; 1897 1898 if ( 0 == GetDspCommObject()->GetDigitalModes() ) 1899 return ECHOSTATUS_DIGITAL_MODE_NOT_SUPPORTED; 1900 1901 if ( TRUE == GetDspCommObject()->IsTransportActive() ) 1902 { 1903 ECHO_DEBUGPRINTF( ( "CEchoGals::SetDigitalMode() Cannot set the digital " 1904 "mode while transport is running\n")); 1905 return ECHOSTATUS_BUSY; 1906 } 1907 byPreviousDigitalMode = GetDspCommObject()->GetDigitalMode(); 1908 Status = GetDspCommObject()->SetDigitalMode( byDigitalMode ); 1909 MixerControlChanged( ECHO_NO_CHANNEL_TYPE, 1910 MXN_DIGITAL_MODE ); 1911 MixerControlChanged( ECHO_NO_CHANNEL_TYPE, 1912 MXN_INPUT_CLOCK ); 1913 1914 // 1915 // If we successfully changed the digital mode from or to ADAT, then 1916 // make sure all output, input and monitor levels are updated by the 1917 // DSP comm object. 1918 // 1919 if ( ECHOSTATUS_OK == Status && 1920 byPreviousDigitalMode != byDigitalMode && 1921 ( DIGITAL_MODE_ADAT == byPreviousDigitalMode || 1922 DIGITAL_MODE_ADAT == byDigitalMode ) ) 1923 { 1924 WORD i, j,wBus,wPipe; 1925 1926 for ( i = 0; i < GetNumBussesIn(); i++ ) 1927 { 1928 for ( j = 0; j < GetNumBussesOut(); j += 2 ) 1929 { 1930 m_MonitorCtrl.SetGain(i,j,ECHOGAIN_UPDATE,FALSE); 1931 } 1932 } 1933 1934 for ( wBus = 0; wBus < GetNumBussesOut(); wBus++) 1935 { 1936 for ( wPipe = 0; wPipe < GetNumPipesOut(); wPipe++) 1937 { 1938 m_PipeOutCtrl.SetGain(wPipe,wBus,ECHOGAIN_UPDATE,FALSE); 1939 } 1940 } 1941 1942 for ( i = 0; i < GetNumBussesOut(); i++ ) 1943 { 1944 m_BusOutLineLevels[ i ].SetGain(ECHOGAIN_UPDATE,FALSE); 1945 } 1946 1947 for ( i = 0; i < GetNumBussesIn(); i++ ) 1948 { 1949 m_BusInLineLevels[ i ].SetGain( ECHOGAIN_UPDATE ); 1950 } 1951 1952 // 1953 // Now set them all at once, since all the 1954 // fImmediate parameters were set to FALSE. Do the output 1955 // bus _and_ the output pipe in case this is a vmixer card. 1956 // 1957 m_BusOutLineLevels[0].SetGain(ECHOGAIN_UPDATE,TRUE); 1958 m_PipeOutCtrl.SetGain(0,0,ECHOGAIN_UPDATE,TRUE); 1959 } 1960 1961 // 1962 // If the card has just been put in ADAT mode, it is possible 1963 // that the locked sample rate is greater than 48KHz, which is not allowed 1964 // in ADAT mode. If this happens, change the locked rate to 48. 1965 // 1966 if ( (DIGITAL_MODE_ADAT == byDigitalMode) && 1967 (m_wFlags & ECHOGALS_FLAG_SAMPLE_RATE_LOCKED) && 1968 (m_dwLockedSampleRate > 48000) ) 1969 { 1970 m_dwLockedSampleRate = 48000; 1971 } 1972 1973 return Status; 1974 1975} // ECHOSTATUS CEchoGals::SetDigitalMode( ... ) 1976 1977 1978/* 1979 1980The output bus gain controls aren't actually implemented in the hardware; 1981insted they are virtual controls created by the generic code. 1982 1983The signal sent to an output bus is a mix of the monitors and output pipes 1984routed to that bus; the output bus gain is therefore implemented by tweaking 1985each appropriate monitor and output pipe gain. 1986 1987*/ 1988 1989 1990//=========================================================================== 1991// 1992// Adjust all the monitor levels for a particular output bus 1993// 1994// For efficiency, fImmediate is set to FALSE in the call 1995// to SetGain; all the monitor and pipe out gains are 1996// sent to the DSP at once by AdjustPipesOutForBusOut. 1997// 1998//=========================================================================== 1999 2000ECHOSTATUS CEchoGals::AdjustMonitorsForBusOut(WORD wBusOut) 2001{ 2002 WORD wBusIn; 2003 2004 // 2005 // Poke the monitors 2006 // 2007 for (wBusIn = 0; wBusIn < GetNumBussesIn(); wBusIn++) 2008 { 2009 m_MonitorCtrl.SetGain(wBusIn,wBusOut,ECHOGAIN_UPDATE,FALSE); 2010 } 2011 2012 return ECHOSTATUS_OK; 2013 2014} // AdjustMonitorsForBusOut 2015 2016 2017//=========================================================================== 2018// 2019// Adjust all the pipe out levels for a particular output bus 2020// 2021// For efficiency, fImmediate is set to FALSE in the call 2022// to SetGain; all the monitor and pipe out gains are 2023// sent to the DSP at once by AdjustPipesOutForBusOut. 2024// 2025//=========================================================================== 2026 2027ECHOSTATUS CEchoGals::AdjustPipesOutForBusOut(WORD wBusOut,INT32 iBusOutGain) 2028{ 2029 ECHO_DEBUGPRINTF(("CEchoGals::AdjustPipesOutForBusOut wBusOut %d iBusOutGain %ld\n", 2030 wBusOut, 2031 iBusOutGain)); 2032 2033 // 2034 // Round down to the nearest even bus 2035 // 2036 wBusOut &= 0xfffe; 2037 2038 m_PipeOutCtrl.SetGain(wBusOut,wBusOut,ECHOGAIN_UPDATE,FALSE); 2039 wBusOut++; 2040 m_PipeOutCtrl.SetGain(wBusOut,wBusOut,ECHOGAIN_UPDATE,TRUE); 2041 2042 return ECHOSTATUS_OK; 2043 2044} // AdjustPipesOutForBusOut 2045 2046 2047 2048//=========================================================================== 2049// 2050// GetAudioLatency - returns the latency for a single pipe 2051// 2052//=========================================================================== 2053 2054void CEchoGals::GetAudioLatency(ECHO_AUDIO_LATENCY *pLatency) 2055{ 2056 DWORD dwLatency; 2057 2058 if (FALSE == pLatency->wIsInput) 2059 { 2060 if (pLatency->wPipe >= GetFirstDigitalBusOut()) 2061 dwLatency = m_wDigitalOutputLatency; 2062 else 2063 dwLatency = m_wAnalogOutputLatency; 2064 } 2065 else 2066 { 2067 if (pLatency->wPipe >= GetFirstDigitalBusIn()) 2068 dwLatency = m_wDigitalInputLatency; 2069 else 2070 dwLatency = m_wAnalogInputLatency; 2071 } 2072 2073 pLatency->dwLatency = dwLatency; 2074 2075} // GetAudioLatency 2076