/*=============================================================================
	CAAudioUnit.h
 
	$Log: CAAudioUnit.h,v $
	Revision 1.47  2004/12/05 02:34:21  bills
	some tweaks to the multi can do
	
	Revision 1.46  2004/12/03 23:06:11  bills
	optimise CanDo checks
	
	Revision 1.45  2004/12/02 02:48:36  bills
	add full multi-bus CanDo support (inc. definition of CAAUChanHelper class)
	
	Revision 1.44  2004/11/29 22:58:27  bills
	add fast dispatch for MusicDeviceMIDIEvent
	
	Revision 1.43  2004/11/10 19:04:37  bills
	add global sample rate method
	
	Revision 1.42  2004/09/28 23:14:41  jcm10
	flat includes
	
	Revision 1.41  2004/07/26 19:28:23  bills
	define equality operator
	
	Revision 1.40  2004/04/28 18:41:58  bills
	add HasCustomView
	
	Revision 1.39  2004/04/17 23:25:10  bills
	add method to get an AU's latency
	
	Revision 1.38  2003/12/13 23:31:50  bills
	add handlers for dynamic/configurable I/O
	
	Revision 1.37  2003/12/03 23:05:01  bills
	don't link in AUGraph API
	
	Revision 1.36  2003/11/20 22:56:53  dwyatt
	__COREAUDIO_USE_FLAT_INCLUDES__
	
	Revision 1.35  2003/10/29 01:54:00  bills
	add element count API
	
	Revision 1.34  2003/10/25 00:17:46  bills
	fast dispatch support
	
	Revision 1.33  2003/10/22 00:33:26  bills
	add convenience for render notifications
	
	Revision 1.32  2003/10/19 20:46:40  bills
	add a preroll call
	
	Revision 1.31  2003/10/18 18:48:26  bills
	add supports num channels query
	
	Revision 1.30  2003/10/09 05:30:25  bills
	change GetNumChannels to NumberChannels for consistency
	
	Revision 1.29  2003/09/16 06:26:43  bills
	some methods should be const
	
	Revision 1.28  2003/08/16 20:55:15  bills
	add a couple more convenience calls
	
	Revision 1.27  2003/08/06 22:20:23  bills
	handle present preset property correctly
	
	Revision 1.26  2003/08/02 00:27:22  dwyatt
	add simple wrappers for most AudioUnit* functions
	
	Revision 1.25  2003/08/01 20:25:19  mhopkins
	Moved PrintMatrixMixerVolumes() into MatrixMixerVolumes.cpp
	
	Revision 1.24  2003/07/16 00:59:37  bills
	get/set AUPreset
	
	Revision 1.23  2003/07/08 14:53:42  bills
	reinstate alternate SetChanLayout
	
	Revision 1.22  2003/07/08 06:22:57  bills
	SetChannelLayout
	
	Revision 1.21  2003/07/08 00:03:57  bills
	remove unused ACL methods
	
	Revision 1.20  2003/07/07 18:36:05  bills
	fix some of the channel layout methods
	
	Revision 1.19  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.18  2003/06/02 19:25:15  bills
	tweaks
	
	Revision 1.17  2003/05/11 22:07:45  bills
	some tweaks
	
	Revision 1.16  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.15  2003/04/29 17:57:58  bills
	add print for matrix mixer
	
	Revision 1.14  2003/04/02 18:20:02  bills
	changes to make persistence work
	
	Revision 1.13  2003/03/30 23:10:45  bills
	ChannelMap to ChannelLayout
	
	Revision 1.12  2003/03/23 02:53:12  bills
	some rewrite and add methods
	
	Revision 1.11  2003/03/21 06:53:36  bills
	prelim impl of CA-AU persistence
	
	Revision 1.10  2003/03/19 21:16:34  bills
	changes to how CAAudioUnit manages its internal state
	
	Revision 1.9  2003/03/17 02:56:12  bills
	AU can now keep track of its active busses
	
	Revision 1.8  2003/03/15 18:32:35  bills
	some API tweaks
	
	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:21  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.

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

#ifndef __CAAudioUnit_h__
#define __CAAudioUnit_h__

#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
	#include <CoreServices/CoreServices.h>
	#include <CoreAudio/CoreAudio.h>
	#include <AudioUnit/AUComponent.h>
	#include <AudioToolbox/AUGraph.h>
#else
	#include <ConditionalMacros.h>
	#include <CoreServices.h>
	#include <CoreAudioTypes.h>
	#include <AUComponent.h>
	#include <AUGraph.h>
#endif

#include <vector>
#include "CAStreamBasicDescription.h"
#include "CAComponent.h"
#include "CAAudioChannelLayout.h"

// defined below
class CAAUChanHelper;

// These constructors will NOT throw exceptions - so "check" after creation if AU IsValid()
// The destructor will NOT automatically close the AU down
// This state should be managed by the Caller
// once closed, the unit represented by this object is no longer valid
// it is up to the user of this object to ensure its validity is in sync 
// if it is removed from a graph

// methods that can significantly change the state of the AU (like its format) are
// NOT const whereas those that don't change the externally related state of the AU are not const

class CAAudioUnit {
public:
	typedef std::vector<AudioChannelLayoutTag> 	ChannelTagVector;
	typedef ChannelTagVector::iterator 			ChannelTagVectorIter;

public:
							CAAudioUnit () 
								: mDataPtr(0) {}

							CAAudioUnit (const AudioUnit& inUnit);

							CAAudioUnit (const AUNode &inNode, const AudioUnit& inUnit);

							CAAudioUnit (const CAAudioUnit& y)
								: mDataPtr(0) { *this = y; }

	static OSStatus			Open (const CAComponent& inComp, CAAudioUnit &outUnit);

							~CAAudioUnit ();

	
	CAAudioUnit&			operator= (const CAAudioUnit& y);

	bool					operator== (const CAAudioUnit& y) const;

	bool					operator== (const AudioUnit& y) const;

#pragma mark __State Management	
	bool					IsValid () const;
	
	AudioUnit				AU() const;
	operator AudioUnit () const { return AU(); }

	const CAComponent&		Comp() const { return mComp; }
	
	bool					FromAUGraph () const { return GetAUNode() != 0 || GetAUNode() != -1; }
	
	AUNode					GetAUNode () const;
	operator AUNode () const { return GetAUNode(); }
	
#pragma mark __API Wrapper
	OSStatus				Initialize() const { return AudioUnitInitialize(AU()); }
	OSStatus				Uninitialize() const { return AudioUnitUninitialize(AU()); }
	OSStatus				GetPropertyInfo(AudioUnitPropertyID propID, AudioUnitScope scope, AudioUnitElement element,
											UInt32 *outDataSize, Boolean *outWritable) const
							{
								return AudioUnitGetPropertyInfo(AU(), propID, scope, element, outDataSize, outWritable);
							}
	OSStatus				GetProperty(AudioUnitPropertyID propID, AudioUnitScope scope, AudioUnitElement element,
											void *outData, UInt32 *ioDataSize) const
							{
								return AudioUnitGetProperty(AU(), propID, scope, element, outData, ioDataSize);
							}
	OSStatus				SetProperty(AudioUnitPropertyID propID, AudioUnitScope scope, AudioUnitElement element,
											const void *inData, UInt32 inDataSize)
							{
								return AudioUnitSetProperty(AU(), propID, scope, element, inData, inDataSize);
							}
	OSStatus				SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
											Float32 value, UInt32 bufferOffsetFrames=0);
							
	OSStatus				GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
											Float32 &outValue) const;

	OSStatus				Render (AudioUnitRenderActionFlags 				* ioActionFlags,
												const AudioTimeStamp 		* inTimeStamp,
												UInt32						inOutputBusNumber,
												UInt32						inNumberFrames,
												AudioBufferList				* ioData);
															
	OSStatus				Reset (AudioUnitScope scope, AudioUnitElement element)
							{
								return AudioUnitReset (AU(), scope, element);
							}
	OSStatus				GlobalReset ()
							{
								return AudioUnitReset (AU(), kAudioUnitScope_Global, 0);
							}

	OSStatus 				Preroll (UInt32 inFrameSize);

	OSStatus				AddRenderNotify (AURenderCallback   inProc, void *inProcRefCon)
							{
								return AudioUnitAddRenderNotify (AU(), inProc, inProcRefCon);
							}
	
	OSStatus				RemoveRenderNotify (AURenderCallback   inProc, void *inProcRefCon)
							{
								return AudioUnitRemoveRenderNotify (AU(), inProc, inProcRefCon);
							}
	

// Fast dispatch support for MIDI Effects or Music Devices	
	OSStatus				MIDIEvent (UInt32					inStatus,
										UInt32					inData1,
										UInt32					inData2,
										UInt32					inOffsetSampleFrame);

#pragma mark __Format Utilities
		// typically you ask this about an AU
		// These Questions are asking about Input and Output...

		// These ones just say whether an AU can do a single combination of channels
		// and is fine if the AU has a single output (and if an input, a single input)
	bool					CanDo (int inChannelsInOut) const
							{
								return CanDo (inChannelsInOut, inChannelsInOut);
							}
							
	bool					CanDo (		int 				inChannelsIn, 
										int 				inChannelsOut) const;
		
		// This version does a more thorough test for ANY AU with ANY ins/outs
		// you pass in the channel helper (for the current element count on that scope)
		
	bool					CanDo (		const CAAUChanHelper		&input,
										const CAAUChanHelper		&output) const;
	
	bool					SupportsNumChannels () const;
	
	bool					HasChannelLayouts (AudioUnitScope 		inScope, 
											AudioUnitElement 		inEl) const;
		
	bool					GetChannelLayouts (AudioUnitScope 		inScope,
									AudioUnitElement 				inEl,
									ChannelTagVector				&outChannelVector) const;
	
	OSStatus				GetChannelLayout (AudioUnitScope 		inScope,
											AudioUnitElement 		inEl,
											CAAudioChannelLayout	&outLayout) const;	

	OSStatus				SetChannelLayout (AudioUnitScope 		inScope, 
											AudioUnitElement 		inEl,
											CAAudioChannelLayout	&inLayout);

	OSStatus				SetChannelLayout (AudioUnitScope 		inScope, 
											AudioUnitElement 		inEl,
											AudioChannelLayout		&inLayout,
											UInt32					inSize);
											
	OSStatus				ClearChannelLayout (AudioUnitScope		inScope,
											AudioUnitElement		inEl);
												
	OSStatus				GetFormat (AudioUnitScope					inScope,
											AudioUnitElement			inEl,
											AudioStreamBasicDescription	&outFormat) const;
	// if an AudioChannelLayout is either required or set, this call can fail
	// and the SetChannelLayout call should be used to set the format
	OSStatus				SetFormat (AudioUnitScope							inScope,
											AudioUnitElement					inEl,
											const AudioStreamBasicDescription	&inFormat);

	OSStatus				GetSampleRate (AudioUnitScope		inScope,
											AudioUnitElement	inEl,
											Float64				&outRate) const;
	OSStatus				SetSampleRate (AudioUnitScope		inScope,
											AudioUnitElement	inEl,
											Float64				inRate);

	// this sets the sample rate on all in/out buses of the AU
	OSStatus				SetSampleRate (Float64				inSampleRate);
	
	OSStatus				NumberChannels (AudioUnitScope		inScope,
											AudioUnitElement	inEl,
											UInt32				&outChans) const;

	OSStatus				IsElementCountWritable (AudioUnitScope inScope, bool &outWritable) const;

	OSStatus				GetElementCount (AudioUnitScope 	inScope, UInt32 &outCount) const;

	OSStatus				SetElementCount (AudioUnitScope		inScope, UInt32 inCount);
		
		// value of -1 for outTotalNumChannels indicates no restriction on num channels
		// for ex. the Matrix Mixer satisfies this (its in/out element count is writable, and can be set to 
		// any number of channels.
		// outTotalNumChannels is only valid if method returns true...
	bool					HasDynamicInputs (SInt32 &outTotalNumChannels) const
							{
								return HasDynamicScope (kAudioUnitScope_Input, outTotalNumChannels);
							}
							
	bool					HasDynamicOutputs (SInt32 &outTotalNumChannels) const
							{
								return HasDynamicScope (kAudioUnitScope_Output, outTotalNumChannels);
							}
	
		// here, if the in (or out) elements are dynamic, then you supply the number of elements
		// you want on in (or out) scope, and the number of channels on each consecutive element
	OSStatus				ConfigureDynamicInput (UInt32 inNumElements, UInt32 *inChannelsPerElement, Float64 inSampleRate)
							{
								return ConfigureDynamicScope (kAudioUnitScope_Input, inNumElements, inChannelsPerElement, inSampleRate);
							}
							
	OSStatus				ConfigureDynamicOutput (UInt32 inNumElements, UInt32 *inChannelsPerElement, Float64 inSampleRate)
							{
								return ConfigureDynamicScope (kAudioUnitScope_Output, inNumElements, inChannelsPerElement, inSampleRate);
							}

	bool					CanBypass 		() const;

	bool					GetBypass 		() const;

	OSStatus				SetBypass 		(bool				inBypass) const;
	
	Float64					Latency () const;
	
		// these calls just deal with the global preset state
		// you could rescope them to deal with presets on the part scope
	OSStatus				GetAUPreset (CFPropertyListRef &outData) const;

	OSStatus				SetAUPreset (CFPropertyListRef &inData);
	
	OSStatus				GetPresentPreset (AUPreset &outData) const;
	
	OSStatus				SetPresentPreset (AUPreset &inData);
	
	bool					HasCustomView () const;
	
#pragma mark __Print	
	void					Print () const { Print (stdout); }
	void					Print (FILE* file) const;
	
private:
	CAComponent				mComp;
	
	class AUState;
	AUState*		mDataPtr;
		
		// this can throw - so wrap this up in a static that returns a result code...
	CAAudioUnit (const CAComponent& inComp);

	bool				HasDynamicScope (AudioUnitScope inScope, SInt32 &outTotalNumChannels) const;
	OSStatus			ConfigureDynamicScope (AudioUnitScope   inScope, 
											UInt32				inNumElements, 
											UInt32				*inChannelsPerElement, 
											Float64				inSampleRate);
	bool				ValidateChannelPair (int 				inChannelsIn, 
											int 				inChannelsOut,
											const AUChannelInfo * info,
											UInt32				numChanInfo) const;
											
	bool				ValidateDynamicScope (AudioUnitScope	inScope, 
											SInt32				&outTotalNumChannels, 
											const AUChannelInfo * info, 
											UInt32 numInfo) const;
	bool				CheckOneSide (const CAAUChanHelper		&inHelper, 
											bool				checkOutput, 
											const AUChannelInfo *info, 
											UInt32				numInfo) const;
	
};

class CAAUChanHelper {
public:
				CAAUChanHelper() 
					: mChans(mStaticChans), mNumEls(0), mDidAllocate(false) 
				{
					memset (mChans, 0, sizeof(UInt32) * 8);
				}
				CAAUChanHelper(const CAAudioUnit &inAU, AudioUnitScope inScope);
				CAAUChanHelper (const CAAUChanHelper &c) :mChans(mStaticChans), mNumEls(0), mDidAllocate(false) { *this = c; }
				
				~CAAUChanHelper();

	CAAUChanHelper& operator= (const CAAUChanHelper &c);

	UInt32		* mChans;
	UInt32		mNumEls;
	
private:
	UInt32 mStaticChans[8];
	bool mDidAllocate;
};

#endif
