/*=============================================================================
	CAAudioUnit.cpp
 
	$Log: CAAudioUnit.cpp,v $
	Revision 1.64  2005/01/12 20:30:13  luke
	add newlines to make gcc 4.0 happy
	
	Revision 1.63  2004/12/11 00:05:53  jcm10
	ad missing include and skip new MusicDevice entry on Windows
	
	Revision 1.62  2004/12/05 02:34:21  bills
	some tweaks to the multi can do
	
	Revision 1.61  2004/12/03 23:06:11  bills
	optimise CanDo checks
	
	Revision 1.60  2004/12/02 03:03:13  bills
	some cleanup
	
	Revision 1.59  2004/12/02 02:48:36  bills
	add full multi-bus CanDo support (inc. definition of CAAUChanHelper class)
	
	Revision 1.58  2004/11/29 22:58:27  bills
	add fast dispatch for MusicDeviceMIDIEvent
	
	Revision 1.57  2004/11/22 23:10:49  bills
	on second thought
	
	Revision 1.56  2004/11/22 23:03:47  bills
	tweak to HasDynamic test
	
	Revision 1.55  2004/11/10 19:04:36  bills
	add global sample rate method
	
	Revision 1.54  2004/08/24 00:01:47  bills
	fix warnings
	
	Revision 1.53  2004/07/26 19:28:23  bills
	define equality operator
	
	Revision 1.52  2004/07/09 02:50:53  bills
	fix handling of AUOutputBL
	
	Revision 1.51  2004/05/22 00:41:17  bills
	fix HasDynamicScope
	
	Revision 1.50  2004/04/28 18:41:58  bills
	add HasCustomView
	
	Revision 1.49  2004/04/17 23:25:10  bills
	add method to get an AU's latency
	
	Revision 1.48  2004/04/15 20:05:15  bills
	don't be particular about whether its an AU or not...
	
	Revision 1.47  2004/03/16 20:23:20  bills
	move over to AUOutputBL
	
	Revision 1.46  2003/12/13 23:32:27  bills
	add handlers for dynamic/configurable I/O
	
	Revision 1.45  2003/12/04 22:54:54  luke
	CABufferList prepare buffer takes frames not bytes
	
	Revision 1.44  2003/12/03 23:14:41  bills
	changes to CABufferList (and no dependencies on AUGrah)
	
	Revision 1.43  2003/12/02 20:50:44  luke
	depend on CABufferList instead of AUBuffer
	
	Revision 1.42  2003/12/02 00:15:46  bills
	some fixes for ref counting thread-safe-ness
	
	Revision 1.41  2003/11/20 22:56:53  dwyatt
	__COREAUDIO_USE_FLAT_INCLUDES__
	
	Revision 1.40  2003/10/29 01:54:00  bills
	add element count API
	
	Revision 1.39  2003/10/25 00:17:46  bills
	fast dispatch support
	
	Revision 1.38  2003/10/19 20:46:40  bills
	add a preroll call
	
	Revision 1.37  2003/10/18 18:48:26  bills
	add supports num channels query
	
	Revision 1.36  2003/10/09 05:30:25  bills
	change GetNumChannels to NumberChannels for consistency
	
	Revision 1.35  2003/08/24 02:27:12  bills
	fix handling of numChannels
	
	Revision 1.34  2003/08/06 22:20:23  bills
	handle present preset property correctly
	
	Revision 1.33  2003/08/01 20:25:19  mhopkins
	Moved PrintMatrixMixerVolumes() into MatrixMixerVolumes.cpp
	
	Revision 1.32  2003/07/16 00:59:37  bills
	get/set AUPreset
	
	Revision 1.31  2003/07/08 14:53:42  bills
	reinstate alternate SetChanLayout
	
	Revision 1.30  2003/07/08 06:23:36  bills
	SetChannelLayout
	
	Revision 1.29  2003/07/08 00:03:57  bills
	remove unused ACL methods
	
	Revision 1.28  2003/07/07 19:38:20  dwyatt
	use CAReferenceCounted
	
	Revision 1.27  2003/07/07 18:36:05  bills
	fix some of the channel layout methods
	
	Revision 1.26  2003/07/06 18:26:35  bills
	cleanup canDo loop
	
	Revision 1.25  2003/06/15 23:58:48  bills
	make the opening of an AU a static method that returns a bad result code if it fails (nothing worse than failing silently)
	
	Revision 1.24  2003/06/03 22:06:48  bills
	remove speaker config fallback
	
	Revision 1.23  2003/06/02 19:25:15  bills
	tweaks
	
	Revision 1.22  2003/05/25 02:45:13  bills
	don't use the busInUse stuff
	
	Revision 1.21  2003/05/11 22:07:45  bills
	some tweaks
	
	Revision 1.20  2003/05/10 19:44:44  bills
	tweak print of MM
	
	Revision 1.19  2003/05/09 06:42:22  bills
	print bus state for MM
	
	Revision 1.18  2003/04/30 06:49:31  bills
	better use of const to define those things in an AU that should and shouldn't be changed if the AU is part of a graph
	
	Revision 1.17  2003/04/29 17:57:58  bills
	add print for matrix mixer
	
	Revision 1.16  2003/04/20 09:15:51  bills
	fix operator=
	
	Revision 1.15  2003/04/02 18:20:02  bills
	changes to make persistence work
	
	Revision 1.14  2003/03/30 23:10:45  bills
	ChannelMap to ChannelLayout
	
	Revision 1.13  2003/03/23 02:53:12  bills
	some rewrite and add methods
	
	Revision 1.12  2003/03/21 06:53:36  bills
	prelim impl of CA-AU persistence
	
	Revision 1.11  2003/03/19 21:16:34  bills
	changes to how CAAudioUnit manages its internal state
	
	Revision 1.10  2003/03/17 02:56:12  bills
	AU can now keep track of its active busses
	
	Revision 1.9  2003/03/15 19:14:58  bills
	update handling of n->m channel handling
	
	Revision 1.8  2003/03/15 18:57:34  bills
	update handling of n->m channel handling
	
	Revision 1.7  2003/03/14 22:47:32  bills
	ACM handling
	
	Revision 1.6  2003/03/13 18:19:13  bills
	add handling of AudioChannelLayouts
	
	Revision 1.5  2003/03/12 23:38:20  bills
	CAAudioUnit::CanDo
	
	Revision 1.4  2003/03/12 06:49:45  bills
	redo CAAudioUnit to dynamically manage its state
	
	Revision 1.3  2003/03/12 05:05:05  bills
	refactor persistence
	
	Revision 1.2  2003/03/11 20:57:29  bills
	first pass at save/restore state
	
	Revision 1.1  2003/03/11 00:38:21  bills
	initial checkin
	

	Created by William Stewart on Sat Mar 08 2003.
	Copyright (c) 2003 Apple Computer. All rights reserved.

=============================================================================*/

#include "CAAudioUnit.h"

#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
	#include <AudioUnit/MusicDevice.h>
#else
	#include <MusicDevice.h>
#endif

#include "CAReferenceCounted.h"
#include "AUOutputBL.h" //this is for the Preroll only


struct StackAUChannelInfo {
		StackAUChannelInfo (UInt32 inSize) : mChanInfo ((AUChannelInfo*)malloc (inSize)) {}
		~StackAUChannelInfo() { free (mChanInfo); }
		
	AUChannelInfo* mChanInfo;
};



class CAAudioUnit::AUState : public CAReferenceCounted  {
public:
	AUState (Component inComp)
						: mUnit(0), mNode (0)
						{ 
							OSStatus result = ::OpenAComponent (inComp, &mUnit); 
							if (result)
								throw result;
							Init();
						}

	AUState (const AUNode &inNode, const AudioUnit& inUnit)
						: mUnit (inUnit), mNode (inNode) 
						{
							Init();
						}
						
	~AUState();
											
	AudioUnit			mUnit;
	AUNode				mNode;

	OSStatus			GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
											Float32 &outValue) const
	{
			if (mGetParamProc != NULL) {
				return reinterpret_cast<AudioUnitGetParameterProc>(mGetParamProc) (mConnInstanceStorage, 
										inID, scope, element, &outValue);
			}							
		return AudioUnitGetParameter(mUnit, inID, scope, element, &outValue);
	}

	OSStatus			SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
											Float32 value, UInt32 bufferOffsetFrames)
	{
			if (mSetParamProc != NULL) {
				return reinterpret_cast<AudioUnitSetParameterProc>(mSetParamProc) (mConnInstanceStorage, 
										inID, scope, element, value, bufferOffsetFrames);
			}							
			return AudioUnitSetParameter(mUnit, inID, scope, element, value, bufferOffsetFrames);
	}
	
	OSStatus			Render (AudioUnitRenderActionFlags *  ioActionFlags,
								const AudioTimeStamp *        inTimeStamp,
								UInt32                        inOutputBusNumber,
								UInt32                        inNumberFrames,
								AudioBufferList *             ioData)
	{
		if (mRenderProc != NULL) {
			return reinterpret_cast<AudioUnitRenderProc>(mRenderProc) (mConnInstanceStorage, 
									ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
		}							
		return AudioUnitRender(mUnit, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
	}
	
	OSStatus		MIDIEvent (UInt32					inStatus,
								UInt32					inData1,
								UInt32					inData2,
								UInt32					inOffsetSampleFrame)
	{
		if (mMIDIEventProc != NULL) {
			return reinterpret_cast<MusicDeviceMIDIEventProc>(mMIDIEventProc) (mConnInstanceStorage, 
									inStatus, inData1, inData2, inOffsetSampleFrame);
		}
#if !TARGET_OS_WIN32
		return MusicDeviceMIDIEvent (mUnit, inStatus, inData1, inData2, inOffsetSampleFrame);
#else
		return 0;
#endif
	}

private:
	// get the fast dispatch pointers
	void Init() 
	{
		UInt32 size = sizeof(AudioUnitRenderProc);
		if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
								kAudioUnitScope_Global, kAudioUnitRenderSelect,
								&mRenderProc, &size) != noErr)
			mRenderProc = NULL;
		if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
								kAudioUnitScope_Global, kAudioUnitGetParameterSelect,
								&mGetParamProc, &size) != noErr)
			mGetParamProc = NULL;
		if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
								kAudioUnitScope_Global, kAudioUnitSetParameterSelect,
								&mSetParamProc, &size) != noErr)
			mSetParamProc = NULL;

		if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
								kAudioUnitScope_Global, kMusicDeviceMIDIEventSelect,
								&mMIDIEventProc, &size) != noErr)
			mMIDIEventProc = NULL;
		
		if (mRenderProc || mGetParamProc || mSetParamProc || mMIDIEventProc)
			mConnInstanceStorage = GetComponentInstanceStorage(mUnit);
		else
			mConnInstanceStorage = NULL;
	}
	
	ProcPtr						mRenderProc, mGetParamProc, mSetParamProc, mMIDIEventProc;

	void *						mConnInstanceStorage;

private:
		// get the compiler to tell us when we do a bad thing!!!
	AUState () {}
	AUState (const AUState&) {}
	AUState& operator= (const AUState&) { return *this; } 
};						
						
						
CAAudioUnit::AUState::~AUState ()
{
	if (mUnit && (mNode == 0)) {
		::CloseComponent (mUnit);
	}
	mNode = 0;
	mUnit = 0;
}

OSStatus		CAAudioUnit::Open (const CAComponent& inComp, CAAudioUnit &outUnit)
{
	try {
		outUnit = inComp; 
		return noErr;
	} catch (OSStatus res) {
		return res;
	} catch (...) {
		return -1;
	}
}

CAAudioUnit::CAAudioUnit (const AudioUnit& inUnit)
	: mComp (inUnit), mDataPtr (new AUState (-1, inUnit))
{
}

CAAudioUnit::CAAudioUnit (const CAComponent& inComp)
	: mComp (inComp), mDataPtr (0)
{
	mDataPtr = new AUState (mComp.Comp());
}

CAAudioUnit::CAAudioUnit (const AUNode &inNode, const AudioUnit& inUnit)
	: mComp (inUnit), mDataPtr(new AUState (inNode, inUnit)) 
{
}

CAAudioUnit::~CAAudioUnit ()
{
	if (mDataPtr) {
		mDataPtr->release();
		mDataPtr = NULL;
	}
}

CAAudioUnit&	CAAudioUnit::operator= (const CAAudioUnit &a)
{
	if (mDataPtr != a.mDataPtr) {
		if (mDataPtr)
			mDataPtr->release();
	
		if ((mDataPtr = a.mDataPtr) != NULL)
			mDataPtr->retain();
		
		mComp = a.mComp;
	}
	
	return *this;
}

bool			CAAudioUnit::operator== (const CAAudioUnit& y) const
{
	if (mDataPtr == y.mDataPtr) return true;
	AudioUnit au1 = mDataPtr ? mDataPtr->mUnit : 0;
	AudioUnit au2 = y.mDataPtr ? y.mDataPtr->mUnit : 0;
	return au1 == au2;
}

bool			CAAudioUnit::operator== (const AudioUnit& y) const
{
	if (!mDataPtr) return false;
	return mDataPtr->mUnit == y;
}

#pragma mark __State Management	

bool			CAAudioUnit::IsValid () const 
{ 
	return mDataPtr ? mDataPtr->mUnit != 0 : false; 
}
	
AudioUnit		CAAudioUnit::AU() const 
{ 
	return mDataPtr ? mDataPtr->mUnit : 0; 
}

AUNode			CAAudioUnit::GetAUNode () const
{
	return mDataPtr ? mDataPtr->mNode : 0; 
}

#pragma mark __Format Handling
	
bool		CAAudioUnit::CanDo (	int 				inChannelsIn, 
									int 				inChannelsOut) const
{		
	// this is the default assumption of an audio effect unit
	Boolean* isWritable = 0;
	UInt32	dataSize = 0;
		// lets see if the unit has any channel restrictions
	OSStatus result = AudioUnitGetPropertyInfo (AU(),
									kAudioUnitProperty_SupportedNumChannels,
									kAudioUnitScope_Global, 0,
									&dataSize, isWritable); //don't care if this is writable
		
		// if this property is NOT implemented an FX unit
		// is expected to deal with same channel valance in and out
	if (result) 
	{
		if (Comp().Desc().IsEffect() && (inChannelsIn == inChannelsOut)
			|| Comp().Desc().IsOffline() && (inChannelsIn == inChannelsOut))
		{
			return true;
		}
		else 
		{
			// the au should either really tell us about this
			// or we will assume the worst
			return false;
		}
	}
	
	StackAUChannelInfo info (dataSize);
	
	result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
							kAudioUnitScope_Global, 0,
							info.mChanInfo, &dataSize);
	if (result) { return false; }
	
	return ValidateChannelPair (inChannelsIn, inChannelsOut, info.mChanInfo, (dataSize / sizeof (AUChannelInfo)));
}

bool	CAAudioUnit::ValidateChannelPair (int 				inChannelsIn, 
										int 				inChannelsOut,
										const AUChannelInfo * info,
										UInt32				numChanInfo) const
{
// we've the following cases (some combinations) to test here:
/*
>0		An explicit number of channels on either side
0		that side (generally input!) has no elements
-1		wild card:
-1,-1	any num channels as long as same channels on in and out
-1,-2	any num channels channels on in and out - special meaning
-2+ 	indicates total num channs AU can handle 
			- elements configurable to any num channels, 
			- element count in scope must be writable
*/

	//now chan layout can contain -1 for either scope (ie. doesn't care)
	for (unsigned int i = 0; i < numChanInfo; ++i)
	{
			//less than zero on both sides - check for special attributes
		if ((info[i].inChannels < 0) && (info[i].outChannels < 0))
		{
				// these are our wild card matches
			if (info[i].inChannels == -1 && info[i].outChannels == -1) {
				if (inChannelsOut == inChannelsIn) {
					return true;
				}
			}
			else if ((info[i].inChannels == -1 && info[i].outChannels == -2)
					|| (info[i].inChannels == -2 && info[i].outChannels == -1)) 
			{
				return true;
			}
				// these are our total num channels matches
				// element count MUST be writable
			else {
				bool outWrite = false; bool inWrite = false;
				IsElementCountWritable (kAudioUnitScope_Output, outWrite);
				IsElementCountWritable (kAudioUnitScope_Input, inWrite);
				if (inWrite && outWrite) {
					if ((inChannelsOut <= abs(info[i].outChannels))
						&& (inChannelsIn <= abs(info[i].inChannels))) 
					{
						return true;
					}
				}
			}
		}
			
			// special meaning on input, specific num on output
		else if (info[i].inChannels < 0) {
			if (info[i].outChannels == inChannelsOut) 
			{
					// can do any in channels
				if (info[i].inChannels == -1) {
					return true;
				} 
					// total chans on input
				else {
					bool inWrite = false;
					IsElementCountWritable (kAudioUnitScope_Input, inWrite);
					if (inWrite && (inChannelsIn <= abs(info[i].inChannels))) {
						return true;
					}
				}
			}
		}
		
			// special meaning on output, specific num on input
		else if (info[i].outChannels < 0) {
			if (info[i].inChannels == inChannelsIn) 
			{
					// can do any out channels
				if (info[i].outChannels == -1) {
					return true;
				} 
					// total chans on output
				else {
					bool outWrite = false;
					IsElementCountWritable (kAudioUnitScope_Output, outWrite);
					if (outWrite && (inChannelsOut <= abs(info[i].outChannels))) {
						return true;
					}
				}
			}
		}

			// both chans in struct >= 0 - thus has to explicitly match
		else if ((info[i].inChannels == inChannelsIn) && (info[i].outChannels == inChannelsOut)) {
			return true;
		} 
		
			// now check to see if a wild card on the args (inChannelsIn or inChannelsOut chans is zero) is found 
			// tells us to match just one side of the scopes
		else if (inChannelsIn == 0) {
			if (info[i].outChannels == inChannelsOut) {
				return true;
			}
		}
		else if (inChannelsOut == 0) {
			if (info[i].inChannels == inChannelsIn) {
				return true;
			}
		}
	}
	
	return false;
}

bool CheckDynCount (SInt32 inTotalChans, const CAAUChanHelper &inHelper)
{
	int totalChans = 0;
	for (unsigned int i = 0; i < inHelper.mNumEls; ++i)
		totalChans += inHelper.mChans[i];
	return (totalChans <= inTotalChans);
}

bool	CAAudioUnit::CheckOneSide (const CAAUChanHelper		&inHelper, 
									bool					checkOutput, 
									const AUChannelInfo		*info, 
									UInt32					numInfo) const
{
		// now we can use the wildcard option (see above impl) to see if this matches
	for (unsigned int el = 0; el < inHelper.mNumEls; ++el) {
		bool testAlready = false;
		for (unsigned int i = 0; i < el; ++i) {
			if (inHelper.mChans[i] == inHelper.mChans[el]) {
				testAlready = true;
				break;
			}
		}
		if (!testAlready) {
			if (checkOutput) {
				if (!ValidateChannelPair (0, inHelper.mChans[el], info, numInfo)) return false;
			} else {
				if (!ValidateChannelPair (inHelper.mChans[el], 0, info, numInfo)) return false;
			}
		}
	}
	return true;
}

bool		CAAudioUnit::CanDo (const CAAUChanHelper		&inputs,  
								const CAAUChanHelper		&outputs) const

{
// first check our state
		// huh!
	if (inputs.mNumEls == 0 && outputs.mNumEls == 0) return false;
	
	UInt32 elCount;
	if (GetElementCount (kAudioUnitScope_Input, elCount)) { return false; }
	if (elCount != inputs.mNumEls) return false;

	if (GetElementCount (kAudioUnitScope_Output, elCount)) { return false; }
	if (elCount != outputs.mNumEls) return false;
		
// (1) special cases (effects and sources (generators and instruments) only)
	UInt32	dataSize = 0;
	if (GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
									kAudioUnitScope_Global, 0, &dataSize, NULL) != noErr) 
	{
		if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline()) {
			UInt32 numChan = outputs.mNumEls > 0 ? outputs.mChans[0] : inputs.mChans[0];
			for (unsigned int in = 0; in < inputs.mNumEls; ++in)
				if (numChan != inputs.mChans[in]) return false;
			for (unsigned int out = 0; out < outputs.mNumEls; ++out)
				if (numChan != outputs.mChans[out]) return false;
			return true;
		}
		
			// in this case, all the channels have to match the current config
		if (Comp().Desc().IsGenerator() || Comp().Desc().IsMusicDevice()) {
			for (unsigned int in = 0; in < inputs.mNumEls; ++in) {
				UInt32 chan;
				if (!NumberChannels (kAudioUnitScope_Input, in, chan)) return false;
				if (chan != UInt32(inputs.mChans[in])) return false;
			}
			for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
				UInt32 chan;
				if (!NumberChannels (kAudioUnitScope_Output, out, chan)) return false;
				if (chan != UInt32(outputs.mChans[out])) return false;
			}
			return true;
		}
		
			// if we get here we can't determine anything about channel capabilities
		return false;
	}

	StackAUChannelInfo info (dataSize);
	
	if (GetProperty (kAudioUnitProperty_SupportedNumChannels,
							kAudioUnitScope_Global, 0,
							info.mChanInfo, &dataSize) != noErr)
	{ 
		return false; 
	}
	
	int numInfo = dataSize / sizeof(AUChannelInfo);
	
// (2) Test for dynamic capability (or no elements on that scope)
	SInt32 dynInChans = 0;
	if (ValidateDynamicScope (kAudioUnitScope_Input, dynInChans, info.mChanInfo, numInfo)) {
		if (CheckDynCount (dynInChans, inputs) == false) return false;
	}

	SInt32 dynOutChans = 0;
	if (ValidateDynamicScope (kAudioUnitScope_Output, dynOutChans, info.mChanInfo, numInfo)) {
		if (CheckDynCount (dynOutChans, outputs) == false) return false;
	}

	if (dynOutChans && dynInChans) { return true; }

// (3)	Just need to test one side
	if (dynInChans || (inputs.mNumEls == 0)) {
		return CheckOneSide (outputs, true, info.mChanInfo, numInfo);
	}

	if (dynOutChans || (outputs.mNumEls == 0)) {
		return CheckOneSide (inputs, false, info.mChanInfo, numInfo);
	}

// (4) - not a dynamic AU, has ins and outs, and has channel constraints so we test every possible pairing
	for (unsigned int in = 0; in < inputs.mNumEls; ++in) 
	{
		bool testInAlready = false;
		for (unsigned int i = 0; i < in; ++i) {
			if (inputs.mChans[i] == inputs.mChans[in]) {
				testInAlready = true;
				break;
			}
		}
		if (!testInAlready) {
			for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
					// try to save a little bit and not test the same pairing multiple times...
				bool testOutAlready = false;
				for (unsigned int i = 0; i < out; ++i) {
					if (outputs.mChans[i] == outputs.mChans[out]) {
						testOutAlready = true;
						break;
					}
				}
				if (!testOutAlready) {
					if (!ValidateChannelPair (inputs.mChans[in], outputs.mChans[out],info.mChanInfo, numInfo)) {
						return false;
					}
				}
			}
		}
	}
	
	return true;
}

bool		CAAudioUnit::SupportsNumChannels () const
{
	// this is the default assumption of an audio effect unit
	Boolean* isWritable = 0;
	UInt32	dataSize = 0;
		// lets see if the unit has any channel restrictions
	OSStatus result = AudioUnitGetPropertyInfo (AU(),
									kAudioUnitProperty_SupportedNumChannels,
									kAudioUnitScope_Global, 0,
									&dataSize, isWritable); //don't care if this is writable
		
		// if this property is NOT implemented an FX unit
		// is expected to deal with same channel valance in and out
	if (result) {
		if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline())
			return true;
	}
	return result == noErr;
}

bool		CAAudioUnit::GetChannelLayouts (AudioUnitScope 			inScope,
										AudioUnitElement 			inEl,
										ChannelTagVector			&outChannelVector) const
{
	if (HasChannelLayouts (inScope, inEl) == false) return false; 

	UInt32 dataSize;
	OSStatus result = AudioUnitGetPropertyInfo (AU(),
								kAudioUnitProperty_SupportedChannelLayoutTags,
								inScope, inEl,
								&dataSize, NULL);

	if (result == kAudioUnitErr_InvalidProperty) {
		// if we get here we can do layouts but we've got the speaker config property
		outChannelVector.erase (outChannelVector.begin(), outChannelVector.end());
		outChannelVector.push_back (kAudioChannelLayoutTag_Stereo);
		outChannelVector.push_back (kAudioChannelLayoutTag_StereoHeadphones);
		outChannelVector.push_back (kAudioChannelLayoutTag_Quadraphonic);
		outChannelVector.push_back (kAudioChannelLayoutTag_AudioUnit_5_0);
		return true;
	}

	if (result) return false;
	
	bool canDo = false;
		// OK lets get our channel layouts and see if the one we want is present
	AudioChannelLayoutTag* info = (AudioChannelLayoutTag*)malloc (dataSize);
	result = AudioUnitGetProperty (AU(),
							kAudioUnitProperty_SupportedChannelLayoutTags,
							inScope, inEl,
							info, &dataSize);
	if (result) goto home;
	
	outChannelVector.erase (outChannelVector.begin(), outChannelVector.end());
	for (unsigned int i = 0; i < (dataSize / sizeof (AudioChannelLayoutTag)); ++i)
		outChannelVector.push_back (info[i]);

home:
	free (info);
	return canDo;
}

bool		CAAudioUnit::HasChannelLayouts (AudioUnitScope 		inScope, 
										AudioUnitElement 		inEl) const
{
	OSStatus result = AudioUnitGetPropertyInfo (AU(),
									kAudioUnitProperty_SupportedChannelLayoutTags,
									inScope, inEl,
									NULL, NULL);
	return !result;
}

OSStatus	CAAudioUnit::GetChannelLayout (AudioUnitScope 		inScope,
										AudioUnitElement 		inEl,
										CAAudioChannelLayout	&outLayout) const
{
	UInt32 size;
	OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_AudioChannelLayout,
									inScope, inEl, &size, NULL);
	if (result) return result;
	
	AudioChannelLayout *layout = (AudioChannelLayout*)malloc (size);

	require_noerr (result = AudioUnitGetProperty (AU(), kAudioUnitProperty_AudioChannelLayout,
									inScope, inEl, layout, &size), home);

	outLayout = CAAudioChannelLayout (layout);
	
home:
	free (layout);
	return result;
}

OSStatus	CAAudioUnit::SetChannelLayout (AudioUnitScope 		inScope,
									AudioUnitElement 			inEl,
									CAAudioChannelLayout 		&inLayout)
{
	OSStatus result = AudioUnitSetProperty (AU(),
									kAudioUnitProperty_AudioChannelLayout,
									inScope, inEl,
									inLayout, inLayout.Size());
	return result;
}

OSStatus	CAAudioUnit::SetChannelLayout (AudioUnitScope 			inScope, 
											AudioUnitElement 		inEl,
											AudioChannelLayout		&inLayout,
											UInt32					inSize)
{
	OSStatus result = AudioUnitSetProperty (AU(),
									kAudioUnitProperty_AudioChannelLayout,
									inScope, inEl,
									&inLayout, inSize);
	return result;
}

OSStatus		CAAudioUnit::ClearChannelLayout (AudioUnitScope	inScope,
											AudioUnitElement	inEl)
{
	return AudioUnitSetProperty (AU(),
							kAudioUnitProperty_AudioChannelLayout,
							inScope, inEl, NULL, 0);
}

OSStatus	CAAudioUnit::GetFormat (AudioUnitScope				inScope,
									AudioUnitElement			inEl,
									AudioStreamBasicDescription	&outFormat) const
{
	UInt32 dataSize = sizeof (AudioStreamBasicDescription);
	return AudioUnitGetProperty (AU(), kAudioUnitProperty_StreamFormat,
								inScope, inEl, 
								&outFormat, &dataSize);
}

OSStatus	CAAudioUnit::SetFormat (AudioUnitScope						inScope,
									AudioUnitElement					inEl,
									const AudioStreamBasicDescription	&inFormat)
{
	return AudioUnitSetProperty (AU(), kAudioUnitProperty_StreamFormat,
								inScope, inEl,
								const_cast<AudioStreamBasicDescription*>(&inFormat), 
								sizeof (AudioStreamBasicDescription));
}

OSStatus	CAAudioUnit::GetSampleRate (AudioUnitScope		inScope,
										AudioUnitElement	inEl,
										Float64				&outRate) const
{
	UInt32 dataSize = sizeof (Float64);
	return AudioUnitGetProperty (AU(), kAudioUnitProperty_SampleRate,
								inScope, inEl, 
								&outRate, &dataSize);
}

OSStatus	CAAudioUnit::SetSampleRate (AudioUnitScope		inScope,
										AudioUnitElement	inEl,
										Float64				inRate)
{
	return AudioUnitSetProperty (AU(), kAudioUnitProperty_SampleRate,
								inScope, inEl,
								&inRate, sizeof (Float64));
}

OSStatus	CAAudioUnit::SetSampleRate (Float64			inSampleRate)
{
	OSStatus result;
	
	UInt32 elCount;
	require_noerr (result = GetElementCount(kAudioUnitScope_Input, elCount), home);
	if (elCount) {
		for (unsigned int i = 0; i < elCount; ++i) {
			require_noerr (result = SetSampleRate (kAudioUnitScope_Input, i, inSampleRate), home);
		}
	}

	require_noerr (result = GetElementCount(kAudioUnitScope_Output, elCount), home);
	if (elCount) {
		for (unsigned int i = 0; i < elCount; ++i) {
			require_noerr (result = SetSampleRate (kAudioUnitScope_Output, i, inSampleRate), home);
		}
	}
	
home:
	return result;
}

OSStatus	CAAudioUnit::NumberChannels (AudioUnitScope		inScope,
										AudioUnitElement	inEl,
										UInt32				&outChans) const
{
	AudioStreamBasicDescription desc;
	OSStatus result = GetFormat (inScope, inEl, desc);
	if (!result)
		outChans = desc.mChannelsPerFrame;
	return result;
}

OSStatus		CAAudioUnit::IsElementCountWritable (AudioUnitScope inScope, bool &outWritable) const
{
	Boolean isWritable;
	UInt32 outDataSize;
	OSStatus result = GetPropertyInfo (kAudioUnitProperty_ElementCount, inScope, 0, &outDataSize, &isWritable);
	if (result)
		return result;
	outWritable = isWritable ? true : false;
	return noErr;	
}

OSStatus		CAAudioUnit::GetElementCount (AudioUnitScope inScope, UInt32 &outCount) const
{
	UInt32 propSize = sizeof(outCount);
	return GetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &outCount, &propSize);
}

OSStatus		CAAudioUnit::SetElementCount (AudioUnitScope inScope, UInt32 inCount)
{
	return SetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &inCount, sizeof(inCount));
}

bool			CAAudioUnit::HasDynamicScope (AudioUnitScope inScope, SInt32 &outTotalNumChannels) const
{
	// ok - now we need to check the AU's capability here.
	// this is the default assumption of an audio effect unit
	Boolean* isWritable = 0;
	UInt32	dataSize = 0;
	OSStatus result = GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
								kAudioUnitScope_Global, 0,
								&dataSize, isWritable); //don't care if this is writable
		
		// AU has to explicitly tell us about this.
	if (result) return false;

	StackAUChannelInfo info (dataSize);
	
	result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
							kAudioUnitScope_Global, 0,
							info.mChanInfo, &dataSize);
	if (result) return false;

	return ValidateDynamicScope (inScope, outTotalNumChannels, info.mChanInfo, (dataSize / sizeof(AUChannelInfo)));
}

// as we've already checked that the element count is writable
// the following conditions will match this..
/*
-1, -2 ->	signifies no restrictions
-2, -1 ->	signifies no restrictions -> in this case outTotalNumChannels == -1 (any num channels)

-N 	(where N is less than -2), signifies the total channel count on the scope side (in or out)
*/
bool	CAAudioUnit::ValidateDynamicScope (AudioUnitScope		inScope, 
											SInt32				&outTotalNumChannels, 
											const AUChannelInfo *info, 
											UInt32				numInfo) const
{
	bool writable = false;
	OSStatus result = IsElementCountWritable (inScope, writable);
	if (result || (writable == false))
		return false;

	//now chan layout can contain -1 for either scope (ie. doesn't care)
	for (unsigned int i = 0; i < numInfo; ++i)
	{
		// lets test the special wild card case first...
		// this says the AU can do any num channels on input or output - for eg. Matrix Mixer
		if (((info[i].inChannels == -1) && (info[i].outChannels == -2))
			|| ((info[i].inChannels == -2) && (info[i].outChannels == -1)))
		{
			outTotalNumChannels = -1;
			return true;
		}
		
		// ok lets now test our special case....
		if (inScope == kAudioUnitScope_Input) {
				// isn't dynamic on this side at least
			if (info[i].inChannels >= 0)
				continue;
				
			if (info[i].inChannels < -2) {
				outTotalNumChannels = abs (info[i].inChannels);
				return true;
			}
		} 
		
		else if (inScope == kAudioUnitScope_Output) {
				// isn't dynamic on this side at least
			if (info[i].outChannels >= 0)
				continue;
				
			if (info[i].outChannels < -2) {
				outTotalNumChannels = abs (info[i].outChannels);
				return true;
			}
		} 
		
		else {
			break; // wrong scope was specified
		}
	}
	
	return false;	
}

OSStatus	CAAudioUnit::ConfigureDynamicScope (AudioUnitScope 		inScope, 
											UInt32 					inNumElements, 
											UInt32 					*inChannelsPerElement, 
											Float64 				inSampleRate)
{
	SInt32 numChannels = 0;
	bool isDyamic = HasDynamicScope (inScope, numChannels);
	if (isDyamic == false)
		return kAudioUnitErr_InvalidProperty;
	
	//lets to a sanity check...
	// if numChannels == -1, then it can do "any"...
	if (numChannels > 0) {
		SInt32 count = 0;
		for (unsigned int i = 0; i < inNumElements; ++i)
			count += inChannelsPerElement[i];
		if (count > numChannels)
			return kAudioUnitErr_InvalidPropertyValue;
	}
	
	OSStatus result = SetElementCount (inScope, inNumElements);
	if (result)
		return result;
		
	CAStreamBasicDescription desc;
	desc.mSampleRate = inSampleRate;
	for (unsigned int i = 0; i < inNumElements; ++i) {
		desc.SetCanonical (inChannelsPerElement[i], false);
		result = SetFormat (inScope, i, desc);
		if (result)
			return result;
	}
	return noErr;
}

#pragma mark __Properties

bool		CAAudioUnit::CanBypass () const
{
	Boolean outWritable;
	OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_BypassEffect,
									kAudioUnitScope_Global, 0,
									NULL, &outWritable);
	return (!result && outWritable);
}

bool		CAAudioUnit::GetBypass 		() const
{
	UInt32 dataSize = sizeof (UInt32);
	UInt32 outBypass;
	OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_BypassEffect,
								kAudioUnitScope_Global, 0,
								&outBypass, &dataSize);
	return (result ? false : outBypass);
}

OSStatus	CAAudioUnit::SetBypass 		(bool	inBypass) const
{	
	UInt32 bypass = inBypass ? 1 : 0;
	return AudioUnitSetProperty (AU(), kAudioUnitProperty_BypassEffect,
								kAudioUnitScope_Global, 0,
								&bypass, sizeof (UInt32));
}

Float64		CAAudioUnit::Latency () const
{
	Float64 secs;
	UInt32 size = sizeof(secs);
	if (GetProperty (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &secs, &size))
		return 0;
	return secs;
}

OSStatus	CAAudioUnit::GetAUPreset (CFPropertyListRef &outData) const
{
	UInt32 dataSize = sizeof(outData);
	return AudioUnitGetProperty (AU(), kAudioUnitProperty_ClassInfo,
								kAudioUnitScope_Global, 0,
								&outData, &dataSize);
}

OSStatus	CAAudioUnit::SetAUPreset (CFPropertyListRef &inData)
{
	return AudioUnitSetProperty (AU(), kAudioUnitProperty_ClassInfo,
								kAudioUnitScope_Global, 0,
								&inData, sizeof (CFPropertyListRef));
}

OSStatus	CAAudioUnit::GetPresentPreset (AUPreset &outData) const
{
	UInt32 dataSize = sizeof(outData);
	OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_PresentPreset,
								kAudioUnitScope_Global, 0,
								&outData, &dataSize);
	if (result == kAudioUnitErr_InvalidProperty) {
		dataSize = sizeof(outData);
		result = AudioUnitGetProperty (AU(), kAudioUnitProperty_CurrentPreset,
									kAudioUnitScope_Global, 0,
									&outData, &dataSize);
		if (result == noErr) {
			// we now retain the CFString in the preset so for the client of this API
			// it is consistent (ie. the string should be released when done)
			if (outData.presetName)
				CFRetain (outData.presetName);
		}
	}
	return result;
}
	
OSStatus	CAAudioUnit::SetPresentPreset (AUPreset &inData)
{
	OSStatus result = AudioUnitSetProperty (AU(), kAudioUnitProperty_PresentPreset,
								kAudioUnitScope_Global, 0,
								&inData, sizeof (AUPreset));
	if (result == kAudioUnitErr_InvalidProperty) {
		result = AudioUnitSetProperty (AU(), kAudioUnitProperty_CurrentPreset,
								kAudioUnitScope_Global, 0,
								&inData, sizeof (AUPreset));
	}
	return result;
}

bool		CAAudioUnit::HasCustomView () const
{
	UInt32 dataSize = 0;
	OSStatus result = GetPropertyInfo(kAudioUnitProperty_GetUIComponentList,
                                        kAudioUnitScope_Global, 0,
                                        &dataSize, NULL);
	if (result || !dataSize) {
		dataSize = 0;
		result = GetPropertyInfo(kAudioUnitProperty_CocoaUI,
                                        kAudioUnitScope_Global, 0,
                                        &dataSize, NULL);
		if (result || !dataSize)
			return false;
	}
	return true;
}

OSStatus		CAAudioUnit::GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
											Float32 &outValue) const
{
	return mDataPtr ? mDataPtr->GetParameter (inID, scope, element, outValue) : paramErr;
}

OSStatus		CAAudioUnit::SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
											Float32 value, UInt32 bufferOffsetFrames)
{
	return mDataPtr ? mDataPtr->SetParameter (inID, scope, element, value, bufferOffsetFrames) : paramErr;
}

OSStatus		CAAudioUnit::MIDIEvent (UInt32			inStatus,
								UInt32					inData1,
								UInt32					inData2,
								UInt32					inOffsetSampleFrame)
{
	return mDataPtr ? mDataPtr->MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame) : paramErr;
}

#pragma mark __Render

OSStatus		CAAudioUnit::Render (AudioUnitRenderActionFlags 			* ioActionFlags,
												const AudioTimeStamp 		* inTimeStamp,
												UInt32						inOutputBusNumber,
												UInt32						inNumberFrames,
												AudioBufferList				* ioData)
{
	return mDataPtr ? mDataPtr->Render (ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData) : paramErr;
}

static AURenderCallbackStruct sRenderCallback;
static OSStatus PrerollRenderProc (	void 						* /*inRefCon*/, 
								AudioUnitRenderActionFlags		* /*inActionFlags*/,
								const AudioTimeStamp 			* /*inTimeStamp*/, 
								UInt32 							/*inBusNumber*/,
								UInt32							/*inNumFrames*/, 
								AudioBufferList 				*ioData)
{
	AudioBuffer *buf = ioData->mBuffers;
	for (UInt32 i = ioData->mNumberBuffers; i--; ++buf)
		memset((Byte *)buf->mData, 0, buf->mDataByteSize);

	return noErr;
}

OSStatus 	CAAudioUnit::Preroll (UInt32 inFrameSize)
{
	CAStreamBasicDescription desc;
	OSStatus result = GetFormat (kAudioUnitScope_Input, 0, desc);
	bool hasInput = false;
			//we have input	
	if (result == noErr) 
	{
		sRenderCallback.inputProc = PrerollRenderProc;
		sRenderCallback.inputProcRefCon = 0;
		
		result = SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 
								0, &sRenderCallback, sizeof(sRenderCallback));
		if (result) return result;
		hasInput = true;
	}
	
	AudioUnitRenderActionFlags flags = 0;
	AudioTimeStamp time;
	memset (&time, 0, sizeof(time));
	time.mFlags = kAudioTimeStampSampleTimeValid;

	CAStreamBasicDescription outputFormat;
	require_noerr (result = GetFormat (kAudioUnitScope_Output, 0, outputFormat), home);
	{
		AUOutputBL list (outputFormat, inFrameSize);
		list.Prepare ();
		
		require_noerr (result = Render (&flags, &time, 0, inFrameSize, list.ABL()), home);
		require_noerr (result = GlobalReset(), home);
	}

home:
	if (hasInput) {
            // remove our installed callback
		sRenderCallback.inputProc = 0;
		sRenderCallback.inputProcRefCon = 0;
		
		SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 
								0, &sRenderCallback, sizeof(sRenderCallback));
	}
	return result;
}

#pragma mark __CAAUChanHelper

CAAUChanHelper::CAAUChanHelper(const CAAudioUnit &inAU, AudioUnitScope inScope)
	:mChans(NULL), mNumEls(0), mDidAllocate(false)
{
	UInt32 elCount;
	if (inAU.GetElementCount (inScope, elCount)) return;
	if (elCount > 8) {
		mChans = new UInt32[elCount];
		mDidAllocate = true;
		memset (mChans, 0, sizeof(int) * elCount);
	} else {
		mChans = mStaticChans;
		memset (mChans, 0, sizeof(int) * 8);
	}
	for (unsigned int i = 0; i < elCount; ++i) {
		UInt32 numChans;
		if (inAU.NumberChannels (inScope, i, numChans)) return;
		mChans[i] = numChans;
	}
	mNumEls = elCount;
}

CAAUChanHelper::~CAAUChanHelper()
{
	if (mDidAllocate) delete [] mChans;
}

CAAUChanHelper&		CAAUChanHelper::operator= (const CAAUChanHelper &c) 
{ 
	if (mDidAllocate) delete [] mChans;
	if (c.mDidAllocate) {
		mChans = new UInt32[c.mNumEls];
		mDidAllocate = true;
	} else {
		mDidAllocate = false;
		mChans = mStaticChans;
	}
	memcpy (mChans, c.mChans, c.mNumEls * sizeof(int));
	
	return *this; 
}

#pragma mark __Print Utilities

void		CAAudioUnit::Print (FILE* file) const
{
	fprintf (file, "AudioUnit:0x%X\n", int(AU()));
	if (IsValid()) { 
		fprintf (file, "\tnode=%ld\t", GetAUNode()); Comp().Print (file);
	}
}
