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 { 118 for (wStereoBus = 0; wStereoBus < wNumStereoBusses; wStereoBus++) 119 { 120 WORD wIndex; 121 122 wIndex = GetIndex(wPipe,wStereoBus << 1); 123 124 // 125 // Pans 126 // 127 if (0 == (wPipe & 1)) 128 { 129 // 130 // Even channel - pan hard left 131 // 132 m_Pans[wIndex] = 0; 133 m_PanDbs[wIndex].iLeft = 0; 134 m_PanDbs[wIndex].iRight = GENERIC_TO_DSP( ECHOGAIN_MUTED ); 135 } 136 else 137 { 138 // 139 // Odd channel - pan hard right 140 // 141 m_Pans[wIndex] = MAX_MIXER_PAN; 142 m_PanDbs[wIndex].iLeft = GENERIC_TO_DSP( ECHOGAIN_MUTED ); 143 m_PanDbs[wIndex].iRight = 0; 144 } 145 146 // 147 // Mutes 148 // 149 if ((wPipe >> 1) == wStereoBus) 150 { 151 m_Mutes[wIndex] = FALSE; 152 } 153 else 154 { 155 m_Mutes[wIndex] = TRUE; 156 } 157 158 // 159 // Set the gain to the DSP; use fImmedate = FALSE here 160 // to make this faster 161 // 162 SetGain(wPipe,wStereoBus << 1,ECHOGAIN_UPDATE,FALSE); 163 164 } 165 } 166 167 // 168 // Set the gain one more time with the immediate flag set to 169 // make sure the DSP gets the message 170 // 171 SetGain(0,0,ECHOGAIN_UPDATE,TRUE); 172 } 173 else 174 { 175 // 176 // Allocate arrays for no vmixer support - don't need pans 177 // 178 dwBytes = sizeof(INT8) * m_wNumPipesOut; 179 OsAllocateNonPaged(dwBytes,(void **) &m_Gains); 180 if (NULL == m_Gains) 181 { 182 return ECHOSTATUS_NO_MEM; 183 } 184 185 dwBytes = sizeof(BYTE) * m_wNumPipesOut; 186 OsAllocateNonPaged(dwBytes,(void **) &m_Mutes); 187 if (NULL == m_Mutes) 188 { 189 OsFreeNonPaged(m_Gains); 190 return ECHOSTATUS_NO_MEM; 191 } 192 193 } 194 195 return ECHOSTATUS_OK; 196 197 } // Init 198 199 200 //***************************************************************************** 201 // 202 // Cleanup - free allocated memory 203 // 204 //***************************************************************************** 205 206 void CPipeOutCtrl::Cleanup() 207 { 208 if (m_Gains) 209 OsFreeNonPaged(m_Gains); 210 211 if (m_Mutes) 212 OsFreeNonPaged(m_Mutes); 213 214 if (m_Pans) 215 OsFreeNonPaged(m_Pans); 216 217 if (m_PanDbs) 218 OsFreeNonPaged(m_PanDbs); 219 220 } // Cleanup 221 222 223 //***************************************************************************** 224 // 225 // Set and get gain 226 // 227 // For cards without vmixers, output bus gain is not handled by the DSP. 228 // Instead, the driver adjusts the output pipe volumes by the output bus gain 229 // and sends that value to the DSP. 230 // 231 // For cards with vmixers, the output bus gain is handled by the DSP, so 232 // the gain setting does not need to take into account the output bus gain 233 // stored by the driver. 234 // 235 //***************************************************************************** 236 237 ECHOSTATUS CPipeOutCtrl::SetGain 238 ( 239 WORD wPipeOut, 240 WORD wBusOut, 241 INT32 iGain, 242 BOOL fImmediate 243 ) 244 { 245 INT32 iBusOutGain; 246 ECHOSTATUS Status; 247 248 if ( NULL == m_pEG) 249 return ECHOSTATUS_DSP_DEAD; 250 251 if (!m_fHasVmixer && (wPipeOut != wBusOut)) 252 return ECHOSTATUS_OK; 253 254 if ((NULL == m_Gains) || (NULL == m_Mutes)) 255 return ECHOSTATUS_NO_MEM; 256 257 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 258 { 259 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetGain - out of range pipe %d bus %d\n", 260 wPipeOut,wBusOut)); 261 return ECHOSTATUS_INVALID_PARAM; 262 } 263 264 WORD wIndex = GetIndex(wPipeOut,wBusOut); 265 266 /* 267 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetGain pipe %d bus %d gain 0x%lx index %d\n", 268 wPipeOut,wBusOut,iGain,wIndex)); 269 */ 270 271 if (ECHOGAIN_UPDATE == iGain) 272 { 273 iGain = DSP_TO_GENERIC( m_Gains[ wIndex ] ); 274 } 275 else 276 { 277 if (iGain > ECHOGAIN_MAXOUT) 278 iGain = ECHOGAIN_MAXOUT; 279 else if (iGain < ECHOGAIN_MUTED) 280 iGain = ECHOGAIN_MUTED; 281 282 m_Gains[ wIndex ] = (INT8) GENERIC_TO_DSP( iGain ); 283 284 // 285 // Store the notify 286 // 287 m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_LEVEL,wPipeOut,wBusOut); 288 } 289 290 if (m_fHasVmixer) 291 { 292 wBusOut &= 0xfffe; 293 294 if (NULL == m_Pans) 295 return ECHOSTATUS_NO_MEM; 296 297 // 298 // For vmixer cards, the DSP handles the output bus gain, 299 // so no need to account for it here. Vmixer output pipes 300 // do have to handle panning. 301 // 302 INT32 iLeft = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iLeft ); 303 INT32 iRight = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iRight ); 304 305 // 306 // Add master gain values 307 // 308 iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut].GetGain(); 309 iLeft += iBusOutGain; 310 iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut+1].GetGain(); 311 iRight += iBusOutGain; 312 313 // 314 // Muting and clamping 315 // 316 if (m_Mutes[wIndex]) 317 { 318 iLeft = ECHOGAIN_MUTED; 319 iRight = ECHOGAIN_MUTED; 320 } 321 else 322 { 323 if ( (m_pEG->m_BusOutLineLevels[wBusOut].IsMuteOn()) || 324 (iLeft < ECHOGAIN_MUTED)) 325 { 326 iLeft = ECHOGAIN_MUTED; 327 } 328 else if (iLeft > ECHOGAIN_MAXOUT) 329 { 330 iLeft = ECHOGAIN_MAXOUT; 331 } 332 333 if ( (m_pEG->m_BusOutLineLevels[wBusOut + 1].IsMuteOn()) || 334 (iRight < ECHOGAIN_MUTED)) 335 { 336 iRight = ECHOGAIN_MUTED; 337 } 338 else if (iRight > ECHOGAIN_MAXOUT) 339 { 340 iRight = ECHOGAIN_MAXOUT; 341 } 342 } 343 344 // 345 // Set the left channel gain 346 // 347 Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut, 348 wBusOut, 349 iLeft, 350 FALSE); 351 if (ECHOSTATUS_OK == Status) 352 { 353 // 354 // And the right channel 355 // 356 Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut, 357 wBusOut + 1, 358 iRight, 359 fImmediate); 360 } 361 362 } 363 else 364 { 365 // 366 // Add this output pipe gain to the output bus gain 367 // Since these gains are in decibels, it's OK to just add them 368 // 369 iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut].GetGain(); 370 iGain += iBusOutGain; 371 372 // 373 // Mute this output pipe if this output bus is muted 374 // 375 if (m_Mutes[ wIndex ] || 376 (m_pEG->m_BusOutLineLevels[wBusOut].IsMuteOn()) ) 377 { 378 iGain = ECHOGAIN_MUTED; 379 } 380 else 381 { 382 // 383 // Clamp the output pipe gain if necessary 384 // 385 if (iGain < ECHOGAIN_MUTED) 386 iGain = ECHOGAIN_MUTED; 387 else if (iGain > ECHOGAIN_MAXOUT) 388 iGain = ECHOGAIN_MAXOUT; 389 390 } 391 392 // 393 // Set the gain 394 // 395 Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut, 396 wBusOut, 397 iGain, 398 fImmediate); 399 400 } 401 402 return Status; 403 } 404 405 406 ECHOSTATUS CPipeOutCtrl::GetGain(WORD wPipeOut, WORD wBusOut, INT32 &iGain) 407 { 408 WORD wIndex = GetIndex(wPipeOut,wBusOut); 409 410 if (NULL == m_Gains) 411 return ECHOSTATUS_NO_MEM; 412 413 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 414 { 415 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetGain - out of range pipe %d bus %d\n", 416 wPipeOut,wBusOut)); 417 return ECHOSTATUS_INVALID_PARAM; 418 } 419 420 iGain = DSP_TO_GENERIC( m_Gains[wIndex] ); 421 422 /* 423 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetGain pipe %d bus %d gain 0x%lx index %d\n", 424 wPipeOut,wBusOut,iGain,wIndex)); 425 */ 426 427 return ECHOSTATUS_OK; 428 } 429 430 431 //***************************************************************************** 432 // 433 // Set and get mute 434 // 435 //***************************************************************************** 436 437 ECHOSTATUS CPipeOutCtrl::SetMute 438 ( 439 WORD wPipeOut, 440 WORD wBusOut, 441 BOOL bMute, 442 BOOL fImmediate 443 ) 444 { 445 if (!m_fHasVmixer && (wPipeOut != wBusOut)) 446 return ECHOSTATUS_OK; 447 448 if (NULL == m_Mutes) 449 return ECHOSTATUS_NO_MEM; 450 451 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 452 { 453 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetMute - out of range pipe %d bus %d\n", 454 wPipeOut,wBusOut)); 455 return ECHOSTATUS_INVALID_PARAM; 456 } 457 458 WORD wIndex = GetIndex(wPipeOut,wBusOut); 459 460 /* 461 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetMute wPipeOut %d wBusOut %d bMute %ld\n", 462 wPipeOut,wBusOut,bMute)); 463 */ 464 465 // 466 // Store the mute 467 // 468 m_Mutes[ wIndex ] = (BYTE) bMute; 469 470 // 471 // Store the notify 472 // 473 m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_MUTE,wPipeOut,wBusOut); 474 475 // 476 // Call the SetGain function to do all the heavy lifting 477 // Use the ECHOGAIN_UPDATE value to tell the function to 478 // recalculate the gain setting using the currently stored value. 479 // 480 return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE,fImmediate); 481 } 482 483 484 ECHOSTATUS CPipeOutCtrl::GetMute(WORD wPipeOut, WORD wBusOut, BOOL &bMute) 485 { 486 WORD wIndex = GetIndex(wPipeOut,wBusOut); 487 488 if (NULL == m_Mutes) 489 return ECHOSTATUS_NO_MEM; 490 491 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 492 { 493 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetMute - out of range pipe %d bus %d\n", 494 wPipeOut,wBusOut)); 495 return ECHOSTATUS_INVALID_PARAM; 496 } 497 498 bMute = (BOOL) m_Mutes[ wIndex ]; 499 500 /* 501 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetMute wPipeOut %d wBusOut %d bMute %ld\n", 502 wPipeOut,wBusOut,bMute)); 503 */ 504 505 return ECHOSTATUS_OK; 506 } 507 508 509 //***************************************************************************** 510 // 511 // Set and get pan (vmixer only) 512 // 513 //***************************************************************************** 514 515 ECHOSTATUS CPipeOutCtrl::SetPan(WORD wPipeOut, WORD wBusOut, INT32 iPan) 516 { 517 if (!m_fHasVmixer) 518 return ECHOSTATUS_OK; 519 520 if (NULL == m_Pans) 521 return ECHOSTATUS_NO_MEM; 522 523 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 524 { 525 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetPan - out of range pipe %d bus %d\n", 526 wPipeOut,wBusOut)); 527 return ECHOSTATUS_INVALID_PARAM; 528 } 529 530 531 WORD wIndex = GetIndex(wPipeOut,wBusOut); 532 533 // 534 // Clamp it and stash it 535 // 536 if (iPan < 0) 537 iPan = 0; 538 else if (iPan > MAX_MIXER_PAN) 539 iPan = MAX_MIXER_PAN; 540 541 m_Pans[wIndex] = (WORD) iPan; 542 543 // 544 // Store the notify 545 // 546 m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_PAN,wPipeOut,wBusOut); 547 548 // 549 // Convert this pan setting into left and right dB values 550 // 551 m_PanDbs[wIndex].iLeft = (INT8) GENERIC_TO_DSP( PanToDb(MAX_MIXER_PAN - iPan) ); 552 m_PanDbs[wIndex].iRight = (INT8) GENERIC_TO_DSP( PanToDb(iPan) ); 553 554 // 555 // Again, SetGain does all the hard work 556 // 557 return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE); 558 } 559 560 561 ECHOSTATUS CPipeOutCtrl::GetPan(WORD wPipeOut, WORD wBusOut, INT32 &iPan) 562 { 563 WORD wIndex = GetIndex(wPipeOut,wBusOut); 564 565 if (NULL == m_Pans) 566 return ECHOSTATUS_NO_MEM; 567 568 if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut)) 569 { 570 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetPan - out of range pipe %d bus %d\n", 571 wPipeOut,wBusOut)); 572 return ECHOSTATUS_INVALID_PARAM; 573 } 574 575 iPan = m_Pans[ wIndex ]; 576 577 return ECHOSTATUS_OK; 578 } 579