1/********************************************************************************************************************************** 2* 3* OpenAL cross platform audio library 4* Copyright (c) 2004, Apple Computer, Inc., Copyright (c) 2012, Apple Inc. All rights reserved. 5* 6* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following 7* conditions are met: 8* 9* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 11* disclaimer in the documentation and/or other materials provided with the distribution. 12* 3. Neither the name of Apple Inc. ("Apple") nor the names of its contributors may be used to endorse or promote products derived 13* from this software without specific prior written permission. 14* 15* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 16* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 17* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 18* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 19* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 20* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21* 22**********************************************************************************************************************************/ 23 24#include "oalContext.h" 25#include "oalSource.h" 26 27// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 28// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 29// OALContexts 30// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 31// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32#pragma mark ***** OALContexts - Public Methods ***** 33OALContext::OALContext (const uintptr_t inSelfToken, OALDevice *inOALDevice, const ALCint *inAttributeList, UInt32 &inBusCount, Float64 &inMixerRate) 34 : 35#if LOG_CONTEXT_VERBOSE 36 mSelfToken (inSelfToken), 37#endif 38// mProcessingActive(true), 39 mOwningDevice(inOALDevice), 40 mMixerNode(0), 41 mMixerUnit (0), 42 mSourceMap (NULL), 43 mSourceMapLock ("OALContext::SourceMapLock"), 44 mDeadSourceMap (NULL), 45 mDeadSourceMapLock ("OALContext::DeadSourceMapLock"), 46 mDistanceModel(AL_INVERSE_DISTANCE_CLAMPED), 47 mSpeedOfSound(343.3), 48 mDopplerFactor(1.0), 49 mDopplerVelocity(1.0), 50 mListenerGain(1.0), 51 mAttributeListSize(0), 52 mAttributeList(NULL), 53 mDistanceScalingRequired(false), 54 mCalculateDistance(true), 55 mRenderQuality(ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_LOW), 56 mSpatialSetting(0), 57 mBusCount(inBusCount), 58 mInUseFlag(0), 59 mMixerOutputRate(inMixerRate), 60 mDefaultReferenceDistance(1.0), 61 mDefaultMaxDistance(100000.0), 62 mUserSpecifiedBusCounts(false), 63 mRenderThreadID(0), 64 mSettableMixerAttenuationCurves(false), 65 mASAReverbState(0), 66 mASAReverbRoomType(0), 67 mASAReverbGlobalLevel(0.0), 68 mASAReverbQuality(ALC_ASA_REVERB_QUALITY_Low), 69 mASAReverbEQGain(0.0), 70 mASAReverbEQBandwidth(3.0), 71 mASAReverbEQFrequency(800.0), 72 mOutputCapturer(nullptr) 73#if LOG_BUS_CONNECTIONS 74 , mMonoSourcesConnected(0), 75 mStereoSourcesConnected(0) 76#endif 77{ 78#if LOG_CONTEXT_VERBOSE 79 DebugMessageN1("OALContext::OALContext() - OALContext = %ld", (long int) mSelfToken); 80#endif 81 mBusInfo = (BusInfo *) calloc (1, sizeof(BusInfo) * mBusCount); 82 83// UInt32 monoSources = 0; 84 UInt32 stereoSources = 1; // default 85 86 UInt32 inAttributeListSize = 0; 87 Boolean userSetMixerOutputRate = false; 88 89 if (inAttributeList) 90 { 91 ALCint* currentAttribute = ( ALCint*) inAttributeList; 92 // ATTRIBUTE LIST 93 while (*currentAttribute != 0) 94 { 95 switch (*currentAttribute) 96 { 97 case ALC_FREQUENCY: 98 mMixerOutputRate = (Float64)currentAttribute[1]; 99 userSetMixerOutputRate = true; 100 break; 101 case ALC_REFRESH: 102 break; 103 case ALC_SYNC: 104 break; 105 case ALC_MONO_SOURCES: 106 mUserSpecifiedBusCounts = true; 107// monoSources = currentAttribute[1]; 108 break; 109 case ALC_STEREO_SOURCES: 110 mUserSpecifiedBusCounts = true; 111 stereoSources = currentAttribute[1]; 112 break; 113 default: 114 // is this a failure? 115 break; 116 } 117 currentAttribute += 2; 118 inAttributeListSize += 2; 119 } 120 121 // if no mixer output rate was set, pound in the default rate 122 if (!userSetMixerOutputRate) 123 { 124 // add 2 for the key and value, and 1 for the terminator 125 mAttributeListSize = inAttributeListSize + 3; 126 mAttributeList = (ALCint*) calloc (1, mAttributeListSize * sizeof(ALCint)); 127 mAttributeList[inAttributeListSize] = (ALCint)ALC_FREQUENCY; 128 mAttributeList[inAttributeListSize+1] = (ALCint)mMixerOutputRate; 129 // add the null terminator 130 mAttributeList[inAttributeListSize+2] = 0; 131 } 132 133 else { 134 // add for the terminator 135 mAttributeListSize = inAttributeListSize + 1; 136 mAttributeList = (ALCint*) calloc (1, mAttributeListSize * sizeof(ALCint)); 137 } 138 139 memcpy(mAttributeList, inAttributeList, inAttributeListSize * sizeof(ALCint)); 140 } 141 142 // initialize mContextInfo 143 mListenerPosition[0] = 0.0; 144 mListenerPosition[1] = 0.0; 145 mListenerPosition[2] = 0.0; 146 147 mListenerVelocity[0] = 0.0; 148 mListenerVelocity[1] = 0.0; 149 mListenerVelocity[2] = 0.0; 150 151 mListenerOrientationForward[0] = 0.0; 152 mListenerOrientationForward[1] = 0.0; 153 mListenerOrientationForward[2] = -1.0; 154 155 mListenerOrientationUp[0] = 0.0; 156 mListenerOrientationUp[1] = 1.0; 157 mListenerOrientationUp[2] = 0.0; 158 159 InitializeMixer(stereoSources); 160 161 mSourceMap = new OALSourceMap(); 162 mDeadSourceMap = new OALSourceMap(); 163} 164 165// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 166OALContext::~OALContext() 167{ 168#if LOG_CONTEXT_VERBOSE 169 DebugMessageN1("OALContext::~OALContext() - OALContext = %ld", (long int) mSelfToken); 170#endif 171 172#if CAPTURE_AUDIO_TO_FILE 173 gTheCapturer->Stop(); 174 delete(gTheCapturer); 175 gTheCapturer = NULL; 176#endif 177 178 mOwningDevice->RemoveContext(this); 179 180 // delete all the sources that were created by this context 181 if (mSourceMap) 182 { 183 for (UInt32 i = 0; i < mSourceMap->Size(); i++) 184 { 185 OALSource *oalSource = mSourceMap->GetSourceByIndex(0); 186 if (oalSource) 187 { 188 mSourceMap->Remove(oalSource->GetToken()); 189 delete oalSource; 190 } 191 } 192 delete mSourceMap; 193 } 194 195 if (mDeadSourceMap) 196 { 197 CleanUpDeadSourceList(); 198 delete mDeadSourceMap; 199 } 200 201 if(mOutputCapturer) 202 { 203 delete mOutputCapturer; 204 mOutputCapturer = NULL; 205 } 206 207 if (mAttributeList) 208 free(mAttributeList); 209 210 if(mBusInfo) 211 free(mBusInfo); 212 213} 214 215// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 216void OALContext::CleanUpDeadSourceList() 217{ 218#if LOG_CONTEXT_VERBOSE 219 DebugMessageN1("OALContext::CleanUpDeadSourceList() - OALContext = %ld", (long int) mSelfToken); 220#endif 221 if (mDeadSourceMap) 222 { 223 UInt32 index = 0; 224 for (UInt32 i = 0; i < mDeadSourceMap->Size(); i++) 225 { 226 OALSource* source = mDeadSourceMap->GetSourceByIndex(index); 227 if (source) 228 { 229 if (source->IsSafeForDeletion()) 230 { 231 //DebugMessageN1("OALContext::CleanUpTheDeadSourceList removing source id = %ld", source->GetToken()); 232 mDeadSourceMap->Remove(source->GetToken()); 233 delete (source); 234 } 235 else 236 { 237 //DebugMessageN1("OALContext::CleanUpTheDeadSourceList NOT SAFE RIGHT NOW to delete source id = %ld", source->GetToken()); 238 index++; 239 } 240 } 241 else 242 index++; 243 } 244 } 245} 246 247// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 248/* 249 3DMixer Version Info 250 251 - Pre 2.0 Mixer must be at least version 1.3 for OpenAL 252 - 3DMixer 2.0 253 Bug in Distance Attenuationg/Reverb code requires that distances be scaled in OAL before passing to mixer 254 - 3DMixer 2.1 255 Fixes bug in 2.0 but also has a bug related to OAL fixes for correctly caclulating the vector of moving object 256 - 3DMixer 2.1.x 257 Fixes bugs in 2.1 and adds support for Linear Attenuation 258 - 3DMixer 2.2 259 Adds Linear/Exponential Attenuation and Reverb/Occlusion/Obstruction. 260 (note) Linear & Exponential Attenuation is done manually by the OALSource object if 2.2 Mixer is not present 261*/ 262 263void OALContext::InitializeMixer(UInt32 inStereoBusCount) 264{ 265#if LOG_CONTEXT_VERBOSE 266 DebugMessageN2("OALContext::InitializeMixer() - OALContext:inStereoBusCount = %ld:%d", (long int) mSelfToken, inStereoBusCount); 267#endif 268 OSStatus result = noErr; 269 UInt32 propSize; 270 271 try { 272 // ~~~~~~~~~~~~~~~~~~~ GET 3DMIXER VERSION 273 if (Get3DMixerVersion() < k3DMixerVersion_1_3) 274 throw -1; // should not happen because OpenDevice should have failed beforehand 275 276 if (Get3DMixerVersion() == k3DMixerVersion_2_0) 277 { 278 mDistanceScalingRequired = true; 279 } 280 else if (Get3DMixerVersion() >= k3DMixerVersion_2_2) 281 { 282 mSettableMixerAttenuationCurves = true; 283 } 284 285 ComponentDescription mixerCD; 286 mixerCD.componentFlags = 0; 287 mixerCD.componentFlagsMask = 0; 288 mixerCD.componentType = kAudioUnitType_Mixer; 289 mixerCD.componentSubType = kAudioUnitSubType_3DMixer; 290 mixerCD.componentManufacturer = kAudioUnitManufacturer_Apple; 291 292 // CREATE NEW NODE FOR THE GRAPH 293 result = AUGraphNewNode (mOwningDevice->GetGraph(), &mixerCD, 0, NULL, &mMixerNode); 294 THROW_RESULT 295 296 result = AUGraphGetNodeInfo (mOwningDevice->GetGraph(), mMixerNode, 0, 0, 0, &mMixerUnit); 297 THROW_RESULT 298 299 // Get Default Distance Setting when the good 3DMixer is around 300 if (Get3DMixerVersion() >= k3DMixerVersion_2_0) 301 { 302 MixerDistanceParams distanceParams; 303 propSize = sizeof(distanceParams); 304 result = AudioUnitGetProperty(mMixerUnit, kAudioUnitProperty_3DMixerDistanceParams, kAudioUnitScope_Input, 1, &distanceParams, &propSize); 305 if (result == noErr) 306 { 307 mDefaultReferenceDistance = distanceParams.mReferenceDistance; 308 mDefaultMaxDistance = distanceParams.mMaxDistance; 309 } 310 } 311 312 // Set the Output Format of the Mixer AU 313 CAStreamBasicDescription format; 314 UInt32 propSize = sizeof(format); 315 /*result =*/ AudioUnitGetProperty(mOwningDevice->GetOutputAU(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, &propSize); 316 317 format.SetCanonical (mOwningDevice->GetDesiredRenderChannelCount(), false); // determine how many channels to render to 318 format.mSampleRate = GetMixerRate(); // Sample Rate (either the default out rate of the Output AU or a User Specified rate) 319 320 result = AudioUnitSetProperty (mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, sizeof(format)); 321 THROW_RESULT 322 323 // Frames Per Slice - moved from ConnectContext call in device class 324 UInt32 mixerFPS = 0; 325 UInt32 dataSize = sizeof(mixerFPS); 326 /*result =*/ AudioUnitGetProperty(mMixerUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &mixerFPS, &dataSize); 327 if (mixerFPS < mOwningDevice->GetFramesPerSlice()) 328 { 329 mixerFPS = mOwningDevice->GetFramesPerSlice(); 330 result = AudioUnitSetProperty( mMixerUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &mixerFPS, sizeof(mixerFPS)); 331 THROW_RESULT 332 } 333 334 // REVERB off by default 335 /*result =*/ AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_UsesInternalReverb, kAudioUnitScope_Global, 0, &mASAReverbState, sizeof(mASAReverbState)); 336 // ignore result 337 338 // MIXER BUS COUNT 339 if (Get3DMixerVersion() < k3DMixerVersion_2_0) 340 { 341 mBusCount = kDefaultMaximumMixerBusCount; // 1.3 version of the mixer did not allow a change in the bus count 342 } 343 else 344 { 345 // set the bus count on the mixer if necessary 346 UInt32 currentBusCount; 347 propSize = sizeof(currentBusCount); 348 result = AudioUnitGetProperty ( mMixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, ¤tBusCount, &propSize); 349 if ((result == noErr) && (mBusCount != currentBusCount)) 350 { 351 result = AudioUnitSetProperty ( mMixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &mBusCount, propSize); 352 if (result != noErr) 353 { 354 // couldn't set the bus count so make sure we know just how many busses there are 355 propSize = sizeof(mBusCount); 356 AudioUnitGetProperty ( mMixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &mBusCount, &propSize); 357 } 358 } 359 } 360 361 // SET UP STEREO/MONO BUSSES 362 CAStreamBasicDescription theOutFormat; 363 theOutFormat.mSampleRate = mMixerOutputRate; 364 theOutFormat.mFormatID = kAudioFormatLinearPCM; 365 theOutFormat.mFramesPerPacket = 1; 366 theOutFormat.mBytesPerFrame = sizeof (Float32); 367 theOutFormat.mBitsPerChannel = sizeof (Float32) * 8; 368 theOutFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; 369 theOutFormat.mBytesPerPacket = sizeof (Float32); 370 371 for (UInt32 i = 0; i < mBusCount; i++) 372 { 373 // Distance Attenuation: for pre v2.0 mixer 374 SetDistanceAttenuation (i, kDefaultReferenceDistance, kDefaultMaximumDistance, kDefaultRolloff); 375 376 theOutFormat.mChannelsPerFrame = (i < inStereoBusCount) ? 2 : 1; 377 OSStatus result = AudioUnitSetProperty ( mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 378 i, &theOutFormat, sizeof(CAStreamBasicDescription)); 379 THROW_RESULT 380 381 mBusInfo[i].mNumberChannels = theOutFormat.mChannelsPerFrame; 382 mBusInfo[i].mSourceAttached = kNoSourceAttached; 383 mBusInfo[i].mReverbState = mASAReverbState; 384 385 // set kAudioUnitProperty_SpatializationAlgorithm 386 UInt32 spatAlgo = (theOutFormat.mChannelsPerFrame == 2) ? kSpatializationAlgorithm_StereoPassThrough : mSpatialSetting; 387 AudioUnitSetProperty( mMixerUnit, kAudioUnitProperty_SpatializationAlgorithm, kAudioUnitScope_Input, i, &spatAlgo, sizeof(spatAlgo)); 388 389 // set kAudioUnitProperty_3DMixerRenderingFlags (distance attenuation) for mono busses 390 if (theOutFormat.mChannelsPerFrame == 1) 391 { 392 UInt32 render_flags_3d = k3DMixerRenderingFlags_DistanceAttenuation; 393 if (mRenderQuality == ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_HIGH) 394 render_flags_3d += k3DMixerRenderingFlags_InterAuralDelay; // off by default, on if the user sets High Quality rendering 395 396 // Render Flags 397 /*result =*/ AudioUnitSetProperty( mMixerUnit, kAudioUnitProperty_3DMixerRenderingFlags, kAudioUnitScope_Input, i, &render_flags_3d, sizeof(render_flags_3d)); 398 } 399 } 400 401 // Initialize Busses - attributes may affect this operation 402 InitRenderQualityOnBusses(); 403 } 404 catch(OSStatus result){ 405 throw result; 406 } 407 catch(...){ 408 throw -1; 409 } 410} 411 412// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 413// should only be called when the mixer is NOT connected 414void OALContext::ConfigureMixerFormat() 415{ 416#if LOG_CONTEXT_VERBOSE 417 DebugMessageN1("OALContext::ConfigureMixerFormat() - OALContext = %ld", (long int) mSelfToken); 418#endif 419 // Set the Output Format of the Mixer AU 420 CAStreamBasicDescription format; 421 UInt32 propSize = sizeof(format); 422 AudioUnitGetProperty(mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, &propSize); 423 424 format.SetCanonical (mOwningDevice->GetDesiredRenderChannelCount(), false); // determine how many channels to render to 425 format.mSampleRate = GetMixerRate(); // Sample Rate (either the default out rate of the Output AU or a User Specified rate) 426 427 OSStatus result = AudioUnitSetProperty (mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, sizeof(format)); 428 THROW_RESULT 429 430 // Initialize Busses - render channel attributes attributes may affect this operation 431 InitRenderQualityOnBusses(); 432} 433 434// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 435void OALContext::CopyAttributeList( ALCint* outAttrList) 436{ 437#if LOG_CONTEXT_VERBOSE 438 DebugMessageN1("OALContext::CopyAttributeList() - OALContext = %ld", (long int) mSelfToken); 439#endif 440 memcpy(outAttrList, mAttributeList, mAttributeListSize*sizeof(ALCint)); 441} 442 443// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 444void OALContext::AddSource(ALuint inSourceToken) 445{ 446#if LOG_CONTEXT_VERBOSE 447 DebugMessageN2("OALContext::AddSource() - OALContext:inSourceToken = %ld:%d", (long int) mSelfToken, inSourceToken); 448#endif 449 try { 450 OALSource *newSource = new OALSource (inSourceToken, this); 451 452 { 453 CAGuard::Locker locked(mSourceMapLock); 454 mSourceMap->Add(inSourceToken, &newSource); 455 } 456 { 457 CAGuard::Locker locked(mDeadSourceMapLock); 458 CleanUpDeadSourceList(); 459 } 460 } 461 catch (...) { 462 throw; 463 } 464} 465 466// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 467// You MUST call ReleaseSource after you have completed use of the source object 468OALSource* OALContext::ProtectSource(ALuint inSourceToken) 469{ 470#if LOG_CONTEXT_VERBOSE 471 DebugMessageN2("OALContext::ProtectSource() - OALContext:inSourceToken = %ld:%d", (long int) mSelfToken, inSourceToken); 472#endif 473 OALSource *newSource = NULL; 474 475 CAGuard::Locker locked(mSourceMapLock); 476 477 newSource = mSourceMap->Get(inSourceToken); 478 479 if (newSource) 480 newSource->SetInUseFlag(); 481 482 return newSource; 483} 484 485// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 486// You MUST call ReleaseSource after you have completed use of the source object 487OALSource* OALContext::GetSourceForRender(ALuint inSourceToken) 488{ 489#if LOG_CONTEXT_VERBOSE 490 DebugMessageN2("OALContext::GetSourceForRender() - OALContext:inSourceToken = %ld:%d", (long int) mSelfToken, inSourceToken); 491#endif 492 OALSource *newSource = NULL; 493 494 CAMutex::Tryer tryer(mSourceMapLock); 495 496 if (tryer.HasLock()) 497 { 498 newSource = mSourceMap->Get(inSourceToken); 499 500 if (newSource) 501 newSource->SetInUseFlag(); 502 } 503 504 return newSource; 505} 506 507// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 508// You MUST call ReleaseSource after you have completed use of the dead source object 509OALSource* OALContext::GetDeadSourceForRender(ALuint inSourceToken) 510{ 511#if LOG_CONTEXT_VERBOSE 512 DebugMessageN2("OALContext::GetDeadSourceForRender() - OALContext:inSourceToken = %ld:%d", (long int) mSelfToken, inSourceToken); 513#endif 514 OALSource *deadSource = NULL; 515 516 CAMutex::Tryer tryer(mDeadSourceMapLock); 517 518 if (tryer.HasLock()) 519 { 520 deadSource = mDeadSourceMap->Get(inSourceToken); 521 522 if (deadSource) 523 deadSource->SetInUseFlag(); 524 } 525 526 return deadSource; 527} 528 529// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 530void OALContext::ReleaseSource(OALSource* inSource) 531{ 532#if LOG_CONTEXT_VERBOSE 533 DebugMessageN2("OALContext::ReleaseSource() - OALContext:inSource = %ld:%d", (long int) mSelfToken, inSource->GetToken()); 534#endif 535 if (inSource) 536 inSource->ClearInUseFlag(); 537} 538 539// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 540void OALContext::RemoveSource(ALuint inSourceToken) 541{ 542#if LOG_CONTEXT_VERBOSE 543 DebugMessageN2("OALContext::RemoveSource() - OALContext:inSourceToken = %ld:%d", (long int) mSelfToken, inSourceToken); 544#endif 545 OALSource *oalSource = mSourceMap->Get(inSourceToken); 546 if (oalSource != NULL) 547 { 548 oalSource->SetUpDeconstruction(); 549 550 { 551 CAGuard::Locker locked(mSourceMapLock); 552 mSourceMap->Remove(inSourceToken); // do not allow any more threads to use this source object 553 } 554 { 555 CAGuard::Locker locked(mDeadSourceMapLock); 556 mDeadSourceMap->Add(inSourceToken, &oalSource); // remove it later when it is safe 557 CleanUpDeadSourceList(); // now is a good time to actually delete other sources marked for deletion 558 } 559 } 560} 561 562// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 563void OALContext::ProcessContext() 564{ 565#if LOG_CONTEXT_VERBOSE 566 DebugMessageN1("OALContext::ProcessContext() - OALContext = %ld", (long int) mSelfToken); 567#endif 568 return; // NO OP 569 570#if 0 571 // This code breaks Doom 3 [4554491] - The 1.0 implementation was a no op. 572 // Since alcProcessContext()/alcSuspendContext() are also no ops on sound c ards with OpenAL imps, this should be ok 573 574 if (mProcessingActive == true) 575 return; // NOP 576 577 ConnectMixerToDevice(); 578 mProcessingActive = true; 579 return; 580#endif 581} 582 583// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 584void OALContext::SuspendContext() 585{ 586#if LOG_CONTEXT_VERBOSE 587 DebugMessageN1("OALContext::SuspendContext() - OALContext = %ld", (long int) mSelfToken); 588#endif 589 return; // NO OP 590 591#if 0 592 // This code breaks Doom 3 [4554491] - The 1.0 implementation was a no op. 593 // Since alcProcessContext()/alcSuspendContext() are also no ops on sound c ards with OpenAL imps, this should be ok 594 595 if (mProcessingActive == false) 596 return; // NOP 597 598 DeviceDisconnect(); 599 mProcessingActive = false; 600#endif 601} 602 603// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 604void OALContext::ConnectMixerToDevice() 605{ 606#if LOG_CONTEXT_VERBOSE 607 DebugMessageN1("OALContext::ConnectMixerToDevice() - OALContext = %ld", (long int) mSelfToken); 608#endif 609 mOwningDevice->ConnectContext(this); 610 611#if CAPTURE_AUDIO_TO_FILE 612 DebugMessage ("ABOUT TO START CAPTURE"); 613 614 gCapturerDataFormat.mSampleRate = 44100.0; 615 gCapturerDataFormat.mFormatID = kAudioFormatLinearPCM; 616 gCapturerDataFormat.mFormatFlags = kAudioFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; 617 gCapturerDataFormat.mBytesPerPacket = 4; 618 gCapturerDataFormat.mFramesPerPacket = 1; 619 gCapturerDataFormat.mBytesPerFrame = 4; 620 gCapturerDataFormat.mChannelsPerFrame = 2; 621 gCapturerDataFormat.mBitsPerChannel = 16; 622 gCapturerDataFormat.mReserved = 0; 623 624 gFileURL = CFURLCreateWithFileSystemPath(NULL, CFSTR("<PathToCapturedFileLocation>CapturedAudio.wav"), kCFURLPOSIXPathStyle, false); 625 gTheCapturer = new CAAudioUnitOutputCapturer(mMixerUnit, gFileURL, 'WAVE', gCapturerDataFormat); 626 627 gTheCapturer->Start(); 628#endif 629 630 OSStatus result = AUGraphAddRenderNotify(mOwningDevice->GetGraph(), ContextNotificationProc, this); 631 THROW_RESULT 632} 633 634void OALContext::DisconnectMixerFromDevice() 635{ 636 mOwningDevice->DisconnectContext(this); 637 638#if CAPTURE_AUDIO_TO_FILE 639 DebugMessage ("STOPPING CAPTURE"); 640 641 gTheCapturer->Stop(); 642#endif 643 644 OSStatus result = AUGraphRemoveRenderNotify(mOwningDevice->GetGraph(), ContextNotificationProc, this); 645 THROW_RESULT 646} 647 648 649// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 650void OALContext::SetDistanceModel(UInt32 inDistanceModel) 651{ 652#if LOG_CONTEXT_VERBOSE || LOG_GRAPH_AND_MIXER_CHANGES 653 DebugMessageN2("OALContext::SetDistanceModel() - OALContext:inDistanceModel = %ld:%d", (long int) mSelfToken, inDistanceModel); 654#endif 655// OSStatus result = noErr; 656 UInt32 curve; 657 658 if (mDistanceModel != inDistanceModel) 659 { 660 //UInt32 curve; 661 switch (inDistanceModel) 662 { 663 case AL_INVERSE_DISTANCE: 664 case AL_INVERSE_DISTANCE_CLAMPED: 665 mCalculateDistance = true; 666 if (mSettableMixerAttenuationCurves) 667 { 668 curve =2 /* k3DMixerAttenuationCurve_Inverse*/; 669 for (UInt32 i = 0; i < mBusCount; i++) 670 { 671 /*result =*/ AudioUnitSetProperty( mMixerUnit, 3013 /*kAudioUnitProperty_3DMixerAttenuationCurve*/, kAudioUnitScope_Input, i, &curve, sizeof(curve)); 672 } 673 } 674 else 675 { 676 // NOTHING TO DO 677 if (Get3DMixerVersion() >= k3DMixerVersion_2_0) 678 { 679 // unnecessary if changing between AL_INVERSE_DISTANCE & AL_INVERSE_DISTANCE_CLAMPED 680 if ((mDistanceModel != AL_INVERSE_DISTANCE) && (mDistanceModel != AL_INVERSE_DISTANCE_CLAMPED)) 681 { 682 // this is the 2.0-2.1 mixer 683 } 684 } 685 else 686 { 687 // kAudioUnitProperty_3DMixerDistanceAtten gets set by the source each time via a call to SetDistanceAttenuation() 688 // nothing more to do now 689 } 690 } 691 692 break; 693 694 case AL_LINEAR_DISTANCE: 695 case AL_LINEAR_DISTANCE_CLAMPED: 696 if (mSettableMixerAttenuationCurves) 697 { 698 mCalculateDistance = true; 699 curve = 3 /*k3DMixerAttenuationCurve_Linear*/; 700 for (UInt32 i = 0; i < mBusCount; i++) 701 { 702 /*result =*/ AudioUnitSetProperty( mMixerUnit, 3013 /*kAudioUnitProperty_3DMixerAttenuationCurve*/, kAudioUnitScope_Input, i, &curve, sizeof(curve)); 703 } 704 } 705 else 706 { 707 mCalculateDistance = false; 708 // turn off distance attenuation altogether 709 // the source will then apply the linear distance formula as a gain scalar and set the bus gain instead of setting any distance 710 } 711 break; 712 713 case AL_EXPONENT_DISTANCE: 714 case AL_EXPONENT_DISTANCE_CLAMPED: 715 if (mSettableMixerAttenuationCurves) 716 { 717 mCalculateDistance = true; 718 // set the mixer for Exponential Attenuation 719 curve = 1 /*k3DMixerAttenuationCurve_Exponential*/; 720 for (UInt32 i = 0; i < mBusCount; i++) 721 { 722 /*result =*/ AudioUnitSetProperty( mMixerUnit, 3013 /*kAudioUnitProperty_3DMixerAttenuationCurve*/, kAudioUnitScope_Input, i, &curve, sizeof(curve)); 723 } 724 } 725 else 726 { 727 mCalculateDistance = false; 728 // turn off distance attenuation altogether 729 // the source will then apply the exponential distance formula as a gain scalar and set the bus gain instead of setting any distance 730 } 731 break; 732 733 case AL_NONE: 734 { 735 mCalculateDistance = false; 736 // turn off distance attenuation altogether 737 } 738 739 break; 740 741 742 default: 743 break; 744 } 745 746 mDistanceModel = inDistanceModel; 747 if (mSourceMap) 748 { 749 CAGuard::Locker locked(mSourceMapLock); 750 mSourceMap->MarkAllSourcesForRecalculation(); 751 } 752 } 753} 754 755// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 756void OALContext::SetDopplerFactor(Float32 inDopplerFactor) 757{ 758#if LOG_CONTEXT_VERBOSE 759 DebugMessageN2("OALContext::SetDopplerFactor() - OALContext:inDopplerFactor = %ld:%f", (long int) mSelfToken, inDopplerFactor); 760#endif 761 if (mDopplerFactor != inDopplerFactor) 762 { 763 mDopplerFactor = inDopplerFactor; 764 if (mSourceMap) 765 { 766 CAGuard::Locker locked(mSourceMapLock); 767 mSourceMap->MarkAllSourcesForRecalculation(); 768 } 769 } 770} 771 772// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 773void OALContext::SetDopplerVelocity(Float32 inDopplerVelocity) 774{ 775#if LOG_CONTEXT_VERBOSE 776 DebugMessageN2("OALContext::SetDopplerVelocity() - OALContext:inDopplerVelocity = %ld:%f", (long int) mSelfToken, inDopplerVelocity); 777#endif 778 if (mDopplerVelocity != inDopplerVelocity) 779 { 780 mDopplerVelocity = inDopplerVelocity; 781 // if (mSourceMap) 782 // mSourceMap->MarkAllSourcesForRecalculation(); 783 } 784} 785 786// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 787void OALContext::SetSpeedOfSound(Float32 inSpeedOfSound) 788{ 789#if LOG_CONTEXT_VERBOSE 790 DebugMessageN2("OALContext::SetSpeedOfSound() - OALContext:inSpeedOfSound = %ld:%f", (long int) mSelfToken, inSpeedOfSound); 791#endif 792 if (mSpeedOfSound != inSpeedOfSound) 793 { 794 mSpeedOfSound = inSpeedOfSound; 795 if (mSourceMap) 796 { 797 CAGuard::Locker locked(mSourceMapLock); 798 mSourceMap->MarkAllSourcesForRecalculation(); 799 } 800 } 801} 802 803// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 804void OALContext::SetListenerGain(Float32 inGain) 805{ 806#if LOG_CONTEXT_VERBOSE 807 DebugMessageN2("OALContext::SetListenerGain() - OALContext:inGain = %ld:%f", (long int) mSelfToken, inGain); 808#endif 809 if (inGain < 0.0f) 810 throw (OSStatus) AL_INVALID_VALUE; 811 812 if (mListenerGain != inGain) 813 { 814 mListenerGain = inGain; 815 816 Float32 db = 20.0 * log10(inGain); // convert to db 817 AudioUnitSetParameter (mMixerUnit, k3DMixerParam_Gain, kAudioUnitScope_Output, 0, db, 0); 818 } 819} 820 821// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 822UInt32 OALContext::GetSourceCount() 823{ 824#if LOG_CONTEXT_VERBOSE 825 DebugMessageN1("OALContext::GetSourceCount() - OALContext = %ld", (long int) mSelfToken); 826#endif 827 UInt32 count = 0; 828 829 if (mSourceMap) 830 { 831 CAGuard::Locker locked(mSourceMapLock); 832 count = mSourceMap->Size(); 833 } 834 835 return count; 836} 837 838// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 839void OALContext::SetListenerPosition(Float32 posX, Float32 posY, Float32 posZ) 840{ 841#if LOG_CONTEXT_VERBOSE 842 DebugMessageN4("OALContext::SetListenerPosition() - OALContext:posX:posY:posZ = %ld:%f:%f:%f", (long int) mSelfToken, posX, posY, posZ); 843#endif 844 if (isnan(posX) || isnan(posY) || isnan(posZ)) 845 throw ((OSStatus) AL_INVALID_VALUE); 846 847 if ( (mListenerPosition[0] == posX) && 848 (mListenerPosition[1] == posY ) && 849 (mListenerPosition[2] == posZ) ) 850 return; 851 852 mListenerPosition[0] = posX; 853 mListenerPosition[1] = posY; 854 mListenerPosition[2] = posZ; 855 856 if (mSourceMap) 857 { 858#if LOG_GRAPH_AND_MIXER_CHANGES 859 DebugMessageN4("OALContext::SetListenerPosition called - OALSource = %f:%f:%f/%ld\n", posX, posY, posZ, mSelfToken); 860#endif 861 CAGuard::Locker locked(mSourceMapLock); 862 // moving the listener effects the coordinate translation for ALL the sources 863 mSourceMap->MarkAllSourcesForRecalculation(); 864 } 865} 866 867// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 868void OALContext::SetListenerVelocity(Float32 posX, Float32 posY, Float32 posZ) 869{ 870#if LOG_CONTEXT_VERBOSE 871 DebugMessageN4("OALContext::SetListenerVelocity() - OALContext:posX:posY:posZ = %ld:%f:%f:%f", (long int) mSelfToken, posX, posY, posZ); 872#endif 873 mListenerVelocity[0] = posX; 874 mListenerVelocity[1] = posY; 875 mListenerVelocity[2] = posZ; 876 877 if (mSourceMap) 878 { 879#if LOG_GRAPH_AND_MIXER_CHANGES 880 DebugMessage("OALContext::SetListenerVelocity: MarkAllSourcesForRecalculation called\n"); 881#endif 882 CAGuard::Locker locked(mSourceMapLock); 883 // moving the listener effects the coordinate translation for ALL the sources 884 mSourceMap->MarkAllSourcesForRecalculation(); 885 } 886} 887 888// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 889void OALContext::SetListenerOrientation( Float32 forwardX, Float32 forwardY, Float32 forwardZ, 890 Float32 upX, Float32 upY, Float32 upZ) 891{ 892#if LOG_CONTEXT_VERBOSE 893 DebugMessageN7("OALContext::SetListenerOrientation() - OALContext:forwardX:forwardY:forwardZ:upX:upY:upZ = %ld:%f:%f:%f:%f:%f:%f", (long int) mSelfToken, forwardX, forwardY, forwardZ, upX, upY, upZ); 894#endif 895 896 if (isnan(forwardX) || isnan(forwardY) || isnan(forwardZ) || isnan(upX) || isnan(upY) || isnan(upZ)) 897 throw ((OSStatus) AL_INVALID_VALUE); 898 899 if ( (mListenerOrientationForward[0] == forwardX) && 900 (mListenerOrientationForward[1] == forwardY ) && 901 (mListenerOrientationForward[2] == forwardZ) && 902 (mListenerOrientationUp[0] == upX) && 903 (mListenerOrientationUp[1] == upY ) && 904 (mListenerOrientationUp[2] == upZ) ) 905 return; 906 907 mListenerOrientationForward[0] = forwardX; 908 mListenerOrientationForward[1] = forwardY; 909 mListenerOrientationForward[2] = forwardZ; 910 mListenerOrientationUp[0] = upX; 911 mListenerOrientationUp[1] = upY; 912 mListenerOrientationUp[2] = upZ; 913 914 if (mSourceMap) 915 { 916#if LOG_GRAPH_AND_MIXER_CHANGES 917 DebugMessage("OALContext::SetListenerOrientation: MarkAllSourcesForRecalculation called\n"); 918#endif 919 CAGuard::Locker locked(mSourceMapLock); 920 // moving the listener effects the coordinate translation for ALL the sources 921 mSourceMap->MarkAllSourcesForRecalculation(); 922 } 923} 924 925// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 926UInt32 OALContext::GetDesiredRenderChannels(UInt32 inDeviceChannels) 927{ 928#if LOG_CONTEXT_VERBOSE 929 DebugMessageN2("OALContext::GetDesiredRenderChannels() - OALContext:inDeviceChannels = %ld:%d", (long int) mSelfToken, inDeviceChannels); 930#endif 931 UInt32 returnValue = inDeviceChannels; 932 933 if ((Get3DMixerVersion() < k3DMixerVersion_2_0) && (returnValue == 4)) 934 { 935 // quad did not work properly before version 2.0 of the 3DMixer, so just render to stereo 936 returnValue = 2; 937 } 938 else if (inDeviceChannels < 4) 939 { 940 // guard against the possibility of multi channel hw that has never been given a preferred channel layout 941 // Or, that a 3 channel layout was returned (which is unsupported by the 3DMixer) 942 returnValue = 2; 943 } 944 else if ((inDeviceChannels > 5) && (Get3DMixerVersion() < k3DMixerVersion_2_3)) 945 { 946 // 3DMixer ver. 2.2 and below could only render a maximum of 5 channels 947 returnValue = 5; 948 } 949 else if(inDeviceChannels > 8) 950 { 951 // Current 3DMixer can handle a maximum of 8 channels 952 returnValue = 8; 953 } 954 955 return returnValue; 956} 957 958// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 959void OALContext::InitRenderQualityOnBusses() 960{ 961#if LOG_CONTEXT_VERBOSE 962 DebugMessageN1("OALContext::InitRenderQualityOnBusses() - OALContext = %ld", (long int) mSelfToken); 963#endif 964 UInt32 channelCount = mOwningDevice->GetDesiredRenderChannelCount(); 965 966 if (channelCount > 2) 967 { 968 // at this time, there is only one spatial quality being used for multi channel hw 969 DebugMessage("********** InitRenderQualityOnBusses:kDefaultMultiChannelQuality ***********"); 970 mSpatialSetting = kDefaultMultiChannelQuality; 971 } 972 else if (mRenderQuality == ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_LOW) 973 { 974 // this is the default case for stereo 975 DebugMessage("********** InitRenderQualityOnBusses:kDefaultLowQuality ***********"); 976 mSpatialSetting = kDefaultLowQuality; 977 } 978 else 979 { 980 DebugMessage("********** InitRenderQualityOnBusses:kDefaultHighQuality ***********"); 981 mSpatialSetting = kDefaultHighQuality; 982 } 983 984 UInt32 render_flags_3d = k3DMixerRenderingFlags_DistanceAttenuation; 985 if (mRenderQuality == ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_HIGH) 986 { 987 // off by default, on if the user sets High Quality rendering, as HRTF requires InterAuralDelay to be on 988 render_flags_3d += k3DMixerRenderingFlags_InterAuralDelay; 989 } 990 991 if (mASAReverbState > 0) 992 { 993 // off by default, on if the user turns on Reverb, as it requires DistanceDiffusion to be on 994 render_flags_3d += k3DMixerRenderingFlags_DistanceDiffusion; 995 render_flags_3d += (1L << 6 /* k3DMixerRenderingFlags_ConstantReverbBlend*/); 996 } 997 998 OSStatus result = noErr; 999 UInt32 propSize; 1000 CAStreamBasicDescription format; 1001 for (UInt32 i = 0; i < mBusCount; i++) 1002 { 1003 propSize = sizeof(format); 1004 result = AudioUnitGetProperty ( mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, i, &format, &propSize); 1005 1006 // only reset the mono channels, stereo channels are always set to stereo pass thru regardless of render quality setting 1007 if ((result == noErr) && (format.NumberChannels() == 1)) 1008 { 1009 // Spatialization 1010 /*result =*/ AudioUnitSetProperty( mMixerUnit, kAudioUnitProperty_SpatializationAlgorithm, kAudioUnitScope_Input, 1011 i, &mSpatialSetting, sizeof(mSpatialSetting)); 1012 1013 // Render Flags 1014 /*result =*/ AudioUnitSetProperty( mMixerUnit, kAudioUnitProperty_3DMixerRenderingFlags, kAudioUnitScope_Input, 1015 i, &render_flags_3d, sizeof(render_flags_3d)); 1016 } 1017 } 1018} 1019 1020// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1021void OALContext::InitRenderQualityOnSources() 1022{ 1023#if LOG_CONTEXT_VERBOSE 1024 DebugMessageN1("OALContext::InitRenderQualityOnSources() - OALContext = %ld", (long int) mSelfToken); 1025#endif 1026 1027 if (mSourceMap) 1028 { 1029 for (UInt32 i = 0; i < mSourceMap->Size(); i++) 1030 { 1031 OALSource *oalSource = mSourceMap->GetSourceByIndex(0); 1032 if (oalSource) 1033 { 1034 ProtectSource(oalSource->GetToken()); 1035 oalSource->SetRenderQuality(GetRenderQuality()); 1036 ReleaseSource(oalSource); 1037 } 1038 } 1039 } 1040} 1041 1042// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1043void OALContext::SetRenderQuality (UInt32 inRenderQuality) 1044{ 1045#if LOG_CONTEXT_VERBOSE 1046 DebugMessageN2("OALContext::SetRenderQuality() - OALContext:inRenderQuality = %ld:%d", (long int) mSelfToken, inRenderQuality); 1047#endif 1048 if (mRenderQuality == inRenderQuality) 1049 return; // nothing to do; 1050 1051 // make sure a valid quality setting is requested 1052 if (!IsValidRenderQuality(inRenderQuality)) 1053 throw (OSStatus) AL_INVALID_VALUE; 1054 1055 mRenderQuality = inRenderQuality; 1056 1057 // change the spatialization for all mono busses on the mixer 1058 InitRenderQualityOnBusses(); 1059 1060 // reset the render quality on all the sources to the new render quality 1061 InitRenderQualityOnSources(); 1062} 1063 1064// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1065void OALContext::SetSourceDesiredRenderQualityOnBus (UInt32 inRenderQuality, int inBus) 1066{ 1067#if LOG_CONTEXT_VERBOSE 1068 DebugMessageN3("OALContext::SetSourceDesiredRenderQualityOnBus() - OALContext:inRenderQuality:inBus = %d:%d:%d", (int) mSelfToken, (int) inRenderQuality, inBus); 1069#endif 1070 1071 //if we're in multi-channel mode, don't change the render quality 1072 UInt32 channelCount = mOwningDevice->GetDesiredRenderChannelCount(); 1073 if (channelCount > 2) 1074 return; 1075 1076 OSStatus err = noErr; 1077 int spatialSetting = -1; 1078 CAStreamBasicDescription format; UInt32 propSize = sizeof(format); 1079 1080 // make sure a valid quality setting is requested 1081 if (!IsValidRenderQuality(inRenderQuality)) 1082 { 1083 //err = -50; 1084 goto end; 1085 } 1086 1087 if (inRenderQuality == ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_LOW) 1088 { 1089 // this is the default case for non headphone quality 1090 spatialSetting = kSpatializationAlgorithm_EqualPowerPanning; 1091 } 1092 else if (inRenderQuality == ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_HIGH) 1093 { 1094 spatialSetting = kSpatializationAlgorithm_HRTF; 1095 } 1096 else 1097 { 1098 //err = -50; // unknown quality setting 1099 goto end; 1100 } 1101 1102 //check if it is appropriate to set the render quality based on the format of the bus 1103 err = AudioUnitGetProperty ( mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, inBus, &format, &propSize); 1104 if (err == noErr) 1105 { 1106 //the spatial algorithm should be set only for Mono busses (Stereo busses are automatically set to kSpatializationAlgorithm_StereoPassThrough) 1107 if ((format.mChannelsPerFrame == 1) && (spatialSetting != -1)) 1108 { 1109 err = AudioUnitSetProperty( mMixerUnit, kAudioUnitProperty_SpatializationAlgorithm, kAudioUnitScope_Input, inBus, &spatialSetting, sizeof(spatialSetting)); 1110 if (err) 1111 goto end; 1112 } 1113 } 1114 1115end: 1116 return; 1117} 1118 1119// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1120UInt32 OALContext::GetRenderQualityForBus (int inBus) 1121{ 1122#if LOG_CONTEXT_VERBOSE 1123 DebugMessageN1(XLOG_TRACE, kOALLogScope_Context, "OALContext::GetRenderQualityForBus() - inBus = %d", inBus); 1124#endif 1125 1126 //if we encounter any errors along the way, return the context's render quality 1127 UInt32 renderQuality = mRenderQuality; 1128 1129 UInt32 spatialAlgo = 0; UInt32 propSize = sizeof(spatialAlgo); 1130 OSStatus result = AudioUnitGetProperty(mMixerUnit, kAudioUnitProperty_SpatializationAlgorithm, kAudioUnitScope_Input, inBus, &spatialAlgo, &propSize); 1131 if(result) { 1132 goto end; 1133 } 1134 1135 switch (spatialAlgo) 1136 { 1137 case kSpatializationAlgorithm_EqualPowerPanning: 1138 renderQuality = ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_LOW; 1139 break; 1140 1141 case kSpatializationAlgorithm_HRTF: 1142 renderQuality = ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_HIGH; 1143 break; 1144 1145 default: 1146 break; 1147 } 1148 1149end: 1150 return renderQuality; 1151} 1152 1153 1154// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1155void OALContext::SetDistanceAttenuation(UInt32 inBusIndex, Float64 inRefDist, Float64 inMaxDist, Float64 inRolloff) 1156{ 1157 if (Get3DMixerVersion() >= k3DMixerVersion_2_0) 1158 return; // unnecessary with v2.0 mixer 1159 1160 Float64 maxattenuationDB = 20 * log10(inRefDist / (inRefDist + (inRolloff * (inMaxDist - inRefDist)))); 1161 Float64 maxattenuation = pow(10, (maxattenuationDB/20)); 1162 Float64 distAttenuation = (log(1/maxattenuation))/(log(inMaxDist)) - 1.0; 1163 1164 #if 0 1165 DebugMessageN1("SetDistanceAttenuation:Reference Distance = %f", inRefDist); 1166 DebugMessageN1("SetDistanceAttenuation:Maximum Distance = %f", inMaxDist); 1167 DebugMessageN1("SetDistanceAttenuation:Rolloff = %f", inRolloff); 1168 DebugMessageN1("SetDistanceAttenuati2on:Max Attenuation DB = %f", maxattenuationDB); 1169 DebugMessageN1("SetDistanceAttenuation:Max Attenuation Scalar = %f", maxattenuation); 1170 DebugMessageN1("SetDistanceAttenuation:distAttenuation = %f", distAttenuation); 1171 1172 #endif 1173 1174 AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_3DMixerDistanceAtten, kAudioUnitScope_Input, inBusIndex, &distAttenuation, sizeof(distAttenuation)); 1175 return; 1176} 1177 1178// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1179UInt32 OALContext::GetAvailableMonoBus (ALuint inSourceToken) 1180{ 1181#if LOG_CONTEXT_VERBOSE 1182 DebugMessageN2("OALContext::GetAvailableMonoBus() - OALContext:inSourceToken = %ld:%d", (long int) mSelfToken, inSourceToken); 1183#endif 1184 // look for a bus already set for mono 1185 for (UInt32 i = 0; i < mBusCount; i++) 1186 { 1187 if ((mBusInfo[i].mSourceAttached == kNoSourceAttached) && mBusInfo[i].mNumberChannels == 1) 1188 { 1189 mBusInfo[i].mSourceAttached = inSourceToken; 1190#if LOG_BUS_CONNECTIONS 1191 mMonoSourcesConnected++; 1192 DebugMessageN2("GetAvailableMonoBus1: Sources Connected, Mono = %ld, Stereo = %ld", mMonoSourcesConnected, mStereoSourcesConnected); 1193 DebugMessageN1("GetAvailableMonoBus1: BUS_NUMBER = %ld", i); 1194#endif 1195 return (i); 1196 } 1197 } 1198 1199 // do not try and switch a bus to mono if the appliction specified mono and stereo bus counts 1200 if (!mUserSpecifiedBusCounts) 1201 { 1202 // couldn't find a mono bus, so find any available channel and make it mono 1203 for (UInt32 i = 0; i < mBusCount; i++) 1204 { 1205 if (mBusInfo[i].mSourceAttached == kNoSourceAttached) 1206 { 1207 #if LOG_BUS_CONNECTIONS 1208 mMonoSourcesConnected++; 1209 DebugMessageN2("GetAvailableMonoBus2: Sources Connected, Mono = %ld, Stereo = %ld", mMonoSourcesConnected, mStereoSourcesConnected); 1210 #endif 1211 CAStreamBasicDescription theOutFormat; 1212 theOutFormat.mChannelsPerFrame = 1; 1213 theOutFormat.mSampleRate = GetMixerRate(); // as a default, set the bus to the mixer's output rate, it should get reset if necessary later on 1214 theOutFormat.mFormatID = kAudioFormatLinearPCM; 1215 theOutFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; 1216 theOutFormat.mBytesPerPacket = sizeof (Float32); 1217 theOutFormat.mFramesPerPacket = 1; 1218 theOutFormat.mBytesPerFrame = sizeof (Float32); 1219 theOutFormat.mBitsPerChannel = sizeof (Float32) * 8; 1220 OSStatus result = AudioUnitSetProperty ( mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1221 i, &theOutFormat, sizeof(CAStreamBasicDescription)); 1222 THROW_RESULT 1223 1224 mBusInfo[i].mSourceAttached = inSourceToken; 1225 mBusInfo[i].mNumberChannels = 1; 1226 AudioUnitSetProperty( mMixerUnit, kAudioUnitProperty_SpatializationAlgorithm, kAudioUnitScope_Input, 1227 i, &mSpatialSetting, sizeof(mSpatialSetting)); 1228 1229 UInt32 render_flags_3d = k3DMixerRenderingFlags_DistanceAttenuation; 1230 if (mRenderQuality == ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_HIGH) 1231 render_flags_3d += k3DMixerRenderingFlags_InterAuralDelay; // off by default, on if the user sets High Quality rendering 1232 1233 // Render Flags 1234 /*result =*/ AudioUnitSetProperty( mMixerUnit, kAudioUnitProperty_3DMixerRenderingFlags, kAudioUnitScope_Input, 1235 i, &render_flags_3d, sizeof(render_flags_3d)); 1236 1237 return (i); 1238 } 1239 } 1240 } 1241 1242 DebugMessage("ERROR: GetAvailableMonoBus: COULD NOT GET A MONO BUS"); 1243 throw (-1); // no inputs available 1244} 1245 1246// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1247UInt32 OALContext::GetAvailableStereoBus (ALuint inSourceToken) 1248{ 1249#if LOG_CONTEXT_VERBOSE 1250 DebugMessageN2("OALContext::GetAvailableStereoBus() - OALContext:inSourceToken = %ld:%d", (long int) mSelfToken, inSourceToken); 1251#endif 1252 for (UInt32 i = 0; i < mBusCount; i++) 1253 { 1254 if ((mBusInfo[i].mSourceAttached == kNoSourceAttached) && mBusInfo[i].mNumberChannels == 2) 1255 { 1256 mBusInfo[i].mSourceAttached = inSourceToken; 1257#if LOG_BUS_CONNECTIONS 1258 mStereoSourcesConnected++; 1259 DebugMessageN2("GetAvailableStereoBus1: Sources Connected, Mono = %ld, Stereo = %ld", mMonoSourcesConnected, mStereoSourcesConnected); 1260 DebugMessageN1("GetAvailableStereoBus1: BUS_NUMBER = %ld", i); 1261#endif 1262 return (i); 1263 } 1264 } 1265 1266 // do not try and switch a bus to stereo if the appliction specified mono and stereo bus counts 1267 if (!mUserSpecifiedBusCounts) 1268 { 1269 // couldn't find one, so look for a mono channel, make it stereo and set to kSpatializationAlgorithm_StereoPassThrough 1270 for (UInt32 i = 0; i < mBusCount; i++) 1271 { 1272 if (mBusInfo[i].mSourceAttached == kNoSourceAttached) 1273 { 1274 1275 #if LOG_BUS_CONNECTIONS 1276 mStereoSourcesConnected++; 1277 DebugMessageN2("GetAvailableStereoBus2: Sources Connected, Mono = %ld, Stereo = %ld", mMonoSourcesConnected, mStereoSourcesConnected); 1278 DebugMessageN1("GetAvailableStereoBus2: BUS_NUMBER = %ld", i); 1279 #endif 1280 CAStreamBasicDescription theOutFormat; 1281 theOutFormat.mChannelsPerFrame = 2; 1282 theOutFormat.mSampleRate = GetMixerRate(); 1283 theOutFormat.mFormatID = kAudioFormatLinearPCM; 1284 theOutFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; 1285 theOutFormat.mBytesPerPacket = sizeof (Float32); 1286 theOutFormat.mFramesPerPacket = 1; 1287 theOutFormat.mBytesPerFrame = sizeof (Float32); 1288 theOutFormat.mBitsPerChannel = sizeof (Float32) * 8; 1289 OSStatus result = AudioUnitSetProperty ( mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1290 i, &theOutFormat, sizeof(CAStreamBasicDescription)); 1291 THROW_RESULT 1292 1293 mBusInfo[i].mSourceAttached = inSourceToken; 1294 mBusInfo[i].mNumberChannels = 2; 1295 1296 UInt32 spatAlgo = kSpatializationAlgorithm_StereoPassThrough; 1297 AudioUnitSetProperty( mMixerUnit, kAudioUnitProperty_SpatializationAlgorithm, kAudioUnitScope_Input, 1298 i, &spatAlgo, sizeof(spatAlgo)); 1299 1300 return (i); 1301 } 1302 } 1303 } 1304 1305 DebugMessage("ERROR: GetAvailableStereoBus: COULD NOT GET A STEREO BUS"); 1306 throw (-1); // no inputs available 1307} 1308 1309// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1310void OALContext::SetBusAsAvailable (UInt32 inBusIndex) 1311{ 1312#if LOG_CONTEXT_VERBOSE 1313 DebugMessageN2("OALContext::SetBusAsAvailable() - OALContext:inBusIndex = %ld:%d", (long int) mSelfToken, inBusIndex); 1314#endif 1315 mBusInfo[inBusIndex].mSourceAttached = kNoSourceAttached; 1316 1317#if LOG_BUS_CONNECTIONS 1318 if (mBusInfo[inBusIndex].mNumberChannels == 1) 1319 mMonoSourcesConnected--; 1320 else 1321 mStereoSourcesConnected--; 1322 1323 DebugMessageN2("SetBusAsAvailable: Sources Connected, Mono = %ld, Stereo = %ld", mMonoSourcesConnected, mStereoSourcesConnected); 1324#endif 1325} 1326 1327// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1328// Apple Environmental Audio (ASA) Extension 1329// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1330void OALContext::SetReverbRoomType(UInt32 inRoomType) 1331{ 1332#if LOG_CONTEXT_VERBOSE 1333 DebugMessageN2("OALContext::SetReverbRoomType() - OALContext:inRoomType = %ld:%d", (long int) mSelfToken, inRoomType); 1334#endif 1335 if (mASAReverbRoomType == inRoomType) 1336 return; // nothing to do; 1337 1338 mASAReverbRoomType = inRoomType; 1339 1340 OSStatus result = AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_ReverbRoomType, kAudioUnitScope_Global, 0, &mASAReverbRoomType, sizeof(mASAReverbRoomType)); 1341 THROW_RESULT 1342} 1343 1344// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1345void OALContext::SetReverbLevel(Float32 inReverbLevel) 1346{ 1347#if LOG_CONTEXT_VERBOSE 1348 DebugMessageN2("OALContext::SetReverbLevel() - OALContext:inReverbLevel = %ld:%f", (long int) mSelfToken, inReverbLevel); 1349#endif 1350 1351 if (mASAReverbGlobalLevel == inReverbLevel) 1352 return; // nothing to do; 1353 1354 mASAReverbGlobalLevel = inReverbLevel; 1355 1356 OSStatus result = AudioUnitSetParameter (mMixerUnit, 6 /*k3DMixerParam_GlobalReverbGain*/, kAudioUnitScope_Global, 0, mASAReverbGlobalLevel, 0); 1357 THROW_RESULT 1358} 1359 1360// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1361void OALContext::SetReverbState(UInt32 inReverbState) 1362{ 1363#if LOG_CONTEXT_VERBOSE 1364 DebugMessageN2("OALContext::SetReverbState() - OALContext:inReverbState = %ld:%d", (long int) mSelfToken, inReverbState); 1365#endif 1366 if (mASAReverbState == inReverbState) 1367 return; // nothing to do; 1368 1369 mASAReverbState = inReverbState; 1370 1371 OSStatus result = AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_UsesInternalReverb, kAudioUnitScope_Global, 0, &mASAReverbState, sizeof(mASAReverbState)); 1372 if (result == noErr) 1373 InitRenderQualityOnBusses(); // distance diffusion needs to be reset on the busses now 1374} 1375 1376// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1377void OALContext::SetReverbQuality(UInt32 inQuality) 1378{ 1379#if LOG_CONTEXT_VERBOSE 1380 DebugMessageN2("OALContext::SetReverbQuality() - OALContext:inQuality = %ld:%d", (long int) mSelfToken, inQuality); 1381#endif 1382 if (mASAReverbQuality == inQuality) 1383 return; // nothing to do; 1384 1385 mASAReverbQuality = inQuality; 1386 1387 AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_RenderQuality, kAudioUnitScope_Global, 0, &mASAReverbQuality, sizeof(mASAReverbQuality)); 1388} 1389 1390// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1391void OALContext::SetReverbEQGain(Float32 inGain) 1392{ 1393#if LOG_CONTEXT_VERBOSE 1394 DebugMessageN2("OALContext::SetReverbEQGain() - OALContext:inGain = %ld:%f", (long int) mSelfToken, inGain); 1395#endif 1396 if (mASAReverbEQGain != inGain) 1397 { 1398 mASAReverbEQGain = inGain; 1399 OSStatus result = AudioUnitSetParameter (mMixerUnit, 20000 + 16 /*kReverbParam_FilterGain*/, kAudioUnitScope_Global, 0, mASAReverbEQGain, 0); 1400 THROW_RESULT 1401 } 1402} 1403 1404// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1405void OALContext::SetReverbEQBandwidth(Float32 inBandwidth) 1406{ 1407#if LOG_CONTEXT_VERBOSE 1408 DebugMessageN2("OALContext::SetReverbEQBandwidth() - OALContext:inBandwidth = %ld:%f", (long int) mSelfToken, inBandwidth); 1409#endif 1410 if (mASAReverbEQBandwidth != inBandwidth) 1411 { 1412 mASAReverbEQBandwidth = inBandwidth; 1413 OSStatus result = AudioUnitSetParameter (mMixerUnit, 20000 + 15 /*kReverbParam_FilterBandwidth*/, kAudioUnitScope_Global, 0, mASAReverbEQBandwidth, 0); 1414 THROW_RESULT 1415 } 1416} 1417 1418// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1419void OALContext::SetReverbEQFrequency(Float32 inFrequency) 1420{ 1421#if LOG_CONTEXT_VERBOSE 1422 DebugMessageN2("OALContext::SetReverbEQFrequency() - OALContext:inFrequency = %ld:%f", (long int) mSelfToken, inFrequency); 1423#endif 1424 if (mASAReverbEQFrequency != inFrequency) 1425 { 1426 mASAReverbEQFrequency = inFrequency; 1427 OSStatus result = AudioUnitSetParameter (mMixerUnit, 20000 + 14 /*kReverbParam_FilterFrequency*/, kAudioUnitScope_Global, 0, mASAReverbEQFrequency, 0); 1428 THROW_RESULT 1429 } 1430} 1431 1432// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1433UInt32 OALContext::GetReverbQuality() 1434{ 1435#if LOG_CONTEXT_VERBOSE 1436 DebugMessageN1("OALContext::GetReverbQuality() - OALContext = %ld", (long int) mSelfToken); 1437#endif 1438 return mASAReverbQuality; 1439} 1440 1441// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1442Float32 OALContext::GetReverbEQGain() 1443{ 1444#if LOG_CONTEXT_VERBOSE 1445 DebugMessageN1("OALContext::GetReverbEQGain() - OALContext = %ld", (long int) mSelfToken); 1446#endif 1447 OSStatus result = AudioUnitGetParameter(mMixerUnit, 20000 + 16 /*kReverbParam_FilterGain*/,kAudioUnitScope_Global,0, &mASAReverbEQGain); 1448 if (result) 1449 return 0.0; 1450 1451 return mASAReverbEQGain; 1452} 1453 1454// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1455Float32 OALContext::GetReverbEQBandwidth() 1456{ 1457#if LOG_CONTEXT_VERBOSE 1458 DebugMessageN1("OALContext::GetReverbEQBandwidth() - OALContext = %ld", (long int) mSelfToken); 1459#endif 1460 OSStatus result = AudioUnitGetParameter(mMixerUnit, 20000 + 15 /*kReverbParam_FilterBandwidth*/,kAudioUnitScope_Global,0, &mASAReverbEQBandwidth); 1461 if (result) 1462 return 0.0; 1463 1464 return mASAReverbEQBandwidth; 1465} 1466 1467// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1468Float32 OALContext::GetReverbEQFrequency() 1469{ 1470#if LOG_CONTEXT_VERBOSE 1471 DebugMessageN1("OALContext::GetReverbEQFrequency() - OALContext = %ld", (long int) mSelfToken); 1472#endif 1473 OSStatus result = AudioUnitGetParameter(mMixerUnit, 20000 + 14 /*kReverbParam_FilterFrequency*/,kAudioUnitScope_Global,0, &mASAReverbEQFrequency); 1474 if (result) 1475 return 0.0; 1476 1477 return mASAReverbEQFrequency; 1478} 1479 1480// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1481void OALContext::SetReverbPreset (FSRef* inRef) 1482{ 1483#if LOG_CONTEXT_VERBOSE 1484 DebugMessageN1("OALContext::SetReverbPreset() - OALContext = %ld", (long int) mSelfToken); 1485#endif 1486 try { 1487 Boolean status; 1488 SInt32 result = 0; 1489 CFURLRef fileURL = CFURLCreateFromFSRef (kCFAllocatorDefault, inRef); 1490 if (fileURL) 1491 { 1492 // Read the XML file. 1493 CFDataRef resourceData = NULL; 1494 1495 status = CFURLCreateDataAndPropertiesFromResource (kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &result); 1496 CFRelease (fileURL); // no longer needed 1497 1498 if (status == false || result) 1499 throw (OSStatus) -1; 1500 else 1501 { 1502 CFStringRef errString = NULL; 1503 CFPropertyListRef theData = NULL; 1504 theData = CFPropertyListCreateFromXMLData (kCFAllocatorDefault, resourceData, kCFPropertyListImmutable, &errString); 1505 CFRelease (resourceData); 1506 if (errString) 1507 CFRelease (errString); 1508 1509 if (theData == NULL || errString) 1510 { 1511 if (theData) 1512 CFRelease (theData); 1513 throw (OSStatus) -1; 1514 } 1515 else 1516 { 1517 result = AudioUnitSetProperty(mMixerUnit, 3012 /*kAudioUnitProperty_ReverbPreset*/, kAudioUnitScope_Global, 0, &theData, sizeof(theData) ); 1518 CFRelease (theData); 1519 THROW_RESULT 1520 } 1521 } 1522 } 1523 else 1524 throw (OSStatus) -1; 1525 } 1526 catch (OSStatus result) { 1527 throw result; 1528 } 1529 catch (...) { 1530 throw (OSStatus) -1; 1531 } 1532 1533 return; 1534} 1535 1536// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1537OSStatus OALContext::ContextNotificationProc ( void *inRefCon, 1538 AudioUnitRenderActionFlags *inActionFlags, 1539 const AudioTimeStamp *inTimeStamp, 1540 UInt32 inBusNumber, 1541 UInt32 inNumberFrames, 1542 AudioBufferList *ioData) 1543{ 1544#if LOG_CONTEXT_VERBOSE 1545 DebugMessage("OALContext::ContextNotificationProc()"); 1546#endif 1547 OALContext* THIS = (OALContext*)inRefCon; 1548 1549 // we have no use for a pre-render notification, we only care about the post-render 1550 if (*inActionFlags & kAudioUnitRenderAction_PreRender) 1551 { 1552 THIS->mRenderThreadID = pthread_self(); 1553 THIS->DoPreRender(); 1554 } 1555 1556 else if (*inActionFlags & kAudioUnitRenderAction_PostRender) 1557 return THIS->DoPostRender(); 1558 1559 return (noErr); 1560} 1561 1562// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1563OSStatus OALContext::DoPreRender () 1564{ 1565#if LOG_CONTEXT_VERBOSE 1566 DebugMessageN1("OALContext::DoPreRender() - OALContext = %ld", (long int) mSelfToken); 1567#endif 1568#if LOG_MESSAGE_QUEUE 1569 DebugMessageN1("OALContext::DoPreRender"); 1570#endif 1571 1572 for (UInt32 i=0; i < mBusCount; i++) 1573 { 1574 OALSource *oalSource = GetSourceForRender(mBusInfo[i].mSourceAttached); 1575 if (oalSource != NULL) 1576 { 1577 oalSource->DoPreRender(); 1578 ReleaseSource(oalSource); 1579 } 1580 } 1581 1582 return noErr; 1583} 1584 1585// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1586OSStatus OALContext::DoPostRender () 1587{ 1588#if LOG_CONTEXT_VERBOSE 1589 DebugMessageN1("OALContext::DoPostRender() - OALContext = %ld", (long int) mSelfToken); 1590#endif 1591#if LOG_MESSAGE_QUEUE 1592 DebugMessageN1("OALContext::DoPostRender"); 1593#endif 1594 1595 for (UInt32 i=0; i < mBusCount; i++) 1596 { 1597 OALSource *oalSource = GetSourceForRender(mBusInfo[i].mSourceAttached); 1598 if (oalSource == NULL) 1599 { 1600 //if we have a source that needs post-render but has been deleted. We need to just deconstruct 1601 oalSource = GetDeadSourceForRender(mBusInfo[i].mSourceAttached); 1602 if (oalSource) 1603 { 1604 //clear all the current messages 1605 oalSource->ClearMessageQueue(); 1606 oalSource->AddPlaybackMessage((UInt32)kMQ_DeconstructionStop, NULL, 0); 1607 } 1608 else continue; 1609 } 1610 1611 oalSource->DoPostRender(); 1612 ReleaseSource(oalSource); 1613 } 1614 1615 return noErr; 1616} 1617 1618// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1619// Output Capturer Extension 1620// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1621 1622OSStatus OALContext::OutputCapturerCreate(Float64 inSampleRate, UInt32 inOALFormat, UInt32 inBufferSize) 1623{ 1624 try { 1625 if (!mOutputCapturer) 1626 { 1627 mOutputCapturer = new OALCaptureMixer(GetMixerUnit(), inSampleRate, inOALFormat, inBufferSize); 1628 } 1629 else 1630 { 1631 if (mOutputCapturer->IsCapturing()) 1632 { 1633 throw static_cast<OSStatus>(-1); 1634 } 1635 delete mOutputCapturer; mOutputCapturer = NULL; 1636 mOutputCapturer = new OALCaptureMixer(GetMixerUnit(), inSampleRate, inOALFormat, inBufferSize); 1637 } 1638 } 1639 catch (OSStatus result) { 1640 delete mOutputCapturer; 1641 mOutputCapturer = NULL; 1642 return result; 1643 } 1644 catch (...) { 1645 delete mOutputCapturer; 1646 mOutputCapturer = NULL; 1647 return static_cast<OSStatus>(-1); 1648 } 1649 1650 return noErr; 1651} 1652 1653// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1654OSStatus OALContext::OutputCapturerStart() 1655{ 1656 if (mOutputCapturer) 1657 mOutputCapturer->StartCapture(); 1658 else 1659 return static_cast<OSStatus>(-1); //object doesn't exist 1660 1661 return noErr; 1662} 1663 1664// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1665OSStatus OALContext::OutputCapturerStop() 1666{ 1667 if (mOutputCapturer) 1668 mOutputCapturer->StopCapture(); 1669 else 1670 return static_cast<OSStatus>(-1); //object doesn't exist 1671 1672 return noErr; 1673} 1674 1675// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1676OSStatus OALContext::OutputCapturerGetFrames(UInt32 inFrameCount, UInt8* inBuffer) 1677{ 1678 if (mOutputCapturer) 1679 return mOutputCapturer->GetFrames(inFrameCount, inBuffer); 1680 else 1681 return static_cast<OSStatus>(-1); //object doesn't exist 1682} 1683 1684// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1685UInt32 OALContext::OutputCapturerAvailableFrames() 1686{ 1687 if (mOutputCapturer) 1688 { 1689 return mOutputCapturer->AvailableFrames(); 1690 } 1691 1692 return 0; 1693} 1694 1695// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1696