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 7* that the following 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 13* products derived 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 TO, 16* 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 LIMITED 18* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 19* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 20* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21* 22**********************************************************************************************************************************/ 23 24/* 25 Each OALSource object maintains a BufferQueue and an ACMap. The buffer queue is an ordered list of BufferInfo structs. 26 These structs contain an OAL buffer and other pertinent data. The ACMap is a multimap of ACInfo structs. These structs each contain an 27 AudioConverter and the input format of the AudioConverter. The AudioConverters are created as needed each time a buffer with a new 28 format is added to the queue. This allows differently formatted data to be queued seemlessly. The correct AC is used for each 29 buffer as the BufferInfo keeps track of the appropriate AC to use. 30*/ 31 32#include "oalSource.h" 33#include "oalBuffer.h" 34#include "oalImp.h" 35 36#define LOG_PLAYBACK 0 37#define LOG_VERBOSE 0 38#define LOG_BUFFER_QUEUEING 0 39#define LOG_DOPPLER 0 40#define LOG_MESSAGE_QUEUE 0 41 42#define CALCULATE_POSITION 1 // this should be true except for testing 43 44// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 45#define MIXER_PARAMS_UNDEFINED 0 46 47#if MIXER_PARAMS_UNDEFINED 48typedef struct MixerDistanceParams { 49 Float32 mReferenceDistance; 50 Float32 mMaxDistance; 51 Float32 mMaxAttenuation; 52} MixerDistanceParams; 53 54enum { 55 kAudioUnitProperty_3DMixerDistanceParams = 3010 56}; 57#endif 58 59inline bool zapBadness(Float32& inValue) 60{ 61 Float32 absInValue = fabs(inValue); 62 63 if (!(absInValue > 1e-15 && absInValue < 1e15)){ 64 // inValue was one of the following: 0.0, infinity, denormal or NaN 65 inValue = 0.0; 66 return true; 67 } 68 69 return false; 70} 71 72// if dopplerShift = inifinity then peg to 16 (4 octaves up) 73// if dopplershift is a denormal then peg to .125 (3 octaves down) 74// if nan, then set to 1.0 (no doppler) 75// if 0.0 then set to 1.0 which is no shift 76inline bool zapBadnessForDopplerShift(Float32& inValue) 77{ 78 Float32 absInValue = fabs(inValue); 79 80 if (isnan(inValue) || (inValue == 0.0)) 81 inValue = 1.0; 82 else if (absInValue > 1e15) 83 inValue = 16.0; 84 else if (absInValue < 1e-15) 85 inValue = .125; 86 87 return false; 88} 89 90// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 91// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 92// OALSource 93// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 94// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 95 96// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 97#pragma mark ***** PUBLIC ***** 98// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 99OALSource::OALSource (ALuint inSelfToken, OALContext *inOwningContext) 100 : mSelfToken (inSelfToken), 101 mSafeForDeletion(false), 102 mOwningContext(inOwningContext), 103 mIsSuspended(false), 104 mCalculateDistance(true), 105 mResetBusFormat(true), 106 mResetBus(false), 107 mResetPitch(true), 108 mBufferQueueActive(NULL), 109 mBufferQueueInactive(NULL), 110 mBufferQueueTemp(NULL), 111 mQueueLength(0), 112 mTempQueueLength(0), 113 mBuffersQueuedForClear(0), 114 mCurrentBufferIndex(0), 115 mQueueIsProcessed(true), 116 mInUseFlag(0), 117 mSourceLock("OAL:SourceLock"), 118 mCurrentPlayBus (kSourceNeedsBus), 119 mACMap(NULL), 120 mOutputSilence(false), 121 mLooping(AL_FALSE), 122 mSourceRelative(AL_FALSE), 123 mSourceType(AL_UNDETERMINED), 124 mConeInnerAngle(360.0), 125 mConeOuterAngle(360.0), 126 mConeOuterGain(0.0), 127 mConeGainScaler(1.0), 128 mAttenuationGainScaler(1.0), 129 mReadIndex(0.0), 130 mTempSourceStorageBufferSize(2048), // only used if preferred mixer is unavailable 131 mState(AL_INITIAL), 132 mGain(1.0), 133 mPitch(1.0), // this is the user pitch setting changed via the OAL APIs 134 mDopplerScaler(1.0), 135 mRollOffFactor(kDefaultRolloff), 136 mReferenceDistance(kDefaultReferenceDistance), 137 mMaxDistance(kDefaultMaximumDistance), // ***** should be MAX_FLOAT 138 mMinGain(0.0), 139 mMaxGain(1.0), 140 mRampState(kNoRamping), 141 mBufferCountToUnqueueInPostRender(0), 142 mTransitioningToFlushQ(false), 143 mASAReverbSendLevel(0.0), 144 mASAOcclusion(0.0), 145 mASAObstruction(0.0), 146 mRenderQuality(ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_LOW), 147 mRogerBeepNode(0), 148 mRogerBeepAU(0), 149 mASARogerBeepEnable(false), 150 mASARogerBeepOn(true), // RogerBeep AU does not bypass by default 151 mASARogerBeepGain(0.0), 152 mASARogerBeepSensitivity(0), 153 mASARogerBeepType(0), 154 mASARogerBeepPreset(0), 155 mDistortionNode(0), 156 mDistortionAU(0), 157 mASADistortionEnable(false), 158 mASADistortionOn(true), // Distortion AU does not bypass by default 159 mASADistortionMix(0.0), 160 mASADistortionType(0), 161 mASADistortionPreset(0), 162 mSourceNotifications(NULL) 163{ 164#if LOG_VERBOSE 165 DebugMessageN1("OALSource::OALSource() - OALSource = %ld", (long int) mSelfToken); 166#endif 167 168 mPosition[0] = 0.0; 169 mPosition[1] = 0.0; 170 mPosition[2] = 0.0; 171 172 mVelocity[0] = 0.0; 173 mVelocity[1] = 0.0; 174 mVelocity[2] = 0.0; 175 176 mConeDirection[0] = 0.0; 177 mConeDirection[1] = 0.0; 178 mConeDirection[2] = 0.0; 179 180 mBufferQueueActive = new BufferQueue(); 181 mBufferQueueActive->Reserve(512); 182 183 mBufferQueueInactive = new BufferQueue(); 184 mBufferQueueInactive->Reserve(512); 185 186 mBufferQueueTemp = new BufferQueue(); 187 mBufferQueueTemp->Reserve(128); 188 189 mACMap = new ACMap(); 190 191 mReferenceDistance = mOwningContext->GetDefaultReferenceDistance(); 192 mMaxDistance = mOwningContext->GetDefaultMaxDistance(); 193 194 mRenderQuality = mOwningContext->GetRenderQuality(); 195 196 if (Get3DMixerVersion() < k3DMixerVersion_2_0) 197 { 198 // since the preferred mixer is not available, some temporary storgae will be needed for SRC 199 // for now assume that sources will not have more than 2 channels of data 200 mTempSourceStorage = (AudioBufferList *) malloc ((offsetof(AudioBufferList, mBuffers)) + (2 * sizeof(AudioBuffer))); 201 202 mTempSourceStorage->mBuffers[0].mDataByteSize = mTempSourceStorageBufferSize; 203 mTempSourceStorage->mBuffers[0].mData = malloc(mTempSourceStorageBufferSize); 204 205 mTempSourceStorage->mBuffers[1].mDataByteSize = mTempSourceStorageBufferSize; 206 mTempSourceStorage->mBuffers[1].mData = malloc(mTempSourceStorageBufferSize); 207 } 208 else 209 { 210 mTempSourceStorage = NULL; 211 } 212} 213 214// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 215OALSource::~OALSource() 216{ 217#if LOG_VERBOSE 218 DebugMessageN1("OALSource::~OALSource() - OALSource = %ld", (long int) mSelfToken); 219#endif 220 221 // release the 3DMixer bus if necessary 222 if (mCurrentPlayBus != kSourceNeedsBus) 223 { 224 ReleaseNotifyAndRenderProcs(); 225 mOwningContext->SetBusAsAvailable (mCurrentPlayBus); 226 mCurrentPlayBus = kSourceNeedsBus; 227 } 228 229 // empty the two queues 230 FlushBufferQueue(); 231 232 // empty the message queue 233 ClearMessageQueue(); 234 235 if (mTempSourceStorage) 236 free(mTempSourceStorage); 237 238 delete (mBufferQueueInactive); 239 delete (mBufferQueueActive); 240 delete (mBufferQueueTemp); 241 242 // remove all the AudioConverters that were created for the buffer queue of this source object 243 if (mACMap) 244 { 245 mACMap->RemoveAllConverters(); 246 delete (mACMap); 247 } 248 249 if (mSourceNotifications) 250 delete mSourceNotifications; 251} 252 253// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 254// this is only callled by the context when removing a source, which is thread protected already 255void OALSource::SetUpDeconstruction() 256{ 257#if LOG_VERBOSE 258 DebugMessageN1("OALSource::SetUpDeconstruction() - OALSource = %ld", (long int) mSelfToken); 259#endif 260 261 try { 262 // wait if in render, then prevent rendering til completion 263 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 264 265 // don't allow synchronous source manipulation 266 CAGuard::Locker sourceLock(mSourceLock); 267 268 switch (mState) 269 { 270 // if rendering is occurring right now, then let the PostRender tear things down and set mSafeForDeletion to true 271 // all transition states represent a source that is actually rendering data 272 case AL_PLAYING: 273 case kTransitionToStop: 274 case kTransitionToPlay: 275 case kTransitionToPause: 276 case kTransitionToRewind: 277 case kTransitionToRetrigger: 278 case kTransitionToResume: 279 { 280#if LOG_MESSAGE_QUEUE 281 DebugMessageN1("OALSource::SetUpDeconstruction ADDING : kMQ_Stop - OALSource = %ld", (long int) mSelfToken); 282#endif 283 AddPlaybackMessage(kMQ_DeconstructionStop, NULL, 0); 284 break; 285 } 286 287 default: 288 mSafeForDeletion = true; 289 break; 290 } 291 292 SetPlaybackState(kTransitionToStop); 293 mTransitioningToFlushQ = true; 294 } 295 catch (OSStatus result) { 296 DebugMessageN2("SetUpDeconstruction FAILED source = %d, err = %d\n", (int) mSelfToken, (int)result); 297 throw (result); 298 } 299} 300 301// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 302void OALSource::Suspend () 303{ 304} 305 306// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 307void OALSource::Unsuspend () 308{ 309} 310 311// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 312// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 313// SET METHODS 314// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 315// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 316void OALSource::SetPitch (float inPitch) 317{ 318#if LOG_VERBOSE 319 DebugMessageN2("OALSource::SetPitch() - OALSource:inPitch = %ld:%f", (long int) mSelfToken, inPitch); 320#endif 321 if (inPitch < 0.0f) 322 throw ((OSStatus) AL_INVALID_VALUE); 323 324 // don't allow synchronous source manipulation 325 CAGuard::Locker sourceLock(mSourceLock); 326 327 if ((inPitch == mPitch) && (mResetPitch == false)) 328 return; // nothing to do 329 330 mPitch = inPitch; 331 332 // 1.3 3DMixer does not work properly when doing SRC on a mono bus 333 if (Get3DMixerVersion() < k3DMixerVersion_2_0) 334 return; 335 336 Float32 newPitch = mPitch * mDopplerScaler; 337 if (mCurrentPlayBus != kSourceNeedsBus) 338 { 339 BufferInfo *oalBuffer = mBufferQueueActive->Get(mCurrentBufferIndex); 340 341 if (oalBuffer != NULL) 342 { 343#if LOG_GRAPH_AND_MIXER_CHANGES 344 DebugMessageN2("OALSource::SetPitch: k3DMixerParam_PlaybackRate called - OALSource:mPitch = %ld:%f\n", mSelfToken, mPitch ); 345#endif 346 347 OSStatus result = AudioUnitSetParameter (mOwningContext->GetMixerUnit(), k3DMixerParam_PlaybackRate, kAudioUnitScope_Input, mCurrentPlayBus, newPitch, 0); 348 if (result != noErr) 349 DebugMessageN3("OALSource::SetPitch: k3DMixerParam_PlaybackRate called - OALSource = %ld mPitch = %f result = %d\n", (long int) mSelfToken, mPitch, (int)result ); 350 } 351 352 mResetPitch = false; 353 } 354 else 355 mResetPitch = true; // the source is not currently connected to a bus, so make this change when play is called 356} 357 358// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 359void OALSource::SetGain (float inGain) 360{ 361#if LOG_VERBOSE 362 DebugMessageN2("OALSource::SetGain() - OALSource:inGain = %ld:%f", (long int) mSelfToken, inGain); 363#endif 364 if (inGain < 0.0f) 365 throw ((OSStatus) AL_INVALID_VALUE); 366 367 // don't allow synchronous source manipulation 368 CAGuard::Locker sourceLock(mSourceLock); 369 370 if (mGain != inGain) 371 { 372 mGain = inGain; 373 UpdateBusGain(); 374 } 375} 376 377// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 378void OALSource::SetMinGain (Float32 inMinGain) 379{ 380#if LOG_VERBOSE 381 DebugMessageN2("OALSource::SetMinGain - OALSource:inMinGain = %ld:%f\n", (long int) mSelfToken, inMinGain); 382#endif 383 if ((inMinGain < 0.0f) || (inMinGain > 1.0f)) 384 throw ((OSStatus) AL_INVALID_VALUE); 385 386 // don't allow synchronous source manipulation 387 CAGuard::Locker sourceLock(mSourceLock); 388 389 if (mMinGain != inMinGain) 390 { 391 mMinGain = inMinGain; 392 UpdateMinBusGain(); 393 } 394} 395 396// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 397void OALSource::SetMaxGain (Float32 inMaxGain) 398{ 399#if LOG_VERBOSE 400 DebugMessageN2("OALSource::SetMaxGain - OALSource:inMaxGain = %ld:%f\n", (long int) mSelfToken, inMaxGain); 401#endif 402 403 if ((inMaxGain < 0.0f) || (inMaxGain > 1.0f)) 404 throw ((OSStatus) AL_INVALID_VALUE); 405 406 // don't allow synchronous source manipulation 407 CAGuard::Locker sourceLock(mSourceLock); 408 409 if (mMaxGain != inMaxGain) 410 { 411 mMaxGain = inMaxGain; 412 UpdateMaxBusGain(); 413 } 414} 415 416// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 417Float32 OALSource::GetMaxAttenuation(Float32 inRefDistance, Float32 inMaxDistance, Float32 inRolloff) 418{ 419#if LOG_VERBOSE 420 DebugMessageN4("OALSource::GetMaxAttenuation() - OALSource:inRefDistance:inMaxDistance:inRolloff = %ld:%f:%f:%f", (long int) mSelfToken, inRefDistance, inMaxDistance, inRolloff); 421#endif 422 Float32 returnValue = 20 * log10(inRefDistance / (inRefDistance + (inRolloff * (inMaxDistance - inRefDistance)))); 423 424 if (returnValue < 0.0) 425 returnValue *= -1.0; 426 else 427 returnValue = 0.0; // if db result was positive, clamp it to zero 428 429 return returnValue; 430} 431 432// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 433OSStatus OALSource::SetDistanceParams(bool inChangeReferenceDistance, bool inChangeMaxDistance) 434{ 435#if LOG_VERBOSE 436 DebugMessageN3("OALSource::SetDistanceParams() - OALSource:inChangeReferenceDistance:inChangeMaxDistance = %ld:%d:%d", (long int) mSelfToken, inChangeReferenceDistance, inChangeMaxDistance); 437#endif 438 OSStatus result = noErr; 439 440 // don't allow synchronous source manipulation 441 CAGuard::Locker sourceLock(mSourceLock); 442 443 if (Get3DMixerVersion() < k3DMixerVersion_2_0) 444 { 445 // the pre-2.0 3DMixer does not accept kAudioUnitProperty_3DMixerDistanceParams, it has do some extra work and use the DistanceAtten property instead 446 mOwningContext->SetDistanceAttenuation(mCurrentPlayBus, mReferenceDistance, mMaxDistance, mRollOffFactor); 447 } 448 else 449 { 450 MixerDistanceParams distanceParams; 451 UInt32 propSize = sizeof(distanceParams); 452 result = AudioUnitGetProperty(mOwningContext->GetMixerUnit(), kAudioUnitProperty_3DMixerDistanceParams, kAudioUnitScope_Input, mCurrentPlayBus, &distanceParams, &propSize); 453 if (result == noErr) 454 { 455 Float32 rollOff = mRollOffFactor; 456 457 if (mOwningContext->IsDistanceScalingRequired()) 458 { 459 // scale the reference distance 460 distanceParams.mReferenceDistance = (mReferenceDistance/mMaxDistance) * kDistanceScalar; 461 // limit the max distance 462 distanceParams.mMaxDistance = kDistanceScalar; 463 // scale the rolloff 464 rollOff *= (kDistanceScalar/mMaxDistance); 465 } 466 else 467 { 468 if (inChangeReferenceDistance) 469 distanceParams.mReferenceDistance = mReferenceDistance; 470 else if (inChangeMaxDistance) 471 distanceParams.mMaxDistance = mMaxDistance; 472 } 473 474 distanceParams.mMaxAttenuation = GetMaxAttenuation(distanceParams.mReferenceDistance, distanceParams.mMaxDistance, rollOff); 475 476 if ((mReferenceDistance == mMaxDistance) && (Get3DMixerVersion() < k3DMixerVersion_2_2)) 477 distanceParams.mMaxDistance = distanceParams.mReferenceDistance + .01; // pre 2.2 3DMixer may crash if max and reference distances are equal 478 479 result = AudioUnitSetProperty(mOwningContext->GetMixerUnit(), kAudioUnitProperty_3DMixerDistanceParams, kAudioUnitScope_Input, mCurrentPlayBus, &distanceParams, sizeof(distanceParams)); 480 } 481 } 482 483 return result; 484} 485 486// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 487void OALSource::SetReferenceDistance (Float32 inReferenceDistance) 488{ 489#if LOG_VERBOSE 490 DebugMessageN2("OALSource::SetReferenceDistance - OALSource:inReferenceDistance = %ld:%f\n", (long int) mSelfToken, inReferenceDistance); 491#endif 492 493 if (inReferenceDistance < 0.0f) 494 throw ((OSStatus) AL_INVALID_VALUE); 495 496 // don't allow synchronous source manipulation 497 CAGuard::Locker sourceLock(mSourceLock); 498 499 if (inReferenceDistance == mReferenceDistance) 500 return; // nothing to do 501 502 mReferenceDistance = inReferenceDistance; 503 504 if (!mOwningContext->DoSetDistance()) 505 return; // nothing else to do? 506 507 if (mCurrentPlayBus != kSourceNeedsBus) 508 { 509 SetDistanceParams(true, false); 510 } 511} 512 513// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 514void OALSource::SetMaxDistance (Float32 inMaxDistance) 515{ 516#if LOG_VERBOSE 517 DebugMessageN2("OALSource::SetMaxDistance - OALSource:inMaxDistance = %ld:%f\n", (long int) mSelfToken, inMaxDistance); 518#endif 519 520 if (inMaxDistance < 0.0f) 521 throw ((OSStatus) AL_INVALID_VALUE); 522 523 // don't allow synchronous source manipulation 524 CAGuard::Locker sourceLock(mSourceLock); 525 526 if (inMaxDistance == mMaxDistance) 527 return; // nothing to do 528 529 mMaxDistance = inMaxDistance; 530 531 if (!mOwningContext->DoSetDistance()) 532 return; // nothing else to do? 533 534 if (mCurrentPlayBus != kSourceNeedsBus) 535 { 536 SetDistanceParams(false, true); 537 } 538} 539 540// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 541void OALSource::SetRollOffFactor (Float32 inRollOffFactor) 542{ 543#if LOG_VERBOSE 544 DebugMessageN2("OALSource::SetRollOffFactor - OALSource:inRollOffFactor = %ld:%f\n", (long int) mSelfToken, inRollOffFactor); 545#endif 546 547 if (inRollOffFactor < 0.0f) 548 throw ((OSStatus) AL_INVALID_VALUE); 549 550 // don't allow synchronous source manipulation 551 CAGuard::Locker sourceLock(mSourceLock); 552 553 if (inRollOffFactor == mRollOffFactor) 554 return; // nothing to do 555 556 mRollOffFactor = inRollOffFactor; 557 558 if (!mOwningContext->DoSetDistance()) 559 return; // nothing else to do? 560 561 if (mCurrentPlayBus != kSourceNeedsBus) 562 { 563 SetDistanceParams(false, false); 564 } 565} 566 567// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 568void OALSource::SetLooping (UInt32 inLooping) 569{ 570#if LOG_VERBOSE 571 DebugMessageN2("OALSource::SetLooping called - OALSource:inLooping = %ld:%ld\n", (long int) mSelfToken, (long int) inLooping); 572#endif 573 574 // don't allow synchronous source manipulation 575 CAGuard::Locker sourceLock(mSourceLock); 576 577 mLooping = inLooping; 578} 579 580// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 581void OALSource::SetPosition (Float32 inX, Float32 inY, Float32 inZ) 582{ 583#if LOG_VERBOSE 584 DebugMessageN4("OALSource::SetPosition called - OALSource:X:Y:Z = %ld:%f:%f:%f\n", (long int) mSelfToken, inX, inY, inZ); 585#endif 586 587 if (isnan(inX) || isnan(inY) || isnan(inZ)) 588 throw ((OSStatus) AL_INVALID_VALUE); 589 590 // don't allow synchronous source manipulation 591 CAGuard::Locker sourceLock(mSourceLock); 592 593 mPosition[0] = inX; 594 mPosition[1] = inY; 595 mPosition[2] = inZ; 596 597 mCalculateDistance = true; // change the distance next time the PreRender proc or a new Play() is called 598} 599 600// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 601void OALSource::SetVelocity (Float32 inX, Float32 inY, Float32 inZ) 602{ 603#if LOG_VERBOSE 604 DebugMessageN4("OALSource::SetVelocity called - OALSource:inX:inY:inY = %ld:%f:%f:%f", (long int) mSelfToken, inX, inY, inZ); 605#endif 606 607 // don't allow synchronous source manipulation 608 CAGuard::Locker sourceLock(mSourceLock); 609 610 mVelocity[0] = inX; 611 mVelocity[1] = inY; 612 mVelocity[2] = inZ; 613 614 mCalculateDistance = true; // change the velocity next time the PreRender proc or a new Play() is called 615} 616 617// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 618void OALSource::SetDirection (Float32 inX, Float32 inY, Float32 inZ) 619{ 620#if LOG_VERBOSE 621 DebugMessageN4("OALSource::SetDirection called - OALSource:inX:inY:inY = %ld:%f:%f:%f", (long int) mSelfToken, inX, inY, inZ); 622#endif 623 624 // don't allow synchronous source manipulation 625 CAGuard::Locker sourceLock(mSourceLock); 626 627 mConeDirection[0] = inX; 628 mConeDirection[1] = inY; 629 mConeDirection[2] = inZ; 630 631 mCalculateDistance = true; // change the direction next time the PreRender proc or a new Play() is called 632} 633 634// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 635void OALSource::SetSourceRelative (UInt32 inSourceRelative) 636{ 637#if LOG_VERBOSE 638 DebugMessageN2("OALSource::SetSourceRelative called - OALSource:inSourceRelative = %ld:%ld", (long int) mSelfToken, (long int) inSourceRelative); 639#endif 640 641 if ((inSourceRelative != AL_FALSE) && (inSourceRelative != AL_TRUE)) 642 throw ((OSStatus) AL_INVALID_VALUE); 643 644 // don't allow synchronous source manipulation 645 CAGuard::Locker sourceLock(mSourceLock); 646 647 mSourceRelative = inSourceRelative; 648 mCalculateDistance = true; // change the source relative next time the PreRender proc or a new Play() is called 649} 650 651// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 652void OALSource::SetChannelParameters () 653{ 654#if LOG_VERBOSE 655 DebugMessageN1("OALSource::SetChannelParameters called - OALSource = %ld\n", (long int) mSelfToken); 656#endif 657 658 SetReferenceDistance(mReferenceDistance); 659 SetMaxDistance(mMaxDistance); 660 SetRollOffFactor(mRollOffFactor); 661 662 mCalculateDistance = true; 663} 664 665// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 666void OALSource::SetConeInnerAngle (Float32 inConeInnerAngle) 667{ 668#if LOG_VERBOSE 669 DebugMessageN2("OALSource::SetConeInnerAngle called - OALSource:inConeInnerAngle = %ld:%f\n", (long int) mSelfToken, inConeInnerAngle); 670#endif 671 672 // don't allow synchronous source manipulation 673 CAGuard::Locker sourceLock(mSourceLock); 674 675 if (mConeInnerAngle != inConeInnerAngle) 676 { 677 mConeInnerAngle = inConeInnerAngle; 678 mCalculateDistance = true; 679 } 680} 681 682// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 683void OALSource::SetConeOuterAngle (Float32 inConeOuterAngle) 684{ 685#if LOG_VERBOSE 686 DebugMessageN2("OALSource::SetConeOuterAngle called - OALSource:inConeOuterAngle = %ld:%f\n", (long int) mSelfToken, inConeOuterAngle); 687#endif 688 689 // don't allow synchronous source manipulation 690 CAGuard::Locker sourceLock(mSourceLock); 691 692 if (mConeOuterAngle != inConeOuterAngle) 693 { 694 mConeOuterAngle = inConeOuterAngle; 695 mCalculateDistance = true; 696 } 697} 698 699// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 700void OALSource::SetConeOuterGain (Float32 inConeOuterGain) 701{ 702#if LOG_VERBOSE 703 DebugMessageN2("OALSource::SetConeOuterGain called - OALSource:inConeOuterGain = %ld:%f\n", (long int) mSelfToken, inConeOuterGain); 704#endif 705 if (inConeOuterGain < 0.0f) 706 throw ((OSStatus) AL_INVALID_VALUE); 707 708 if (inConeOuterGain <= 1.0) 709 { 710 // don't allow synchronous source manipulation 711 CAGuard::Locker sourceLock(mSourceLock); 712 713 if (mConeOuterGain != inConeOuterGain) 714 { 715 mConeOuterGain = inConeOuterGain; 716 mCalculateDistance = true; 717 } 718 } 719} 720 721// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 722// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 723// GET METHODS 724// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 725// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 726Float32 OALSource::GetPitch () 727{ 728#if LOG_VERBOSE 729 DebugMessageN1("OALSource::GetPitch called - OALSource = %ld\n", (long int) mSelfToken); 730#endif 731 return mPitch; 732} 733 734// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 735Float32 OALSource::GetDopplerScaler () 736{ 737#if LOG_VERBOSE 738 DebugMessageN1("OALSource::GetDopplerScaler called - OALSource = %ld\n", (long int) mSelfToken); 739#endif 740 return mDopplerScaler; 741} 742 743// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 744Float32 OALSource::GetGain () 745{ 746#if LOG_VERBOSE 747 DebugMessageN1("OALSource::GetGain called - OALSource = %ld\n", (long int) mSelfToken); 748#endif 749 return mGain; 750} 751 752// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 753Float32 OALSource::GetMinGain () 754{ 755#if LOG_VERBOSE 756 DebugMessageN1("OALSource::GetMinGain called - OALSource = %ld\n", (long int) mSelfToken); 757#endif 758 return mMinGain; 759} 760 761// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 762Float32 OALSource::GetMaxGain () 763{ 764#if LOG_VERBOSE 765 DebugMessageN1("OALSource::GetMaxGain called - OALSource = %ld\n", (long int) mSelfToken); 766#endif 767 return mMaxGain; 768} 769 770// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 771Float32 OALSource::GetReferenceDistance () 772{ 773#if LOG_VERBOSE 774 DebugMessageN1("OALSource::GetReferenceDistance called - OALSource = %ld\n", (long int) mSelfToken); 775#endif 776 return mReferenceDistance; 777} 778 779// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 780Float32 OALSource::GetMaxDistance () 781{ 782#if LOG_VERBOSE 783 DebugMessageN1("OALSource::GetMaxDistance called - OALSource = %ld\n", (long int) mSelfToken); 784#endif 785 return mMaxDistance; 786} 787 788// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 789Float32 OALSource::GetRollOffFactor () 790{ 791#if LOG_VERBOSE 792 DebugMessageN1("OALSource::GetRollOffFactor called - OALSource = %ld\n", (long int) mSelfToken); 793#endif 794 return mRollOffFactor; 795} 796 797// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 798UInt32 OALSource::GetLooping () 799{ 800#if LOG_VERBOSE 801 DebugMessageN1("OALSource::GetLooping called - OALSource = %ld\n", (long int) mSelfToken); 802#endif 803 return mLooping; 804} 805 806// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 807void OALSource::GetPosition (Float32 &inX, Float32 &inY, Float32 &inZ) 808{ 809#if LOG_VERBOSE 810 DebugMessageN1("OALSource::GetPosition called - OALSource = %ld\n", (long int) mSelfToken); 811#endif 812 813 inX = mPosition[0]; 814 inY = mPosition[1]; 815 inZ = mPosition[2]; 816} 817 818// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 819void OALSource::GetVelocity (Float32 &inX, Float32 &inY, Float32 &inZ) 820{ 821#if LOG_VERBOSE 822 DebugMessageN1("OALSource::GetVelocity called - OALSource = %ld\n", (long int) mSelfToken); 823#endif 824 825 inX = mVelocity[0]; 826 inY = mVelocity[1]; 827 inZ = mVelocity[2]; 828} 829 830// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 831void OALSource::GetDirection (Float32 &inX, Float32 &inY, Float32 &inZ) 832{ 833#if LOG_VERBOSE 834 DebugMessageN1("OALSource::GetDirection called - OALSource = %ld\n", (long int) mSelfToken); 835#endif 836 837 inX = mConeDirection[0]; 838 inY = mConeDirection[1]; 839 inZ = mConeDirection[2]; 840} 841 842// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 843UInt32 OALSource::GetSourceRelative () 844{ 845#if LOG_VERBOSE 846 DebugMessageN1("OALSource::GetSourceRelative called - OALSource = %ld\n", (long int) mSelfToken); 847#endif 848 return mSourceRelative; 849} 850 851// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 852UInt32 OALSource::GetSourceType () 853{ 854#if LOG_VERBOSE 855 DebugMessageN1("OALSource::GetSourceType called - OALSource = %ld\n", (long int) mSelfToken); 856#endif 857 return mSourceType; 858} 859 860// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 861Float32 OALSource::GetConeInnerAngle () 862{ 863#if LOG_VERBOSE 864 DebugMessageN1("OALSource::GetConeInnerAngle called - OALSource = %ld\n", (long int) mSelfToken); 865#endif 866 return mConeInnerAngle; 867} 868 869// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 870Float32 OALSource::GetConeOuterAngle () 871{ 872#if LOG_VERBOSE 873 DebugMessageN1("OALSource::GetConeOuterAngle called - OALSource = %ld\n", (long int) mSelfToken); 874#endif 875 return mConeOuterAngle; 876} 877 878// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 879Float32 OALSource::GetConeOuterGain () 880{ 881#if LOG_VERBOSE 882 DebugMessageN1("OALSource::GetConeOuterGain called - OALSource = %ld\n", (long int) mSelfToken); 883#endif 884 return mConeOuterGain; 885} 886 887// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 888UInt32 OALSource::GetState() 889{ 890#if LOG_VERBOSE 891 DebugMessageN1("OALSource::GetState called - OALSource = %ld\n", (long int) mSelfToken); 892#endif 893 // if the source is in any of the transition states, return its immediate future state 894 // else return its actual state 895 switch(mState) 896 { 897 case kTransitionToPlay: 898 case kTransitionToRetrigger: 899 case kTransitionToResume: 900 return AL_PLAYING; 901 break; 902 903 case kTransitionToStop: 904 return AL_STOPPED; 905 break; 906 907 case kTransitionToPause: 908 return AL_PAUSED; 909 break; 910 911 case kTransitionToRewind: 912 return AL_INITIAL; 913 break; 914 915 default: 916 break; 917 } 918 919 return mState; 920} 921// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 922ALuint OALSource::GetToken() 923{ 924#if LOG_VERBOSE 925 DebugMessageN1("OALSource::GetToken called - OALSource = %ld\n", (long int) mSelfToken); 926#endif 927 return mSelfToken; 928} 929 930// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 931// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 932// BUFFER QUEUE METHODS 933// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 934// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 935UInt32 OALSource::GetQLengthPriv() 936{ 937#if LOG_VERBOSE 938 DebugMessageN1("OALSource::GetQLengthPriv() called - OALSource = %ld", (long int) mSelfToken); 939#endif 940 941 // the Q length is the size of the inactive & active lists 942 return mQueueLength; 943} 944 945// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 946 947UInt32 OALSource::GetQLength() 948{ 949#if LOG_VERBOSE 950 DebugMessageN1("OALSource::GetQLength() called OALSource = %ld", (long int) mSelfToken); 951#endif 952 953 if(IsSourceTransitioningToFlushQ()) 954 return mTempQueueLength; 955 956 // the Q length is the size of the inactive & active lists 957 return mQueueLength; 958} 959 960// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 961UInt32 OALSource::GetBuffersProcessed() 962{ 963#if LOG_VERBOSE 964 DebugMessageN1("OALSource::GetBuffersProcessed called - OALSource = %ld\n", (long int) mSelfToken); 965#endif 966 if (mState == AL_INITIAL) 967 return 0; 968 969 if(IsSourceTransitioningToFlushQ()) 970 return 0; 971 972 if ((mState == AL_INITIAL) || 973 (mState == AL_STOPPED) || 974 (mState == kTransitionToStop) || 975 (mState == kTransitionToRewind) ) 976 return GetQLength(); 977 978 if (mQueueIsProcessed) 979 { 980 // fixes 4085888 981 // When the Q ran dry it might not have been possible to modify the Buffer Q Lists 982 // This means that there could be one left over buffer in the active Q list which should not be there 983 984 // wait if in render, then prevent rendering til completion 985 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 986 987 // don't allow synchronous source manipulation 988 CAGuard::Locker sourceLock(mSourceLock); 989 990 ClearActiveQueue(); 991 } 992 993 return mBufferQueueInactive->GetQueueSize(); 994} 995 996// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 997void OALSource::SetBuffer (ALuint inBufferToken, OALBuffer *inBuffer) 998{ 999#if LOG_VERBOSE 1000 DebugMessageN3("OALSource::SetBuffer called - OALSource:inBufferToken:inBuffer = %ld:%ld:%p\n", (long int) mSelfToken, (long int) inBufferToken, inBuffer); 1001#endif 1002 if (inBuffer == NULL) 1003 return; // invalid case 1004 1005 // wait if in render, then prevent rendering til completion 1006 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 1007 1008 // don't allow synchronous source manipulation 1009 CAGuard::Locker sourceLock(mSourceLock); 1010 1011 switch (mState) 1012 { 1013 case AL_PLAYING: 1014 case AL_PAUSED: 1015 case kTransitionToPlay: 1016 case kTransitionToRetrigger: 1017 case kTransitionToResume: 1018 case kTransitionToPause: 1019 throw (OSStatus)AL_INVALID_OPERATION; 1020 break; 1021 1022 case kTransitionToStop: 1023 case kTransitionToRewind: 1024 { 1025 if (inBufferToken == 0) 1026 { 1027 mSourceType = AL_UNDETERMINED; 1028 mTransitioningToFlushQ = true; 1029 mTempQueueLength = 0; //reset the temp queue length 1030 FlushTempBufferQueue(); 1031 } 1032 1033#if LOG_MESSAGE_QUEUE 1034 DebugMessageN1("OALSource::SetBuffer - kMQ_SetBuffer added to MQ - OALSource = %ld", (long int) mSelfToken); 1035#endif 1036 inBuffer->SetIsInPostRenderMessageQueue(true); 1037 AddPlaybackMessage(kMQ_SetBuffer, inBuffer, 0); 1038 break; 1039 } 1040 1041 default: 1042 { 1043 // In the initial or stopped state it is ok to flush the buffer Qs and add this new buffer right now, so empty the two queues 1044 FlushBufferQueue(); 1045 1046 // if inBufferToken == 0, do nothing, passing NONE to this method is legal and should merely empty the queue 1047 if (inBufferToken != 0) 1048 { 1049 AppendBufferToQueue(inBufferToken, inBuffer); 1050 mSourceType = AL_STATIC; 1051 } 1052 else 1053 mSourceType = AL_UNDETERMINED; 1054 } 1055 } 1056} 1057 1058// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1059// this should only be called when the source is render and edit protected 1060void OALSource::FlushBufferQueue() 1061{ 1062#if LOG_VERBOSE 1063 DebugMessageN1("OALSource::FlushBufferQueue called - OALSource = %ld\n", (long int) mSelfToken); 1064#endif 1065 UInt32 count = mBufferQueueInactive->GetQueueSize(); 1066 for (UInt32 i = 0; i < count; i++) 1067 { 1068 mBufferQueueInactive->RemoveQueueEntryByIndex(this, 0, true); 1069 mBuffersQueuedForClear = 0; 1070 } 1071 1072 count = mBufferQueueActive->GetQueueSize(); 1073 for (UInt32 i = 0; i < count; i++) 1074 { 1075 mBufferQueueActive->RemoveQueueEntryByIndex(this, 0, true); 1076 } 1077 1078 //now that both queues have been modified, the queue length can be set 1079 SetQueueLength(); 1080} 1081 1082// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1083void OALSource::FlushTempBufferQueue() 1084{ 1085#if LOG_VERBOSE 1086 DebugMessageN1("OALSource::FlushTempBufferQueue() - OALSource = %ld", (long int) mSelfToken); 1087#endif 1088 1089 UInt32 count = mBufferQueueTemp->GetQueueSize(); 1090 for (UInt32 i = 0; i < count; i++) 1091 mBufferQueueInactive->RemoveQueueEntryByIndex(this, 0, false); 1092} 1093 1094// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1095ALuint OALSource::GetBuffer() 1096{ 1097#if LOG_VERBOSE 1098 DebugMessageN2("OALSource::GetBuffer called - OALSource:currentBuffer = %ld:%ld\n", (long int)mSelfToken, (long int)mBufferQueueActive->GetBufferTokenByIndex(mCurrentBufferIndex)); 1099#endif 1100 // wait if in render, then prevent rendering til completion 1101 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 1102 1103 // need to make sure no other thread touches the active queue 1104 CAGuard::Locker sourceLock(mSourceLock); 1105 1106 // it isn't clear what this should return if there is a buffer queue (AL_STREAMING), 1107 // so only return a value when in AL_STATIC mode 1108 ALuint bufferID = 0; 1109 if (mSourceType == AL_STATIC) 1110 { 1111 if (mBufferQueueActive->GetQueueSize() > 0) 1112 bufferID = mBufferQueueActive->GetBufferTokenByIndex(0); 1113 else if (mBufferQueueInactive->GetQueueSize() > 0) 1114 bufferID = mBufferQueueInactive->GetBufferTokenByIndex(0); 1115 } 1116 1117 return bufferID; 1118} 1119 1120// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1121// public method 1122void OALSource::AddToQueue(ALuint inBufferToken, OALBuffer *inBuffer) 1123{ 1124#if LOG_VERBOSE 1125 DebugMessageN3("OALSource::AddToQueue called - OALSource:inBufferToken:inBuffer = %ld:%ld:%p\n", (long int) mSelfToken, (long int) inBufferToken, inBuffer); 1126#endif 1127 // wait if in render, then prevent rendering til completion 1128 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 1129 1130 // don't allow synchronous source manipulation 1131 CAGuard::Locker sourceLock(mSourceLock); 1132 1133 if (mSourceType == AL_STATIC) 1134 throw (OSStatus)AL_INVALID_OPERATION; 1135 1136 if (mSourceType == AL_UNDETERMINED) 1137 mSourceType = AL_STREAMING; 1138 1139 AppendBufferToQueue(inBufferToken, inBuffer); 1140} 1141 1142// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1143void OALSource::AddToTempQueue(ALuint inBufferToken, OALBuffer *inBuffer) 1144{ 1145#if LOG_VERBOSE 1146 DebugMessageN3("OALSource::AddToQueue called - OALSource:inBufferToken:inBuffer = %ld:%ld:%p\n", (long int) mSelfToken, (long int) inBufferToken, inBuffer); 1147#endif 1148 // wait if in render, then prevent rendering til completion 1149 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 1150 1151 // don't allow synchronous source manipulation 1152 CAGuard::Locker sourceLock(mSourceLock); 1153 1154 mBufferQueueTemp->AppendBuffer(this, inBufferToken, inBuffer, 0); 1155 //Protect the buffer so it can't be deleted. This lock is released when the buffer is handed off to the active queue 1156 inBuffer->UseThisBuffer(this); 1157 ++mTempQueueLength; 1158 AddPlaybackMessage(kMQ_AddBuffersToQueue, NULL, 1); 1159} 1160 1161// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1162// this should only be called when the source is render and edit protected 1163void OALSource::AppendBufferToQueue(ALuint inBufferToken, OALBuffer *inBuffer) 1164{ 1165#if LOG_VERBOSE 1166 DebugMessageN3("OALSource::AppendBufferToQueue called (START) - OALSource:inBufferToken:inBuffer = %ld:%ld:%p", (long int)mSelfToken, (long int)inBufferToken, inBuffer); 1167#endif 1168 OSStatus result = noErr; 1169#if LOG_BUFFER_QUEUEING 1170 DebugMessageN2("OALSource::AppendBufferToQueue called (START) - OALSource:inBufferToken = %ld:%ld", (long int)mSelfToken, (long int)inBufferToken); 1171#endif 1172 1173 try { 1174 if (mBufferQueueActive->Empty()) 1175 { 1176 mCurrentBufferIndex = 0; // this is the first buffer in the queue 1177 mQueueIsProcessed = false; 1178 } 1179 1180 // do we need an AC for the format of this buffer? 1181 if (inBuffer->HasBeenConverted()) 1182 { 1183 // the data was already convertered to the mixer format, no AC is necessary (as indicated by the ACToken setting of zero) 1184 mBufferQueueActive->AppendBuffer(this, inBufferToken, inBuffer, 0); 1185 SetQueueLength(); 1186 } 1187 else 1188 { 1189 // check the format against the real format of the data, NOT the input format of the converter which may be subtly different 1190 // both in SampleRate and Interleaved flags 1191 ALuint outToken = 0; 1192 mACMap->GetACForFormat(inBuffer->GetFormat(), outToken); 1193 if (outToken == 0) 1194 { 1195 // create an AudioConverter for this format because there isn't one yet 1196 AudioConverterRef converter = 0; 1197 CAStreamBasicDescription inFormat; 1198 1199 inFormat.SetFrom(*(inBuffer->GetFormat())); 1200 1201 // if the source is mono, set the flags to be non interleaved, so a reinterleaver does not get setup when 1202 // completely unnecessary - since the flags on output are always set to non interleaved 1203 if (inFormat.NumberChannels() == 1) 1204 inFormat.mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 1205 1206 // output should have actual number of channels, but frame/packet size is for single channel 1207 // this is to make de interleaved data work correctly with > 1 channel 1208 CAStreamBasicDescription outFormat; 1209 outFormat.mChannelsPerFrame = inFormat.NumberChannels(); 1210 outFormat.mSampleRate = inFormat.mSampleRate; 1211 outFormat.mFormatID = kAudioFormatLinearPCM; 1212 outFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; 1213 outFormat.mBytesPerPacket = sizeof (Float32); 1214 outFormat.mFramesPerPacket = 1; 1215 outFormat.mBytesPerFrame = sizeof (Float32); 1216 outFormat.mBitsPerChannel = sizeof (Float32) * 8; 1217 1218 result = AudioConverterNew(&inFormat, &outFormat, &converter); 1219 THROW_RESULT 1220 1221 ACInfo info; 1222 info.mInputFormat = *(inBuffer->GetFormat()); 1223 info.mConverter = converter; 1224 1225 // add this AudioConverter to the source's ACMap 1226 ALuint newACToken = GetNewToken(); 1227 mACMap->Add(newACToken, &info); 1228 // add the buffer to the queue - each buffer now knows which AC to use when it is converted in the render proc 1229 mBufferQueueActive->AppendBuffer(this, inBufferToken, inBuffer, newACToken); 1230 SetQueueLength(); 1231 } 1232 else 1233 { 1234 // there is already an AC for this buffer's data format, so just append the buffer to the queue 1235 mBufferQueueActive->AppendBuffer(this, inBufferToken, inBuffer, outToken); 1236 SetQueueLength(); 1237 } 1238 } 1239 1240 inBuffer->UseThisBuffer(this); 1241 } 1242 catch (OSStatus result) { 1243 DebugMessageN1("APPEND BUFFER FAILED %ld\n", (long int) mSelfToken); 1244 throw (result); 1245 } 1246 1247#if LOG_BUFFER_QUEUEING 1248 DebugMessageN2("OALSource::AppendBufferToQueue called (END) - OALSource:QLength = %ld:%ld", (long int)mSelfToken, mBufferQueueInactive->Size() + mBufferQueueActive->Size()); 1249#endif 1250 1251 return; 1252} 1253 1254// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1255// Do NOT call this from the render thread 1256void OALSource::RemoveBuffersFromQueue(UInt32 inCount, ALuint *outBufferTokens) 1257{ 1258#if LOG_VERBOSE 1259 DebugMessageN2("OALSource::RemoveBuffersFromQueue called - OALSource:inCount = %ld:%ld\n", (long int) mSelfToken, (long int) inCount); 1260#endif 1261 if (inCount == 0) 1262 return; 1263 1264#if LOG_BUFFER_QUEUEING 1265 DebugMessageN2("OALSource::RemoveBuffersFromQueue called (START) - OALSource:inCount = %ld:%ld", (long int)mSelfToken, inCount); 1266#endif 1267 1268 try { 1269 1270 // wait if in render, then prevent rendering til completion 1271 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 1272 1273 // don't allow synchronous source manipulation 1274 CAGuard::Locker sourceLock(mSourceLock); 1275 1276 //this check also accounts for the case when the source might be transitioning to flush the Qs 1277 if (inCount > GetQLength()) 1278 throw ((OSStatus) AL_INVALID_OPERATION); 1279 1280 if (outBufferTokens == NULL) 1281 throw ((OSStatus) AL_INVALID_OPERATION); 1282 1283 // 1) determine if it is a legal request 1284 // 2) determine if buffers can be removed now or at PostRender time 1285 // 3) get the buffer names that will be removed and if safe, remove them now 1286 1287 //If the source is transitioning to flush, then that's the first case to check because mState could contain a stale value 1288 if (IsSourceTransitioningToFlushQ()) 1289 { 1290#if LOG_BUFFER_QUEUEING 1291 DebugMessageN2(" OALSource::RemoveBuffersFromQueue IsSourceTransitioningToFlushQ() - OALSource:inCount = %ld:%ld", (long int)mSelfToken, inCount); 1292#endif 1293 AddPlaybackMessage((UInt32) kMQ_ClearBuffersFromQueue, NULL, inCount); 1294 } 1295 else if ((mState == AL_PLAYING) || (mState == AL_PAUSED)) 1296 { 1297 if (mLooping == true) 1298 throw ((OSStatus) AL_INVALID_OPERATION); 1299 else if (inCount > (UInt32)mBufferQueueInactive->GetQueueSize()) 1300 throw ((OSStatus) AL_INVALID_OPERATION); 1301 } 1302 else if ((mState == AL_STOPPED) || (mState == AL_INITIAL)) 1303 { 1304 ClearActiveQueue(); 1305 if (inCount > (UInt32)mBufferQueueInactive->GetQueueSize()) 1306 { 1307 DebugMessageN3(" OALSource::RemoveBuffersFromQueue mState == AL_STOPPED - OALSource:GetQLength():mBufferQueueInactive->Size() = %d:%d:%d", (int) mSelfToken, (int)GetQLength(), (int)mBufferQueueInactive->GetQueueSize()); 1308 throw ((OSStatus) AL_INVALID_OPERATION); 1309 } 1310 } 1311 else if((mState == kTransitionToStop) || (mState == kTransitionToRewind)) 1312 { 1313#if LOG_BUFFER_QUEUEING 1314 DebugMessageN2(" RemoveBuffersFromQueue kTransitionState - OALSource:inCount = %ld:%ld", (long int)mSelfToken, inCount); 1315#endif 1316#if LOG_MESSAGE_QUEUE 1317 DebugMessageN1(" RemoveBuffersFromQueue ADDING : kMQ_ClearBuffersFromQueue - OALSource = %ld", (long int) mSelfToken); 1318#endif 1319 1320 AddPlaybackMessage(kMQ_ClearBuffersFromQueue, NULL, inCount); 1321 } 1322 1323 for (UInt32 i = mBuffersQueuedForClear; i < mBuffersQueuedForClear+inCount; i++) 1324 { 1325 //If the source is transitioning to flush, then that's the first case to check because mState could contain a stale value 1326 if (IsSourceTransitioningToFlushQ()) 1327 { 1328 //get the buffer token from the temp queue 1329 outBufferTokens[i] = mBufferQueueTemp->GetBufferTokenByIndex(i); 1330 --mTempQueueLength; 1331 } 1332 else if ((mState == kTransitionToStop) || (mState == kTransitionToRewind)) 1333 { 1334 //DebugMessageN1(" * RemoveBuffersFromQueue kTransitionState - GetQLengthPriv() = %ld", (long int) GetQLengthPriv()); 1335 // we're transitioning, so let the caller know what buffers will be removed, but don't actually do it until the deferred message is acted on 1336 // first return the token for the buffers in the inactive queue, then the active queue 1337 if (i < (UInt32)mBufferQueueInactive->GetQueueSize()) 1338 { 1339 outBufferTokens[i-mBuffersQueuedForClear] = mBufferQueueInactive->GetBufferTokenByIndex(i); 1340 //DebugMessageN1(" DEFERRED * RemoveBuffersFromQueue kTransitionState - mBufferQueueInactive->GetBufferTokenByIndex(i) = %ld", (long int) outBufferTokens[i]); 1341 } 1342 else 1343 { 1344 outBufferTokens[i-mBuffersQueuedForClear] = mBufferQueueActive->GetBufferTokenByIndex(i - mBufferQueueInactive->GetQueueSize()); 1345 //DebugMessageN1(" DEFERRED * RemoveBuffersFromQueue kTransitionState - mBufferQueueActive->GetBufferTokenByIndex(i) = %ld", (long int) outBufferTokens[i]); 1346 } 1347 } 1348 else 1349 { 1350 ALuint outToken = 0; 1351 // it is safe to remove the buffers from the inactive Q 1352 outToken = mBufferQueueInactive->RemoveQueueEntryByIndex(this, 0, true); 1353 mBuffersQueuedForClear = 0; 1354 SetQueueLength(); 1355 #if LOG_MESSAGE_QUEUE 1356 DebugMessageN1(" Just Removed Buffer Id from inactive Q = %ld", (long int) outToken); 1357 #endif 1358 outBufferTokens[i] = outToken; 1359 } 1360 } 1361 1362 if ((mState == kTransitionToStop) || (mState == kTransitionToRewind)) 1363 { 1364 // we need to keep track of how many buffers need to be clear but have not yet 1365 mBuffersQueuedForClear+= inCount; 1366 SetQueueLength(); 1367 } 1368 1369 if (GetQLength() == 0) 1370 mSourceType = AL_UNDETERMINED; // Q has been cleared and is now available for both static or streaming usage 1371 1372 } 1373 catch (OSStatus result) { 1374 DebugMessageN1(" REMOVE BUFFER FAILED, OALSource = %ld\n", (long int) mSelfToken); 1375 throw (result); 1376 } 1377 1378#if LOG_BUFFER_QUEUEING 1379 DebugMessageN2(" OALSource:RemoveBuffersFromQueue called (END) - OALSource:QLength = %ld:%ld", (long int)mSelfToken, mBufferQueueInactive->Size() + mBufferQueueActive->Size()); 1380#endif 1381 1382 return; 1383} 1384 1385// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1386// Called Only From The Post Render Proc 1387// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1388void OALSource::PostRenderRemoveBuffersFromQueue(UInt32 inBuffersToUnqueue) 1389{ 1390#if LOG_VERBOSE 1391 DebugMessageN2("OALSource::PostRenderRemoveBuffersFromQueue called - OALSource:inBuffersToUnqueue = %ld:%ld\n", (long int) mSelfToken, (long int) inBuffersToUnqueue); 1392#endif 1393 1394#if LOG_BUFFER_QUEUEING 1395 DebugMessageN2("OALSource::PostRenderRemoveBuffersFromQueue called (START) - OALSource:inCount = %ld:%ld", (long int)mSelfToken, inBuffersToUnqueue); 1396#endif 1397 1398 try { 1399 ClearActiveQueue(); 1400 1401 if (inBuffersToUnqueue > (UInt32)mBufferQueueInactive->GetQueueSize()) 1402 { 1403 DebugMessageN3(" OALSource::PostRenderRemoveBuffersFromQueue mState == AL_STOPPED - OALSource:GetQLength():mBufferQueueInactive->Size() = %d:%d:%d", (int) mSelfToken, (int)GetQLength(), (int)mBufferQueueInactive->GetQueueSize()); 1404 throw ((OSStatus) AL_INVALID_OPERATION); 1405 } 1406 1407 for (UInt32 i = 0; i < inBuffersToUnqueue; i++) 1408 { 1409 mBufferQueueInactive->RemoveQueueEntryByIndex(this, 0, true); 1410 // we have now cleared all buffers slated for removal, so we can reset the queued for clear index 1411 mBuffersQueuedForClear = 0; 1412 SetQueueLength(); 1413 //DebugMessageN1("***** OALSource::PostRenderRemoveBuffersFromQueue buffer removed = %ld", bufferToken); 1414 } 1415 1416 if (GetQLength() == 0) 1417 mSourceType = AL_UNDETERMINED; // Q has been cleared and is now available for both static or streaming usage 1418 1419 } 1420 catch (OSStatus result) { 1421 DebugMessageN1(" REMOVE BUFFER FAILED, OALSource = %ld\n", (long int) mSelfToken); 1422 throw (result); 1423 } 1424 1425#if LOG_BUFFER_QUEUEING 1426 DebugMessageN2(" OALSource:PostRenderRemoveBuffersFromQueue called (END) - OALSource:QLength = %ld:%ld\n", (long int)mSelfToken, mBufferQueueInactive->Size() + mBufferQueueActive->Size()); 1427#endif 1428 1429 return; 1430} 1431 1432// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1433void OALSource::PostRenderAddBuffersToQueue (UInt32 inNumBuffersToQueue) 1434{ 1435#if LOG_VERBOSE 1436 DebugMessageN3("OALSource::PostRenderAddBuffersToQueue - OALSource:inBufferToken:inBuffer = %ld:%ld:%p", (long int) mSelfToken, (long int) inBufferToken, inBuffer); 1437#endif 1438 1439 try 1440 { 1441 if (mSourceType == AL_STATIC) 1442 throw (OSStatus) AL_INVALID_OPERATION; 1443 1444 if (mSourceType == AL_UNDETERMINED) 1445 mSourceType = AL_STREAMING; 1446 1447 while (inNumBuffersToQueue--) 1448 { 1449 // Get buffer #i from Temp List 1450 BufferInfo* bufferInfo = mBufferQueueTemp->Get(0); 1451 // Append it to Active List 1452 AppendBufferToQueue(bufferInfo->mBufferToken, bufferInfo->mBuffer); 1453 // Remove it from Temp List 1454 mBufferQueueTemp->RemoveQueueEntryByIndex(this, 0, true); 1455 } 1456 } 1457 catch (OSStatus result) { 1458 DebugMessageN1(" REMOVE BUFFER FAILED, OALSource = %ld\n", (long int) mSelfToken); 1459 throw (result); 1460 } 1461} 1462 1463// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1464void OALSource::PostRenderSetBuffer (ALuint inBufferToken, OALBuffer *inBuffer) 1465{ 1466#if LOG_VERBOSE 1467 DebugMessageN3("OALSource::PostRenderSetBuffer - OALSource:inBufferToken:inBuffer = %ld:%ld:%p", (long int) mSelfToken, (long int) inBufferToken, inBuffer); 1468#endif 1469 FlushBufferQueue(); 1470 1471 // if inBufferToken == 0, do nothing, passing NONE to this method is legal and should merely empty the queue 1472 if (inBufferToken != 0) 1473 { 1474 AppendBufferToQueue(inBufferToken, inBuffer); 1475 mSourceType = AL_STATIC; 1476 } 1477 else 1478 { 1479 mSourceType = AL_UNDETERMINED; 1480 mTransitioningToFlushQ = false; 1481 } 1482} 1483 1484// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1485// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1486// PLAYBACK METHODS 1487// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1488// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1489 1490void OALSource::SetupMixerBus() 1491{ 1492#if LOG_VERBOSE 1493 DebugMessageN1("OALSource::SetupMixerBus called - OALSource = %ld\n", (long int) mSelfToken); 1494#endif 1495 OSStatus result = noErr; 1496 CAStreamBasicDescription desc; 1497 UInt32 propSize = 0; 1498 BufferInfo* buffer = mBufferQueueActive->Get(mCurrentBufferIndex); 1499 1500 try { 1501 if (buffer == NULL) 1502 throw (OSStatus)-1; 1503 1504 if (mCurrentPlayBus == kSourceNeedsBus) 1505 { 1506 // the bus stream format will get set if necessary while getting the available bus 1507 mCurrentPlayBus = (buffer->mBuffer->GetNumberChannels() == 1) ? mOwningContext->GetAvailableMonoBus(mSelfToken) : mOwningContext->GetAvailableStereoBus(mSelfToken); 1508 if (mCurrentPlayBus == -1) 1509 throw (OSStatus) -1; 1510 1511 if (Get3DMixerVersion() >= k3DMixerVersion_2_0) 1512 { 1513// Float32 rollOff = mRollOffFactor; 1514 Float32 refDistance = mReferenceDistance; 1515 Float32 maxDistance = mMaxDistance; 1516 1517 if (mOwningContext->IsDistanceScalingRequired()) 1518 { 1519 refDistance = (mReferenceDistance/mMaxDistance) * kDistanceScalar; 1520 maxDistance = kDistanceScalar; 1521// rollOff *= (kDistanceScalar/mMaxDistance); 1522 } 1523 1524 Float32 testAttenuation = GetMaxAttenuation(mReferenceDistance, mMaxDistance, mRollOffFactor); 1525 1526 // Set the MixerDistanceParams for the new bus if necessary 1527 MixerDistanceParams distanceParams; 1528 propSize = sizeof(distanceParams); 1529 result = AudioUnitGetProperty(mOwningContext->GetMixerUnit(), kAudioUnitProperty_3DMixerDistanceParams, kAudioUnitScope_Input, mCurrentPlayBus, &distanceParams, &propSize); 1530 1531 if ((result == noErr) && ((distanceParams.mReferenceDistance != refDistance) || 1532 (distanceParams.mMaxDistance != maxDistance) || 1533 (distanceParams.mMaxAttenuation != testAttenuation))) 1534 { 1535 distanceParams.mMaxAttenuation = testAttenuation; 1536 1537 if (mOwningContext->IsDistanceScalingRequired()) 1538 { 1539 1540 distanceParams.mReferenceDistance = (mReferenceDistance/mMaxDistance) * kDistanceScalar; 1541 // limit the max distance 1542 distanceParams.mMaxDistance = kDistanceScalar; 1543 distanceParams.mMaxAttenuation = testAttenuation; 1544 } 1545 else 1546 { 1547 distanceParams.mReferenceDistance = mReferenceDistance; 1548 distanceParams.mMaxDistance = mMaxDistance; 1549 } 1550 1551 if ((mReferenceDistance == mMaxDistance) && (Get3DMixerVersion() < k3DMixerVersion_2_2)) 1552 distanceParams.mMaxDistance = distanceParams.mReferenceDistance + .01; // pre 2.2 3DMixer may crash if max and reference distances are equal 1553 1554 /*result =*/ AudioUnitSetProperty(mOwningContext->GetMixerUnit(), kAudioUnitProperty_3DMixerDistanceParams, kAudioUnitScope_Input, mCurrentPlayBus, &distanceParams, sizeof(distanceParams)); 1555 } 1556 } 1557 else 1558 { 1559 // the pre-2.0 3DMixer does not accept kAudioUnitProperty_3DMixerDistanceParams, it has do some extra work and use the DistanceAtten property instead 1560 mOwningContext->SetDistanceAttenuation(mCurrentPlayBus, mReferenceDistance, mMaxDistance, mRollOffFactor); 1561 } 1562 1563 UpdateBusReverb(); 1564 UpdateBusOcclusion(); 1565 UpdateBusObstruction(); 1566 } 1567 1568 // get the sample rate of the bus 1569 propSize = sizeof(desc); 1570 /*result =*/ AudioUnitGetProperty(mOwningContext->GetMixerUnit(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, mCurrentPlayBus, &desc, &propSize); 1571 1572 mResetPitch = true; 1573 mCalculateDistance = true; 1574 if (desc.mSampleRate != buffer->mBuffer->GetSampleRate()) 1575 mResetBusFormat = true; // only reset the bus stream format if it is different than sample rate of the data 1576 1577 // *************************** Set properties for the mixer bus 1578 1579 ChangeChannelSettings(); 1580 UpdateBusGain(); 1581 UpdateBusRenderQuality(); 1582 } 1583 catch (OSStatus result) { 1584 DebugMessageN2(" SetupMixerBus FAILED, OALSource:error = %d:%d\n", (int) mSelfToken, (int)result); 1585 throw (result); 1586 } 1587 catch (...) { 1588 DebugMessageN1(" SetupMixerBus FAILED, OALSource = %ld\n", (long int) mSelfToken); 1589 throw; 1590 } 1591 1592 return; 1593} 1594 1595// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1596void OALSource::ResetMixerBus() 1597{ 1598#if LOG_VERBOSE 1599 DebugMessageN1("OALSource::ResetMixerBus() - OALSource = %ld", (long int) mSelfToken); 1600#endif 1601 OSStatus result = noErr; 1602 try { 1603 //this should only get called when the source has a bus 1604 1605 if (mCurrentPlayBus != kSourceNeedsBus) 1606 { 1607 //a mixer bus should be setup with the desired parameters before it is reset 1608 //setup is handled by the SetupMixerBus method 1609 result = AudioUnitReset(mOwningContext->GetMixerUnit(), kAudioUnitScope_Input, mCurrentPlayBus); 1610 THROW_RESULT 1611 } 1612 } 1613 1614 catch (OSStatus result) { 1615 DebugMessageN2(" ResetMixerBus FAILED, OALSource:error = %d:%d\n", (int) mSelfToken, (int)result); 1616 throw (result); 1617 } 1618 catch (...) { 1619 DebugMessageN1(" ResetMixerBus FAILED, OALSource = %ld\n", (long int) mSelfToken); 1620 throw; 1621 } 1622 1623 return; 1624} 1625 1626// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1627void OALSource::SetupRogerBeepAU() 1628{ 1629#if LOG_VERBOSE 1630 DebugMessageN1("OALSource::SetupRogerBeepAU called - OALSource = %ld\n", (long int) mSelfToken); 1631#endif 1632 1633 ComponentDescription desc; 1634 desc.componentFlags = 0; 1635 desc.componentFlagsMask = 0; 1636 desc.componentType = kAudioUnitType_Effect; 1637 desc.componentSubType = kRogerBeepType; 1638 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 1639 1640 // CREATE NEW NODE FOR THE GRAPH 1641 OSStatus result = AUGraphNewNode (mOwningContext->GetGraph(), &desc, 0, NULL, &mRogerBeepNode); 1642 THROW_RESULT 1643 1644 result = AUGraphGetNodeInfo (mOwningContext->GetGraph(), mRogerBeepNode, 0, 0, 0, &mRogerBeepAU); 1645 THROW_RESULT 1646} 1647 1648void OALSource::SetupDistortionAU() 1649{ 1650#if LOG_VERBOSE 1651 DebugMessageN1("OALSource::SetupDistortionAU called - OALSource = %ld\n", (long int) mSelfToken); 1652#endif 1653 ComponentDescription desc; 1654 desc.componentFlags = 0; 1655 desc.componentFlagsMask = 0; 1656 desc.componentType = kAudioUnitType_Effect; 1657 desc.componentSubType = kDistortionType; 1658 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 1659 1660 // CREATE NEW NODE FOR THE GRAPH 1661 OSStatus result = AUGraphNewNode (mOwningContext->GetGraph(), &desc, 0, NULL, &mDistortionNode); 1662 THROW_RESULT 1663 1664 result = AUGraphGetNodeInfo (mOwningContext->GetGraph(), mDistortionNode, 0, 0, 0, &mDistortionAU); 1665 THROW_RESULT 1666} 1667 1668// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1669// returns false if there is no data to play 1670bool OALSource::PrepBufferQueueForPlayback() 1671{ 1672#if LOG_VERBOSE 1673 DebugMessageN1("OALSource::PrepBufferQueueForPlayback called - OALSource = %ld\n", (long int) mSelfToken); 1674#endif 1675 BufferInfo *buffer = NULL; 1676 1677 JoinBufferLists(); 1678 1679 if (mBufferQueueActive->Empty()) 1680 return false; // there isn't anything to do 1681 1682 // Get the format for the first buffer in the queue 1683 buffer = NextPlayableBufferInActiveQ(); 1684 if (buffer == NULL) 1685 return false; // there isn't anything to do 1686 1687#if LOG_PLAYBACK 1688 DebugMessage("OALSource::PrepBufferQueueForPlayback called - Format of 1st buffer in the Q =\n"); 1689 buffer->mBuffer->PrintFormat(); 1690#endif 1691 1692 // WARM THE BUFFERS 1693 // when playing, touch all the audio data in memory once before it is needed in the render proc (RealTime thread) 1694 { 1695 volatile UInt32 X; 1696 UInt32 *start = (UInt32 *)buffer->mBuffer->GetDataPtr(); 1697 UInt32 *end = (UInt32 *)(buffer->mBuffer->GetDataPtr() + (buffer->mBuffer->GetDataSize() &0xFFFFFFFC)); 1698 while (start < end) 1699 { 1700 X = *start; 1701 start += 1024; 1702 } 1703 } 1704 1705 if (buffer->mBuffer->HasBeenConverted() == false) 1706 AudioConverterReset(mACMap->Get(buffer->mACToken)); 1707 1708 return true; 1709} 1710 1711// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1712void OALSource::Play() 1713{ 1714#if LOG_VERBOSE 1715 DebugMessageN1("OALSource::Play called - OALSource = %ld\n", (long int) mSelfToken); 1716#endif 1717#if LOG_PLAYBACK 1718 DebugMessageN2("OALSource::Play called - OALSource:mState = %ld:%ld\n", (long int) mSelfToken, mState); 1719#endif 1720 try { 1721 // wait if in render, then prevent rendering til completion 1722 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 1723 1724 // don't allow synchronous source manipulation 1725 CAGuard::Locker sourceLock(mSourceLock); 1726 1727 if (GetQLengthPriv() == 0) 1728 return; // nothing to do 1729 1730 switch (mState) 1731 { 1732 case AL_PLAYING: 1733 case kTransitionToPlay: 1734 case kTransitionToResume: 1735 case kTransitionToRetrigger: 1736 if (mRampState != kRampingComplete) 1737 { 1738 mRampState = kRampDown; 1739#if LOG_MESSAGE_QUEUE 1740 DebugMessageN1("OALSource::Play (AL_PLAYING state) - kMQ_Retrigger added to MQ - OALSource = %ld", (long int) mSelfToken); 1741#endif 1742 AddPlaybackMessage(kMQ_Retrigger, NULL, 0); 1743 SetPlaybackState(kTransitionToRetrigger); 1744 } 1745 break; 1746 1747 case AL_PAUSED: 1748 Resume(); 1749 break; 1750 1751 case kTransitionToPause: 1752#if LOG_MESSAGE_QUEUE 1753 DebugMessageN1("OALSource::Play (kTransitionToPause state) - kMQ_Resume added to MQ - OALSource = %ld", (long int) mSelfToken); 1754#endif 1755 AddPlaybackMessage((UInt32) kMQ_Resume, NULL, 0); 1756 SetPlaybackState(kTransitionToResume); 1757 break; 1758 1759 case kTransitionToStop: 1760 case kTransitionToRewind: 1761 if (mRampState != kRampingComplete) 1762 { 1763 mRampState = kRampDown; 1764#if LOG_MESSAGE_QUEUE 1765 DebugMessageN1("OALSource::Play (kTransitionToStop/kTransitionToRewind state) - kMQ_Play added to MQ - OALSource = %ld", (long int) mSelfToken); 1766#endif 1767 AddPlaybackMessage(kMQ_Play, NULL, 0); 1768 SetPlaybackState(kTransitionToPlay); 1769 } 1770 break; 1771 1772 default: 1773 { 1774 // get the buffer q in a ready state for playback 1775 PrepBufferQueueForPlayback(); 1776 1777 // set up a mixer bus now 1778 SetupMixerBus(); 1779 CAStreamBasicDescription format; 1780 UInt32 propSize = sizeof(format); 1781 OSStatus result = noErr; 1782 1783 if(mASARogerBeepEnable || mASADistortionEnable) 1784 { 1785 mRenderElement = 0; 1786 result = AudioUnitGetProperty(mOwningContext->GetMixerUnit(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, mCurrentPlayBus, &format, &propSize); 1787 THROW_RESULT 1788 1789 if(mASARogerBeepEnable) 1790 { 1791 // set AU format to mixer format for input/output 1792 result = AudioUnitSetProperty (mRogerBeepAU, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, sizeof(format)); 1793 THROW_RESULT 1794 result = AudioUnitSetProperty (mRogerBeepAU, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof(format)); 1795 THROW_RESULT 1796 result = AudioUnitInitialize(mRogerBeepAU); 1797 THROW_RESULT 1798 // connect roger beep AU to 3D Mixer 1799 result = AUGraphConnectNodeInput(mOwningContext->GetGraph(), mRogerBeepNode, 0, mOwningContext->GetMixerNode(), mCurrentPlayBus); 1800 THROW_RESULT 1801 1802 if(!mASADistortionEnable) 1803 { 1804 // connect render proc to unit if distortion is not enabled 1805 result = AUGraphGetNodeInfo (mOwningContext->GetGraph(), mRogerBeepNode, 0, 0, 0, &mRenderUnit); 1806 THROW_RESULT; 1807 } 1808 } 1809 1810 if(mASADistortionEnable) 1811 { 1812 1813 // set AU format to mixer format for input/output 1814 result = AudioUnitSetProperty (mDistortionAU, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, sizeof(format)); 1815 THROW_RESULT 1816 result = AudioUnitSetProperty (mDistortionAU, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof(format)); 1817 THROW_RESULT 1818 result = AudioUnitInitialize(mDistortionAU); 1819 THROW_RESULT 1820 1821 // distortion unit will always be first if it exists 1822 result = AUGraphGetNodeInfo (mOwningContext->GetGraph(), mDistortionNode, 0, 0, 0, &mRenderUnit); 1823 THROW_RESULT 1824 1825 if(mASARogerBeepEnable) 1826 result = AUGraphConnectNodeInput(mOwningContext->GetGraph(), mDistortionNode, 0, mRogerBeepNode, 0); 1827 else 1828 result = AUGraphConnectNodeInput(mOwningContext->GetGraph(), mDistortionNode, 0, mOwningContext->GetMixerNode(), mCurrentPlayBus); 1829 1830 THROW_RESULT 1831 } 1832 1833 result = AUGraphUpdate(mOwningContext->GetGraph(), NULL); 1834 THROW_RESULT 1835 } 1836 else 1837 { 1838 mRenderUnit = mOwningContext->GetMixerUnit(); 1839 mRenderElement = mCurrentPlayBus; 1840 } 1841 THROW_RESULT 1842 1843 SetPlaybackState(AL_PLAYING,true); 1844 if (mRampState != kRampingComplete) 1845 mRampState = kRampUp; 1846 mResetBus = true; 1847 mQueueIsProcessed = false; 1848 // attach the notify and render procs to the first unit in the sequence 1849 AddNotifyAndRenderProcs(); 1850 } 1851 } 1852 } 1853 catch (OSStatus result) { 1854 DebugMessageN2("PLAY FAILED source = %ld, err = %ld\n", (long int) mSelfToken, (long int)result); 1855 throw (result); 1856 } 1857} 1858 1859// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1860void OALSource::Rewind() 1861{ 1862#if LOG_VERBOSE 1863 DebugMessageN1("OALSource::Rewind called - OALSource = %ld\n", (long int) mSelfToken); 1864#endif 1865#if LOG_PLAYBACK 1866 DebugMessageN2("OALSource::Rewind called - OALSource:mState = %ld:%ld\n", (long int) mSelfToken, mState); 1867#endif 1868 1869 // wait if in render, then prevent rendering til completion 1870 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 1871 1872 // don't allow synchronous source manipulation 1873 CAGuard::Locker sourceLock(mSourceLock); 1874 1875 switch (mState) 1876 { 1877 case AL_PLAYING: 1878 { 1879#if LOG_MESSAGE_QUEUE 1880 DebugMessageN1("Rewind(AL_PLAYING) ADDING : kMQ_Rewind - OALSource = %ld", (long int) mSelfToken); 1881#endif 1882 1883 SetPlaybackState(kTransitionToRewind); 1884 if (mRampState != kRampingComplete) 1885 mRampState = kRampDown; 1886 AddPlaybackMessage(kMQ_Rewind, NULL, 0); 1887 break; 1888 1889 } 1890 case AL_PAUSED: 1891 if (mCurrentPlayBus != kSourceNeedsBus) 1892 { 1893 mOwningContext->SetBusAsAvailable (mCurrentPlayBus); 1894 mCurrentPlayBus = kSourceNeedsBus; 1895 } 1896 JoinBufferLists(); // just reset the buffer queue now 1897 SetPlaybackState(AL_INITIAL,true); 1898 break; 1899 1900 case AL_STOPPED: 1901 JoinBufferLists(); // just reset the buffer queue now 1902 SetPlaybackState(AL_INITIAL,true); 1903 break; 1904 1905 case kTransitionToPlay: 1906 case kTransitionToResume: 1907 case kTransitionToRetrigger: 1908 case kTransitionToStop: 1909 case kTransitionToPause: 1910 { 1911#if LOG_MESSAGE_QUEUE 1912 DebugMessageN1("OALSource::Rewind - kMQ_Rewind added to MQ - OALSource = %ld", (long int) mSelfToken); 1913#endif 1914 SetPlaybackState(kTransitionToRewind); 1915 AddPlaybackMessage((UInt32) kMQ_Rewind, NULL, 0); 1916 break; 1917 } 1918 1919 case AL_INITIAL: 1920 case kTransitionToRewind: 1921 break; 1922 } 1923} 1924 1925// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1926void OALSource::Pause() 1927{ 1928#if LOG_VERBOSE 1929 DebugMessageN1("OALSource::Pause called - OALSource = %ld\n", (long int) mSelfToken); 1930#endif 1931#if LOG_PLAYBACK 1932 DebugMessageN2("OALSource::Pause called - OALSource:mState = %ld:%ld\n", (long int) mSelfToken, mState); 1933#endif 1934 1935 // wait if in render, then prevent rendering til completion 1936 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 1937 1938 // don't allow synchronous source manipulation 1939 CAGuard::Locker sourceLock(mSourceLock); 1940 1941 switch (mState) 1942 { 1943 case AL_PLAYING: 1944 { 1945 if (mRampState != kRampingComplete) 1946 mRampState = kRampDown; 1947#if LOG_MESSAGE_QUEUE 1948 DebugMessageN1("Pause(AL_PLAYING) ADDING : kMQ_Pause - OALSource = %ld", (long int) mSelfToken); 1949#endif 1950 AddPlaybackMessage(kMQ_Pause, NULL, 0); 1951 SetPlaybackState(kTransitionToPause); 1952 } 1953 case AL_INITIAL: 1954 case AL_STOPPED: 1955 case AL_PAUSED: 1956 case kTransitionToPause: 1957 case kTransitionToStop: 1958 case kTransitionToRewind: 1959 break; 1960 1961 case kTransitionToPlay: 1962 case kTransitionToResume: 1963 case kTransitionToRetrigger: 1964 { 1965#if LOG_MESSAGE_QUEUE 1966 DebugMessageN1("OALSource::Pause - kMQ_Pause added to MQ - OALSource = %ld", (long int) mSelfToken); 1967#endif 1968 AddPlaybackMessage((UInt32) kMQ_Pause, NULL, 0); 1969 SetPlaybackState(kTransitionToPause); 1970 break; 1971 } 1972 1973 default: 1974 // do nothing it's either stopped or initial right now 1975 break; 1976 } 1977} 1978 1979// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1980void OALSource::Resume() 1981{ 1982#if LOG_VERBOSE 1983 DebugMessageN1("OALSource::Resume called - OALSource = %ld\n", (long int) mSelfToken); 1984#endif 1985#if LOG_PLAYBACK 1986 DebugMessageN2("OALSource::Resume called - OALSource:mState = %ld:%ld\n", (long int) mSelfToken, mState); 1987#endif 1988 1989 // wait if in render, then prevent rendering til completion 1990 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 1991 1992 // don't allow synchronous source manipulation 1993 CAGuard::Locker sourceLock(mSourceLock); 1994 1995 switch (mState) 1996 { 1997 case AL_PLAYING: 1998 case AL_INITIAL: 1999 case AL_STOPPED: 2000 case kTransitionToPlay: 2001 case kTransitionToResume: 2002 case kTransitionToRetrigger: 2003 case kTransitionToStop: 2004 case kTransitionToRewind: 2005 break; 2006 2007 case AL_PAUSED: 2008 if (mRampState != kRampingComplete) 2009 mRampState = kRampUp; 2010 AddNotifyAndRenderProcs(); 2011 SetPlaybackState(AL_PLAYING,true); 2012 break; 2013 2014 case kTransitionToPause: 2015#if LOG_MESSAGE_QUEUE 2016 DebugMessageN1("OALSource::Resume - kMQ_Resume added to MQ - OALSource = %ld", (long int) mSelfToken); 2017#endif 2018 AddPlaybackMessage((UInt32) kMQ_Resume, NULL, 0); 2019 SetPlaybackState(kTransitionToResume); 2020 break; 2021 } 2022} 2023 2024// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2025void OALSource::Stop() 2026{ 2027#if LOG_VERBOSE 2028 DebugMessageN1("OALSource::Stop called - OALSource = %ld\n", (long int) mSelfToken); 2029#endif 2030#if LOG_PLAYBACK 2031 DebugMessageN2("OALSource::Stop called - OALSource:mState = %ld:%ld\n", (long int) mSelfToken, mState); 2032#endif 2033 2034 // wait if in render, then prevent rendering til completion 2035 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 2036 2037 // don't allow synchronous source manipulation 2038 CAGuard::Locker sourceLock(mSourceLock); 2039 2040 switch (mState) 2041 { 2042 case AL_PAUSED: 2043 if (mCurrentPlayBus != kSourceNeedsBus) 2044 { 2045 mOwningContext->SetBusAsAvailable (mCurrentPlayBus); 2046 mCurrentPlayBus = kSourceNeedsBus; 2047 SetPlaybackState(AL_STOPPED,true); 2048 if(mBufferQueueActive->GetQueueSize() > 0) 2049 { 2050 if (mSourceNotifications) 2051 mSourceNotifications->CallSourceNotifications(AL_BUFFERS_PROCESSED); 2052 } 2053 } 2054 break; 2055 case AL_PLAYING: 2056 { 2057#if LOG_MESSAGE_QUEUE 2058 DebugMessageN1("OALSource::Stop (AL_PLAYING state) ADDING : kMQ_Stop - OALSource = %ld", (long int) mSelfToken); 2059#endif 2060 SetPlaybackState(kTransitionToStop); 2061 if (mRampState != kRampingComplete) 2062 mRampState = kRampDown; 2063 AddPlaybackMessage(kMQ_Stop, NULL, 0); 2064 } 2065 break; 2066 2067 case kTransitionToStop: 2068 case kTransitionToRewind: 2069 break; 2070 2071 case kTransitionToPlay: 2072 case kTransitionToResume: 2073 case kTransitionToRetrigger: 2074 case kTransitionToPause: 2075 { 2076#if LOG_MESSAGE_QUEUE 2077 DebugMessageN1("OALSource::Stop (kTransitionState state) ADDING : kMQ_Stop - OALSource = %ld", (long int) mSelfToken); 2078#endif 2079 AddPlaybackMessage(kMQ_Stop, NULL, 0); 2080 SetPlaybackState(kTransitionToStop); 2081 break; 2082 } 2083 2084 default: 2085 // do nothing it's either stopped or initial right now 2086 break; 2087 } 2088} 2089 2090// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2091UInt32 OALSource::SecondsToFrames(Float32 inSeconds) 2092{ 2093#if LOG_VERBOSE 2094 DebugMessageN2("OALSource::SecondsToFrames called - OALSource:inSeconds = %ld:%f\n", (long int) mSelfToken, inSeconds); 2095#endif 2096 BufferInfo *curBufferInfo = mBufferQueueActive->Get(mCurrentBufferIndex); 2097 UInt32 frames = 0; 2098 2099 if (curBufferInfo) 2100 frames = (UInt32)(inSeconds * curBufferInfo->mBuffer->GetSampleRate()); 2101 // Check for uint overflow 2102 if ((SInt32) frames < (SInt32) inSeconds) 2103 throw (OSStatus) AL_INVALID_VALUE; 2104 2105 return frames; 2106} 2107 2108// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2109// this method should round off to the packet index that contains the requested frame offset 2110UInt32 OALSource::BytesToFrames(Float32 inBytes) 2111{ 2112#if LOG_VERBOSE 2113 DebugMessageN2("OALSource::BytesToFrames called - OALSource:inBytes = %ld:%f\n", (long int) mSelfToken, inBytes); 2114#endif 2115 BufferInfo *curBufferInfo = mBufferQueueActive->Get(mCurrentBufferIndex); 2116 if (curBufferInfo) 2117 { 2118 UInt32 frameIndex = (UInt32)(inBytes / curBufferInfo->mBuffer->GetBytesPerPacket()); // how many packets is this 2119 frameIndex *= curBufferInfo->mBuffer->GetFramesPerPacket(); // translate packets to frames 2120 return frameIndex; 2121 } 2122 2123 return 0; 2124} 2125 2126// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2127UInt32 OALSource::FramesToSecondsInt(UInt32 inFrames) 2128{ 2129#if LOG_VERBOSE 2130 DebugMessageN2("OALSource::FramesToSecondsInt called - OALSource:inFrames = %ld:%d\n", (long int) mSelfToken, inFrames); 2131#endif 2132 BufferInfo *curBufferInfo = mBufferQueueActive->Get(mCurrentBufferIndex); 2133 if (curBufferInfo == NULL) 2134 curBufferInfo = mBufferQueueInactive->Get(0); // if the active Q is dry, check the inactive Q 2135 2136 if (curBufferInfo) 2137 return (UInt32)(inFrames / curBufferInfo->mBuffer->GetSampleRate()); 2138 2139 return 0; 2140} 2141 2142// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2143Float32 OALSource::FramesToSecondsFloat(UInt32 inFrames) 2144{ 2145#if LOG_VERBOSE 2146 DebugMessageN2("OALSource::FramesToSecondsFloat called - OALSource:inFrames = %ld:%f\n", (long int) mSelfToken, inFrames); 2147#endif 2148 BufferInfo *curBufferInfo = mBufferQueueActive->Get(mCurrentBufferIndex); 2149 if (curBufferInfo == NULL) 2150 curBufferInfo = mBufferQueueInactive->Get(0); // if the active Q is dry, check the inactive Q 2151 2152 if (curBufferInfo) 2153 return (inFrames / curBufferInfo->mBuffer->GetSampleRate()); 2154 2155 return 0; 2156} 2157 2158// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2159UInt32 OALSource::FramesToBytes(UInt32 inFrames) 2160{ 2161#if LOG_VERBOSE 2162 DebugMessageN2("OALSource::FramesToBytes called - OALSource:inFrames = %ld:%d\n", (long int) mSelfToken, inFrames); 2163#endif 2164 // any buffer in the q will do, they should all be the same 2165 BufferInfo *curBufferInfo = mBufferQueueActive->Get(mCurrentBufferIndex); 2166 if (curBufferInfo == NULL) 2167 curBufferInfo = mBufferQueueInactive->Get(0); // the active Q must be empty, check the inactive Q 2168 2169 if (curBufferInfo) 2170 { 2171 if(curBufferInfo->mBuffer->GetFramesPerPacket() != 0) 2172 return (inFrames / curBufferInfo->mBuffer->GetFramesPerPacket()) * curBufferInfo->mBuffer->GetBytesPerPacket(); 2173 } 2174 2175 return 0; 2176} 2177 2178// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2179UInt32 OALSource::GetQueueFrameOffset() 2180{ 2181#if LOG_VERBOSE 2182 DebugMessageN1("OALSource::GetQueueFrameOffset called - OALSource = %ld\n", (long int) mSelfToken); 2183#endif 2184 UInt32 inInactiveQFrames = mBufferQueueInactive->GetQueueSizeInFrames(); 2185 UInt32 inActiveQFrames = 0; 2186 2187 for (UInt32 i = 0; i < mCurrentBufferIndex; i++) 2188 { 2189 inActiveQFrames += mBufferQueueActive->GetBufferFrameCount(i); 2190 } 2191 2192 inActiveQFrames += mBufferQueueActive->GetCurrentFrame(mCurrentBufferIndex); 2193 2194 return inActiveQFrames + inInactiveQFrames; 2195} 2196 2197// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2198UInt32 OALSource::GetQueueOffset(UInt32 inOffsetType) 2199{ 2200#if LOG_VERBOSE 2201 DebugMessageN2("OALSource::GetQueueOffset called - OALSource:GetQueueOffset = %ld:%d\n", (long int) mSelfToken, inOffsetType); 2202#endif 2203 CAGuard::Locker sourceLock(mSourceLock); 2204 2205 switch (inOffsetType) 2206 { 2207 case kSecondsOffset: 2208 return (FramesToSecondsInt(GetQueueFrameOffset())); 2209 break; 2210 case kSampleOffset: 2211 return (GetQueueFrameOffset()); 2212 break; 2213 case kByteOffset: 2214 return (FramesToBytes(GetQueueFrameOffset())); 2215 break; 2216 default: 2217 break; 2218 } 2219 2220 return 0; 2221} 2222 2223// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2224Float32 OALSource::GetQueueOffsetSecondsFloat() 2225{ 2226#if LOG_VERBOSE 2227 DebugMessageN1("OALSource::GetQueueOffsetSecondsFloat called - OALSource = %ld\n", (long int) mSelfToken); 2228#endif 2229 CAGuard::Locker sourceLock(mSourceLock); 2230 2231 return (FramesToSecondsFloat(GetQueueFrameOffset())); 2232} 2233 2234// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2235void OALSource::SetQueueOffset(UInt32 inOffsetType, Float32 inOffset) 2236{ 2237#if LOG_VERBOSE 2238 DebugMessageN3("OALSource::SetQueueOffset called - OALSource:inOffsetType:inOffset = %ld:%d:%f\n", (long int) mSelfToken, inOffsetType, inOffset); 2239#endif 2240#if LOG_PLAYBACK 2241 DebugMessageN1("OALSource::SetQueueOffset - OALSource = %ld", (long int) mSelfToken); 2242#endif 2243 UInt32 frameOffset = 0; 2244 2245 if (inOffset < 0.0f) 2246 throw (OSStatus)AL_INVALID_VALUE; 2247 2248 // wait if in render, then prevent rendering til completion 2249 OALRenderLocker::RenderLocker locked(mRenderLocker, InRenderThread()); 2250 2251 // don't allow synchronous source manipulation 2252 CAGuard::Locker sourceLock(mSourceLock); 2253 2254 // first translate the entered offset into a frame offset 2255 switch (inOffsetType) 2256 { 2257 case kSecondsOffset: 2258 frameOffset = SecondsToFrames(inOffset); 2259 break; 2260 case kSampleOffset: 2261 frameOffset = (UInt32) inOffset; // samples = frames in this case 2262 break; 2263 case kByteOffset: 2264 frameOffset = BytesToFrames(inOffset); 2265 break; 2266 default: 2267 return; 2268 break; 2269 } 2270 2271 // move the queue to the requested offset either right now, or in the PostRender proc via a queued message 2272 switch (mState) 2273 { 2274 case AL_INITIAL: 2275 case AL_STOPPED: 2276 case AL_PAUSED: 2277 { 2278 Rewind(); // join the active and inactive lists 2279 AdvanceQueueToFrameIndex(frameOffset); 2280 } 2281 break; 2282 2283 case AL_PLAYING: 2284 case kTransitionToStop: 2285 case kTransitionToRewind: 2286 case kTransitionToPause: 2287 case kTransitionToResume: 2288 case kTransitionToRetrigger: 2289 case kTransitionToPlay: 2290 { 2291 // Check this here so we don't throw in the render thread 2292 if (frameOffset > mBufferQueueActive->GetQueueSizeInFrames()) 2293 throw (OSStatus)AL_INVALID_VALUE; 2294 mRampState = kRampDown; 2295 // currently, no consideration for ramping the samples is being taken, so there could be clicks if playhead is moved during a playback 2296 // this is a bug, if SetQueueOffset is called multiple times between render cycles, 2297 // the offset will end up at zero in the post render when mPlaybackHeadPosition gets reset after the first message is addressed 2298 mPlaybackHeadPosition = frameOffset; 2299#if LOG_MESSAGE_QUEUE 2300 DebugMessageN1("OALSource::SetQueueOffset - kMQ_SetFramePosition added to MQ - OALSource = %ld", (long int) mSelfToken); 2301#endif 2302 AddPlaybackMessage(kMQ_SetFramePosition, NULL, 0); 2303 break; 2304 } 2305 2306 default: 2307 break; 2308 } 2309} 2310 2311// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2312// this should only be called when the source is render and edit protected 2313void OALSource::AdvanceQueueToFrameIndex(UInt32 inFrameOffset) 2314{ 2315#if LOG_VERBOSE 2316 DebugMessageN2("OALSource::AdvanceQueueToFrameIndex called - OALSource:inFrameOffset = %ld:%d", (long int) mSelfToken, inFrameOffset); 2317#endif 2318 // Queue should be in an initial state right now. 2319 UInt32 totalQueueFrames = mBufferQueueActive->GetQueueSizeInFrames(); 2320 if (inFrameOffset > totalQueueFrames) 2321 throw (OSStatus)AL_INVALID_VALUE; 2322 2323 // Walk through the buffers until we reach the one that contains this offset. Mark all preceeding buffers as processed and move to inActive Queue 2324 2325 UInt32 frameStartOfCurrentBuffer = 0; 2326 2327 UInt32 count = mBufferQueueActive->GetQueueSize(); 2328 for (UInt32 i = 0; i < count; i++) 2329 { 2330 BufferInfo* curBufferInfo = mBufferQueueActive->Get(0); // get the first buffer info in the Q 2331 UInt32 bufferFrameCount = curBufferInfo->mBuffer->GetFrameCount(); // how many frames of data are in this buffer? 2332 2333 if (frameStartOfCurrentBuffer + bufferFrameCount > inFrameOffset) 2334 { 2335 // this is the buffer that contains the desired offset 2336 mBufferQueueActive->SetFirstBufferOffset(inFrameOffset - frameStartOfCurrentBuffer) ; 2337 break; // we're done 2338 } 2339 else 2340 { 2341 // not there yet. Mark this buffer as processed and move to inactive queue 2342 mBufferQueueActive->SetBufferAsProcessed(0); 2343 mBufferQueueActive->RemoveQueueEntryByIndex(this, 0, false); 2344 mBufferQueueInactive->AppendBuffer(this, curBufferInfo->mBufferToken, curBufferInfo->mBuffer, curBufferInfo->mACToken); 2345 SetQueueLength(); 2346 if (mSourceNotifications) 2347 mSourceNotifications->CallSourceNotifications(AL_BUFFERS_PROCESSED); 2348 } 2349 frameStartOfCurrentBuffer += bufferFrameCount; 2350 } 2351 2352 return; 2353} 2354 2355// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2356#pragma mark ***** Source Notify Ext***** 2357// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2358// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2359 2360// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2361ALenum OALSource::AddNotification(ALuint notificationID, alSourceNotificationProc notifyProc, ALvoid* userData) 2362{ 2363#if LOG_VERBOSE 2364 DebugMessageN4("OALSource::AddNotification called - OALSource:notificationID:notifyProc:userData = %ld:%ld:%p:%p\n", (long int) mSelfToken, (long int) notificationID, notifyProc, userData); 2365#endif 2366 ALenum err = AL_NO_ERROR; 2367 2368 if (mSourceNotifications == NULL) 2369 mSourceNotifications = new SourceNotifications(mSelfToken); 2370 2371 err = mSourceNotifications->AddSourceNotification(notificationID, notifyProc, userData); 2372 2373 return err; 2374} 2375 2376void OALSource::RemoveNotification(ALuint notificationID, alSourceNotificationProc notifyProc, ALvoid* userData) 2377{ 2378#if LOG_VERBOSE 2379 DebugMessageN4("OALSource::RemoveNotification called - OALSource:notificationID:notifyProc:userData = %ld:%ld:%p:%p\n", (long int) mSelfToken, (long int) notificationID, notifyProc, userData); 2380#endif 2381 if (mSourceNotifications == NULL) 2382 return; 2383 2384 mSourceNotifications->RemoveSourceNotification(notificationID, notifyProc, userData); 2385} 2386 2387// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2388#pragma mark ***** Source Spatialization Ext***** 2389// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2390// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2391 2392// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2393OSStatus OALSource::SetRenderQuality(ALuint inValue) 2394{ 2395 OSStatus err = noErr; 2396 if (IsValidRenderQuality(inValue)) 2397 { 2398 mRenderQuality = inValue; 2399 UpdateBusRenderQuality(); 2400 } 2401 else 2402 { 2403 err = -50; //invalid render quality 2404 } 2405 return err; 2406} 2407 2408ALint OALSource::GetRenderQuality() 2409{ 2410 return mRenderQuality; 2411} 2412 2413// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2414// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2415#pragma mark ***** PRIVATE ***** 2416// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2417// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2418 2419// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2420void OALSource::SwapBufferQueues() 2421{ 2422#if LOG_VERBOSE 2423 DebugMessageN1("OALSource::SwapBufferQueues called - OALSource = %ld\n", (long int) mSelfToken); 2424#endif 2425 BufferQueue* tQ = mBufferQueueActive; 2426 mBufferQueueActive = mBufferQueueInactive; 2427 mBufferQueueInactive = tQ; 2428 2429 //now that we have swapped, we have to reset the size variables 2430 mBufferQueueActive->SetQueueSize(); 2431 mBufferQueueInactive->SetQueueSize(); 2432 SetQueueLength(); 2433 2434 mBufferQueueActive->ResetBuffers(); // mark all the buffers as unprocessed 2435 mCurrentBufferIndex = 0; // start at the first buffer in the queue 2436 mQueueIsProcessed = false; 2437} 2438 2439// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2440void OALSource::AddPlaybackMessage(UInt32 inMessage, OALBuffer* inBuffer, UInt32 inNumBuffers) 2441{ 2442#if LOG_VERBOSE 2443 DebugMessageN4("OALSource::AddPlaybackMessage called - OALSource:inMessage:inDeferredAppendBuffer:inBuffersToUnqueue = %ld:%ld:%p:%ld\n", (long int) mSelfToken, (long int) inMessage, inDeferredAppendBuffer, (long int) inBuffersToUnqueue); 2444#endif 2445 PlaybackMessage* pbm = new PlaybackMessage((UInt32) inMessage, inBuffer, inNumBuffers); 2446 mMessageQueue.push_atomic(pbm); 2447} 2448 2449// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2450void OALSource::ClearMessageQueue() 2451{ 2452#if LOG_VERBOSE 2453 DebugMessageN1("OALSource::ClearMessageQueue called - OALSource = %ld\n", (long int) mSelfToken); 2454#endif 2455 PlaybackMessage *messages = mMessageQueue.pop_all_reversed(); 2456 while (messages != NULL) 2457 { 2458 PlaybackMessage *lastMessage = messages; 2459 messages = messages->next(); 2460 delete (lastMessage); // made it, so now get rid of it 2461 } 2462} 2463 2464void OALSource::SetPlaybackState(ALuint inState, bool sendSourceStateChangeNotification) 2465{ 2466#if LOG_VERBOSE 2467 DebugMessageN1("OALSource::SetPlaybackState called - OALSource = %ld\n", (long int) mSelfToken); 2468#endif 2469#if LOG_PLAYBACK 2470 DebugMessageN2("OALSource::SetPlaybackState called - inState = %d - OALSource = %ld", inState, (long int) mSelfToken); 2471#endif 2472 mState = inState; 2473 //sendSourceStateChangeNotification is set to false by default 2474 if (mSourceNotifications && sendSourceStateChangeNotification) 2475 mSourceNotifications->CallSourceNotifications(AL_SOURCE_STATE); 2476} 2477 2478// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2479#pragma mark ***** Buffer Queue Methods ***** 2480// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2481 2482void OALSource::ClearActiveQueue() 2483{ 2484#if LOG_VERBOSE 2485 DebugMessageN1("OALSource::ClearActiveQueue called - OALSource = %ld\n", (long int) mSelfToken); 2486#endif 2487 while (mBufferQueueActive->GetQueueSize() > 0) 2488 { 2489 // Get buffer #i from Active List 2490 BufferInfo *staleBufferInfo = mBufferQueueActive->Get(0); 2491 if (staleBufferInfo) 2492 { 2493 mBufferQueueActive->SetBufferAsProcessed(0); 2494 // Append it to Inactive List 2495 mBufferQueueInactive->AppendBuffer(this, staleBufferInfo->mBufferToken, staleBufferInfo->mBuffer, staleBufferInfo->mACToken); 2496 // Remove it from Active List 2497 mBufferQueueActive->RemoveQueueEntryByIndex(this, 0, false); 2498 SetQueueLength(); 2499 } 2500 } 2501 2502 mCurrentBufferIndex = 0; 2503 mQueueIsProcessed = false; 2504} 2505 2506// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2507// this method should only be called when a looping queue reaches it's end and needs to start over (called from render thread) 2508void OALSource::LoopToBeginning() 2509{ 2510#if LOG_VERBOSE 2511 DebugMessageN1("OALSource::LoopToBeginning called - OALSource = %ld\n", (long int) mSelfToken); 2512#endif 2513 2514 ClearActiveQueue(); 2515 2516 // swap the list pointers now 2517 SwapBufferQueues(); 2518 2519 // Send the notification if necessary 2520 if ((mSourceNotifications) && mLooping) 2521 mSourceNotifications->CallSourceNotifications(AL_QUEUE_HAS_LOOPED); 2522} 2523 2524// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2525// this method should only be called from a non playing state and is used to rejoin the 2 buffer Q lists 2526void OALSource::JoinBufferLists() 2527{ 2528#if LOG_VERBOSE 2529 DebugMessageN1("OALSource::JoinBufferLists called - OALSource = %ld\n", (long int) mSelfToken); 2530#endif 2531 2532 ClearActiveQueue(); 2533 2534 // swap the list pointers now 2535 SwapBufferQueues(); 2536 2537 return; 2538} 2539 2540// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2541// this is ONLY called from the render thread 2542void OALSource::UpdateQueue () 2543{ 2544#if LOG_VERBOSE 2545 DebugMessageN1("OALSource::UpdateQueue called - OALSource = %ld\n", (long int) mSelfToken); 2546#endif 2547 if (mCurrentBufferIndex > 0) 2548 { 2549 BufferInfo *bufferInfo = NULL; 2550 for (UInt32 i = 0; i < mCurrentBufferIndex; i++) 2551 { 2552 // Get buffer #i from Active List 2553 bufferInfo = mBufferQueueActive->Get(0); 2554 if (bufferInfo) 2555 { 2556 // Append it to Inactive List 2557 mBufferQueueInactive->AppendBuffer(this, bufferInfo->mBufferToken, bufferInfo->mBuffer, bufferInfo->mACToken); 2558 // Remove it from Active List 2559 mBufferQueueActive->RemoveQueueEntryByIndex(this, 0, false); 2560 SetQueueLength(); 2561 if (mSourceNotifications) 2562 mSourceNotifications->CallSourceNotifications(AL_BUFFERS_PROCESSED); 2563 } 2564 } 2565 mCurrentBufferIndex = 0; 2566 } 2567} 2568 2569// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2570bool OALSource::IsSourceTransitioningToFlushQ() 2571{ 2572#if LOG_VERBOSE 2573 DebugMessageN1("OALSource::IsSourceTransitioningToFlushQ called - OALSource = %ld\n", (long int) mSelfToken); 2574#endif 2575 return mTransitioningToFlushQ; 2576} 2577 2578// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2579#pragma mark ***** Mixer Bus Methods ***** 2580// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2581void OALSource::DisconnectFromBus() 2582{ 2583#if LOG_VERBOSE 2584 DebugMessageN1("OALSource::DisconnectFromBus called - OALSource = %ld\n", (long int) mSelfToken); 2585#endif 2586 try { 2587 ReleaseNotifyAndRenderProcs(); 2588// OSStatus result = noErr; 2589 2590 // remove the connection between the AUs, if they exist 2591 if(mRogerBeepNode || mDistortionNode) 2592 { 2593 if(mRogerBeepNode && mDistortionNode) 2594 /*result =*/ AUGraphDisconnectNodeInput(mOwningContext->GetGraph(), mRogerBeepNode, 0); 2595 /*result =*/ AUGraphDisconnectNodeInput(mOwningContext->GetGraph(), mOwningContext->GetMixerNode(), mCurrentPlayBus); 2596 /*result =*/ AUGraphUpdate(mOwningContext->GetGraph(), NULL); 2597 } 2598 2599 if (mCurrentPlayBus != kSourceNeedsBus) 2600 { 2601 mOwningContext->SetBusAsAvailable (mCurrentPlayBus); 2602 mCurrentPlayBus = kSourceNeedsBus; 2603 } 2604 } 2605 catch (OSStatus result) { 2606 // swallow the error 2607 } 2608} 2609 2610// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2611// must be called when the source has a mixer bus 2612void OALSource::ChangeChannelSettings() 2613{ 2614#if LOG_VERBOSE 2615 DebugMessageN1("OALSource::ChangeChannelSettings called - OALSource = %ld\n", (long int) mSelfToken); 2616#endif 2617#if CALCULATE_POSITION 2618 2619// bool coneGainChange = false; 2620// bool attenuationGainChange = false; 2621 2622 if (mCalculateDistance == true) 2623 { 2624 BufferInfo *bufferInfo = mBufferQueueActive->Get(mCurrentBufferIndex); 2625 if (bufferInfo) 2626 { 2627#if LOG_GRAPH_AND_MIXER_CHANGES 2628 DebugMessageN1("OALSource::ChangeChannelSettings: k3DMixerParam_Azimuth/k3DMixerParam_Distance called - OALSource = %ld\n", mSelfToken); 2629#endif 2630 // only calculate position if sound is mono - stereo sounds get no location changes 2631 if ( bufferInfo->mBuffer->GetNumberChannels() == 1) 2632 { 2633 Float32 rel_azimuth, rel_distance, rel_elevation, dopplerShift; 2634 2635 CalculateDistanceAndAzimuth(&rel_distance, &rel_azimuth, &rel_elevation, &dopplerShift); 2636 2637 if (dopplerShift != mDopplerScaler) 2638 { 2639 mDopplerScaler = dopplerShift; 2640 mResetPitch = true; 2641 } 2642 2643 // set azimuth 2644 AudioUnitSetParameter(mOwningContext->GetMixerUnit(), k3DMixerParam_Azimuth, kAudioUnitScope_Input, mCurrentPlayBus, rel_azimuth, 0); 2645 // set elevation 2646 AudioUnitSetParameter(mOwningContext->GetMixerUnit(), k3DMixerParam_Elevation, kAudioUnitScope_Input, mCurrentPlayBus, rel_elevation, 0); 2647 2648 mAttenuationGainScaler = 1.0; 2649 if (!mOwningContext->DoSetDistance()) 2650 { 2651 AudioUnitSetParameter(mOwningContext->GetMixerUnit(), k3DMixerParam_Distance, kAudioUnitScope_Input, mCurrentPlayBus, mReferenceDistance, 0);/////// 2652 2653 // If 1.3-2.1 Mixer AND it's Linear, Exponential DO calculate Gain scaler - DO NOT set distance 2654 switch (mOwningContext->GetDistanceModel()) 2655 { 2656 case AL_LINEAR_DISTANCE: 2657 case AL_LINEAR_DISTANCE_CLAMPED: 2658 if (mOwningContext->GetDistanceModel() == AL_LINEAR_DISTANCE_CLAMPED) 2659 { 2660 if (rel_distance < mReferenceDistance) rel_distance = mReferenceDistance; 2661 if (rel_distance > mMaxDistance) rel_distance = mMaxDistance; 2662 } 2663 mAttenuationGainScaler = (1 - mRollOffFactor * (rel_distance - mReferenceDistance) / (mMaxDistance-mReferenceDistance)); 2664 DebugMessageN1("AL_LINEAR_DISTANCE scaler = %f\n", mAttenuationGainScaler); 2665// attenuationGainChange = true; 2666 break; 2667 2668 case AL_EXPONENT_DISTANCE: 2669 case AL_EXPONENT_DISTANCE_CLAMPED: 2670 if (mOwningContext->GetDistanceModel() == AL_EXPONENT_DISTANCE_CLAMPED) 2671 { 2672 if (rel_distance < mReferenceDistance) rel_distance = mReferenceDistance; 2673 if (rel_distance > mMaxDistance) rel_distance = mMaxDistance; 2674 } 2675 mAttenuationGainScaler = pow((rel_distance / mReferenceDistance), (-mRollOffFactor)); 2676 DebugMessageN1("AL_EXPONENT_DISTANCE scaler = %f\n", mAttenuationGainScaler); 2677// attenuationGainChange = true; 2678 break; 2679 2680 case AL_NONE: 2681 mAttenuationGainScaler = 1.0; 2682 break; // nothing to do 2683 } 2684 } 2685 else 2686 { 2687 // if 2.0 and Inverse, SCALE before setting distance 2688 if (mOwningContext->IsDistanceScalingRequired()) // only true for 2.0 mixer doing inverse curve 2689 rel_distance *= (kDistanceScalar/mMaxDistance); 2690 2691 // set distance 2692 AudioUnitSetParameter(mOwningContext->GetMixerUnit(), k3DMixerParam_Distance, kAudioUnitScope_Input, mCurrentPlayBus, rel_distance, 0); 2693 } 2694 2695 // Source Cone Support Here 2696 /*coneGainChange =*/ ConeAttenuation(); 2697 } 2698 } 2699 2700 mCalculateDistance = false; 2701 } 2702 2703#endif // end CALCULATE_POSITION 2704 2705 UpdateBusGain(); 2706 2707 SetPitch (mPitch); 2708 UpdateBusFormat(); 2709} 2710 2711// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2712void OALSource::UpdateBusGain () 2713{ 2714#if LOG_VERBOSE 2715 DebugMessageN1("OALSource::UpdateBusGain called - OALSource = %ld\n", (long int) mSelfToken); 2716#endif 2717 if (mCurrentPlayBus != kSourceNeedsBus) 2718 { 2719 Float32 busGain = mGain; 2720 2721 busGain *= mConeGainScaler; 2722 busGain *= mAttenuationGainScaler; 2723 2724 // clamp the gain used to 0.0-1.0 2725 if (busGain > 1.0) 2726 busGain = 1.0; 2727 else if (busGain < 0.0) 2728 busGain = 0.0; 2729 2730 mOutputSilence = busGain > 0.0 ? false : true; 2731 2732 if (busGain > 0.0) 2733 { 2734 Float32 db = 20.0 * log10(busGain); // convert to db 2735 if (db < -120.0) 2736 db = -120.0; // clamp minimum audible level at -120db 2737 2738//#if LOG_GRAPH_AND_MIXER_CHANGES 2739// DebugMessageN3("OALSource::UpdateBusGain: k3DMixerParam_Gain called - OALSource:busGain:db = %ld:%f:%f\n", mSelfToken, busGain, db ); 2740//#endif 2741 2742 OSStatus result = AudioUnitSetParameter ( mOwningContext->GetMixerUnit(), k3DMixerParam_Gain, kAudioUnitScope_Input, mCurrentPlayBus, db, 0); 2743 THROW_RESULT 2744 } 2745 } 2746} 2747 2748// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2749void OALSource::UpdateMinBusGain () 2750{ 2751#if VERBOSE 2752 DebugMessageN1("OALSource::UpdateMinBusGain called - OALSource = %ld", (long int) mSelfToken); 2753#endif 2754 if (mCurrentPlayBus != kSourceNeedsBus) 2755 { 2756 OSStatus result = AudioUnitSetParameter ( mOwningContext->GetMixerUnit(), k3DMixerParam_MinGain, kAudioUnitScope_Input, mCurrentPlayBus, mMinGain, 0); 2757 THROW_RESULT 2758 } 2759} 2760 2761// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2762void OALSource::UpdateMaxBusGain () 2763{ 2764#if VERBOSE 2765 DebugMessageN1("OALSource::UpdateMaxBusGain called - OALSource = %ld", (long int) mSelfToken); 2766#endif 2767 if (mCurrentPlayBus != kSourceNeedsBus) 2768 { 2769 OSStatus result = AudioUnitSetParameter ( mOwningContext->GetMixerUnit(), k3DMixerParam_MaxGain, kAudioUnitScope_Input, mCurrentPlayBus, mMaxGain, 0); 2770 THROW_RESULT 2771 } 2772} 2773 2774// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2775void OALSource::UpdateBusFormat () 2776{ 2777#if LOG_VERBOSE 2778 DebugMessageN1("OALSource::UpdateBusFormat - OALSource = %ld\n", (long int) mSelfToken); 2779#endif 2780 2781 if (Get3DMixerVersion() < k3DMixerVersion_2_0) // the pre-2.0 3DMixer cannot change stream formats once initialized 2782 return; 2783 2784 if (mResetBusFormat) 2785 { 2786 CAStreamBasicDescription desc; 2787 UInt32 propSize = sizeof(desc); 2788 OSStatus result = AudioUnitGetProperty(mOwningContext->GetMixerUnit(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, mCurrentPlayBus, &desc, &propSize); 2789 if (result == noErr) 2790 { 2791 BufferInfo *buffer = mBufferQueueActive->Get(mCurrentBufferIndex); 2792 if (buffer != NULL) 2793 { 2794 desc.mSampleRate = buffer->mBuffer->GetSampleRate(); 2795 AudioUnitSetProperty(mOwningContext->GetMixerUnit(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, mCurrentPlayBus, &desc, sizeof(desc)); 2796 mResetBusFormat = false; 2797 } 2798 } 2799 } 2800} 2801 2802// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2803void OALSource::UpdateBusRenderQuality () 2804{ 2805#if LOG_VERBOSE 2806 DebugMessageN1("OALSource::UpdateBusRenderQuality - OALSource = %ld\n", (long int) mSelfToken); 2807#endif 2808 2809 if (mCurrentPlayBus != kSourceNeedsBus) 2810 { 2811 mOwningContext->SetSourceDesiredRenderQualityOnBus(mRenderQuality, mCurrentPlayBus); 2812 } 2813} 2814 2815// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2816void OALSource::UpdateBusReverb () 2817{ 2818#if LOG_VERBOSE 2819 DebugMessageN1("OALSource::UpdateBusReverb - OALSource = %ld\n", (long int) mSelfToken); 2820#endif 2821 2822 if (mCurrentPlayBus != kSourceNeedsBus) 2823 { 2824 if (mOwningContext->GetReverbState() == 0) // either reverb is off or not available on this system 2825 return; 2826 2827 AudioUnitSetParameter(mOwningContext->GetMixerUnit(), 5 /*k3DMixerParam_ReverbBlend*/, kAudioUnitScope_Input, mCurrentPlayBus, mASAReverbSendLevel * 100.0, 0); 2828 } 2829} 2830 2831// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2832void OALSource::UpdateBusOcclusion () 2833{ 2834#if LOG_VERBOSE 2835 DebugMessageN1("OALSource::UpdateBusOcclusion - OALSource = %ld\n", (long int) mSelfToken); 2836#endif 2837 2838 if (mCurrentPlayBus != kSourceNeedsBus) 2839 { 2840 if (Get3DMixerVersion() < k3DMixerVersion_2_2) // the pre-2.2 3DMixer does not have occlusion 2841 return; 2842 2843 AudioUnitSetParameter(mOwningContext->GetMixerUnit(), 7 /*k3DMixerParam_OcclusionAttenuation*/, kAudioUnitScope_Input, mCurrentPlayBus, mASAOcclusion, 0); 2844 } 2845} 2846 2847// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2848void OALSource::UpdateBusObstruction () 2849{ 2850#if LOG_VERBOSE 2851 DebugMessageN1("OALSource::UpdateBusObstruction - OALSource = %ld\n", (long int) mSelfToken); 2852#endif 2853 2854 if (mCurrentPlayBus != kSourceNeedsBus) 2855 { 2856 if (Get3DMixerVersion() < k3DMixerVersion_2_2) // the pre-2.2 3DMixer does not have obstruction 2857 return; 2858 2859 AudioUnitSetParameter(mOwningContext->GetMixerUnit(), 8 /*k3DMixerParam_ObstructionAttenuation*/, kAudioUnitScope_Input, mCurrentPlayBus, mASAObstruction, 0); 2860 } 2861} 2862 2863// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2864#pragma mark ***** Render Proc Methods ***** 2865// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2866void OALSource::AddNotifyAndRenderProcs() 2867{ 2868#if LOG_VERBOSE 2869 DebugMessageN1("OALSource::AddNotifyAndRenderProcs - OALSource = %ld\n", (long int) mSelfToken); 2870#endif 2871 if (mCurrentPlayBus == kSourceNeedsBus) 2872 return; 2873 2874 OSStatus result = noErr; 2875 2876 mPlayCallback.inputProc = SourceInputProc; 2877 mPlayCallback.inputProcRefCon = this; 2878 result = AudioUnitSetProperty ( mRenderUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 2879 mRenderElement, &mPlayCallback, sizeof(mPlayCallback)); 2880 THROW_RESULT 2881} 2882 2883// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2884void OALSource::ReleaseNotifyAndRenderProcs() 2885{ 2886#if LOG_VERBOSE 2887 DebugMessageN1("OALSource::ReleaseNotifyAndRenderProcs - OALSource = %ld\n", (long int) mSelfToken); 2888#endif 2889 OSStatus result = noErr; 2890 2891 mPlayCallback.inputProc = 0; 2892 mPlayCallback.inputProcRefCon = 0; 2893 2894 result = AudioUnitSetProperty ( mRenderUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 2895 mRenderElement, &mPlayCallback, sizeof(mPlayCallback)); 2896 THROW_RESULT 2897} 2898 2899// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2900#if USE_AU_TRACER 2901static UInt32 tracerStart = 0xca3d0000; 2902static UInt32 tracerEnd = 0xca3d0004; 2903#include <sys/syscall.h> 2904#include <unistd.h> 2905#endif 2906OSStatus OALSource::SourceInputProc ( void *inRefCon, 2907 AudioUnitRenderActionFlags *inActionFlags, 2908 const AudioTimeStamp *inTimeStamp, 2909 UInt32 inBusNumber, 2910 UInt32 inNumberFrames, 2911 AudioBufferList *ioData) 2912{ 2913#if LOG_VERBOSE 2914 DebugMessage("OALSource::ReleaseNotifyAndRenderProcs"); 2915#endif 2916 OALSource* THIS = (OALSource*)inRefCon; 2917 2918 THIS->SetInUseFlag(); 2919 2920 if (THIS->mOutputSilence) 2921 *inActionFlags |= kAudioUnitRenderAction_OutputIsSilence; 2922 else 2923 *inActionFlags &= 0xEF; // the mask for the kAudioUnitRenderAction_OutputIsSilence bit 2924 2925#if USE_AU_TRACER 2926 syscall(180, tracerStart, inBusNumber, ioData->mNumberBuffers, 0, 0); 2927#endif 2928 2929 OSStatus result = noErr; 2930 if (Get3DMixerVersion() >= k3DMixerVersion_2_0) 2931 result = THIS->DoRender (ioData); // normal case 2932 else 2933 result = THIS->DoSRCRender (ioData); // pre 2.0 mixer case 2934 2935#if USE_AU_TRACER 2936 syscall(180, tracerEnd, inBusNumber, ioData->mNumberBuffers, 0, 0); 2937#endif 2938 2939 THIS->ClearInUseFlag(); 2940 2941 return (result); 2942} 2943 2944// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2945OSStatus OALSource::DoPreRender () 2946{ 2947#if LOG_VERBOSE 2948 DebugMessageN1("OALSource::DoPreRender - OALSource = %ld\n", (long int) mSelfToken); 2949#endif 2950 BufferInfo *bufferInfo = NULL; 2951 OSStatus err = noErr; 2952 2953 OALRenderLocker::RenderTryer tried(mRenderLocker); 2954 2955 if (!tried.Acquired()) 2956 return err; 2957 2958 bufferInfo = mBufferQueueActive->Get(mCurrentBufferIndex); 2959 if (bufferInfo == NULL) 2960 { 2961 // if there are no messages on the Q by now, then the source will be disconnected and reset in the PostRender Proc 2962 mQueueIsProcessed = true; 2963 err = -1; // there are no buffers 2964 } 2965 2966 return (err); 2967} 2968 2969// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2970OSStatus OALSource::ACComplexInputDataProc ( AudioConverterRef inAudioConverter, 2971 UInt32 *ioNumberDataPackets, 2972 AudioBufferList *ioData, 2973 AudioStreamPacketDescription **outDataPacketDescription, 2974 void* inUserData) 2975{ 2976#if LOG_VERBOSE 2977 DebugMessage("OALSource::DoPreRender - OALSource = %ld\n"); 2978#endif 2979 OSStatus err = noErr; 2980 OALSource* THIS = (OALSource*)inUserData; 2981 BufferInfo *bufferInfo = THIS->mBufferQueueActive->Get(THIS->mCurrentBufferIndex); 2982 UInt32 sourcePacketsLeft = 0; 2983 2984 if (bufferInfo == NULL) 2985 { 2986 ioData->mBuffers[0].mData = NULL; // return nothing 2987 ioData->mBuffers[0].mDataByteSize = 0; // return nothing 2988 *ioNumberDataPackets = 0; 2989 return -1; 2990 } 2991 2992 sourcePacketsLeft = (bufferInfo->mBuffer->GetDataSize() - bufferInfo->mOffset) / bufferInfo->mBuffer->GetBytesPerPacket(); 2993 2994 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2995 // BUFFER EMPTY: If the buffer is empty now, decide on returning an error based on what gets played next in the queue 2996 if (sourcePacketsLeft == 0) 2997 { 2998 bufferInfo->mProcessedState = kProcessed; // this buffer is done 2999 bufferInfo->mOffset = 0; // will be ready for the next time 3000 3001 THIS->mCurrentBufferIndex++; 3002 BufferInfo *nextBufferInfo = THIS->NextPlayableBufferInActiveQ(); 3003 3004 // see if there is a next buffer or if the queue is looping and should return to the start 3005 if ((nextBufferInfo != NULL) || (THIS->mLooping == true)) 3006 { 3007 // either we will loop back to the beginning or will use a new buffer 3008 if (nextBufferInfo == NULL) 3009 { 3010 THIS->LoopToBeginning(); 3011 } 3012 3013 err = OALSourceError_CallConverterAgain; 3014 } 3015 else 3016 { 3017 // looping is false and there are no more buffers so we are really out of data 3018 // return what we have and no error, the AC should then be reset in the RenderProc 3019 // and what ever data is in the AC should get returned 3020 THIS->mBufferQueueActive->SetBufferAsProcessed(THIS->mCurrentBufferIndex); 3021 THIS->mQueueIsProcessed = true; // we are done now, the Q is dry 3022 } 3023 3024 ioData->mBuffers[0].mData = NULL; // return nothing 3025 ioData->mBuffers[0].mDataByteSize = 0; // return nothing 3026 *ioNumberDataPackets = 0; 3027 } 3028 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3029 // BUFFER HAS DATA 3030 else 3031 { 3032 // return the entire request or the remainder of the buffer 3033 if (sourcePacketsLeft < *ioNumberDataPackets) 3034 *ioNumberDataPackets = sourcePacketsLeft; 3035 3036 ioData->mBuffers[0].mData = bufferInfo->mBuffer->GetDataPtr() + bufferInfo->mOffset; // point to the data we are providing 3037 ioData->mBuffers[0].mDataByteSize = *ioNumberDataPackets * bufferInfo->mBuffer->GetBytesPerPacket(); 3038 bufferInfo->mOffset += ioData->mBuffers[0].mDataByteSize; 3039 } 3040 3041 return (err); 3042} 3043 3044// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3045// Find the next playable buffer in the Active Buffer Q 3046BufferInfo* OALSource::NextPlayableBufferInActiveQ()// this method updates mCurrentBufferIndex as well 3047{ 3048#if LOG_VERBOSE 3049 DebugMessageN1("OALSource::NextPlayableBufferInActiveQ - OALSource = %ld\n", (long int) mSelfToken); 3050#endif 3051 // try and walk through the active buffer list 3052 BufferInfo* bufferInfo = NULL; 3053 bool done = false; 3054 3055 while (!done) 3056 { 3057 bufferInfo = mBufferQueueActive->Get(mCurrentBufferIndex); 3058 if (bufferInfo == NULL) 3059 { 3060 done = true; // there are no more valid buffers in this list 3061 } 3062 else if (bufferInfo->mBufferToken == AL_NONE) 3063 { 3064 // mark as processed 3065 bufferInfo->mProcessedState = kProcessed; // this buffer is done 3066 mCurrentBufferIndex++; 3067 } 3068 else if (bufferInfo->mBuffer->GetDataPtr() == NULL) 3069 { 3070 // mark as processed 3071 bufferInfo->mProcessedState = kProcessed; // this buffer is done 3072 mCurrentBufferIndex++; 3073 } 3074 else 3075 return bufferInfo; 3076 } 3077 return NULL; 3078} 3079 3080// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3081OSStatus OALSource::DoRender (AudioBufferList *ioData) 3082{ 3083 OSStatus err = noErr; 3084 UInt32 packetsRequestedFromRenderProc = ioData->mBuffers[0].mDataByteSize / sizeof(Float32); 3085 UInt32 packetsObtained = 0; 3086 UInt32 packetCount; 3087 struct { 3088 AudioBufferList abl; 3089 AudioBuffer buffer; 3090 }t; 3091 AudioBufferList * tBufferList = (AudioBufferList *) &t.abl; 3092 UInt32 dataByteSize = ioData->mBuffers[0].mDataByteSize; 3093 BufferInfo *bufferInfo = NULL; 3094 3095 tBufferList->mNumberBuffers = ioData->mNumberBuffers; 3096 // buffer 1 3097 tBufferList->mBuffers[0].mNumberChannels = ioData->mBuffers[0].mNumberChannels; 3098 tBufferList->mBuffers[0].mDataByteSize = ioData->mBuffers[0].mDataByteSize; 3099 tBufferList->mBuffers[0].mData = ioData->mBuffers[0].mData; 3100 if (tBufferList->mNumberBuffers > 1) 3101 { 3102 // buffer 2 3103 tBufferList->mBuffers[1].mNumberChannels = ioData->mBuffers[1].mNumberChannels; 3104 tBufferList->mBuffers[1].mDataByteSize = ioData->mBuffers[1].mDataByteSize; 3105 tBufferList->mBuffers[1].mData = ioData->mBuffers[1].mData; 3106 } 3107 3108 OALRenderLocker::RenderTryer tried(mRenderLocker); 3109 3110 if (!tried.Acquired()) 3111 { 3112 // source is being edited. can't render, so the ioData buffers must be cleared out 3113 for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) 3114 memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize); 3115 return noErr; 3116 } 3117 3118 //the bus attached to a source is reset whenever the source is played from an initial or stopped state 3119 if(mResetBus) 3120 { 3121 ResetMixerBus(); 3122 mResetBus = false; 3123 } 3124 3125 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3126 // 1st move past any AL_NONE Buffers 3127 3128 // update the Q lists before returning any data 3129 UpdateQueue(); 3130 3131 // if there are no more buffers in the active Q, go back to the beginning if in Loop mode, else pad zeroes and clean up in the PostRender proc 3132 bufferInfo = NextPlayableBufferInActiveQ(); // this method updates mCurrentBufferIndex as well 3133 3134 if ((bufferInfo == NULL) && (mLooping == true)) 3135 { 3136 // swap the list pointers now 3137 SwapBufferQueues(); 3138 bufferInfo = NextPlayableBufferInActiveQ(); // this method updates mCurrentBufferIndex as well 3139 if (bufferInfo == NULL) 3140 goto Finished; // there are no buffers 3141 3142 // Send the notification if necessary 3143 if (mSourceNotifications) 3144 mSourceNotifications->CallSourceNotifications(AL_QUEUE_HAS_LOOPED); 3145 } 3146 else if (bufferInfo == NULL) 3147 { 3148 // if there are no messages on the Q by now, then the source will be disconnected and reset in the PostRender Proc 3149 mQueueIsProcessed = true; 3150 // stop rendering, there is no more data 3151 goto Finished; // there are no buffers 3152 } 3153 3154 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3155 3156 ChangeChannelSettings(); 3157 3158 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3159 3160 // walk through as many buffers as needed to satisfy the request AudioConverterFillComplexBuffer will 3161 // get called each time the data format changes until enough packets have been obtained or the q is empty. 3162 while ((packetsObtained < packetsRequestedFromRenderProc) && (mQueueIsProcessed == false)) 3163 { 3164 BufferInfo *bufferInfo = NextPlayableBufferInActiveQ(); // this method updates mCurrentBufferIndex as well 3165 if (bufferInfo == NULL) 3166 { 3167 // just zero out the remainder of the buffer 3168 // if there are no messages on the Q by now, then the source will be disconnected and reset in the PostRender Proc 3169 mQueueIsProcessed = true; 3170 goto Finished; 3171 } 3172 3173 bufferInfo->mProcessedState = kInProgress; 3174 // buffer 1 3175 UInt32 byteCount = packetsObtained * sizeof(Float32); 3176 tBufferList->mBuffers[0].mDataByteSize = dataByteSize - byteCount; 3177 tBufferList->mBuffers[0].mData = (Byte *) ioData->mBuffers[0].mData + byteCount; 3178 if (tBufferList->mNumberBuffers > 1) 3179 { 3180 // buffer 2 3181 tBufferList->mBuffers[1].mDataByteSize = tBufferList->mBuffers[0].mDataByteSize; 3182 tBufferList->mBuffers[1].mData = (Byte *) ioData->mBuffers[1].mData + byteCount; 3183 } 3184 3185 if (bufferInfo->mBuffer->HasBeenConverted() == false) 3186 { 3187 // CONVERT THE BUFFER DATA 3188 AudioConverterRef converter = mACMap->Get(bufferInfo->mACToken); 3189 3190 packetCount = packetsRequestedFromRenderProc - packetsObtained; 3191 // if OALSourceError_CallConverterAgain is returned, there is nothing to do, just go around again and try and get the data 3192 err = AudioConverterFillComplexBuffer(converter, ACComplexInputDataProc, this, &packetCount, tBufferList, NULL); 3193 packetsObtained += packetCount; 3194 3195 if (mQueueIsProcessed == true) 3196 { 3197 AudioConverterReset(converter); 3198 } 3199 else if ((packetsObtained < packetsRequestedFromRenderProc) && (err == noErr)) 3200 { 3201 // we didn't get back what we asked for, but no error implies we have used up the data of this format 3202 // so reset this converter so it will be ready for the next time 3203 AudioConverterReset(converter); 3204 } 3205 } 3206 else 3207 { 3208 // Data has already been converted to the mixer's format, so just do a copy (should be mono only) 3209 UInt32 bytesRemaining = bufferInfo->mBuffer->GetDataSize() - bufferInfo->mOffset; 3210 UInt32 framesRemaining = bytesRemaining / sizeof(Float32); 3211 UInt32 bytesToCopy = 0; 3212 UInt32 framesToCopy = packetsRequestedFromRenderProc - packetsObtained; 3213 3214 if (framesRemaining < framesToCopy) 3215 framesToCopy = framesRemaining; 3216 3217 bytesToCopy = framesToCopy * sizeof(Float32); 3218 // we're in a mono only case, so only copy the first buffer 3219 memcpy(tBufferList->mBuffers->mData, bufferInfo->mBuffer->GetDataPtr() + bufferInfo->mOffset, bytesToCopy); 3220 bufferInfo->mOffset += bytesToCopy; 3221 packetsObtained += framesToCopy; 3222 3223 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3224 // this block of code is the same as that found in the fill proc - 3225 // it is for determining what to do when a buffer runs out of data 3226 3227 if (bufferInfo->mOffset == bufferInfo->mBuffer->GetDataSize()) 3228 { 3229 mCurrentBufferIndex++; 3230 // see if there is a next buffer or if the queue is looping and should return to the start 3231 BufferInfo *nextBufferInfo = NextPlayableBufferInActiveQ(); // this method updates mCurrentBufferIndex as well 3232 if ((nextBufferInfo != NULL) || (mLooping == true)) 3233 { 3234 if (nextBufferInfo == NULL) 3235 LoopToBeginning(); 3236 } 3237 else 3238 { 3239 // looping is false and there are no more buffers so we are really out of data 3240 // return what we have and no error 3241 // if there are no messages on the Q by now, then the source will be disconnected and reset in the PostRender Proc 3242 mQueueIsProcessed = true; // we are done now, the Q is dry 3243 } 3244 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3245 } 3246 } 3247 } 3248 3249Finished: 3250 3251 // if there wasn't enough data left, be sure to silence the end of the buffers 3252 if (packetsObtained < packetsRequestedFromRenderProc) 3253 { 3254 UInt32 byteCount = packetsObtained * sizeof(Float32); 3255 tBufferList->mBuffers[0].mDataByteSize = dataByteSize - byteCount; 3256 tBufferList->mBuffers[0].mData = (Byte *) ioData->mBuffers[0].mData + byteCount; 3257 memset(tBufferList->mBuffers[0].mData, 0, tBufferList->mBuffers[0].mDataByteSize); 3258 if (tBufferList->mNumberBuffers > 1) 3259 { 3260 tBufferList->mBuffers[1].mDataByteSize = tBufferList->mBuffers[0].mDataByteSize; 3261 tBufferList->mBuffers[1].mData = (Byte *) ioData->mBuffers[1].mData + byteCount; 3262 memset(tBufferList->mBuffers[1].mData, 0, tBufferList->mBuffers[1].mDataByteSize); 3263 } 3264 } 3265 3266 // ramp the buffer up or down to avoid any clicking 3267 if (mRampState == kRampDown) 3268 { 3269 // ramp down these samples to avoid any clicking - this is the last buffer before disconnecting in Post Render 3270 RampDown(ioData); 3271 mRampState = kRampingComplete; 3272 } 3273 else if (mRampState == kRampUp) 3274 { 3275 // this is the first buffer since resuming, so ramp these samples up 3276 RampUp(ioData); 3277 mRampState = kRampingComplete; 3278 } 3279 3280 return (noErr); 3281} 3282 3283// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3284OSStatus OALSource::DoPostRender () 3285{ 3286 bool renderProcsRemoved = false; 3287 PlaybackMessage* lastMessage; 3288 3289 OALRenderLocker::RenderTryer tried(mRenderLocker); 3290 3291 if (!tried.Acquired()) 3292 return noErr; 3293 3294 try { 3295 // all messages must be executed after the last buffer has been ramped down 3296 if (mRampState == kRampingComplete) 3297 { 3298#if LOG_MESSAGE_QUEUE 3299 DebugMessageN1("FLUSHING MESSAGE QUEUE START - OALSource = %ld", (long int) mSelfToken); 3300#endif 3301 PlaybackMessage *messages = mMessageQueue.pop_all_reversed(); 3302 while (messages != NULL) 3303 { 3304 switch (messages->mMessageID) 3305 { 3306 case kMQ_Stop: 3307#if LOG_MESSAGE_QUEUE 3308 DebugMessage(" MQ:kMQ_Stop"); 3309#endif 3310 if (mState != AL_STOPPED) 3311 { 3312 DisconnectFromBus(); 3313 SetPlaybackState(AL_STOPPED, true); 3314 UInt32 count = mBufferQueueActive->GetQueueSize(); 3315 3316 ClearActiveQueue(); 3317 if((count > 0) && mSourceNotifications) 3318 { 3319 // since there were still pending buffers in the active queue, moving to stopped means there was 3320 // a change in buffers processed, and need for notification 3321 mSourceNotifications->CallSourceNotifications(AL_BUFFERS_PROCESSED); 3322 } 3323 } 3324 break; 3325 3326 case kMQ_Retrigger: 3327#if LOG_MESSAGE_QUEUE 3328 DebugMessage(" MQ:kMQ_Retrigger"); 3329#endif 3330 LoopToBeginning(); 3331 if (renderProcsRemoved) 3332 { 3333 AddNotifyAndRenderProcs(); 3334 renderProcsRemoved = false; 3335 } 3336 3337 // in case it was also paused while processing these Q commands 3338 if (mState != AL_PLAYING) { 3339 SetPlaybackState(AL_PLAYING, true); 3340 } 3341 3342 break; 3343 3344 case kMQ_Rewind: 3345#if LOG_MESSAGE_QUEUE 3346 DebugMessage(" MQ:kMQ_Rewind"); 3347#endif 3348 if (mState != AL_INITIAL) 3349 { 3350 JoinBufferLists(); 3351 DisconnectFromBus(); 3352 SetPlaybackState(AL_INITIAL, true); 3353 mQueueIsProcessed = false; 3354 } 3355 break; 3356 3357 3358 case kMQ_ClearBuffersFromQueue: 3359#if LOG_MESSAGE_QUEUE 3360 DebugMessage(" MQ:kMQ_ClearBuffersFromQueue"); 3361#endif 3362 // when unqueue buffers is called while a source is in transition, the action must be deferred so the audio data can finish up 3363 PostRenderRemoveBuffersFromQueue(messages->mNumBuffers); 3364 break; 3365 3366 case kMQ_AddBuffersToQueue: 3367#if LOG_MESSAGE_QUEUE 3368 DebugMessageN1(" MQ:kMQ_AddBuffersToQueue: mBuffer->GetToken() = %ld", (long int) messages->mBuffer->GetToken()); 3369#endif 3370 //when queue buffers is called while a source is in transition, the action must be deferred so the audio data can finish up 3371 PostRenderAddBuffersToQueue(messages->mNumBuffers); 3372 mRampState = kNoRamping; 3373 break; 3374 3375 case kMQ_SetBuffer: 3376#if LOG_MESSAGE_QUEUE 3377 DebugMessageN1(" MQ:kMQ_SetBuffer: mAppendBuffer->GetToken() = %ld", (long int) messages->mAppendBuffer->GetToken()); 3378#endif 3379 PostRenderSetBuffer(messages->mBuffer->GetToken(), messages->mBuffer); 3380 SetPlaybackState(AL_STOPPED, true); 3381 break; 3382 3383 case kMQ_Play: 3384#if LOG_MESSAGE_QUEUE 3385 DebugMessage(" MQ:kMQ_Play"); 3386#endif 3387 Play(); 3388 break; 3389 3390 case kMQ_Pause: 3391#if LOG_MESSAGE_QUEUE 3392 DebugMessage(" MQ:kMQ_Pause"); 3393#endif 3394 SetPlaybackState(AL_PAUSED, true); 3395 ReleaseNotifyAndRenderProcs(); 3396 renderProcsRemoved = true; 3397 break; 3398 3399 case kMQ_Resume: 3400#if LOG_MESSAGE_QUEUE 3401 DebugMessage(" MQ:kMQ_Pause"); 3402#endif 3403 if (mRampState != kRampingComplete) 3404 mRampState = kRampUp; 3405 AddNotifyAndRenderProcs(); 3406 SetPlaybackState(AL_PLAYING,true); 3407 break; 3408 3409 case kMQ_SetFramePosition: 3410#if LOG_MESSAGE_QUEUE 3411 DebugMessage(" MQ:kMQ_SetFramePosition"); 3412#endif 3413 // Rewind the Buffer Q 3414 LoopToBeginning(); 3415 AdvanceQueueToFrameIndex(mPlaybackHeadPosition); 3416 mPlaybackHeadPosition = 0; 3417 break; 3418 3419 case kMQ_DeconstructionStop: 3420 { 3421#if LOG_MESSAGE_QUEUE 3422 DebugMessage(" MQ:kMQ_DeconstructionStop"); 3423#endif 3424 DisconnectFromBus(); 3425 SetPlaybackState(AL_STOPPED, true); 3426 FlushBufferQueue(); // release attachement to any buffers 3427 mSafeForDeletion = true; // now the CleanUp Sources method of the context can safely delete this object 3428 3429 // before returning, delete all remaining messages on the queue so they do not get leaked when the object is deconstructed 3430 ClearMessageQueue(); 3431 3432 goto Finished; // skip any remaining MQ messages 3433 } 3434 3435 default: 3436#if LOG_MESSAGE_QUEUE 3437 DebugMessage(" MQ:WARNING - UNIMPLEMENTED MESSAGE..."); 3438#endif 3439 break; 3440 } 3441 3442 lastMessage = messages; 3443 messages = messages->next(); 3444 delete (lastMessage); // made it, so now get rid of it 3445 } 3446 mRampState = kNoRamping; 3447#if LOG_MESSAGE_QUEUE 3448 DebugMessageN1("FLUSHING MESSAGE QUEUE END - OALSource = %ld", (long int) mSelfToken); 3449#endif 3450 } 3451 3452Finished: 3453 3454 if (mQueueIsProcessed) 3455 { 3456 // this means that the data ran out on it's own and we are not stopped as a result of a queued message 3457 DisconnectFromBus(); 3458 SetPlaybackState(AL_STOPPED,true); 3459 UInt32 count = mBufferQueueActive->GetQueueSize(); 3460 ClearActiveQueue(); 3461 3462 if((count > 0) && mSourceNotifications) 3463 { 3464 // since there were still pending buffers in the active queue, moving to stopped means there was 3465 // a change in buffers processed, and need for notification 3466 mSourceNotifications->CallSourceNotifications(AL_BUFFERS_PROCESSED); 3467 } 3468 } 3469 } 3470 catch(...){ 3471 DebugMessageN1("OALSource::DoPostRender:ERROR - OALSource = %ld", (long int) mSelfToken); 3472 } 3473 3474 return noErr; 3475} 3476 3477// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3478void OALSource::RampDown (AudioBufferList *ioData) 3479{ 3480#if LOG_VERBOSE 3481 DebugMessageN1("OALSource::RampDown - OALSource = %ld\n", (long int) mSelfToken); 3482#endif 3483 UInt32 sampleCount = (ioData->mBuffers[0].mDataByteSize / sizeof (Float32)); 3484 3485 Float32 slope = 1.0/sampleCount; 3486 for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) 3487 { 3488 Float32 scalar = 1.0; 3489 Float32 *sample = (Float32*) ioData->mBuffers[i].mData; 3490 for (UInt32 count = sampleCount; count > 0 ; count--) 3491 { 3492 *sample *= scalar; 3493 scalar -= slope; 3494 sample++; 3495 } 3496 } 3497} 3498 3499// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3500void OALSource::RampUp (AudioBufferList *ioData) 3501{ 3502#if LOG_VERBOSE 3503 DebugMessageN1("OALSource::RampUp - OALSource = %ld\n", (long int) mSelfToken); 3504#endif 3505 UInt32 sampleCount = (ioData->mBuffers[0].mDataByteSize / sizeof (Float32)); 3506 3507 Float32 slope = 1.0/sampleCount; 3508 for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) 3509 { 3510 Float32 scalar = 0.0; 3511 Float32 *sample = (Float32*) ioData->mBuffers[i].mData; 3512 for (UInt32 count = sampleCount; count > 0 ; count--) 3513 { 3514 *sample *= scalar; 3515 scalar += slope; 3516 sample++; 3517 } 3518 } 3519} 3520 3521// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3522// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3523// Support for Pre 2.0 3DMixer 3524// 3525// Pull the audio data by using DoRender(), and then Sample Rate Convert it to the mixer's 3526// output sample rate so the 1.3 mixer doesn't have to do any SRC. 3527// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3528// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3529 3530OSStatus OALSource::DoSRCRender( AudioBufferList *ioData ) 3531{ 3532#if LOG_VERBOSE 3533 DebugMessageN1("OALSource::DoSRCRender - OALSource = %ld\n", (long int) mSelfToken); 3534#endif 3535 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3536 BufferInfo *bufferInfo = NULL; 3537 3538 OALRenderLocker::RenderTryer tried(mRenderLocker); 3539 3540 if (!tried.Acquired()) 3541 { 3542 // source is being edited. can't render, so the ioData buffers must be cleared out 3543 for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) 3544 memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize); 3545 3546 return noErr; 3547 } 3548 3549 //the bus attached to a source is reset whenever the source is played from an initial or stopped state 3550 if(mResetBus) 3551 { 3552 ResetMixerBus(); 3553 mResetBus = false; 3554 } 3555 3556 // 1st move past any AL_NONE Buffers 3557 bufferInfo = NextPlayableBufferInActiveQ(); 3558 if (bufferInfo == NULL) 3559 { 3560 // if there are no messages on the Q by now, then the source will be disconnected and reset in the PostRender Proc 3561 mQueueIsProcessed = true; 3562 return -1; // there are no more buffers 3563 } 3564 3565 // update the Q lists before returning any data 3566 UpdateQueue(); 3567 3568 // if there are no more buffers in the active Q, go back to the beginning if in Loop mode, else pad zeroes and clean up in the PostRender proc 3569 bufferInfo = mBufferQueueActive->Get(mCurrentBufferIndex); 3570 if ((bufferInfo == NULL) && (mLooping == true)) 3571 { 3572 // swap the list pointers now 3573 SwapBufferQueues(); 3574 if (mSourceNotifications) 3575 mSourceNotifications->CallSourceNotifications(AL_QUEUE_HAS_LOOPED); 3576 } 3577 else if (bufferInfo == NULL) 3578 { 3579 // if there are no messages on the Q by now, then the source will be disconnected and reset in the PostRender Proc 3580 mQueueIsProcessed = true; 3581 // stop rendering, there is no more data 3582 return -1; // there are no buffers 3583 } 3584 3585 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3586 3587 ChangeChannelSettings(); 3588 3589 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3590 3591 double srcSampleRate; srcSampleRate = bufferInfo->mBuffer->GetSampleRate(); 3592 double dstSampleRate; dstSampleRate = mOwningContext->GetMixerRate(); 3593 double ratio; ratio = (srcSampleRate / dstSampleRate) * mPitch * mDopplerScaler; 3594 3595 int nchannels; nchannels = ioData->mNumberBuffers; 3596 3597 if (ratio == 1.0) 3598 { 3599 // no SRC necessary so just call the normal render proc and let it fill out the buffers 3600 return (DoRender(ioData)); 3601 } 3602 3603 // otherwise continue on to do dirty linear interpolation 3604 UInt32 inFramesToProcess; inFramesToProcess = ioData->mBuffers[0].mDataByteSize / sizeof(Float32); 3605 float readIndex; readIndex = mReadIndex; 3606 3607 int readUntilIndex; readUntilIndex = (int) (2.0 + readIndex + inFramesToProcess * ratio ); 3608 int framesToPull; framesToPull = readUntilIndex - 2; 3609 3610 if (framesToPull == 0) 3611 return -1; 3612 3613 // set the buffer size so DoRender will get the correct amount of frames 3614 mTempSourceStorage->mBuffers[0].mDataByteSize = framesToPull * sizeof(UInt32); 3615 mTempSourceStorage->mBuffers[1].mDataByteSize = framesToPull * sizeof(UInt32); 3616 3617 // if the size of the buffers are too small, reallocate them now 3618 if (mTempSourceStorageBufferSize < (framesToPull * sizeof(UInt32))) 3619 { 3620 if (mTempSourceStorage->mBuffers[0].mData != NULL) 3621 free(mTempSourceStorage->mBuffers[0].mData); 3622 3623 if (mTempSourceStorage->mBuffers[1].mData != NULL) 3624 free(mTempSourceStorage->mBuffers[1].mData); 3625 3626 mTempSourceStorageBufferSize = (framesToPull * sizeof(UInt32)); 3627 mTempSourceStorage->mBuffers[0].mData = malloc(mTempSourceStorageBufferSize); 3628 mTempSourceStorage->mBuffers[1].mData = malloc(mTempSourceStorageBufferSize); 3629 } 3630 3631 // get input source audio 3632 mTempSourceStorage->mNumberBuffers = ioData->mNumberBuffers; 3633 for (UInt32 i = 0; i < mTempSourceStorage->mNumberBuffers; i++) 3634 { 3635 mTempSourceStorage->mBuffers[i].mDataByteSize = framesToPull * sizeof(UInt32); 3636 } 3637 3638 OSStatus result; result = DoRender(mTempSourceStorage); 3639 if (result != noErr ) 3640 return result; // !!@ something bad happened (could return error code) 3641 3642 float *pullL; pullL = (float *) mTempSourceStorage->mBuffers[0].mData; 3643 float *pullR; pullR = nchannels > 1 ? (float *) mTempSourceStorage->mBuffers[1].mData: NULL; 3644 3645 // setup a small array of the previous two cached values, plus the first new input frame 3646 float tempL[4]; 3647 float tempR[4]; 3648 tempL[0] = mCachedInputL1; 3649 tempL[1] = mCachedInputL2; 3650 tempL[2] = pullL[0]; 3651 3652 if (pullR) 3653 { 3654 tempR[0] = mCachedInputR1; 3655 tempR[1] = mCachedInputR2; 3656 tempR[2] = pullR[0]; 3657 } 3658 3659 // in first loop start out getting source from this small array, then change sourceL/sourceR to point 3660 // to the buffers containing the new pulled input for the main loop 3661 float *sourceL; sourceL = tempL; 3662 float *sourceR; sourceR = tempR; 3663 if(!pullR) 3664 sourceR = NULL; 3665 3666 // keep around for next time 3667 mCachedInputL1 = pullL[framesToPull - 2]; 3668 mCachedInputL2 = pullL[framesToPull - 1]; 3669 3670 if(pullR) 3671 { 3672 mCachedInputR1 = pullR[framesToPull - 2]; 3673 mCachedInputR2 = pullR[framesToPull - 1]; 3674 } 3675 3676 // quick-and-dirty linear interpolation 3677 int n; n = inFramesToProcess; 3678 3679 float *destL; destL = (float *) ioData->mBuffers[0].mData; 3680 float *destR; destR = (float *) ioData->mBuffers[1].mData; 3681 3682 if (!sourceR) 3683 { 3684 // mono input 3685 3686 // generate output based on previous cached values 3687 while (readIndex < 2.0 && n > 0) 3688 { 3689 int iReadIndex = (int)readIndex; 3690 int iReadIndex2 = iReadIndex + 1; 3691 3692 float frac = readIndex - float(iReadIndex); 3693 3694 float s1 = sourceL[iReadIndex]; 3695 float s2 = sourceL[iReadIndex2]; 3696 float left = s1 + frac * (s2-s1); 3697 3698 *destL++ = left; 3699 3700 readIndex += ratio; 3701 3702 n--; 3703 } 3704 3705 // generate output based on new pulled input 3706 3707 readIndex -= 2.0; 3708 3709 sourceL = pullL; 3710 3711 while (n--) 3712 { 3713 int iReadIndex = (int)readIndex; 3714 int iReadIndex2 = iReadIndex + 1; 3715 3716 float frac = readIndex - float(iReadIndex); 3717 3718 float s1 = sourceL[iReadIndex]; 3719 float s2 = sourceL[iReadIndex2]; 3720 float left = s1 + frac * (s2-s1); 3721 3722 *destL++ = left; 3723 3724 readIndex += ratio; 3725 } 3726 3727 readIndex += 2.0; 3728 } 3729 else 3730 { 3731 // stereo input 3732 // generate output based on previous cached values 3733 while(readIndex < 2.0 && n > 0) 3734 { 3735 int iReadIndex = (int)readIndex; 3736 int iReadIndex2 = iReadIndex + 1; 3737 3738 float frac = readIndex - float(iReadIndex); 3739 3740 float s1 = sourceL[iReadIndex]; 3741 float s2 = sourceL[iReadIndex2]; 3742 float left = s1 + frac * (s2-s1); 3743 3744 float s3 = sourceR[iReadIndex]; 3745 float s4 = sourceR[iReadIndex2]; 3746 float right = s3 + frac * (s4-s3); 3747 3748 *destL++ = left; 3749 *destR++ = right; 3750 3751 readIndex += ratio; 3752 3753 n--; 3754 } 3755 3756 // generate output based on new pulled input 3757 3758 readIndex -= 2.0; 3759 3760 sourceL = pullL; 3761 sourceR = pullR; 3762 3763 while (n--) 3764 { 3765 int iReadIndex = (int)readIndex; 3766 int iReadIndex2 = iReadIndex + 1; 3767 3768 float frac = readIndex - float(iReadIndex); 3769 3770 float s1 = sourceL[iReadIndex]; 3771 float s2 = sourceL[iReadIndex2]; 3772 float left = s1 + frac * (s2-s1); 3773 3774 float s3 = sourceR[iReadIndex]; 3775 float s4 = sourceR[iReadIndex2]; 3776 float right = s3 + frac * (s4-s3); 3777 3778 *destL++ = left; 3779 *destR++ = right; 3780 3781 readIndex += ratio; 3782 } 3783 3784 readIndex += 2.0; 3785 3786 } 3787 3788 // normalize read index back to start of buffer for next time around... 3789 3790 readIndex -= float(framesToPull); 3791 3792 mReadIndex = readIndex; 3793 3794 return noErr; 3795} 3796 3797// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3798// CalculateDistanceAndAzimuth() support 3799// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3800 3801Float32 MAG(ALfloat *inVector) 3802{ 3803 return sqrt(inVector[0] * inVector[0] + inVector[1] * inVector[1] + inVector[2] * inVector[2]); 3804} 3805 3806// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3807void aluCrossproduct(ALfloat *inVector1,ALfloat *inVector2,ALfloat *outVector) 3808{ 3809 outVector[0]=(inVector1[1]*inVector2[2]-inVector1[2]*inVector2[1]); 3810 outVector[1]=(inVector1[2]*inVector2[0]-inVector1[0]*inVector2[2]); 3811 outVector[2]=(inVector1[0]*inVector2[1]-inVector1[1]*inVector2[0]); 3812} 3813 3814// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3815Float32 aluDotproduct(ALfloat *inVector1,ALfloat *inVector2) 3816{ 3817 return (inVector1[0]*inVector2[0]+inVector1[1]*inVector2[1]+inVector1[2]*inVector2[2]); 3818} 3819 3820// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3821void aluNormalize(ALfloat *inVector) 3822{ 3823 ALfloat length,inverse_length; 3824 3825 length=(ALfloat)sqrt(aluDotproduct(inVector,inVector)); 3826 if (length != 0) 3827 { 3828 inverse_length=(1.0f/length); 3829 inVector[0]*=inverse_length; 3830 inVector[1]*=inverse_length; 3831 inVector[2]*=inverse_length; 3832 } 3833} 3834 3835// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3836void aluMatrixVector(ALfloat *vector,ALfloat matrix[3][3]) 3837{ 3838 ALfloat result[3]; 3839 3840 result[0]=vector[0]*matrix[0][0]+vector[1]*matrix[1][0]+vector[2]*matrix[2][0]; 3841 result[1]=vector[0]*matrix[0][1]+vector[1]*matrix[1][1]+vector[2]*matrix[2][1]; 3842 result[2]=vector[0]*matrix[0][2]+vector[1]*matrix[1][2]+vector[2]*matrix[2][2]; 3843 memcpy(vector,result,sizeof(result)); 3844} 3845 3846// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3847inline bool IsZeroVector(Float32* inVector) 3848{ 3849 if ((inVector[0] == 0.0) && (inVector[1] == 0.0) && (inVector[2] == 0.0)) 3850 return true; 3851 else 3852 return false; 3853} 3854 3855// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3856#define LOG_SOURCE_CONES 0 3857bool OALSource::ConeAttenuation() 3858{ 3859#if LOG_VERBOSE 3860 DebugMessageN1("OALSource::ConeAttenuation - OALSource = %ld\n", (long int) mSelfToken); 3861#endif 3862 // determine if attenuation is needed at all 3863 if ( IsZeroVector(mConeDirection) || 3864 ((mConeInnerAngle == 360.0) && (mConeOuterAngle == 360.0)) ) 3865 { 3866 // Not Needed if: AL_Direction is 0,0,0, OR if (Inner and Outer Angle are both 360.0) 3867 if (mConeGainScaler != 1.0) 3868 { 3869 // make sure to reset the bus gain if the current cone scaler is not 1.0 and source cone scaling is no longer required 3870 mConeGainScaler = 1.0; 3871 return true; // let the caller know the bus gain needs resetting 3872 } 3873 return false; // no change has occurred to require a bus gain reset 3874 } 3875 3876 // Calculate the Source Cone Attenuation scaler 3877 3878 Float32 vsl[3]; // source to listener vector 3879 Float32 coneDirection[3]; 3880 Float32 angle; 3881 3882 mOwningContext->GetListenerPosition(&vsl[0], &vsl[1], &vsl[2]); 3883 3884 // calculate the source to listener vector 3885 vsl[0] -= mPosition[0]; 3886 vsl[1] -= mPosition[1]; 3887 vsl[2] -= mPosition[2]; 3888 aluNormalize(vsl); // Normalized source to listener vector 3889 3890 coneDirection[0] = mConeDirection[0]; 3891 coneDirection[1] = mConeDirection[1]; 3892 coneDirection[2] = mConeDirection[2]; 3893 aluNormalize(coneDirection); // Normalized cone direction vector 3894 3895 // calculate the angle between the cone direction vector and the source to listener vector 3896 angle = 180.0 * acos (aluDotproduct(vsl, coneDirection))/M_PI; // convert from radians to degrees 3897 3898 Float32 absAngle = fabs(angle); 3899 Float32 absInnerAngle = fabs(mConeInnerAngle)/2.0; // app provides the size of the entire inner angle 3900 Float32 absOuterAngle = fabs(mConeOuterAngle)/2.0; // app provides the size of the entire outer angle 3901 Float32 newScaler; 3902 3903 if (absAngle <= absInnerAngle) 3904 { 3905 // listener is within the inner cone angle, no attenuation required 3906 newScaler = 1.0; 3907#if LOG_SOURCE_CONES 3908 DebugMessage("ConeAttenuation - Listener is within the inner angle, no Attenuation required"); 3909#endif 3910 } 3911 else if (absAngle >= absOuterAngle) 3912 { 3913 // listener is outside the outer cone angle, sett attenuation to outer cone gain 3914#if LOG_SOURCE_CONES 3915 DebugMessageN1("ConeAttenuation - Listener is outside the outer angle, scaler equals the Outer Cone Gain = %f", mConeOuterGain); 3916#endif 3917 newScaler = mConeOuterGain; 3918 } 3919 else 3920 { 3921 // this source to listener vector is between the inner and outer cone angles so apply some gain scaling 3922 // db or linear? 3923 3924 // as you move from inner to outer, x goes from 0->1 3925 Float32 x = (absAngle - absInnerAngle ) / (absOuterAngle - absInnerAngle ); 3926 3927 newScaler = 1.0/* cone inner gain */ * (1.0 - x) + mConeOuterGain * x; 3928#if LOG_SOURCE_CONES 3929 DebugMessageN1("ConeAttenuation - Listener is between inner and outer angles, scaler equals = %f", newScaler); 3930#endif 3931 } 3932 3933 // there is no need to reset the bus gain if the scaler has not changed (a common scenario) 3934 // change is only necessaery when moving around within the transition zone or crossing between inner, transition and outer zones 3935 if (newScaler != mConeGainScaler) 3936 { 3937 mConeGainScaler = newScaler; 3938 return true; // let the caller know the bus gain needs resetting 3939 } 3940 3941 return false; // no change has occurred to require a bus gain reset 3942} 3943 3944// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3945void OALSource::CalculateDistanceAndAzimuth(Float32 *outDistance, Float32 *outAzimuth, Float32 *outElevation, Float32 *outDopplerShift) 3946{ 3947#if LOG_VERBOSE 3948 DebugMessageN1("OALSource::CalculateDistanceAndAzimuth - OALSource = %ld\n", (long int) mSelfToken); 3949#endif 3950 3951 Float32 ListenerOrientation[6], 3952 ListenerPosition[3], 3953 ListenerVelocity[3], 3954 Angle = 0.0, 3955 Distance = 2.0, 3956 Elevation = 0.0, 3957 Distance_squared = 4.0, 3958 front_back, 3959 SourceToListener[3], 3960 ProjectedSource[3], 3961 UpProjection, 3962 Look_Norm[3], 3963 RightEarVector[3], // previously named U 3964 Up[3], 3965 tPosition[3], 3966 dopplerShift = 1.0; // default at No shift 3967 3968 *outDopplerShift = dopplerShift; // initialize 3969 3970 SourceToListener[0]=0; // initialize 3971 SourceToListener[1]=0; // initialize 3972 SourceToListener[2]=0; // initialize 3973 Up[0]=0; // initialize 3974 Up[1]=0; // initialize 3975 Up[2]=0; // initialize 3976 3977 tPosition[0] = mPosition[0]; 3978 tPosition[1] = mPosition[1]; 3979 tPosition[2] = mPosition[2]; 3980 3981 //Get listener properties 3982 mOwningContext->GetListenerPosition(&ListenerPosition[0], &ListenerPosition[1], &ListenerPosition[2]); 3983 mOwningContext->GetListenerVelocity(&ListenerVelocity[0], &ListenerVelocity[1], &ListenerVelocity[2]); 3984 mOwningContext->GetListenerOrientation(&ListenerOrientation[0], &ListenerOrientation[1], &ListenerOrientation[2], 3985 &ListenerOrientation[3], &ListenerOrientation[4], &ListenerOrientation[5]); 3986 3987 // Get buffer properties 3988 BufferInfo *bufferInfo = mBufferQueueActive->Get(mCurrentBufferIndex); 3989 if (bufferInfo == NULL) 3990 { 3991 // Not sure if this should be the error case 3992 *outDistance = 0.0; 3993 *outAzimuth = 0.0; 3994 *outElevation = 0.0; 3995 return; // there are no buffers 3996 } 3997 3998 // Only apply 3D calculations for mono buffers 3999 if (bufferInfo->mBuffer->GetNumberChannels() == 1) 4000 { 4001 //1. Translate Listener to origin (convert to head relative) 4002 if (mSourceRelative == AL_FALSE) 4003 { 4004 tPosition[0] -= ListenerPosition[0]; 4005 tPosition[1] -= ListenerPosition[1]; 4006 tPosition[2] -= ListenerPosition[2]; 4007 } 4008 //2. Align coordinate system axes 4009 aluCrossproduct(&ListenerOrientation[0],&ListenerOrientation[3],RightEarVector); // Right-ear-vector 4010 aluNormalize(RightEarVector); // Normalized Right-ear-vector 4011 Look_Norm[0] = ListenerOrientation[0]; 4012 Look_Norm[1] = ListenerOrientation[1]; 4013 Look_Norm[2] = ListenerOrientation[2]; 4014 aluNormalize(Look_Norm); 4015 4016 //3. Calculate distance attenuation 4017 Distance_squared = aluDotproduct(tPosition,tPosition); 4018 Distance = sqrt(Distance_squared); 4019 4020 Angle = 0.0f; 4021 4022 //4. Determine Angle of source relative to listener 4023 if(Distance>0.0f){ 4024 SourceToListener[0]=tPosition[0]; 4025 SourceToListener[1]=tPosition[1]; 4026 SourceToListener[2]=tPosition[2]; 4027 // Note: SourceToListener doesn't need to be normalized here. 4028 // Probably better to move this next line into the Doppler 4029 // calculation code so that it can be optimized away if 4030 // DopplerFactor is 0. 4031 aluNormalize(SourceToListener); 4032 4033 aluCrossproduct(RightEarVector, Look_Norm, Up); 4034 UpProjection = aluDotproduct(SourceToListener,Up); 4035 ProjectedSource[0] = SourceToListener[0] - UpProjection*Up[0]; 4036 ProjectedSource[1] = SourceToListener[1] - UpProjection*Up[1]; 4037 ProjectedSource[2] = SourceToListener[2] - UpProjection*Up[2]; 4038 aluNormalize(ProjectedSource); 4039 4040 Angle = 180.0 * acos (aluDotproduct(ProjectedSource, RightEarVector))/M_PI; 4041 zapBadness(Angle); // remove potential NANs 4042 4043 //is the source infront of the listener or behind? 4044 front_back = aluDotproduct(ProjectedSource,Look_Norm); 4045 if(front_back<0.0f) 4046 Angle = 360.0f - Angle; 4047 4048 //translate from cartesian angle to 3d mixer angle 4049 if((Angle>=0.0f)&&(Angle<=270.0f)) 4050 Angle = 90.0f - Angle; 4051 else 4052 Angle = 450.0f - Angle; 4053 } 4054 4055 //5. Calculate elevation 4056 Elevation = 90.0 - 180.0 * acos( aluDotproduct(SourceToListener, Up) )/ 3.141592654f; 4057 zapBadness(Elevation); // remove potential NANs 4058 4059 if(SourceToListener[0]==0.0 && SourceToListener[1]==0.0 && SourceToListener[2]==0.0 ) 4060 Elevation = 0.0; 4061 4062 if (Elevation > 90.0) 4063 Elevation = 180.0 - Elevation; 4064 if (Elevation < -90.0) 4065 Elevation = -180.0 - Elevation; 4066 4067 //6. Calculate doppler 4068 Float32 dopplerFactor = mOwningContext->GetDopplerFactor(); 4069 if (dopplerFactor > 0.0) 4070 { 4071 Float32 speedOfSound = mOwningContext->GetSpeedOfSound(); 4072 4073 Float32 SourceVelocity[3]; 4074 GetVelocity (SourceVelocity[0], SourceVelocity[1], SourceVelocity[2]); 4075 4076 // don't do all these calculations if the sourec and listener have zero velocity 4077 bool SourceHasVelocity = !IsZeroVector(SourceVelocity); 4078 bool ListenerHasVelocity = !IsZeroVector(ListenerVelocity); 4079 if (SourceHasVelocity || ListenerHasVelocity) 4080 { 4081 Float32 NUvls = (aluDotproduct(SourceToListener, ListenerVelocity))/MAG(SourceToListener); 4082 Float32 NUvss = (aluDotproduct(SourceToListener, SourceVelocity))/MAG(SourceToListener); 4083 4084 NUvls = -NUvls; // COMMENT HERE PLEASE 4085 NUvss = -NUvss; // COMMENT HERE PLEASE 4086 4087 NUvls = fmin(NUvls, speedOfSound/dopplerFactor); 4088 NUvss = fmin(NUvss, speedOfSound/dopplerFactor); 4089 4090 dopplerShift = ( (speedOfSound - dopplerFactor * NUvls) / (speedOfSound - dopplerFactor * NUvss) ); 4091 zapBadnessForDopplerShift(dopplerShift); // remove potential NANs 4092 4093 // limit the pitch shifting to 4 octaves up and 3 octaves down 4094 if (dopplerShift > 16.0) 4095 dopplerShift = 16.0; 4096 else if(dopplerShift < 0.125) 4097 dopplerShift = 0.125; 4098 4099 #if LOG_DOPPLER 4100 DebugMessageN1("CalculateDistanceAndAzimuth: dopplerShift after scaling = %f\n", dopplerShift); 4101 #endif 4102 4103 *outDopplerShift = dopplerShift; 4104 } 4105 } 4106 } 4107 else 4108 { 4109 Angle=0.0; 4110 Distance=0.0; 4111 } 4112 4113 if ((Get3DMixerVersion() < k3DMixerVersion_2_0) && (mReferenceDistance > 1.0)) 4114 { 4115 // the pre 2.0 mixer does not have the DistanceParam property so to compensate, 4116 // set the DistanceAtten property correctly for refDist, maxDist, and rolloff, 4117 // and then scale our calculated distance to a reference distance of 1.0 before passing to the mixer 4118 Distance = Distance/mReferenceDistance; 4119 if (Distance > mMaxDistance/mReferenceDistance) 4120 Distance = mMaxDistance/mReferenceDistance; // clamp the distance to the max distance 4121 } 4122 4123 *outDistance = Distance; 4124 *outAzimuth = Angle; 4125 *outElevation = Elevation; 4126} 4127 4128// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4129// Apple Environmental Audio (ASA) Extension 4130// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4131#pragma mark ***** ASA Extension Methods ***** 4132void OALSource::SetReverbSendLevel(Float32 inReverbLevel) 4133{ 4134#if LOG_VERBOSE 4135 DebugMessageN2("OALSource::SetReverbSendLevel - OALSource:inReverbLevel = %ld:%f\n", (long int) mSelfToken, inReverbLevel); 4136#endif 4137#if LOG_GRAPH_AND_MIXER_CHANGES 4138 DebugMessageN2("OALSource::SetReverbSendLevel - OALSource:inReverbLevel = %ld:%f\n", (long int) mSelfToken, inReverbLevel); 4139#endif 4140 4141 if (inReverbLevel < 0.0f || inReverbLevel > 1.0f) 4142 throw (OSStatus)AL_INVALID_VALUE; // must be within 0.0-1.0 range 4143 4144 // don't allow synchronous source manipulation 4145 CAGuard::Locker sourceLock(mSourceLock); 4146 4147 if (inReverbLevel == mASAReverbSendLevel) 4148 return; // nothing to do 4149 4150 4151 mASAReverbSendLevel = inReverbLevel; 4152 UpdateBusReverb(); 4153} 4154 4155// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4156void OALSource::SetOcclusion(Float32 inOcclusion) 4157{ 4158#if LOG_VERBOSE 4159 DebugMessageN2("OALSource::SetOcclusion - OALSource:inOcclusion = %ld:%f\n", (long int) mSelfToken, inOcclusion); 4160#endif 4161#if LOG_GRAPH_AND_MIXER_CHANGES 4162 DebugMessageN2("OALSource::SetOcclusion - OALSource:inOcclusion = %ld:%f\n", (long int) mSelfToken, inOcclusion); 4163#endif 4164 if (inOcclusion < -100.0f || inOcclusion > 0.0f) 4165 throw (OSStatus)AL_INVALID_VALUE; // must be within -100.0 - 0.0 range 4166 4167 if (inOcclusion == mASAOcclusion) 4168 return; // nothing to do 4169 4170 4171 mASAOcclusion = inOcclusion; 4172 4173 UpdateBusOcclusion(); 4174} 4175 4176// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4177void OALSource::SetObstruction(Float32 inObstruction) 4178{ 4179#if LOG_VERBOSE 4180 DebugMessageN2("OALSource::SetObstruction - OALSource:inObstruction = %ld:%f\n", (long int) mSelfToken, inObstruction); 4181#endif 4182#if LOG_GRAPH_AND_MIXER_CHANGES 4183 DebugMessageN2("OALSource::SetObstruction - OALSource:inObstruction = %ld:%f\n", (long int) mSelfToken, inObstruction); 4184#endif 4185 if (inObstruction < -100.0f || inObstruction > 0.0f) 4186 throw (OSStatus)AL_INVALID_VALUE; // must be within -100.0 - 0.0 range 4187 4188 // don't allow synchronous source manipulation 4189 CAGuard::Locker sourceLock(mSourceLock); 4190 4191 if (inObstruction == mASAObstruction) 4192 return; // nothing to do 4193 4194 mASAObstruction = inObstruction; 4195 4196 UpdateBusObstruction(); 4197} 4198 4199// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4200void OALSource::SetRogerBeepEnable(Boolean inEnable) 4201{ 4202#if LOG_VERBOSE 4203 DebugMessageN2("OALSource::SetRogerBeepEnable - OALSource:inEnable = %ld:%d\n", (long int) mSelfToken, inEnable); 4204#endif 4205#if LOG_GRAPH_AND_MIXER_CHANGES 4206 DebugMessageN2("OALSource::SetRogerBeepEnable - OALSource:inEnable = %ld:%d\n", (long int) mSelfToken, inEnable); 4207#endif 4208 4209 // don't allow synchronous source manipulation 4210 CAGuard::Locker sourceLock(mSourceLock); 4211 4212 // Enable cannot be set during playback 4213 if ((mState == AL_PLAYING) || (mState == AL_PAUSED)) 4214 throw (OSStatus) AL_INVALID_OPERATION; 4215 4216 if (inEnable == mASARogerBeepEnable) 4217 return; // nothing to do 4218 4219 mASARogerBeepEnable = inEnable; 4220 4221 if (mASARogerBeepEnable) 4222 { 4223 SetupRogerBeepAU(); 4224 4225 // first get the initial values 4226 UInt32 propSize = sizeof(UInt32); 4227 UInt32 bypassValue; 4228 if(AudioUnitGetProperty(mRogerBeepAU, kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0, &bypassValue, &propSize) == noErr) 4229 mASARogerBeepOn = bypassValue ? 0 : 1; 4230 4231 Float32 paramValue; 4232 if(AudioUnitGetParameter(mRogerBeepAU, 6/*kRogerBeepParam_RogerGain*/, kAudioUnitScope_Global, 0, ¶mValue) == noErr) 4233 mASARogerBeepGain = paramValue; 4234 4235 if(AudioUnitGetParameter(mRogerBeepAU, 4/*kRogerBeepParam_Sensitivity*/, kAudioUnitScope_Global, 0, ¶mValue) == noErr) 4236 mASARogerBeepSensitivity = (UInt32)paramValue; 4237 4238 if(AudioUnitGetParameter(mRogerBeepAU, 5 /*kRogerBeepParam_RogerType*/, kAudioUnitScope_Global, 0, ¶mValue) == noErr) 4239 mASARogerBeepType = (UInt32)paramValue; 4240 4241 //now default the unit off 4242 SetRogerBeepOn(false); 4243 } 4244 4245 else 4246 { 4247 if(mRogerBeepNode) 4248 AUGraphRemoveNode(mOwningContext->GetGraph(), mRogerBeepNode); 4249 mRogerBeepNode = 0; 4250 mRogerBeepAU = 0; 4251 } 4252} 4253 4254// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4255void OALSource::SetRogerBeepOn(Boolean inOn) 4256{ 4257#if LOG_VERBOSE 4258 DebugMessageN2("OALSource::SetRogerBeepOn - OALSource:inOn = %ld:%d\n", (long int) mSelfToken, inOn); 4259#endif 4260#if LOG_GRAPH_AND_MIXER_CHANGES 4261 DebugMessageN2("OALSource::SetRogerBeepOn - OALSource:inOn = %ld:%d\n", (long int) mSelfToken, inOn); 4262#endif 4263 4264 // don't allow synchronous source manipulation 4265 CAGuard::Locker sourceLock(mSourceLock); 4266 4267 if (inOn == mASARogerBeepOn) 4268 return; // nothing to do 4269 4270 mASARogerBeepOn = inOn; 4271 4272 UInt32 bypassValue = mASARogerBeepOn ? 0 : 1; 4273 AudioUnitSetProperty(mRogerBeepAU, kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0, &bypassValue, sizeof(UInt32)); 4274} 4275 4276// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4277void OALSource::SetRogerBeepGain(Float32 inGain) 4278{ 4279#if LOG_VERBOSE 4280 DebugMessageN2("OALSource::SetRogerBeepGain - OALSource:inGain = %ld:%f\n", (long int) mSelfToken, inGain); 4281#endif 4282#if LOG_GRAPH_AND_MIXER_CHANGES 4283 DebugMessageN2("OALSource::SetRogerBeepGain - OALSource:inGain = %ld:%f\n", (long int) mSelfToken, inGain); 4284#endif 4285 4286 if (inGain < -100.0f || inGain > 20.0f) 4287 throw (OSStatus)AL_INVALID_VALUE; // must be within -100.0 - 20.0 range 4288 4289 if(!mASARogerBeepEnable) 4290 throw (OSStatus) AL_INVALID_OPERATION; 4291 4292 if (inGain == mASARogerBeepGain) 4293 return; // nothing to do 4294 4295 mASARogerBeepGain = inGain; 4296 4297 AudioUnitSetParameter(mRogerBeepAU, 6/*kRogerBeepParam_RogerGain*/, kAudioUnitScope_Global, 0, inGain, 0 ); 4298} 4299 4300// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4301void OALSource::SetRogerBeepSensitivity(SInt32 inSensitivity) 4302{ 4303#if LOG_VERBOSE 4304 DebugMessageN2("OALSource::SetRogerBeepSensitivity - OALSource:inSensitivity = %ld:%d\n", (long int) mSelfToken, inSensitivity); 4305#endif 4306#if LOG_GRAPH_AND_MIXER_CHANGES 4307 DebugMessageN2("OALSource::SetRogerBeepSensitivity - OALSource:inSensitivity = %ld:%d\n", (long int) mSelfToken, inSensitivity); 4308#endif 4309 if (inSensitivity < 0 || inSensitivity > 2) 4310 throw (OSStatus)AL_INVALID_VALUE; // must be within 0 - 2 range 4311 4312 // don't allow synchronous source manipulation 4313 CAGuard::Locker sourceLock(mSourceLock); 4314 4315 if(!mASARogerBeepEnable) 4316 throw (OSStatus)AL_INVALID_OPERATION; 4317 4318 if (inSensitivity == mASARogerBeepSensitivity) 4319 return; // nothing to do 4320 4321 mASARogerBeepSensitivity = inSensitivity; 4322 4323 AudioUnitSetParameter(mRogerBeepAU, 4/*kRogerBeepParam_Sensitivity*/, kAudioUnitScope_Global, 0, inSensitivity, 0 ); 4324} 4325 4326// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4327void OALSource::SetRogerBeepType(SInt32 inType) 4328{ 4329#if LOG_VERBOSE 4330 DebugMessageN2("OALSource::SetRogerBeepType - OALSource:inType = %ld:%d\n", (long int) mSelfToken, inType); 4331#endif 4332#if LOG_GRAPH_AND_MIXER_CHANGES 4333 DebugMessageN2("OALSource::SetRogerBeepType - OALSource:inType = %ld:%d\n", (long int) mSelfToken, inType); 4334#endif 4335 4336 if (inType < 0 || inType > 3) 4337 throw (OSStatus)AL_INVALID_VALUE; // must be within 0 - 3 range 4338 4339 // don't allow synchronous source manipulation 4340 CAGuard::Locker sourceLock(mSourceLock); 4341 4342 if(!mASARogerBeepEnable) 4343 throw (OSStatus)AL_INVALID_OPERATION; 4344 4345 if (inType == mASARogerBeepType) 4346 return; // nothing to do 4347 4348 mASARogerBeepType = inType; 4349 4350 AudioUnitSetParameter(mRogerBeepAU, 5 /*kRogerBeepParam_RogerType*/, kAudioUnitScope_Global, 0, inType, 0 ); 4351} 4352 4353// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4354void OALSource::SetRogerBeepPreset(FSRef* inRef) 4355{ 4356#if LOG_VERBOSE 4357 DebugMessageN2("OALSource::SetRogerBeepPreset - OALSource:inRef = %ld:%p\n", (long int) mSelfToken, inRef); 4358#endif 4359 // don't allow synchronous source manipulation 4360 CAGuard::Locker sourceLock(mSourceLock); 4361 4362 try { 4363 Boolean status; 4364 SInt32 result = 0; 4365 CFURLRef fileURL = CFURLCreateFromFSRef (kCFAllocatorDefault, inRef); 4366 if (fileURL) 4367 { 4368 // Read the XML file. 4369 CFDataRef resourceData = NULL; 4370 4371 status = CFURLCreateDataAndPropertiesFromResource (kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &result); 4372 CFRelease (fileURL); // no longer needed 4373 4374 if (status == false || result) 4375 throw (OSStatus) -1; 4376 else 4377 { 4378 CFStringRef errString = NULL; 4379 CFPropertyListRef theData = NULL; 4380 theData = CFPropertyListCreateFromXMLData (kCFAllocatorDefault, resourceData, kCFPropertyListImmutable, &errString); 4381 CFRelease (resourceData); 4382 if (errString) 4383 CFRelease (errString); 4384 4385 if (theData == NULL || errString) 4386 { 4387 if (theData) 4388 CFRelease (theData); 4389 throw (OSStatus) -1; 4390 } 4391 else 4392 { 4393 result = AudioUnitSetProperty(mRogerBeepAU, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &theData, sizeof(theData) ); 4394 CFRelease (theData); 4395 THROW_RESULT 4396 } 4397 } 4398 } 4399 else 4400 throw (OSStatus) -1; 4401 } 4402 catch (OSStatus result) { 4403 throw result; 4404 } 4405 catch (...) { 4406 throw (OSStatus) -1; 4407 } 4408 4409 return; 4410 4411} 4412 4413// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4414void OALSource::SetDistortionEnable(Boolean inEnable) 4415{ 4416#if LOG_VERBOSE 4417 DebugMessageN2("OALSource::SetDistortionEnable - OALSource:inEnable = %ld:%d\n", (long int) mSelfToken, inEnable); 4418#endif 4419#if LOG_GRAPH_AND_MIXER_CHANGES 4420 DebugMessageN2("OALSource::SetDistortionEnable - OALSource:inEnable = %ld:%d\n", (long int) mSelfToken, inEnable); 4421#endif 4422 4423 // don't allow synchronous source manipulation 4424 CAGuard::Locker sourceLock(mSourceLock); 4425 4426 if (inEnable == mASADistortionEnable) 4427 return; // nothing to do 4428 4429 mASADistortionEnable = inEnable; 4430 if (mASADistortionEnable) 4431 { 4432 SetupDistortionAU(); 4433 // first get the default values 4434 UInt32 propSize = sizeof(UInt32); 4435 4436 UInt32 bypassValue; 4437 if(AudioUnitGetProperty(mDistortionAU, kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0, &bypassValue, &propSize) == noErr) 4438 mASADistortionOn = bypassValue ? 0 : 1; 4439 4440 Float32 paramValue; 4441 if(AudioUnitGetParameter(mDistortionAU, 15/*kDistortionParam_FinalMix*/, kAudioUnitScope_Global, 0, ¶mValue) == noErr) 4442 mASADistortionMix = paramValue; 4443 4444 AUPreset distortionType; 4445 propSize = sizeof(distortionType); 4446 if(AudioUnitGetProperty(mDistortionAU, kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0, &distortionType, &propSize) == noErr) 4447 { 4448 if(distortionType.presetName) CFRelease(distortionType.presetName); 4449 mASADistortionType = distortionType.presetNumber; 4450 } 4451 4452 // now default the unit off 4453 SetDistortionOn(false); 4454 } 4455 4456 else 4457 { 4458 if(mDistortionNode) 4459 AUGraphRemoveNode(mOwningContext->GetGraph(), mDistortionNode); 4460 mDistortionNode = 0; 4461 mDistortionAU = 0; 4462 } 4463} 4464 4465// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4466void OALSource::SetDistortionOn(Boolean inOn) 4467{ 4468#if LOG_VERBOSE 4469 DebugMessageN2("OALSource::SetDistortionOn - OALSource:inOn = %ld:%d\n", (long int) mSelfToken, inOn); 4470#endif 4471#if LOG_GRAPH_AND_MIXER_CHANGES 4472 DebugMessageN2("OALSource::SetDistortionOn - OALSource:inOn = %ld:%d\n", (long int) mSelfToken, inOn); 4473#endif 4474 4475 // don't allow synchronous source manipulation 4476 CAGuard::Locker sourceLock(mSourceLock); 4477 4478 if (inOn == mASADistortionOn) 4479 return; // nothing to do 4480 4481 mASADistortionOn = inOn; 4482 4483 UInt32 bypassValue = mASADistortionOn ? 0 : 1; 4484 4485 AudioUnitSetProperty(mDistortionAU, kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0, &bypassValue, sizeof(UInt32)); 4486} 4487 4488// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4489void OALSource::SetDistortionMix(Float32 inMix) 4490{ 4491#if LOG_VERBOSE 4492 DebugMessageN2("OALSource::SetDistortionMix - OALSource:inMix = %ld:%f\n", (long int) mSelfToken, inMix); 4493#endif 4494#if LOG_GRAPH_AND_MIXER_CHANGES 4495 DebugMessageN2("OALSource::SetDistortionMix - OALSource:inMix = %ld:%f\n", (long int) mSelfToken, inMix); 4496#endif 4497 4498 if (inMix < 0.0f || inMix > 100.0f) 4499 throw (OSStatus)AL_INVALID_VALUE; // must be within 0.0 - 100.0 range 4500 4501 // don't allow synchronous source manipulation 4502 CAGuard::Locker sourceLock(mSourceLock); 4503 4504 if(!mASADistortionEnable) 4505 throw (OSStatus)AL_INVALID_OPERATION; 4506 4507 if (inMix == mASADistortionMix) 4508 return; // nothing to do 4509 4510 mASADistortionMix = inMix; 4511 AudioUnitSetParameter(mDistortionAU, 15/*kDistortionParam_FinalMix*/, kAudioUnitScope_Global, 0, inMix, 0 ); 4512} 4513 4514// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4515void OALSource::SetDistortionType(SInt32 inType) 4516{ 4517#if LOG_VERBOSE 4518 DebugMessageN2("OALSource::SetDistortionMix - OALSource:inType = %ld:%df\n", (long int) mSelfToken, inType); 4519#endif 4520#if LOG_GRAPH_AND_MIXER_CHANGES 4521 DebugMessageN2("OALSource::SetDistortionType: OALSource: = %u : inType %d\n", mSelfToken, inType ); 4522#endif 4523 4524 // don't allow synchronous source manipulation 4525 CAGuard::Locker sourceLock(mSourceLock); 4526 4527 if(!mASADistortionEnable) 4528 throw (OSStatus)AL_INVALID_OPERATION; 4529 4530 if (inType == mASADistortionType) 4531 return; // nothing to do 4532 4533 mASADistortionType = inType; 4534 AUPreset distortionType; 4535 distortionType.presetNumber = mASADistortionType; 4536 distortionType.presetName = NULL; 4537 4538 AudioUnitSetProperty(mDistortionAU, kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0, &distortionType, sizeof(AUPreset)); 4539} 4540 4541// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4542void OALSource::SetDistortionPreset(FSRef* inRef) 4543{ 4544#if LOG_VERBOSE 4545 DebugMessageN2("OALSource::SetDistortionPreset - OALSource:inRef = %ld:%p\n", (long int) mSelfToken, inRef); 4546#endif 4547 // don't allow synchronous source manipulation 4548 CAGuard::Locker sourceLock(mSourceLock); 4549 4550 try { 4551 Boolean status; 4552 SInt32 result = 0; 4553 CFURLRef fileURL = CFURLCreateFromFSRef (kCFAllocatorDefault, inRef); 4554 if (fileURL) 4555 { 4556 // Read the XML file. 4557 CFDataRef resourceData = NULL; 4558 4559 status = CFURLCreateDataAndPropertiesFromResource (kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &result); 4560 CFRelease (fileURL); // no longer needed 4561 4562 if (status == false || result) 4563 throw (OSStatus) -1; 4564 else 4565 { 4566 CFStringRef errString = NULL; 4567 CFPropertyListRef theData = NULL; 4568 theData = CFPropertyListCreateFromXMLData (kCFAllocatorDefault, resourceData, kCFPropertyListImmutable, &errString); 4569 CFRelease (resourceData); 4570 if (errString) 4571 CFRelease (errString); 4572 4573 if (theData == NULL || errString) 4574 { 4575 if (theData) 4576 CFRelease (theData); 4577 throw (OSStatus) -1; 4578 } 4579 else 4580 { 4581 result = AudioUnitSetProperty(mDistortionAU, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &theData, sizeof(theData) ); 4582 CFRelease (theData); 4583 THROW_RESULT 4584 } 4585 } 4586 } 4587 else 4588 throw (OSStatus) -1; 4589 } 4590 catch (OSStatus result) { 4591 throw result; 4592 } 4593 catch (...) { 4594 throw (OSStatus) -1; 4595 } 4596 4597 return; 4598 4599} 4600 4601// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4602// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4603#pragma mark _____BufferQueue_____ 4604// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4605// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4606UInt32 BufferQueue::GetCurrentFrame(UInt32 inBufferIndex) 4607{ 4608#if LOG_VERBOSE 4609 DebugMessageN1("BufferQueue::GetCurrentFrame - inBufferIndex = %d", inBufferIndex); 4610#endif 4611 iterator it = begin(); 4612 std::advance(it, inBufferIndex); 4613 if (it != end()) 4614 return (it->mBuffer->GetBytesPerPacket() == 0) ? 0 : it->mOffset/it->mBuffer->GetBytesPerPacket(); 4615 4616 return 0; 4617} 4618 4619// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4620void BufferQueue::AppendBuffer(OALSource* thisSource, ALuint inBufferToken, OALBuffer *inBuffer, ALuint inACToken) 4621{ 4622#if LOG_VERBOSE 4623 DebugMessageN4("BufferQueue::AppendBuffer - thisSource:inBufferToken:inBuffer:inACToken = %d:%d:%p:%d", thisSource->GetToken(), inBufferToken, inBuffer, inACToken); 4624#endif 4625 BufferInfo newBuffer; 4626 4627 newBuffer.mBufferToken = inBufferToken; 4628 newBuffer.mBuffer = inBuffer; 4629 newBuffer.mOffset = 0; 4630 newBuffer.mProcessedState = kPendingProcessing; 4631 newBuffer.mACToken = inACToken; 4632 4633 push_back(value_type (newBuffer)); 4634 SetQueueSize(); 4635} 4636 4637// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4638ALuint BufferQueue::RemoveQueueEntryByIndex(OALSource* thisSource, UInt32 inIndex, bool inReleaseIt) 4639{ 4640#if LOG_VERBOSE 4641 DebugMessageN3("BufferQueue::RemoveQueueEntryByIndex - thisSource:inIndex:inReleaseIt:inACToken = %d:%d:%d", thisSource->GetToken(), inIndex, inReleaseIt); 4642#endif 4643 iterator it = begin(); 4644 ALuint outBufferToken = 0; 4645 4646 std::advance(it, inIndex); 4647 if (it != end()) 4648 { 4649 outBufferToken = it->mBufferToken; 4650 if (inReleaseIt) 4651 it->mBuffer->ReleaseBuffer(thisSource); // if this release decrements the attchment count of this source to zero, it 4652 // will be deleted from the buffers list of attached sources 4653 erase(it); 4654 } 4655 SetQueueSize(); 4656 4657 return (outBufferToken); 4658} 4659 4660// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4661UInt32 BufferQueue::GetQueueSizeInFrames() 4662{ 4663#if LOG_VERBOSE 4664 DebugMessage("BufferQueue::GetQueueSizeInFrames"); 4665#endif 4666 iterator it = begin(); 4667 UInt32 totalFrames = 0; 4668 4669 while (it != end()) 4670 { 4671 totalFrames += it->mBuffer->GetFrameCount(); 4672 ++it; 4673 } 4674 4675 return (totalFrames); 4676} 4677 4678// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4679UInt32 BufferQueue::GetBufferFrameCount(UInt32 inBufferIndex) 4680{ 4681#if LOG_VERBOSE 4682 DebugMessageN1("BufferQueue::GetBufferFrameCount - inBufferIndex = %d",inBufferIndex); 4683#endif 4684 iterator it = begin(); 4685 std::advance(it, inBufferIndex); 4686 if (it != end()) 4687 return (it->mBuffer->GetFrameCount()); 4688 4689 return 0; 4690} 4691 4692// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4693ALuint BufferQueue::GetBufferTokenByIndex(UInt32 inBufferIndex) 4694{ 4695#if LOG_VERBOSE 4696 DebugMessageN1("BufferQueue::GetBufferTokenByIndex - inBufferIndex = %d",inBufferIndex); 4697#endif 4698 iterator it = begin(); 4699 std::advance(it, inBufferIndex); 4700 if (it != end()) 4701 return (it->mBuffer->GetToken()); 4702 4703 return 0; 4704} 4705 4706// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4707void BufferQueue::SetFirstBufferOffset(UInt32 inFrameOffset) 4708{ 4709#if LOG_VERBOSE 4710 DebugMessageN1("BufferQueue::SetFirstBufferOffset - inFrameOffset = %d",inFrameOffset); 4711#endif 4712 iterator it = begin(); 4713 if (it == end()) 4714 return; 4715 4716 UInt32 packetOffset = FrameOffsetToPacketOffset(inFrameOffset); 4717 UInt32 packetSize = GetPacketSize(); 4718 4719 it->mOffset = packetOffset * packetSize; 4720} 4721 4722// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4723UInt32 BufferQueue::GetPacketSize() 4724{ 4725#if LOG_VERBOSE 4726 DebugMessage("BufferQueue::GetPacketSize"); 4727#endif 4728 iterator it = begin(); 4729 if (it != end()) 4730 return(it->mBuffer->GetBytesPerPacket()); 4731 return (0); 4732} 4733 4734// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4735UInt32 BufferQueue::FrameOffsetToPacketOffset(UInt32 inFrameOffset) 4736{ 4737#if LOG_VERBOSE 4738 DebugMessageN1("BufferQueue::FrameOffsetToPacketOffset - inFrameOffset = %d",inFrameOffset); 4739#endif 4740 return inFrameOffset; // this is correct for pcm which is all we're doing right now 4741 4742 // if non pcm formats are used return the packet that contains inFrameOffset, which may back up the 4743 // requested frame - round backward not forward 4744}