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