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, &paramValue) == noErr)
4190			mASARogerBeepGain = paramValue;
4191
4192		if(AudioUnitGetParameter(mRogerBeepAU, 4/*kRogerBeepParam_Sensitivity*/, kAudioUnitScope_Global, 0, &paramValue) == noErr)
4193			mASARogerBeepSensitivity = (UInt32)paramValue;
4194
4195		if(AudioUnitGetParameter(mRogerBeepAU, 5 /*kRogerBeepParam_RogerType*/, kAudioUnitScope_Global, 0, &paramValue) == 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, &paramValue) == 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}