/**********************************************************************************************************************************
*
*   OpenAL cross platform audio library
*   Copyright (c) 2004, Apple Computer, Inc. All rights reserved.
*
*   Redistribution and use in source and binary forms, with or without modification, are permitted provided 
*   that the following conditions are met:
*
*   1.  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
*   2.  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
*       disclaimer in the documentation and/or other materials provided with the distribution. 
*   3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of its contributors may be used to endorse or promote 
*       products derived from this software without specific prior written permission. 
*
*   THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
*   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 
*   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
*   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
*   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
*   USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
**********************************************************************************************************************************/

#include "oalContext.h"
#include "oalSource.h"

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// OALContexts
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#pragma mark ***** OALContexts - Public Methods *****
OALContext::OALContext (const uintptr_t	inSelfToken, OALDevice    *inOALDevice, const ALCint *inAttributeList, UInt32  &inBusCount, Float64	&inMixerRate)
	: 	mSelfToken (inSelfToken),
		mProcessingActive(true),
		mOwningDevice(inOALDevice),
		mMixerNode(0), 
		mMixerUnit (0),
		mSourceMap (NULL),
		mDistanceModel(AL_INVERSE_DISTANCE_CLAMPED),			
		mSpeedOfSound(343.3),
		mDopplerFactor(1.0),
		mDopplerVelocity(1.0),
		mListenerGain(1.0),
		mAttributeListSize(0),
		mAttributeList(NULL),
		mDistanceScalingRequired(false),
		mCalculateDistance(true),
		mStoredInverseAttenuation(1.0),
		mRenderQuality(ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_LOW),
		mSpatialSetting(0),
		mBusCount(inBusCount),
		mMixerOutputRate(inMixerRate),
		mDefaultReferenceDistance(1.0),
        mDefaultMaxDistance(100000.0),
		mUserSpecifiedBusCounts(false),
		mSettableMixerAttenuationCurves(false),
		mASAReverbState(0),		
		mASAReverbRoomType(0),
		mASAReverbGlobalLevel(0.0),
		mASAReverbQuality(ALC_ASA_REVERB_QUALITY_Low),
		mASAReverbEQGain(0.0),
		mASAReverbEQBandwidth(3.0),
		mASAReverbEQFrequency(800.0)
#if LOG_BUS_CONNECTIONS
		, mMonoSourcesConnected(0),
		mStereoSourcesConnected(0)
#endif
{
		mBusInfo = (BusInfo *) calloc (1, sizeof(BusInfo) * mBusCount);

		UInt32		monoSources = 0,
					stereoSources = 1; // default
		
		if (inAttributeList)
		{
			ALCint*		currentAttribute = ( ALCint*) inAttributeList;
			// ATTRIBUTE LIST
			while (*currentAttribute != 0)
			{
				switch (*currentAttribute)
				{
					case ALC_FREQUENCY:
						break;
					case ALC_REFRESH:
						break;
					case ALC_SYNC:
						break;
					case ALC_MONO_SOURCES:
						mUserSpecifiedBusCounts = true;
						monoSources = currentAttribute[1];
						break;
					case ALC_STEREO_SOURCES:
						mUserSpecifiedBusCounts = true;
						stereoSources = currentAttribute[1];
						break;
					default:
						// is this a failure?
						break;
				}
				currentAttribute += 2;
				mAttributeListSize++;
			}
			mAttributeListSize++;	// add for the terminator
			mAttributeList =  (ALCint*) calloc (1, mAttributeListSize * sizeof(ALCint));
			memcpy(mAttributeList, inAttributeList, mAttributeListSize * sizeof(ALCint));
		}

		// initialize  mContextInfo
		mListenerPosition[0] = 0.0;
		mListenerPosition[1] = 0.0;
		mListenerPosition[2] = 0.0;
		
		mListenerVelocity[0] = 0.0;
		mListenerVelocity[1] = 0.0;
		mListenerVelocity[2] = 0.0;
		
		mListenerOrientationForward[0] = 0.0;
		mListenerOrientationForward[1] = 0.0;
		mListenerOrientationForward[2] = -1.0;
		
		mListenerOrientationUp[0] = 0.0;
		mListenerOrientationUp[1] = 1.0;
		mListenerOrientationUp[2] = 0.0;

		InitializeMixer(stereoSources);

		mSourceMap = new OALSourceMap();
		mDeadSourceMap = new OALSourceMap();
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OALContext::~OALContext()
{
	mOwningDevice->RemoveContext(this);

	// delete all the sources that were created by this context
	if (mSourceMap)
	{
		for (UInt32  i = 0; i < mSourceMap->Size(); i++)
		{
			OALSource	*oalSource = mSourceMap->GetSourceByIndex(0);
			if (oalSource)
			{
				mSourceMap->Remove(oalSource->GetToken());
				delete oalSource;
			}
		}
		delete mSourceMap;
	}
	
	if (mDeadSourceMap)
	{
		CleanUpDeadSourceList();
		delete mDeadSourceMap;
	}
	
	if (mAttributeList)
		free(mAttributeList);
	
	if(mBusInfo)
		free(mBusInfo);
		
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void	OALContext::CleanUpDeadSourceList()
{
	if (mDeadSourceMap)
	{
		UInt32	index = 0;
		for (UInt32 i = 0; i < mDeadSourceMap->Size(); i++)
		{
			OALSource*		source = mDeadSourceMap->GetSourceByIndex(index);
			if (source)
			{
				if (source->IsSafeForDeletion())
				{
					//DebugMessageN1("OALContext::CleanUpTheDeadSourceList removing source id = %ld", source->GetToken());
					mDeadSourceMap->Remove(source->GetToken());
					delete (source);
				}
				else
				{
					//DebugMessageN1("OALContext::CleanUpTheDeadSourceList NOT SAFE RIGHT NOW to delete source id = %ld", source->GetToken());
					index++;
				}
			}
			else
				index++;
		}
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
	3DMixer Version Info
	
	- Pre 2.0 Mixer must be at least version 1.3 for OpenAL
	- 3DMixer 2.0
		Bug in Distance Attenuationg/Reverb code requires that distances be scaled in OAL before passing to mixer
	- 3DMixer 2.1
		Fixes bug in 2.0 but also has a bug related to OAL fixes for correctly caclulating the vector of moving object
	- 3DMixer 2.1.x
		Fixes bugs in 2.1 and adds support for Linear Attenuation
	- 3DMixer 2.2
		Adds Linear/Exponential Attenuation and Reverb/Occlusion/Obstruction.
		(note) Linear & Exponential Attenuation is done manually by the OALSource object if 2.2 Mixer is not present
*/

void		OALContext::InitializeMixer(UInt32	inStereoBusCount)
{
    OSStatus	result = noErr;
	UInt32		propSize;

	try {
		// ~~~~~~~~~~~~~~~~~~~ GET 3DMIXER VERSION
		if (Get3DMixerVersion() < k3DMixerVersion_1_3)
			throw -1;                           // should not happen because OpenDevice should have failed beforehand
		
		if (Get3DMixerVersion() == k3DMixerVersion_2_0)
		{
			mDistanceScalingRequired = true; 
		}
		else if (Get3DMixerVersion() >= k3DMixerVersion_2_2)
		{
			mSettableMixerAttenuationCurves = true;
		}

		ComponentDescription	mixerCD;
		mixerCD.componentFlags = 0;        
		mixerCD.componentFlagsMask = 0;     
		mixerCD.componentType = kAudioUnitType_Mixer;          
		mixerCD.componentSubType = kAudioUnitSubType_3DMixer;       
		mixerCD.componentManufacturer = kAudioUnitManufacturer_Apple;  
		
		// CREATE NEW NODE FOR THE GRAPH
		result = AUGraphNewNode (mOwningDevice->GetGraph(), &mixerCD, 0, NULL, &mMixerNode);
			THROW_RESULT

		result = AUGraphGetNodeInfo (mOwningDevice->GetGraph(), mMixerNode, 0, 0, 0, &mMixerUnit);
			THROW_RESULT   

		// Get Default Distance Setting when the good 3DMixer is around
		if (Get3DMixerVersion() >= k3DMixerVersion_2_0)
		{
			MixerDistanceParams		distanceParams;
			propSize = sizeof(distanceParams);
			result = AudioUnitGetProperty(mMixerUnit, kAudioUnitProperty_3DMixerDistanceParams, kAudioUnitScope_Input, 1, &distanceParams, &propSize);
			if (result == noErr)
			{
				mDefaultReferenceDistance = distanceParams.mReferenceDistance;
				mDefaultMaxDistance = distanceParams.mMaxDistance;
			}
		}
		
		// Set the Output Format of the Mixer AU
		CAStreamBasicDescription	format;
		UInt32                      propSize = sizeof(format);
		result = AudioUnitGetProperty(mOwningDevice->GetOutputAU(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, &propSize);

		format.SetCanonical (mOwningDevice->GetDesiredRenderChannelCount(), false);	// determine how many channels to render to
		format.mSampleRate = GetMixerRate();										// Sample Rate (either the default out rate of the Output AU or a User Specified rate)

		result = AudioUnitSetProperty (mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, sizeof(format));
			THROW_RESULT
		
		// Frames Per Slice - moved from ConnectContext call in device class
		UInt32		mixerFPS = 0;
		UInt32		dataSize = sizeof(mixerFPS);
		result = AudioUnitGetProperty(mMixerUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &mixerFPS, &dataSize);
		if (mixerFPS < mOwningDevice->GetFramesPerSlice())
		{
			mixerFPS = mOwningDevice->GetFramesPerSlice();
			result = AudioUnitSetProperty(  mMixerUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &mixerFPS, sizeof(mixerFPS));
				THROW_RESULT
		}

		// REVERB off by default
		result = AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_UsesInternalReverb, kAudioUnitScope_Global, 0, &mASAReverbState, sizeof(mASAReverbState));
		// ignore result

		// MIXER BUS COUNT
		if (Get3DMixerVersion() < k3DMixerVersion_2_0)
		{
			mBusCount = kDefaultMaximumMixerBusCount; // 1.3 version of the mixer did not allow a change in the bus count
		}
		else
		{
			// set the bus count on the mixer if necessary	
			UInt32  currentBusCount;
			propSize = sizeof(currentBusCount);
			result = AudioUnitGetProperty (	mMixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &currentBusCount, &propSize);
			if ((result == noErr) && (mBusCount != currentBusCount))
			{
				result = AudioUnitSetProperty (	mMixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &mBusCount, propSize);
				if (result != noErr)
				{
					// couldn't set the bus count so make sure we know just how many busses there are
					propSize = sizeof(mBusCount);
					AudioUnitGetProperty (	mMixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &mBusCount, &propSize);
				}
			}
		}

		// SET UP STEREO/MONO BUSSES
		CAStreamBasicDescription 	theOutFormat;
		theOutFormat.mSampleRate = mMixerOutputRate;
		theOutFormat.mFormatID = kAudioFormatLinearPCM;
		theOutFormat.mFramesPerPacket = 1;	
		theOutFormat.mBytesPerFrame = sizeof (Float32);
		theOutFormat.mBitsPerChannel = sizeof (Float32) * 8;	
		theOutFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
		theOutFormat.mBytesPerPacket = sizeof (Float32);	

		for (UInt32	i = 0; i < mBusCount; i++)
		{
			// Distance Attenuation: for pre v2.0 mixer
			SetDistanceAttenuation (i, kDefaultReferenceDistance, kDefaultMaximumDistance, kDefaultRolloff);			

			theOutFormat.mChannelsPerFrame = (i < inStereoBusCount) ? 2 : 1;
			OSStatus	result = AudioUnitSetProperty (	mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 
															i, &theOutFormat, sizeof(CAStreamBasicDescription));
				THROW_RESULT

			mBusInfo[i].mNumberChannels = theOutFormat.mChannelsPerFrame; 
			mBusInfo[i].mIsAvailable = true;
			mBusInfo[i].mReverbState = mASAReverbState;

			// set kAudioUnitProperty_SpatializationAlgorithm
			UInt32		spatAlgo = (theOutFormat.mChannelsPerFrame == 2) ? kSpatializationAlgorithm_StereoPassThrough : mSpatialSetting;
			AudioUnitSetProperty(	mMixerUnit, kAudioUnitProperty_SpatializationAlgorithm, kAudioUnitScope_Input, i, &spatAlgo, sizeof(spatAlgo));

			// set kAudioUnitProperty_3DMixerRenderingFlags (distance attenuation) for mono busses
			if (theOutFormat.mChannelsPerFrame == 1)
			{
				UInt32 		render_flags_3d = k3DMixerRenderingFlags_DistanceAttenuation;
				if (mRenderQuality == ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_HIGH)
					render_flags_3d += k3DMixerRenderingFlags_InterAuralDelay; // off by default, on if the user sets High Quality rendering

				// Render Flags
				result = AudioUnitSetProperty(	mMixerUnit, kAudioUnitProperty_3DMixerRenderingFlags, kAudioUnitScope_Input, i, &render_flags_3d, sizeof(render_flags_3d));
			}
		}

		// Initialize Busses - attributes may affect this operation
		InitRenderQualityOnBusses(); 	
	}
	catch(OSStatus	result){
		throw result;
	}
	catch(...){
		throw -1;
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// should only be called when the mixer is NOT connected
void	OALContext::ConfigureMixerFormat()
{
	// Set the Output Format of the Mixer AU
	CAStreamBasicDescription	format;
	UInt32                      propSize = sizeof(format);
	OSStatus	result = AudioUnitGetProperty(mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, &propSize);

	format.SetCanonical (mOwningDevice->GetDesiredRenderChannelCount(), false);	// determine how many channels to render to
	format.mSampleRate = GetMixerRate();										// Sample Rate (either the default out rate of the Output AU or a User Specified rate)

	result = AudioUnitSetProperty (mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &format, sizeof(format));
		THROW_RESULT

	// Initialize Busses - render channel attributes attributes may affect this operation
	InitRenderQualityOnBusses(); 	
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void	OALContext::CopyAttributeList( ALCint*	outAttrList)
{
	memcpy(outAttrList, mAttributeList, mAttributeListSize);
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::AddSource(ALuint	inSourceToken)
{	
	try {
			OALSource	*newSource = new OALSource (inSourceToken, this);
	
			mSourceMap->Add(inSourceToken, &newSource);
			CleanUpDeadSourceList();
	}
	catch (...) {
		throw;
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OALSource*		OALContext::GetSource(ALuint	inSourceToken)
{
	return (mSourceMap->Get(inSourceToken));
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::RemoveSource(ALuint	inSourceToken)
{
	OALSource	*oalSource = mSourceMap->Get(inSourceToken);
	if (oalSource != NULL)
	{
		oalSource->SetUpDeconstruction();
		mSourceMap->Remove(inSourceToken);					// do not allow any more threads to use this source object
		mDeadSourceMap->Add(inSourceToken, &oalSource);		// remove it later when it is safe
		CleanUpDeadSourceList();							// now is a good time to actually delete other sources marked for deletion
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::ProcessContext()
{
	return; // NO OP
	
#if 0
	// This code breaks Doom 3 [4554491] - The 1.0 implementation was a no op.
	// Since alcProcessContext()/alcSuspendContext() are also no ops on sound c ards with OpenAL imps, this should be ok
	
	if (mProcessingActive == true)
		return; // NOP
	
	ConnectMixerToDevice();
	mProcessingActive = true;
	return;
#endif
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::SuspendContext()
{
	return; // NO OP

#if 0
	// This code breaks Doom 3 [4554491] - The 1.0 implementation was a no op.
	// Since alcProcessContext()/alcSuspendContext() are also no ops on sound c ards with OpenAL imps, this should be ok
 
	if (mProcessingActive == false)
		return; // NOP

	DeviceDisconnect();
	mProcessingActive = false;
#endif
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::ConnectMixerToDevice()
{
	mOwningDevice->ConnectContext(this);
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::SetDistanceModel(UInt32	inDistanceModel)
{
#if LOG_GRAPH_AND_MIXER_CHANGES
	DebugMessageN1("OALContext::SetDistanceModel model = 0x%X", inDistanceModel);
#endif
	OSStatus	result = noErr;
	UInt32		curve;
	
	if (mDistanceModel != inDistanceModel)
	{
		//UInt32	curve;
		switch (inDistanceModel)
		{
			case AL_INVERSE_DISTANCE:
			case AL_INVERSE_DISTANCE_CLAMPED:
				mCalculateDistance = true;
				if (mSettableMixerAttenuationCurves)
				{
					curve =2 /* k3DMixerAttenuationCurve_Inverse*/;
					for (UInt32	i = 0; i < mBusCount; i++)
					{
						result = AudioUnitSetProperty(	mMixerUnit, 3013 /*kAudioUnitProperty_3DMixerAttenuationCurve*/, kAudioUnitScope_Input, i, &curve, sizeof(curve));
					}
				}
				else
				{
					// NOTHING TO DO
					if (Get3DMixerVersion() >= k3DMixerVersion_2_0)
					{
						// unnecessary if changing between AL_INVERSE_DISTANCE & AL_INVERSE_DISTANCE_CLAMPED
						if ((mDistanceModel != AL_INVERSE_DISTANCE) && (mDistanceModel != AL_INVERSE_DISTANCE_CLAMPED))
						{
							// this is the 2.0-2.1 mixer
						}
					}
					else
					{
						// kAudioUnitProperty_3DMixerDistanceAtten gets set by the source each time via a call to SetDistanceAttenuation()
						// nothing more to do now
					}
				}
				
				break;
				
			case AL_LINEAR_DISTANCE:
			case AL_LINEAR_DISTANCE_CLAMPED:
				if (mSettableMixerAttenuationCurves)
				{	
					mCalculateDistance = true;
					curve = 3 /*k3DMixerAttenuationCurve_Linear*/;
					for (UInt32	i = 0; i < mBusCount; i++)
					{
						result = AudioUnitSetProperty(	mMixerUnit, 3013 /*kAudioUnitProperty_3DMixerAttenuationCurve*/, kAudioUnitScope_Input, i, &curve, sizeof(curve));
					}
				}
				else
				{
					mCalculateDistance = false;
					// turn off distance attenuation altogether
					// the source will then apply the linear distance formula as a gain scalar and set the bus gain instead of setting any distance
				}
				break;
			
			case AL_EXPONENT_DISTANCE:
			case AL_EXPONENT_DISTANCE_CLAMPED:
				if (mSettableMixerAttenuationCurves)
				{
					mCalculateDistance = true;
					// set the mixer for Exponential Attenuation
					curve = 1 /*k3DMixerAttenuationCurve_Exponential*/;
					for (UInt32	i = 0; i < mBusCount; i++)
					{
						result = AudioUnitSetProperty(	mMixerUnit, 3013 /*kAudioUnitProperty_3DMixerAttenuationCurve*/, kAudioUnitScope_Input, i, &curve, sizeof(curve));
					}
				}
				else
				{
					mCalculateDistance = false;
					// turn off distance attenuation altogether
					// the source will then apply the exponential distance formula as a gain scalar and set the bus gain instead of setting any distance
				}
				break;
				
			case AL_NONE:
				{
					mCalculateDistance = false;
					// turn off distance attenuation altogether
				}
			
				break;
				
				
			default:
				break;
		}

		mDistanceModel = inDistanceModel;
		if (mSourceMap)
		{
             mSourceMap->MarkAllSourcesForRecalculation();
		}
	}
}	

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::SetDopplerFactor(Float32		inDopplerFactor)
{
	if (mDopplerFactor != inDopplerFactor)
	{
		mDopplerFactor = inDopplerFactor;
		if (mSourceMap)
			mSourceMap->MarkAllSourcesForRecalculation();
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::SetDopplerVelocity(Float32	inDopplerVelocity)
{
	if (mDopplerVelocity != inDopplerVelocity)
	{
		mDopplerVelocity = inDopplerVelocity;
		// if (mSourceMap)
		//	 mSourceMap->MarkAllSourcesForRecalculation();
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::SetSpeedOfSound(Float32	inSpeedOfSound)
{
	if (mSpeedOfSound != inSpeedOfSound)
	{
		mSpeedOfSound = inSpeedOfSound;
		if (mSourceMap)
			mSourceMap->MarkAllSourcesForRecalculation();
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::SetListenerGain(Float32	inGain)
{
	if (mListenerGain != inGain)
	{
		mListenerGain = inGain;
		
		Float32	db = 20.0 * log10(inGain); 				// convert to db
		AudioUnitSetParameter (mMixerUnit, k3DMixerParam_Gain, kAudioUnitScope_Output, 0, db, 0);
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UInt32		OALContext::GetSourceCount()
{
	if (mSourceMap)
		return (mSourceMap->Size());

	return (0);
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::SetListenerPosition(Float32	posX, Float32	posY, Float32	posZ) 
{ 
	if (isnan(posX) || isnan(posY) || isnan(posZ))
		throw ((OSStatus) AL_INVALID_VALUE);                        

	if (	(mListenerPosition[0] == posX) 	&& 
			(mListenerPosition[1] == posY )	&& 
			(mListenerPosition[2] == posZ)		)
		return;
	
	mListenerPosition[0] = posX;
	mListenerPosition[1] = posY;
	mListenerPosition[2] = posZ;

	if (mSourceMap)
	{
#if LOG_GRAPH_AND_MIXER_CHANGES
	DebugMessageN4("OALContext::SetListenerPosition called - OALSource = %f:%f:%f/%ld\n", posX, posY, posZ, mSelfToken);
#endif
		// moving the listener effects the coordinate translation for ALL the sources
		mSourceMap->MarkAllSourcesForRecalculation();
	}	
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		OALContext::SetListenerVelocity(Float32	posX, Float32	posY, Float32	posZ) 
{ 
	mListenerVelocity[0] = posX;
	mListenerVelocity[1] = posY;
	mListenerVelocity[2] = posZ;

	if (mSourceMap)
	{
#if LOG_GRAPH_AND_MIXER_CHANGES
	DebugMessage("OALContext::SetListenerVelocity: MarkAllSourcesForRecalculation called\n");
#endif
		// moving the listener effects the coordinate translation for ALL the sources
        mSourceMap->MarkAllSourcesForRecalculation();
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void	OALContext::SetListenerOrientation( Float32	forwardX, 	Float32	forwardY,	Float32	forwardZ,
											Float32	 upX, 		Float32	 upY, 		Float32	 upZ)
{	
	if (isnan(forwardX) || isnan(forwardY) || isnan(forwardZ) || isnan(upX) || isnan(upY) || isnan(upZ))
		throw ((OSStatus) AL_INVALID_VALUE);                        

	if (	(mListenerOrientationForward[0] == forwardX) 	&& 
			(mListenerOrientationForward[1] == forwardY )	&& 
			(mListenerOrientationForward[2] == forwardZ) 	&&
			(mListenerOrientationUp[0] == upX) 				&& 
			(mListenerOrientationUp[1] == upY ) 			&& 
			(mListenerOrientationUp[2] == upZ)					)
		return;

	mListenerOrientationForward[0] = forwardX;
	mListenerOrientationForward[1] = forwardY;
	mListenerOrientationForward[2] = forwardZ;
	mListenerOrientationUp[0] = upX;
	mListenerOrientationUp[1] = upY;
	mListenerOrientationUp[2] = upZ;

	if (mSourceMap)
	{
#if LOG_GRAPH_AND_MIXER_CHANGES
	DebugMessage("OALContext::SetListenerOrientation: MarkAllSourcesForRecalculation called\n");
#endif
		// moving the listener effects the coordinate translation for ALL the sources
		mSourceMap->MarkAllSourcesForRecalculation();
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UInt32	OALContext::GetDesiredRenderChannels(UInt32	inDeviceChannels)
{
    UInt32	returnValue = inDeviceChannels;
	
	if ((Get3DMixerVersion() < k3DMixerVersion_2_0) && (returnValue == 4))
    {
        // quad did not work properly before version 2.0 of the 3DMixer, so just render to stereo
        returnValue = 2;
    }
    else if (inDeviceChannels < 4)
    {
        // guard against the possibility of multi channel hw that has never been given a preferred channel layout
        // Or, that a 3 channel layout was returned (which is unsupported by the 3DMixer)
        returnValue = 2; 
    } 
    else if ((inDeviceChannels > 5) && (Get3DMixerVersion() < k3DMixerVersion_2_3))
    {
		// 3DMixer ver. 2.2 and below could only render a maximum of 5 channels
		returnValue = 5;    
    }
	else if(inDeviceChannels > 8)
	{
		// Current 3DMixer can handle a maximum of 8 channels
		returnValue = 8;
	}
	
	return returnValue;
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void OALContext::InitRenderQualityOnBusses()
{
	UInt32		channelCount = mOwningDevice->GetDesiredRenderChannelCount();

	if (channelCount > 2)
	{
        // at this time, there is only one spatial quality being used for multi channel hw
        DebugMessage("********** InitRenderQualityOnBusses:kDefaultMultiChannelQuality ***********");
		mSpatialSetting = kDefaultMultiChannelQuality;
	}
	else if (mRenderQuality == ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_LOW)
	{
		// this is the default case for stereo
        DebugMessage("********** InitRenderQualityOnBusses:kDefaultLowQuality ***********");
		mSpatialSetting =  kDefaultLowQuality;
	}
	else
	{
		DebugMessage("********** InitRenderQualityOnBusses:kDefaultHighQuality ***********");
		mSpatialSetting = kDefaultHighQuality;
	}
	
	UInt32 		render_flags_3d = k3DMixerRenderingFlags_DistanceAttenuation;
	if (mRenderQuality == ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_HIGH)
	{
    	 // off by default, on if the user sets High Quality rendering, as HRTF requires InterAuralDelay to be on
         render_flags_3d += k3DMixerRenderingFlags_InterAuralDelay;     
	}
    
    if (mASAReverbState > 0)
	{
    	 // off by default, on if the user turns on Reverb, as it requires DistanceDiffusion to be on
    	render_flags_3d += k3DMixerRenderingFlags_DistanceDiffusion;
		render_flags_3d += (1L << 6 /* k3DMixerRenderingFlags_ConstantReverbBlend*/);    
	}
    
	OSStatus                    result = noErr;
    UInt32                      propSize;
    CAStreamBasicDescription	format;
	for (UInt32	i = 0; i < mBusCount; i++)
	{
		propSize = sizeof(format);
		result = AudioUnitGetProperty (	mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, i, &format, &propSize);
		
		// only reset the mono channels, stereo channels are always set to stereo pass thru regardless of render quality setting
        if ((result == noErr) && (format.NumberChannels() == 1)) 
		{
			// Spatialization
			result = AudioUnitSetProperty(	mMixerUnit, kAudioUnitProperty_SpatializationAlgorithm, kAudioUnitScope_Input, 
											i, &mSpatialSetting, sizeof(mSpatialSetting));

			// Render Flags                
            result = AudioUnitSetProperty(	mMixerUnit, kAudioUnitProperty_3DMixerRenderingFlags, kAudioUnitScope_Input, 
											i, &render_flags_3d, sizeof(render_flags_3d));
			
			// Doppler - This must be done AFTER the spatialization setting, because some algorithms explicitly turn doppler on
			UInt32		doppler = kDopplerDefault;
			result = AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_DopplerShift, kAudioUnitScope_Input, i, &doppler, sizeof(doppler));
		}
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void OALContext::SetRenderQuality (UInt32 inRenderQuality)
{
	if (mRenderQuality == inRenderQuality)
		return;	// nothing to do;

	// make sure a valid quality setting is requested
	if (!IsValidRenderQuality(inRenderQuality))
		throw (OSStatus) AL_INVALID_VALUE;

	mRenderQuality = inRenderQuality;
			
	// change the spatialization for all mono busses on the mixer
	InitRenderQualityOnBusses(); 
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void    OALContext::SetDistanceAttenuation(UInt32    inBusIndex, Float64 inRefDist, Float64 inMaxDist, Float64 inRolloff)
{
	if (Get3DMixerVersion() >= k3DMixerVersion_2_0)
		return;     // unnecessary with v2.0 mixer
        
    Float64     maxattenuationDB = 20 * log10(inRefDist / (inRefDist + (inRolloff * (inMaxDist - inRefDist))));
    Float64     maxattenuation = pow(10, (maxattenuationDB/20));                    
    Float64     distAttenuation = (log(1/maxattenuation))/(log(inMaxDist)) - 1.0;

	#if 0
		DebugMessageN1("SetDistanceAttenuation:Reference Distance =  %f", inRefDist);
		DebugMessageN1("SetDistanceAttenuation:Maximum Distance =  %f", inMaxDist);
		DebugMessageN1("SetDistanceAttenuation:Rolloff =  %f", inRolloff);
		DebugMessageN1("SetDistanceAttenuation:Max Attenuation DB =  %f", maxattenuationDB);
		DebugMessageN1("SetDistanceAttenuation:Max Attenuation Scalar =  %f", maxattenuation);
		DebugMessageN1("SetDistanceAttenuation:distAttenuation =  %f", distAttenuation);

	#endif
    
    AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_3DMixerDistanceAtten, kAudioUnitScope_Input, inBusIndex, &distAttenuation, sizeof(distAttenuation));
    return;
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UInt32		OALContext::GetAvailableMonoBus ()
{
	// look for a bus already set for mono
	for (UInt32 i = 0; i < mBusCount; i++)
	{
		if (mBusInfo[i].mIsAvailable == true && mBusInfo[i].mNumberChannels == 1) 
		{
			mBusInfo[i].mIsAvailable = false;
#if LOG_BUS_CONNECTIONS
			mMonoSourcesConnected++;
			DebugMessageN2("GetAvailableMonoBus1: Sources Connected, Mono =  %ld, Stereo = %ld", mMonoSourcesConnected, mStereoSourcesConnected);
			DebugMessageN1("GetAvailableMonoBus1: BUS_NUMBER = %ld", i);
#endif
			return (i);
		}
	}

	// do not try and switch a bus to mono if the appliction specified mono and stereo bus counts
	if (!mUserSpecifiedBusCounts)
	{	
		// couldn't find a mono bus, so find any available channel and make it mono
		for (UInt32 i = 0; i < mBusCount; i++)
		{
			if (mBusInfo[i].mIsAvailable == true) 
			{
	#if LOG_BUS_CONNECTIONS
				mMonoSourcesConnected++;
				DebugMessageN2("GetAvailableMonoBus2: Sources Connected, Mono =  %ld, Stereo = %ld", mMonoSourcesConnected, mStereoSourcesConnected);
	#endif
				CAStreamBasicDescription 	theOutFormat;
				theOutFormat.mChannelsPerFrame = 1;
				theOutFormat.mSampleRate = GetMixerRate();          // as a default, set the bus to the mixer's output rate, it should get reset if necessary later on
				theOutFormat.mFormatID = kAudioFormatLinearPCM;
				theOutFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
				theOutFormat.mBytesPerPacket = sizeof (Float32);	
				theOutFormat.mFramesPerPacket = 1;	
				theOutFormat.mBytesPerFrame = sizeof (Float32);
				theOutFormat.mBitsPerChannel = sizeof (Float32) * 8;	
				OSStatus	result = AudioUnitSetProperty (	mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 
															i, &theOutFormat, sizeof(CAStreamBasicDescription));
					THROW_RESULT

				mBusInfo[i].mIsAvailable = false;
				mBusInfo[i].mNumberChannels = 1; 			
				AudioUnitSetProperty(	mMixerUnit, kAudioUnitProperty_SpatializationAlgorithm, kAudioUnitScope_Input, 
										i, &mSpatialSetting, sizeof(mSpatialSetting));

				UInt32 		render_flags_3d = k3DMixerRenderingFlags_DistanceAttenuation;
				if (mRenderQuality == ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_HIGH)
					render_flags_3d += k3DMixerRenderingFlags_InterAuralDelay; // off by default, on if the user sets High Quality rendering

				// Render Flags
				result = AudioUnitSetProperty(	mMixerUnit, kAudioUnitProperty_3DMixerRenderingFlags, kAudioUnitScope_Input, 
												i, &render_flags_3d, sizeof(render_flags_3d));
				
				return (i);
			}
		}
	}
	
	DebugMessage("ERROR: GetAvailableMonoBus: COULD NOT GET A MONO BUS");
	throw (-1); // no inputs available
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UInt32		OALContext::GetAvailableStereoBus ()
{
	for (UInt32 i = 0; i < mBusCount; i++)
	{
		if (mBusInfo[i].mIsAvailable == true && mBusInfo[i].mNumberChannels == 2) 
		{
			mBusInfo[i].mIsAvailable = false;
#if LOG_BUS_CONNECTIONS
			mStereoSourcesConnected++;
			DebugMessageN2("GetAvailableStereoBus1: Sources Connected, Mono =  %ld, Stereo = %ld", mMonoSourcesConnected, mStereoSourcesConnected);
			DebugMessageN1("GetAvailableStereoBus1: BUS_NUMBER = %ld", i);
#endif
			return (i);
		}
	}

	// do not try and switch a bus to stereo if the appliction specified mono and stereo bus counts
	if (!mUserSpecifiedBusCounts)
	{
		// couldn't find one, so look for a mono channel, make it stereo and set to kSpatializationAlgorithm_StereoPassThrough
		for (UInt32 i = 0; i < mBusCount; i++)
		{
			if (mBusInfo[i].mIsAvailable == true) 
			{

	#if LOG_BUS_CONNECTIONS
				mStereoSourcesConnected++;
				DebugMessageN2("GetAvailableStereoBus2: Sources Connected, Mono =  %ld, Stereo = %ld", mMonoSourcesConnected, mStereoSourcesConnected);
				DebugMessageN1("GetAvailableStereoBus2: BUS_NUMBER = %ld", i);
	#endif
				CAStreamBasicDescription 	theOutFormat;
				theOutFormat.mChannelsPerFrame = 2;
				theOutFormat.mSampleRate = GetMixerRate();
				theOutFormat.mFormatID = kAudioFormatLinearPCM;
				theOutFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
				theOutFormat.mBytesPerPacket = sizeof (Float32);	
				theOutFormat.mFramesPerPacket = 1;	
				theOutFormat.mBytesPerFrame = sizeof (Float32);
				theOutFormat.mBitsPerChannel = sizeof (Float32) * 8;	
				OSStatus	result = AudioUnitSetProperty (	mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 
															i, &theOutFormat, sizeof(CAStreamBasicDescription));
					THROW_RESULT

				mBusInfo[i].mIsAvailable = false;
				mBusInfo[i].mNumberChannels = 2; 

				UInt32		spatAlgo = kSpatializationAlgorithm_StereoPassThrough;
				AudioUnitSetProperty(	mMixerUnit, kAudioUnitProperty_SpatializationAlgorithm, kAudioUnitScope_Input, 
										i, &spatAlgo, sizeof(spatAlgo));

				return (i);
			}
		}
	}
	
	DebugMessage("ERROR: GetAvailableStereoBus: COULD NOT GET A STEREO BUS");
	throw (-1); // no inputs available
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void	OALContext::SetBusAsAvailable (UInt32 inBusIndex)
{	
	mBusInfo[inBusIndex].mIsAvailable = true;

#if LOG_BUS_CONNECTIONS
	if (mBusInfo[inBusIndex].mNumberChannels == 1)
		mMonoSourcesConnected--;
	else
		mStereoSourcesConnected--;

		DebugMessageN2("SetBusAsAvailable: Sources Connected, Mono =  %ld, Stereo = %ld", mMonoSourcesConnected, mStereoSourcesConnected);
#endif
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Apple Environmental Audio (ASA) Extension
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void	OALContext::SetReverbRoomType(UInt32 inRoomType)
{
	if (mASAReverbRoomType == inRoomType)
		return;	// nothing to do;

    mASAReverbRoomType = inRoomType;
        
    OSStatus    result = AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_ReverbRoomType, kAudioUnitScope_Global, 0, &mASAReverbRoomType, sizeof(mASAReverbRoomType));			
		THROW_RESULT
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void	OALContext::SetReverbLevel(Float32 inReverbLevel)
{

	if (mASAReverbGlobalLevel == inReverbLevel)
		return;	// nothing to do;

    mASAReverbGlobalLevel = inReverbLevel;
        
	OSStatus	result = AudioUnitSetParameter (mMixerUnit, 6 /*k3DMixerParam_GlobalReverbGain*/, kAudioUnitScope_Global, 0, mASAReverbGlobalLevel, 0);
		THROW_RESULT
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void	OALContext::SetReverbState(UInt32 inReverbState)
{
	if (mASAReverbState == inReverbState)
		return;	// nothing to do;

    mASAReverbState = inReverbState;
        
    OSStatus    result = AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_UsesInternalReverb, kAudioUnitScope_Global, 0, &mASAReverbState, sizeof(mASAReverbState));			
	if (result == noErr)
        InitRenderQualityOnBusses(); // distance diffusion needs to be reset on the busses now
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void			OALContext::SetReverbQuality(UInt32 inQuality)
{
	if (mASAReverbQuality == inQuality)
		return;	// nothing to do;

    mASAReverbQuality = inQuality;

   AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_RenderQuality, kAudioUnitScope_Global, 0, &mASAReverbQuality, sizeof(mASAReverbQuality));			
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void			OALContext::SetReverbEQGain(Float32 inGain)
{
	if (mASAReverbEQGain != inGain)
	{
		mASAReverbEQGain = inGain;
		OSStatus	result = AudioUnitSetParameter (mMixerUnit, 20000 + 16 /*kReverbParam_FilterGain*/, kAudioUnitScope_Global, 0, mASAReverbEQGain, 0);
			THROW_RESULT
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void			OALContext::SetReverbEQBandwidth(Float32 inBandwidth)
{
	if (mASAReverbEQBandwidth != inBandwidth)
	{
		mASAReverbEQBandwidth = inBandwidth;
		OSStatus	result = AudioUnitSetParameter (mMixerUnit, 20000 + 15 /*kReverbParam_FilterBandwidth*/, kAudioUnitScope_Global, 0, mASAReverbEQBandwidth, 0);
			THROW_RESULT
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void			OALContext::SetReverbEQFrequency(Float32 inFrequency)
{
	if (mASAReverbEQFrequency != inFrequency)
	{
		mASAReverbEQFrequency = inFrequency;
		OSStatus	result = AudioUnitSetParameter (mMixerUnit, 20000 + 14 /*kReverbParam_FilterFrequency*/, kAudioUnitScope_Global, 0, mASAReverbEQFrequency, 0);
			THROW_RESULT
	}
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UInt32			OALContext::GetReverbQuality()
{
	return mASAReverbQuality;
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Float32			OALContext::GetReverbEQGain()
{
	OSStatus	result = AudioUnitGetParameter(mMixerUnit, 20000 + 16 /*kReverbParam_FilterGain*/,kAudioUnitScope_Global,0, &mASAReverbEQGain);
	if (result)
		return 0.0;
	
	return mASAReverbEQGain;
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Float32			OALContext::GetReverbEQBandwidth()
{
	OSStatus	result = AudioUnitGetParameter(mMixerUnit, 20000 + 15 /*kReverbParam_FilterBandwidth*/,kAudioUnitScope_Global,0, &mASAReverbEQBandwidth);
	if (result)
		return 0.0;

	return mASAReverbEQBandwidth;
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Float32			OALContext::GetReverbEQFrequency()
{
	OSStatus	result =  AudioUnitGetParameter(mMixerUnit, 20000 + 14 /*kReverbParam_FilterFrequency*/,kAudioUnitScope_Global,0, &mASAReverbEQFrequency);
	if (result)
		return 0.0;

	return mASAReverbEQFrequency;
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void	OALContext::SetReverbPreset (FSRef* inRef)
{

	try {
			Boolean				status; 
			SInt32				result = 0;
			CFURLRef			fileURL = CFURLCreateFromFSRef (kCFAllocatorDefault, inRef);
			if (fileURL) 
			{
				// Read the XML file.
				CFDataRef		resourceData = NULL;
				
				status = CFURLCreateDataAndPropertiesFromResource (kCFAllocatorDefault, fileURL, &resourceData,	NULL, NULL, &result);
				CFRelease (fileURL);	// no longer needed
				
				if (status == false || result)				
					throw (OSStatus) -1;			
				else
				{
					CFStringRef			errString = NULL;
					CFPropertyListRef   theData = NULL;
					theData = CFPropertyListCreateFromXMLData (kCFAllocatorDefault, resourceData, kCFPropertyListImmutable, &errString);
					CFRelease (resourceData);
					if (errString)
						CFRelease (errString);
					
					if (theData == NULL || errString) 
					{
						if (theData)
							CFRelease (theData);
						throw (OSStatus) -1;			
					}
					else
					{
						result = AudioUnitSetProperty(mMixerUnit, 3012 /*kAudioUnitProperty_ReverbPreset*/, kAudioUnitScope_Global, 0, &theData, sizeof(theData) );
						CFRelease (theData);
						THROW_RESULT
					}				
				}
			}
			else
				throw (OSStatus) -1;			
	}
	catch (OSStatus result) {
		throw result;
	}
	catch (...) {
		throw (OSStatus) -1;			
	}
	
	return;
}

