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