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