1 // **************************************************************************** 2 // 3 // CPipeOutCtrl.cpp 4 // 5 // Class to control output pipes on cards with or without vmixers. 6 // 7 // Copyright Echo Digital Audio Corporation (c) 1998 - 2002 8 // All rights reserved 9 // www.echoaudio.com 10 // 11 // Permission is hereby granted, free of charge, to any person obtaining a 12 // copy of this software and associated documentation files (the 13 // "Software"), to deal with the Software without restriction, including 14 // without limitation the rights to use, copy, modify, merge, publish, 15 // distribute, sublicense, and/or sell copies of the Software, and to 16 // permit persons to whom the Software is furnished to do so, subject to 17 // the following conditions: 18 // 19 // - Redistributions of source code must retain the above copyright 20 // notice, this list of conditions and the following disclaimers. 21 // 22 // - Redistributions in binary form must reproduce the above copyright 23 // notice, this list of conditions and the following disclaimers in the 24 // documentation and/or other materials provided with the distribution. 25 // 26 // - Neither the name of Echo Digital Audio, nor the names of its 27 // contributors may be used to endorse or promote products derived from 28 // this Software without specific prior written permission. 29 // 30 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 31 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 32 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 33 // IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR 34 // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 35 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 36 // SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. 37 // 38 // **************************************************************************** 39 40 #include "CEchoGals.h" 41 #include "CPipeOutCtrl.h" 42 43 extern INT32 PanToDb( INT32 iPan ); 44 45 //***************************************************************************** 46 // 47 // Init 48 // 49 //***************************************************************************** 50 51 ECHOSTATUS CPipeOutCtrl::Init(CEchoGals *pEG) 52 { 53 DWORD dwBytes; 54 WORD wPipe,wStereoBus; 55 56 m_Gains = NULL; 57 m_Mutes = NULL; 58 m_Pans = NULL; 59 m_PanDbs = NULL; 60 61 // 62 // Cache stuff 63 // 64 m_pEG = pEG; 65 m_wNumPipesOut = pEG->GetNumPipesOut(); 66 m_wNumBussesOut = pEG->GetNumBussesOut(); 67 m_fHasVmixer = pEG->HasVmixer(); 68 69 // 70 // Allocate the arrays 71 // 72 if (m_fHasVmixer) 73 { 74 WORD wNumStereoBusses = m_wNumBussesOut >> 1; 75 76 // 77 // Allocate arrays for vmixer support 78 // 79 dwBytes = sizeof(INT8) * m_wNumPipesOut * wNumStereoBusses; 80 OsAllocateNonPaged(dwBytes,(void **) &m_Gains); 81 if (NULL == m_Gains) 82 { 83 return ECHOSTATUS_NO_MEM; 84 } 85 86 dwBytes = sizeof(BYTE) * m_wNumPipesOut * wNumStereoBusses; 87 OsAllocateNonPaged(dwBytes,(void **) &m_Mutes); 88 if (NULL == m_Mutes) 89 { 90 Cleanup(); 91 return ECHOSTATUS_NO_MEM; 92 } 93 94 dwBytes = sizeof(WORD) * m_wNumPipesOut * wNumStereoBusses; 95 OsAllocateNonPaged(dwBytes,(void **) &m_Pans); 96 if (NULL == m_Pans) 97 { 98 Cleanup(); 99 return ECHOSTATUS_NO_MEM; 100 } 101 102 dwBytes = sizeof(PAN_DB) * m_wNumPipesOut * wNumStereoBusses; 103 OsAllocateNonPaged(dwBytes,(void **) &m_PanDbs); 104 if (NULL == m_PanDbs) 105 { 106 Cleanup(); 107 return ECHOSTATUS_NO_MEM; 108 } 109 110 // 111 // Initialize pans and mutes 112 // 113 for (wPipe = 0; wPipe < m_wNumPipesOut; wPipe++) 114 for (wStereoBus = 0; wStereoBus < wNumStereoBusses; wStereoBus++) 115 { 116 WORD wIndex; 117 118 wIndex = GetIndex(wPipe,wStereoBus << 1); 119 120 // 121 // Pans 122 // 123 if (0 == (wPipe & 1)) 124 { 125 // 126 // Even channel - pan hard left 127 // 128 m_Pans[wIndex] = 0; 129 m_PanDbs[wIndex].iLeft = 0; 130 m_PanDbs[wIndex].iRight = GENERIC_TO_DSP( ECHOGAIN_MUTED ); 131 } 132 else 133 { 134 // 135 // Odd channel - pan hard right 136 // 137 m_Pans[wIndex] = MAX_MIXER_PAN; 138 m_PanDbs[wIndex].iLeft = GENERIC_TO_DSP( ECHOGAIN_MUTED ); 139 m_PanDbs[wIndex].iRight = 0; 140 } 141 142 // 143 // Mutes 144 // 145 if ((wPipe >> 1) == wStereoBus) 146 { 147 m_Mutes[wIndex] = FALSE; 148 } 149 else 150 { 151 m_Mutes[wIndex] = TRUE; 152 } 153 154 // 155 // Set the gain to the DSP; use fImmedate = FALSE here 156 // to make this faster 157 // 158 SetGain(wPipe,wStereoBus << 1,ECHOGAIN_UPDATE,FALSE); 159 160 } 161 162 // 163 // Set the gain one more time with the immediate flag set to 164 // make sure the DSP gets the message 165 // 166 SetGain(0,0,ECHOGAIN_UPDATE,TRUE); 167 } 168 else 169 { 170 // 171 // Allocate arrays for no vmixer support - don't need pans 172 // 173 dwBytes = sizeof(INT8) * m_wNumPipesOut; 174 OsAllocateNonPaged(dwBytes,(void **) &m_Gains); 175 if (NULL == m_Gains) 176 { 177 return ECHOSTATUS_NO_MEM; 178 } 179 180 dwBytes = sizeof(BYTE) * m_wNumPipesOut; 181 OsAllocateNonPaged(dwBytes,(void **) &m_Mutes); 182 if (NULL == m_Mutes) 183 { 184 OsFreeNonPaged(m_Gains); 185 return ECHOSTATUS_NO_MEM; 186 } 187 188 } 189 190 return ECHOSTATUS_OK; 191 192 } // Init 193 194 195 //***************************************************************************** 196 // 197 // Cleanup - free allocated memory 198 // 199 //***************************************************************************** 200 201 void CPipeOutCtrl::Cleanup() 202 { 203 if (m_Gains) 204 OsFreeNonPaged(m_Gains); 205 206 if (m_Mutes) 207 OsFreeNonPaged(m_Mutes); 208 209 if (m_Pans) 210 OsFreeNonPaged(m_Pans); 211 212 if (m_PanDbs) 213 OsFreeNonPaged(m_PanDbs); 214 215 } // Cleanup 216 217 218 //***************************************************************************** 219 // 220 // Set and get gain 221 // 222 // For cards without vmixers, output bus gain is not handled by the DSP. 223 // Instead, the driver adjusts the output pipe volumes by the output bus gain 224 // and sends that value to the DSP. 225 // 226 // For cards with vmixers, the output bus gain is handled by the DSP, so 227 // the gain setting does not need to take into account the output bus gain 228 // stored by the driver. 229 // 230 //***************************************************************************** 231 232 ECHOSTATUS CPipeOutCtrl::SetGain 233 ( 234 WORD wPipeOut, 235 WORD wBusOut, 236 INT32 iGain, 237 BOOL fImmediate 238 ) 239 { 240 ECHOSTATUS Status; 241 242 if ( NULL == m_pEG) 243 return ECHOSTATUS_DSP_DEAD; 244 245 if (!m_fHasVmixer && (wPipeOut != wBusOut)) 246 return ECHOSTATUS_OK; 247 248 if ((NULL == m_Gains) || (NULL == m_Mutes)) 249 return ECHOSTATUS_NO_MEM; 250 251 WORD wIndex = GetIndex(wPipeOut,wBusOut); 252 253 /* 254 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetGain pipe %d bus %d gain 0x%lx index %d\n", 255 wPipeOut,wBusOut,iGain,wIndex)); 256 */ 257 258 if (ECHOGAIN_UPDATE == iGain) 259 { 260 iGain = DSP_TO_GENERIC( m_Gains[ wIndex ] ); 261 } 262 else 263 { 264 if (iGain > ECHOGAIN_MAXOUT) 265 iGain = ECHOGAIN_MAXOUT; 266 else if (iGain < ECHOGAIN_MUTED) 267 iGain = ECHOGAIN_MUTED; 268 269 m_Gains[ wIndex ] = GENERIC_TO_DSP( iGain ); 270 271 // 272 // Store the notify 273 // 274 m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_LEVEL,wPipeOut,wBusOut); 275 } 276 277 if (m_fHasVmixer) 278 { 279 wBusOut &= 0xfffe; 280 281 if (NULL == m_Pans) 282 return ECHOSTATUS_NO_MEM; 283 284 // 285 // For vmixer cards, the DSP handles the output bus gain, 286 // so no need to account for it here. Vmixer output pipes 287 // do have to handle panning. 288 // 289 int iLeft = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iLeft ); 290 int iRight = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iRight ); 291 292 if (m_Mutes[wIndex]) 293 { 294 iLeft = ECHOGAIN_MUTED; 295 iRight = ECHOGAIN_MUTED; 296 } 297 else 298 { 299 if (iLeft < ECHOGAIN_MUTED) 300 iLeft = ECHOGAIN_MUTED; 301 else if (iLeft > ECHOGAIN_MAXOUT) 302 iLeft = ECHOGAIN_MAXOUT; 303 304 if (iRight < ECHOGAIN_MUTED) 305 iRight = ECHOGAIN_MUTED; 306 else if (iRight > ECHOGAIN_MAXOUT) 307 iRight = ECHOGAIN_MAXOUT; 308 } 309 310 // 311 // Set the left channel gain 312 // 313 Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut, 314 wBusOut, 315 iLeft, 316 FALSE); 317 if (ECHOSTATUS_OK == Status) 318 { 319 Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut, 320 wBusOut + 1, 321 iRight, 322 fImmediate); 323 } 324 325 } 326 else 327 { 328 int iBusOutGain; 329 330 // 331 // Add this output pipe gain to the output bus gain 332 // Since these gains are in decibels, it's OK to just add them 333 // 334 iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut].GetGain(); 335 iGain += iBusOutGain; 336 337 // 338 // Mute this output pipe if this output bus is muted 339 // 340 if (m_Mutes[ wIndex ] || 341 (m_pEG->m_BusOutLineLevels[wBusOut].IsMuteOn()) ) 342 { 343 iGain = ECHOGAIN_MUTED; 344 } 345 else 346 { 347 // 348 // Clamp the output pipe gain if necessary 349 // 350 if (iGain < ECHOGAIN_MUTED) 351 iGain = ECHOGAIN_MUTED; 352 else if (iGain > ECHOGAIN_MAXOUT) 353 iGain = ECHOGAIN_MAXOUT; 354 355 } 356 357 // 358 // Set the gain 359 // 360 Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut, 361 wBusOut, 362 iGain, 363 fImmediate); 364 365 } 366 367 return Status; 368 } 369 370 371 ECHOSTATUS CPipeOutCtrl::GetGain(WORD wPipeOut, WORD wBusOut, INT32 &iGain) 372 { 373 WORD wIndex = GetIndex(wPipeOut,wBusOut); 374 375 if (NULL == m_Gains) 376 return ECHOSTATUS_NO_MEM; 377 378 iGain = DSP_TO_GENERIC( m_Gains[wIndex] ); 379 380 /* 381 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetGain pipe %d bus %d gain 0x%lx index %d\n", 382 wPipeOut,wBusOut,iGain,wIndex)); 383 */ 384 385 return ECHOSTATUS_OK; 386 } 387 388 389 //***************************************************************************** 390 // 391 // Set and get mute 392 // 393 //***************************************************************************** 394 395 ECHOSTATUS CPipeOutCtrl::SetMute 396 ( 397 WORD wPipeOut, 398 WORD wBusOut, 399 BOOL bMute, 400 BOOL fImmediate 401 ) 402 { 403 if (!m_fHasVmixer && (wPipeOut != wBusOut)) 404 return ECHOSTATUS_OK; 405 406 if (NULL == m_Mutes) 407 return ECHOSTATUS_NO_MEM; 408 409 WORD wIndex = GetIndex(wPipeOut,wBusOut); 410 411 /* 412 ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetMute wPipeOut %d wBusOut %d bMute %ld\n", 413 wPipeOut,wBusOut,bMute)); 414 */ 415 416 // 417 // Store the mute 418 // 419 m_Mutes[ wIndex ] = (BYTE) bMute; 420 421 // 422 // Store the notify 423 // 424 m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_MUTE,wPipeOut,wBusOut); 425 426 // 427 // Call the SetGain function to do all the heavy lifting 428 // Use the ECHOGAIN_UPDATE value to tell the function to 429 // recalculate the gain setting using the currently stored value. 430 // 431 return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE,fImmediate); 432 } 433 434 435 ECHOSTATUS CPipeOutCtrl::GetMute(WORD wPipeOut, WORD wBusOut, BOOL &bMute) 436 { 437 WORD wIndex = GetIndex(wPipeOut,wBusOut); 438 439 if (NULL == m_Mutes) 440 return ECHOSTATUS_NO_MEM; 441 442 bMute = (BOOL) m_Mutes[ wIndex ]; 443 444 /* 445 ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetMute wPipeOut %d wBusOut %d bMute %ld\n", 446 wPipeOut,wBusOut,bMute)); 447 */ 448 449 return ECHOSTATUS_OK; 450 } 451 452 453 //***************************************************************************** 454 // 455 // Set and get pan (vmixer only) 456 // 457 //***************************************************************************** 458 459 ECHOSTATUS CPipeOutCtrl::SetPan(WORD wPipeOut, WORD wBusOut, INT32 iPan) 460 { 461 if (!m_fHasVmixer) 462 return ECHOSTATUS_OK; 463 464 if (NULL == m_Pans) 465 return ECHOSTATUS_NO_MEM; 466 467 WORD wIndex = GetIndex(wPipeOut,wBusOut); 468 469 // 470 // Clamp it and stash it 471 // 472 if (iPan < 0) 473 iPan = 0; 474 else if (iPan > MAX_MIXER_PAN) 475 iPan = MAX_MIXER_PAN; 476 477 m_Pans[wIndex] = (WORD) iPan; 478 479 // 480 // Store the notify 481 // 482 m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_PAN,wPipeOut,wBusOut); 483 484 // 485 // Convert this pan setting into left and right dB values 486 // 487 m_PanDbs[wIndex].iLeft = GENERIC_TO_DSP( PanToDb(MAX_MIXER_PAN - iPan) ); 488 m_PanDbs[wIndex].iRight = GENERIC_TO_DSP( PanToDb(iPan) ); 489 490 // 491 // Again, SetGain does all the hard work 492 // 493 return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE); 494 } 495 496 497 ECHOSTATUS CPipeOutCtrl::GetPan(WORD wPipeOut, WORD wBusOut, INT32 &iPan) 498 { 499 WORD wIndex = GetIndex(wPipeOut,wBusOut); 500 501 if (NULL == m_Pans) 502 return ECHOSTATUS_NO_MEM; 503 504 iPan = m_Pans[ wIndex ]; 505 506 return ECHOSTATUS_OK; 507 } 508