1 // **************************************************************************** 2 // 3 // CPipeOutCtrl.cpp 4 // 5 // Class to control output pipes on cards with or without vmixers. 6 // 7 // ---------------------------------------------------------------------------- 8 // 9 // This file is part of Echo Digital Audio's generic driver library. 10 // Copyright Echo Digital Audio Corporation (c) 1998 - 2005 11 // All rights reserved 12 // www.echoaudio.com 13 // 14 // This library is free software; you can redistribute it and/or 15 // modify it under the terms of the GNU Lesser General Public 16 // License as published by the Free Software Foundation; either 17 // version 2.1 of the License, or (at your option) any later version. 18 // 19 // This library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 // Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public 25 // License along with this library; if not, write to the Free Software 26 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 27 // 28 // **************************************************************************** 29 30 #include "CEchoGals.h" 31 #include "CPipeOutCtrl.h" 32 33 extern INT32 PanToDb( INT32 iPan ); 34 35 36 //***************************************************************************** 37 // 38 // Destructor (this class uses the default constructor) 39 // 40 //***************************************************************************** 41 42 CPipeOutCtrl::~CPipeOutCtrl() 43 { 44 Cleanup(); 45 } 46 47 48 //***************************************************************************** 49 // 50 // Init 51 // 52 //***************************************************************************** 53 54 ECHOSTATUS CPipeOutCtrl::Init(CEchoGals *pEG) 55 { 56 DWORD dwBytes; 57 WORD wPipe,wStereoBus; 58 59 m_Gains = NULL; 60 m_Mutes = NULL; 61 m_Pans = NULL; 62 m_PanDbs = NULL; 63 64 // 65 // Cache stuff 66 // 67 m_pEG = pEG; 68 m_wNumPipesOut = pEG->GetNumPipesOut(); 69 m_wNumBussesOut = pEG->GetNumBussesOut(); 70 m_fHasVmixer = pEG->HasVmixer(); 71 72 // 73 // Allocate the arrays 74 // 75 if (m_fHasVmixer) 76 { 77 WORD wNumStereoBusses = m_wNumBussesOut >> 1; 78 79 // 80 // Allocate arrays for vmixer support 81 // 82 dwBytes = sizeof(INT8) * m_wNumPipesOut * wNumStereoBusses; 83 OsAllocateNonPaged(dwBytes,(void **) &m_Gains); 84 if (NULL == m_Gains) 85 { 86 return ECHOSTATUS_NO_MEM; 87 } 88 89 dwBytes = sizeof(BYTE) * m_wNumPipesOut * wNumStereoBusses; 90 OsAllocateNonPaged(dwBytes,(void **) &m_Mutes); 91 if (NULL == m_Mutes) 92 { 93 Cleanup(); 94 return ECHOSTATUS_NO_MEM; 95 } 96 97 dwBytes = sizeof(WORD) * m_wNumPipesOut * wNumStereoBusses; 98 OsAllocateNonPaged(dwBytes,(void **) &m_Pans); 99 if (NULL == m_Pans) 100 { 101 Cleanup(); 102 return ECHOSTATUS_NO_MEM; 103 } 104 105 dwBytes = sizeof(PAN_DB) * m_wNumPipesOut * wNumStereoBusses; 106 OsAllocateNonPaged(dwBytes,(void **) &m_PanDbs); 107 if (NULL == m_PanDbs) 108 { 109 Cleanup(); 110 return ECHOSTATUS_NO_MEM; 111 } 112 113 // 114 // Initialize pans and mutes 115 // 116 for (wPipe = 0; wPipe < m_wNumPipesOut; wPipe++) 117 for (wStereoBus = 0; wStereoBus < wNumStereoBusses; wStereoBus++) 118 { 119 WORD wIndex; 120 121 wIndex = GetIndex(wPipe,wStereoBus << 1); 122 123 // 124 // Pans 125 // 126 if (0 == (wPipe & 1)) 127 { 128 // 129 // Even channel - pan hard left 130 // 131 m_Pans[wIndex] = 0; 132 m_PanDbs[wIndex].iLeft = 0; 133 m_PanDbs[wIndex].iRight = GENERIC_TO_DSP( ECHOGAIN_MUTED ); 134 } 135 else 136 { 137 // 138 // Odd channel - pan hard right 139 // 140 m_Pans[wIndex] = MAX_MIXER_PAN; 141 m_PanDbs[wIndex].iLeft = GENERIC_TO_DSP( ECHOGAIN_MUTED ); 142 m_PanDbs[wIndex].iRight = 0; 143 } 144 145 // 146 // Mutes 147 // 148 if ((wPipe >> 1) == wStereoBus) 149 { 150 m_Mutes[wIndex] = FALSE; 151 } 152 else 153 { 154 m_Mutes[wIndex] = TRUE; 155 } 156 157 // 158 // Set the gain to the DSP; use fImmedate = FALSE here 159 // to make this faster 160 // 161 SetGain(wPipe,wStereoBus << 1,ECHOGAIN_UPDATE,FALSE); 162 163 } 164 165 // 166 // Set the gain one more time with the immediate flag set to 167 // make sure the DSP gets the message 168 // 169 SetGain(0,0,ECHOGAIN_UPDATE,TRUE); 170 } 171 else 172 { 173 // 174 // Allocate arrays for no vmixer support - don't need pans 175 // 176 dwBytes = sizeof(INT8) * m_wNumPipesOut; 177 OsAllocateNonPaged(dwBytes,(void **) &m_Gains); 178 if (NULL == m_Gains) 179 { 180 return ECHOSTATUS_NO_MEM; 181 } 182 183 dwBytes = sizeof(BYTE) * m_wNumPipesOut; 184 OsAllocateNonPaged(dwBytes,(void **) &m_Mutes); 185 if (NULL == m_Mutes) 186 { 187 OsFreeNonPaged(m_Gains); 188 return ECHOSTATUS_NO_MEM; 189 } 190 191 } 192 193 return ECHOSTATUS_OK; 194 195 } // Init 196 197 198 //***************************************************************************** 199 // 200 // Cleanup - free allocated memory 201 // 202 //***************************************************************************** 203 204 void CPipeOutCtrl::Cleanup() 205 { 206 if (m_Gains) 207 OsFreeNonPaged(m_Gains); 208 209 if (m_Mutes) 210 OsFreeNonPaged(m_Mutes); 211 212 if (m_Pans) 213 OsFreeNonPaged(m_Pans); 214 215 if (m_PanDbs) 216 OsFreeNonPaged(m_PanDbs); 217 218 } // Cleanup 219 220 221 //***************************************************************************** 222 // 223 // Set and get gain 224 // 225 // For cards without vmixers, output bus gain is not handled by the DSP. 226 // Instead, the driver adjusts the output pipe volumes by the output bus gain 227 // and sends that value to the DSP. 228 // 229 // For cards with vmixers, the output bus gain is handled by the DSP, so 230 // the gain setting does not need to take into account the output bus gain 231 // stored by the driver. 232 // 233 //***************************************************************************** 234 235 ECHOSTATUS CPipeOutCtrl::SetGain 236 ( 237 WORD wPipeOut, 238 WORD wBusOut, 239 INT32 iGain, 240 BOOL fImmediate 241 ) 242 { 243 INT32 iBusOutGain; 244 ECHOSTATUS Status; 245 246 if ( NULL == m_pEG) 247 return ECHOSTATUS_DSP_DEAD; 248 249 if (!m_fHasVmixer && (wPipeOut != wBusOut)) 250 return ECHOSTATUS_OK; 251 252 if ((NULL == m_Gains) || (NULL == m_Mutes)) 253 return ECHOSTATUS_NO_MEM; 254 255 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 256 { 257 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetGain - out of range pipe %d bus %d\n", 258 wPipeOut,wBusOut)); 259 return ECHOSTATUS_INVALID_PARAM; 260 } 261 262 WORD wIndex = GetIndex(wPipeOut,wBusOut); 263 264 /* 265 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetGain pipe %d bus %d gain 0x%lx index %d\n", 266 wPipeOut,wBusOut,iGain,wIndex)); 267 */ 268 269 if (ECHOGAIN_UPDATE == iGain) 270 { 271 iGain = DSP_TO_GENERIC( m_Gains[ wIndex ] ); 272 } 273 else 274 { 275 if (iGain > ECHOGAIN_MAXOUT) 276 iGain = ECHOGAIN_MAXOUT; 277 else if (iGain < ECHOGAIN_MUTED) 278 iGain = ECHOGAIN_MUTED; 279 280 m_Gains[ wIndex ] = (INT8) GENERIC_TO_DSP( iGain ); 281 282 // 283 // Store the notify 284 // 285 m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_LEVEL,wPipeOut,wBusOut); 286 } 287 288 if (m_fHasVmixer) 289 { 290 wBusOut &= 0xfffe; 291 292 if (NULL == m_Pans) 293 return ECHOSTATUS_NO_MEM; 294 295 // 296 // For vmixer cards, the DSP handles the output bus gain, 297 // so no need to account for it here. Vmixer output pipes 298 // do have to handle panning. 299 // 300 INT32 iLeft = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iLeft ); 301 INT32 iRight = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iRight ); 302 303 // 304 // Add master gain values 305 // 306 iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut].GetGain(); 307 iLeft += iBusOutGain; 308 iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut+1].GetGain(); 309 iRight += iBusOutGain; 310 311 // 312 // Muting and clamping 313 // 314 if (m_Mutes[wIndex]) 315 { 316 iLeft = ECHOGAIN_MUTED; 317 iRight = ECHOGAIN_MUTED; 318 } 319 else 320 { 321 if ( (m_pEG->m_BusOutLineLevels[wBusOut].IsMuteOn()) || 322 (iLeft < ECHOGAIN_MUTED)) 323 { 324 iLeft = ECHOGAIN_MUTED; 325 } 326 else if (iLeft > ECHOGAIN_MAXOUT) 327 { 328 iLeft = ECHOGAIN_MAXOUT; 329 } 330 331 if ( (m_pEG->m_BusOutLineLevels[wBusOut + 1].IsMuteOn()) || 332 (iRight < ECHOGAIN_MUTED)) 333 { 334 iRight = ECHOGAIN_MUTED; 335 } 336 else if (iRight > ECHOGAIN_MAXOUT) 337 { 338 iRight = ECHOGAIN_MAXOUT; 339 } 340 } 341 342 // 343 // Set the left channel gain 344 // 345 Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut, 346 wBusOut, 347 iLeft, 348 FALSE); 349 if (ECHOSTATUS_OK == Status) 350 { 351 // 352 // And the right channel 353 // 354 Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut, 355 wBusOut + 1, 356 iRight, 357 fImmediate); 358 } 359 360 } 361 else 362 { 363 // 364 // Add this output pipe gain to the output bus gain 365 // Since these gains are in decibels, it's OK to just add them 366 // 367 iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut].GetGain(); 368 iGain += iBusOutGain; 369 370 // 371 // Mute this output pipe if this output bus is muted 372 // 373 if (m_Mutes[ wIndex ] || 374 (m_pEG->m_BusOutLineLevels[wBusOut].IsMuteOn()) ) 375 { 376 iGain = ECHOGAIN_MUTED; 377 } 378 else 379 { 380 // 381 // Clamp the output pipe gain if necessary 382 // 383 if (iGain < ECHOGAIN_MUTED) 384 iGain = ECHOGAIN_MUTED; 385 else if (iGain > ECHOGAIN_MAXOUT) 386 iGain = ECHOGAIN_MAXOUT; 387 388 } 389 390 // 391 // Set the gain 392 // 393 Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut, 394 wBusOut, 395 iGain, 396 fImmediate); 397 398 } 399 400 return Status; 401 } 402 403 404 ECHOSTATUS CPipeOutCtrl::GetGain(WORD wPipeOut, WORD wBusOut, INT32 &iGain) 405 { 406 WORD wIndex = GetIndex(wPipeOut,wBusOut); 407 408 if (NULL == m_Gains) 409 return ECHOSTATUS_NO_MEM; 410 411 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 412 { 413 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetGain - out of range pipe %d bus %d\n", 414 wPipeOut,wBusOut)); 415 return ECHOSTATUS_INVALID_PARAM; 416 } 417 418 iGain = DSP_TO_GENERIC( m_Gains[wIndex] ); 419 420 /* 421 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetGain pipe %d bus %d gain 0x%lx index %d\n", 422 wPipeOut,wBusOut,iGain,wIndex)); 423 */ 424 425 return ECHOSTATUS_OK; 426 } 427 428 429 //***************************************************************************** 430 // 431 // Set and get mute 432 // 433 //***************************************************************************** 434 435 ECHOSTATUS CPipeOutCtrl::SetMute 436 ( 437 WORD wPipeOut, 438 WORD wBusOut, 439 BOOL bMute, 440 BOOL fImmediate 441 ) 442 { 443 if (!m_fHasVmixer && (wPipeOut != wBusOut)) 444 return ECHOSTATUS_OK; 445 446 if (NULL == m_Mutes) 447 return ECHOSTATUS_NO_MEM; 448 449 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 450 { 451 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetMute - out of range pipe %d bus %d\n", 452 wPipeOut,wBusOut)); 453 return ECHOSTATUS_INVALID_PARAM; 454 } 455 456 WORD wIndex = GetIndex(wPipeOut,wBusOut); 457 458 /* 459 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetMute wPipeOut %d wBusOut %d bMute %ld\n", 460 wPipeOut,wBusOut,bMute)); 461 */ 462 463 // 464 // Store the mute 465 // 466 m_Mutes[ wIndex ] = (BYTE) bMute; 467 468 // 469 // Store the notify 470 // 471 m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_MUTE,wPipeOut,wBusOut); 472 473 // 474 // Call the SetGain function to do all the heavy lifting 475 // Use the ECHOGAIN_UPDATE value to tell the function to 476 // recalculate the gain setting using the currently stored value. 477 // 478 return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE,fImmediate); 479 } 480 481 482 ECHOSTATUS CPipeOutCtrl::GetMute(WORD wPipeOut, WORD wBusOut, BOOL &bMute) 483 { 484 WORD wIndex = GetIndex(wPipeOut,wBusOut); 485 486 if (NULL == m_Mutes) 487 return ECHOSTATUS_NO_MEM; 488 489 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 490 { 491 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetMute - out of range pipe %d bus %d\n", 492 wPipeOut,wBusOut)); 493 return ECHOSTATUS_INVALID_PARAM; 494 } 495 496 bMute = (BOOL) m_Mutes[ wIndex ]; 497 498 /* 499 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetMute wPipeOut %d wBusOut %d bMute %ld\n", 500 wPipeOut,wBusOut,bMute)); 501 */ 502 503 return ECHOSTATUS_OK; 504 } 505 506 507 //***************************************************************************** 508 // 509 // Set and get pan (vmixer only) 510 // 511 //***************************************************************************** 512 513 ECHOSTATUS CPipeOutCtrl::SetPan(WORD wPipeOut, WORD wBusOut, INT32 iPan) 514 { 515 if (!m_fHasVmixer) 516 return ECHOSTATUS_OK; 517 518 if (NULL == m_Pans) 519 return ECHOSTATUS_NO_MEM; 520 521 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 522 { 523 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetPan - out of range pipe %d bus %d\n", 524 wPipeOut,wBusOut)); 525 return ECHOSTATUS_INVALID_PARAM; 526 } 527 528 529 WORD wIndex = GetIndex(wPipeOut,wBusOut); 530 531 // 532 // Clamp it and stash it 533 // 534 if (iPan < 0) 535 iPan = 0; 536 else if (iPan > MAX_MIXER_PAN) 537 iPan = MAX_MIXER_PAN; 538 539 m_Pans[wIndex] = (WORD) iPan; 540 541 // 542 // Store the notify 543 // 544 m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_PAN,wPipeOut,wBusOut); 545 546 // 547 // Convert this pan setting into left and right dB values 548 // 549 m_PanDbs[wIndex].iLeft = (INT8) GENERIC_TO_DSP( PanToDb(MAX_MIXER_PAN - iPan) ); 550 m_PanDbs[wIndex].iRight = (INT8) GENERIC_TO_DSP( PanToDb(iPan) ); 551 552 // 553 // Again, SetGain does all the hard work 554 // 555 return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE); 556 } 557 558 559 ECHOSTATUS CPipeOutCtrl::GetPan(WORD wPipeOut, WORD wBusOut, INT32 &iPan) 560 { 561 WORD wIndex = GetIndex(wPipeOut,wBusOut); 562 563 if (NULL == m_Pans) 564 return ECHOSTATUS_NO_MEM; 565 566 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 567 { 568 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetPan - out of range pipe %d bus %d\n", 569 wPipeOut,wBusOut)); 570 return ECHOSTATUS_INVALID_PARAM; 571 } 572 573 iPan = m_Pans[ wIndex ]; 574 575 return ECHOSTATUS_OK; 576 } 577