1 /*
2      File: CAAudioUnit.cpp
3  Abstract: CAAudioUnit.h
4   Version: 1.1
5 
6  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
7  Inc. ("Apple") in consideration of your agreement to the following
8  terms, and your use, installation, modification or redistribution of
9  this Apple software constitutes acceptance of these terms.  If you do
10  not agree with these terms, please do not use, install, modify or
11  redistribute this Apple software.
12 
13  In consideration of your agreement to abide by the following terms, and
14  subject to these terms, Apple grants you a personal, non-exclusive
15  license, under Apple's copyrights in this original Apple software (the
16  "Apple Software"), to use, reproduce, modify and redistribute the Apple
17  Software, with or without modifications, in source and/or binary forms;
18  provided that if you redistribute the Apple Software in its entirety and
19  without modifications, you must retain this notice and the following
20  text and disclaimers in all such redistributions of the Apple Software.
21  Neither the name, trademarks, service marks or logos of Apple Inc. may
22  be used to endorse or promote products derived from the Apple Software
23  without specific prior written permission from Apple.  Except as
24  expressly stated in this notice, no other rights or licenses, express or
25  implied, are granted by Apple herein, including but not limited to any
26  patent rights that may be infringed by your derivative works or by other
27  works in which the Apple Software may be incorporated.
28 
29  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
30  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34 
35  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42  POSSIBILITY OF SUCH DAMAGE.
43 
44  Copyright (C) 2014 Apple Inc. All Rights Reserved.
45 
46 */
47 #include "CAAudioUnit.h"
48 
49 #if !TARGET_OS_IPHONE
50 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
51 	#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/Components.h>
52 	#include <dlfcn.h>
53 #else
54 	#include <Components.h>
55 #endif
56 #endif
57 
58 #include "CAXException.h"
59 #include "CAReferenceCounted.h"
60 #include "AUOutputBL.h" //this is for the Preroll only
61 
62 struct StackAUChannelInfo {
StackAUChannelInfoStackAUChannelInfo63 		StackAUChannelInfo (UInt32 inSize) : mChanInfo ((AUChannelInfo*)malloc (inSize)) {}
~StackAUChannelInfoStackAUChannelInfo64 		~StackAUChannelInfo() { free (mChanInfo); }
65 
66 	AUChannelInfo* mChanInfo;
67 };
68 
69 // is this symbol is not defined then we use the default setting which is that fast dispatch
70 // is supported on a desktop environment
71 #ifndef CA_AU_USE_FAST_DISPATCH
72 	#define CA_AU_USE_FAST_DISPATCH !TARGET_OS_IPHONE
73 #endif
74 
75 #if CA_AU_USE_FAST_DISPATCH
76 static void *LoadGetComponentInstanceStorage (void *inst);
77 #endif
78 
79 
80 class CAAudioUnit::AUState : public CAReferenceCounted  {
81 public:
AUState(AudioComponent inComp)82 	AUState (AudioComponent inComp)
83 						: mUnit(0), mNode (0)
84 						{
85 							OSStatus result = ::AudioComponentInstanceNew (inComp, &mUnit);
86 							if (result)
87 								throw result;
88 							Init();
89 						}
90 
AUState(const AUNode & inNode,const AudioUnit & inUnit)91 	AUState (const AUNode &inNode, const AudioUnit& inUnit)
92 						: mUnit (inUnit), mNode (inNode)
93 						{
94 							Init();
95 						}
96 
97 	~AUState();
98 
99 	AudioUnit			mUnit;
100 	AUNode				mNode;
101 
GetParameter(AudioUnitParameterID inID,AudioUnitScope scope,AudioUnitElement element,Float32 & outValue) const102 	OSStatus			GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
103 											Float32 &outValue) const
104 	{
105 #if CA_AU_USE_FAST_DISPATCH
106 			if (mGetParamProc != NULL) {
107 				return (mGetParamProc) (mConnInstanceStorage, inID, scope, element, &outValue);
108 			}
109 #endif
110 		return AudioUnitGetParameter(mUnit, inID, scope, element, &outValue);
111 	}
112 
SetParameter(AudioUnitParameterID inID,AudioUnitScope scope,AudioUnitElement element,Float32 value,UInt32 bufferOffsetFrames)113 	OSStatus			SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
114 											Float32 value, UInt32 bufferOffsetFrames)
115 	{
116 #if CA_AU_USE_FAST_DISPATCH
117 			if (mSetParamProc != NULL) {
118 				return (mSetParamProc) (mConnInstanceStorage, inID, scope, element, value, bufferOffsetFrames);
119 			}
120 #endif
121 			return AudioUnitSetParameter(mUnit, inID, scope, element, value, bufferOffsetFrames);
122 	}
123 
Render(AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inOutputBusNumber,UInt32 inNumberFrames,AudioBufferList * ioData)124 	OSStatus			Render (AudioUnitRenderActionFlags *  ioActionFlags,
125 								const AudioTimeStamp *        inTimeStamp,
126 								UInt32                        inOutputBusNumber,
127 								UInt32                        inNumberFrames,
128 								AudioBufferList *             ioData)
129 	{
130 #if CA_AU_USE_FAST_DISPATCH
131 		if (mRenderProc != NULL) {
132 			return (mRenderProc) (mConnInstanceStorage, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
133 		}
134 #endif
135 		return AudioUnitRender(mUnit, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
136 	}
137 
MIDIEvent(UInt32 inStatus,UInt32 inData1,UInt32 inData2,UInt32 inOffsetSampleFrame)138 	OSStatus		MIDIEvent (UInt32					inStatus,
139 								UInt32					inData1,
140 								UInt32					inData2,
141 								UInt32					inOffsetSampleFrame)
142 	{
143 #if !TARGET_OS_WIN32
144 #if CA_AU_USE_FAST_DISPATCH
145 		if (mMIDIEventProc != NULL) {
146 			return (mMIDIEventProc) (mConnInstanceStorage, inStatus, inData1, inData2, inOffsetSampleFrame);
147 		}
148 #endif
149 		return MusicDeviceMIDIEvent (mUnit, inStatus, inData1, inData2, inOffsetSampleFrame);
150 #else	// ON WINDOWS _ NO MIDI EVENT dispatch
151 		return paramErr;
152 #endif
153 	}
154 
StartNote(MusicDeviceInstrumentID inInstrument,MusicDeviceGroupID inGroupID,NoteInstanceID * outNoteInstanceID,UInt32 inOffsetSampleFrame,const MusicDeviceNoteParams * inParams)155 	OSStatus				StartNote (MusicDeviceInstrumentID	inInstrument,
156 									MusicDeviceGroupID			inGroupID,
157 									NoteInstanceID *			outNoteInstanceID,
158 									UInt32						inOffsetSampleFrame,
159 									const MusicDeviceNoteParams * inParams)
160 	{
161 #if !TARGET_OS_WIN32
162 #if CA_AU_USE_FAST_DISPATCH
163 		if (mStartNoteProc != NULL) {
164 			return (mStartNoteProc) (mConnInstanceStorage, inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams);
165 		}
166 #endif
167 		return MusicDeviceStartNote (mUnit, inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams);
168 #else
169 		return paramErr;
170 #endif
171 	}
StopNote(MusicDeviceGroupID inGroupID,NoteInstanceID inNoteInstanceID,UInt32 inOffsetSampleFrame)172 	OSStatus				StopNote (MusicDeviceGroupID		inGroupID,
173 									NoteInstanceID				inNoteInstanceID,
174 									UInt32						inOffsetSampleFrame)
175 	{
176 #if !TARGET_OS_WIN32
177 #if CA_AU_USE_FAST_DISPATCH
178 		if (mStopNoteProc != NULL) {
179 			return (mStopNoteProc) (mConnInstanceStorage, inGroupID, inNoteInstanceID, inOffsetSampleFrame);
180 		}
181 #endif
182 		return MusicDeviceStopNote (mUnit, inGroupID, inNoteInstanceID, inOffsetSampleFrame);
183 #else
184 		return paramErr;
185 #endif
186 	}
187 
188 private:
189 	// get the fast dispatch pointers
Init()190 	void Init()
191 	{
192 #if CA_AU_USE_FAST_DISPATCH
193 		UInt32 size = sizeof(AudioUnitRenderProc);
194 		if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
195 								kAudioUnitScope_Global, kAudioUnitRenderSelect,
196 								&mRenderProc, &size) != noErr)
197 			mRenderProc = NULL;
198 
199 		size = sizeof(AudioUnitGetParameterProc);
200 		if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
201 								kAudioUnitScope_Global, kAudioUnitGetParameterSelect,
202 								&mGetParamProc, &size) != noErr)
203 			mGetParamProc = NULL;
204 
205 		size = sizeof(AudioUnitSetParameterProc);
206 		if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
207 								kAudioUnitScope_Global, kAudioUnitSetParameterSelect,
208 								&mSetParamProc, &size) != noErr)
209 			mSetParamProc = NULL;
210 
211 		size = sizeof(MusicDeviceMIDIEventProc);
212 		if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
213 								kAudioUnitScope_Global, kMusicDeviceMIDIEventSelect,
214 								&mMIDIEventProc, &size) != noErr)
215 			mMIDIEventProc = NULL;
216 
217 		size = sizeof(MusicDeviceStartNoteProc);
218 		if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
219 								kAudioUnitScope_Global, kMusicDeviceStartNoteSelect,
220 								&mStartNoteProc, &size) != noErr)
221 			mStartNoteProc = NULL;
222 
223 		size = sizeof(MusicDeviceStopNoteProc);
224 		if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
225 								kAudioUnitScope_Global, kMusicDeviceStopNoteSelect,
226 								&mStopNoteProc, &size) != noErr)
227 			mStopNoteProc = NULL;
228 
229 		if (mRenderProc || mGetParamProc || mSetParamProc || mMIDIEventProc || mStartNoteProc || mStopNoteProc) {
230 			mConnInstanceStorage = LoadGetComponentInstanceStorage ( mUnit );
231 		} else
232 			mConnInstanceStorage = NULL;
233 #else
234 		mConnInstanceStorage = NULL;
235 #endif
236 	}
237 
238 #if CA_AU_USE_FAST_DISPATCH
239 	AudioUnitRenderProc			mRenderProc;
240 	AudioUnitGetParameterProc	mGetParamProc;
241 	AudioUnitSetParameterProc	mSetParamProc;
242 	MusicDeviceMIDIEventProc	mMIDIEventProc;
243 	MusicDeviceStartNoteProc	mStartNoteProc;
244 	MusicDeviceStopNoteProc		mStopNoteProc;
245 #endif
246 
247 	void *						mConnInstanceStorage;
248 
249 private:
250 		// get the compiler to tell us when we do a bad thing!!!
AUState()251 	AUState () {}
252 	AUState (const AUState&);
253 	AUState& operator= (const AUState&);
254 };
255 
256 
~AUState()257 CAAudioUnit::AUState::~AUState ()
258 {
259 	if (mUnit && (mNode == 0)) {
260 		::AudioComponentInstanceDispose (mUnit);
261 	}
262 	mNode = 0;
263 	mUnit = 0;
264 }
265 
Open(const CAComponent & inComp,CAAudioUnit & outUnit)266 OSStatus		CAAudioUnit::Open (const CAComponent& inComp, CAAudioUnit &outUnit)
267 {
268 	try {
269 		outUnit = inComp;
270 		return noErr;
271 	} catch (OSStatus res) {
272 		return res;
273 	} catch (...) {
274 		return -1;
275 	}
276 }
277 
CAAudioUnit(const AudioUnit & inUnit)278 CAAudioUnit::CAAudioUnit (const AudioUnit& inUnit)
279 	: mComp (inUnit), mDataPtr (new AUState (kCAAU_DoNotKnowIfAUNode, inUnit))
280 {
281 }
282 
CAAudioUnit(const CAComponent & inComp)283 CAAudioUnit::CAAudioUnit (const CAComponent& inComp)
284 	: mComp (inComp), mDataPtr (new AUState (mComp.Comp()))
285 {
286 }
287 
CAAudioUnit(const AUNode & inNode,const AudioUnit & inUnit)288 CAAudioUnit::CAAudioUnit (const AUNode &inNode, const AudioUnit& inUnit)
289 	: mComp (inUnit), mDataPtr(new AUState (inNode, inUnit))
290 {
291 }
292 
~CAAudioUnit()293 CAAudioUnit::~CAAudioUnit ()
294 {
295 	Close();
296 }
297 
Close()298 void		CAAudioUnit::Close()
299 {
300 	if (mDataPtr) {
301 		mDataPtr->release();
302 		mDataPtr = NULL;
303 	}
304 }
305 
operator =(const CAAudioUnit & a)306 CAAudioUnit&	CAAudioUnit::operator= (const CAAudioUnit &a)
307 {
308 	if (mDataPtr != a.mDataPtr) {
309 		if (mDataPtr)
310 			mDataPtr->release();
311 
312 		if ((mDataPtr = a.mDataPtr) != NULL)
313 			mDataPtr->retain();
314 
315 		mComp = a.mComp;
316 	}
317 
318 	return *this;
319 }
320 
operator ==(const CAAudioUnit & y) const321 bool			CAAudioUnit::operator== (const CAAudioUnit& y) const
322 {
323 	if (mDataPtr == y.mDataPtr) return true;
324 	AudioUnit au1 = mDataPtr ? mDataPtr->mUnit : 0;
325 	AudioUnit au2 = y.mDataPtr ? y.mDataPtr->mUnit : 0;
326 	return au1 == au2;
327 }
328 
operator ==(const AudioUnit & y) const329 bool			CAAudioUnit::operator== (const AudioUnit& y) const
330 {
331 	if (!mDataPtr) return false;
332 	return mDataPtr->mUnit == y;
333 }
334 
RemovePropertyListener(AudioUnitPropertyID inID,AudioUnitPropertyListenerProc inProc,void * inProcUserData)335 OSStatus		CAAudioUnit::RemovePropertyListener (AudioUnitPropertyID		inID,
336 												AudioUnitPropertyListenerProc	inProc,
337 												void *							inProcUserData)
338 {
339 		// we call this first. If it fails we call the old API as the failure can
340 		// mean that the AU doesn't implement that selector.
341 	OSStatus result = AudioUnitRemovePropertyListenerWithUserData(AU(), inID,
342 									inProc, inProcUserData);
343 	#if !__LP64__ && !TARGET_OS_IPHONE
344 		if (result) result = AudioUnitRemovePropertyListener (AU(), inID, inProc);
345 	#endif
346 	return result;
347 }
348 
349 #pragma mark __State Management
350 
IsValid() const351 bool			CAAudioUnit::IsValid () const
352 {
353 	return mDataPtr ? mDataPtr->mUnit != 0 : false;
354 }
355 
AU() const356 AudioUnit		CAAudioUnit::AU() const
357 {
358 	return mDataPtr ? mDataPtr->mUnit : 0;
359 }
360 
GetAUNode() const361 AUNode			CAAudioUnit::GetAUNode () const
362 {
363 	return mDataPtr ? mDataPtr->mNode : 0;
364 }
365 
366 #pragma mark __Format Handling
367 
CanDo(int inChannelsIn,int inChannelsOut) const368 bool		CAAudioUnit::CanDo (	int 				inChannelsIn,
369 									int 				inChannelsOut) const
370 {
371 	// this is the default assumption of an audio effect unit
372 	Boolean* isWritable = 0;
373 	UInt32	dataSize = 0;
374 		// lets see if the unit has any channel restrictions
375 	OSStatus result = AudioUnitGetPropertyInfo (AU(),
376 									kAudioUnitProperty_SupportedNumChannels,
377 									kAudioUnitScope_Global, 0,
378 									&dataSize, isWritable); //don't care if this is writable
379 
380 		// if this property is NOT implemented an FX unit
381 		// is expected to deal with same channel valance in and out
382 	if (result)
383 	{
384 		if ((Comp().Desc().IsEffect() && inChannelsIn == inChannelsOut)
385 			|| (Comp().Desc().IsOffline() && inChannelsIn == inChannelsOut))
386 		{
387 			return true;
388 		}
389 		else
390 		{
391 			// the au should either really tell us about this
392 			// or we will assume the worst
393 			return false;
394 		}
395 	}
396 
397 	StackAUChannelInfo info (dataSize);
398 
399 	result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
400 							kAudioUnitScope_Global, 0,
401 							info.mChanInfo, &dataSize);
402 	if (result) { return false; }
403 
404 	return ValidateChannelPair (inChannelsIn, inChannelsOut, info.mChanInfo, (dataSize / sizeof (AUChannelInfo)));
405 }
406 
GetChannelInfo(AUChannelInfo ** chaninfo,UInt32 & cnt)407 int    CAAudioUnit::GetChannelInfo (AUChannelInfo** chaninfo, UInt32& cnt)
408 {
409     // this is the default assumption of an audio effect unit
410     Boolean* isWritable = 0;
411     UInt32	dataSize = 0;
412     // lets see if the unit has any channel restrictions
413     OSStatus result = AudioUnitGetPropertyInfo (AU(),
414                                                 kAudioUnitProperty_SupportedNumChannels,
415                                                 kAudioUnitScope_Global, 0,
416                                                 &dataSize, isWritable); //don't care if this is writable
417 
418     // if this property is NOT implemented an FX unit
419     // is expected to deal with same channel valance in and out
420 
421     if (result)
422     {
423         if (Comp().Desc().IsEffect())
424         {
425             return 1;
426         }
427         else if (Comp().Desc().IsGenerator() || Comp().Desc().IsMusicDevice()) {
428             // directly query Bus Formats
429             // Note that that these may refer to different subBusses
430             // (eg. Kick, Snare,.. on a Drummachine)
431             // eventually the Bus-Name for each configuration should be exposed
432             // for the User to select..
433 
434             UInt32 elCountIn, elCountOut;
435 
436             if (GetElementCount (kAudioUnitScope_Input, elCountIn)) return -1;
437             if (GetElementCount (kAudioUnitScope_Output, elCountOut)) return -1;
438 
439             cnt = std::max(elCountIn, elCountOut);
440 
441             *chaninfo = (AUChannelInfo*) malloc (sizeof (AUChannelInfo) * cnt);
442 
443             for (unsigned int i = 0; i < elCountIn; ++i) {
444                 UInt32 numChans;
445                 if (NumberChannels (kAudioUnitScope_Input, i, numChans)) return -1;
446                 (*chaninfo)[i].inChannels = numChans;
447             }
448             for (unsigned int i = elCountIn; i < cnt; ++i) {
449                 (*chaninfo)[i].inChannels = 0;
450             }
451 
452             for (unsigned int i = 0; i < elCountOut; ++i) {
453                 UInt32 numChans;
454                 if (NumberChannels (kAudioUnitScope_Output, i, numChans)) return -1;
455                 (*chaninfo)[i].outChannels = numChans;
456             }
457             for (unsigned int i = elCountOut; i < cnt; ++i) {
458                 (*chaninfo)[i].outChannels = 0;
459             }
460             return 0;
461         }
462         else
463         {
464             // the au should either really tell us about this
465             // or we will assume the worst
466             return -1;
467         }
468     }
469 
470     *chaninfo = (AUChannelInfo*) malloc (dataSize);
471     cnt = dataSize / sizeof (AUChannelInfo);
472 
473     result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
474                           kAudioUnitScope_Global, 0,
475                           *chaninfo, &dataSize);
476 
477     if (result) { return -1; }
478     return 0;
479 }
480 
ValidateChannelPair(int inChannelsIn,int inChannelsOut,const AUChannelInfo * info,UInt32 numChanInfo) const481 bool	CAAudioUnit::ValidateChannelPair (int 				inChannelsIn,
482 										int 				inChannelsOut,
483 										const AUChannelInfo * info,
484 										UInt32				numChanInfo) const
485 {
486 // we've the following cases (some combinations) to test here:
487 /*
488 >0		An explicit number of channels on either side
489 0		that side (generally input!) has no elements
490 -1		wild card:
491 -1,-1	any num channels as long as same channels on in and out
492 -1,-2	any num channels channels on in and out - special meaning
493 -2+ 	indicates total num channs AU can handle
494 			- elements configurable to any num channels,
495 			- element count in scope must be writable
496 */
497 
498 	//now chan layout can contain -1 for either scope (ie. doesn't care)
499 	for (unsigned int i = 0; i < numChanInfo; ++i)
500 	{
501 			//less than zero on both sides - check for special attributes
502 		if ((info[i].inChannels < 0) && (info[i].outChannels < 0))
503 		{
504 				// these are our wild card matches
505 			if (info[i].inChannels == -1 && info[i].outChannels == -1) {
506 				if (inChannelsIn && inChannelsOut) {
507 					if (inChannelsOut == inChannelsIn)
508 						return true;
509 				} else
510 					return true; // if one of these is zero, then a -1 means any
511 			}
512 			else if ((info[i].inChannels == -1 && info[i].outChannels == -2)
513 					|| (info[i].inChannels == -2 && info[i].outChannels == -1))
514 			{
515 				return true;
516 			}
517 				// these are our total num channels matches
518 				// element count MUST be writable
519 			else {
520 				bool outWrite = false; bool inWrite = false;
521 				IsElementCountWritable (kAudioUnitScope_Output, outWrite);
522 				IsElementCountWritable (kAudioUnitScope_Input, inWrite);
523 				if (inWrite && outWrite) {
524 					if ((inChannelsOut <= abs(info[i].outChannels))
525 						&& (inChannelsIn <= abs(info[i].inChannels)))
526 					{
527 						return true;
528 					}
529 				}
530 			}
531 		}
532 
533 			// special meaning on input, specific num on output
534 		else if (info[i].inChannels < 0) {
535 			if (info[i].outChannels == inChannelsOut)
536 			{
537 					// can do any in channels
538 				if (info[i].inChannels == -1) {
539 					return true;
540 				}
541 					// total chans on input
542 				else {
543 					bool inWrite = false;
544 					IsElementCountWritable (kAudioUnitScope_Input, inWrite);
545 					if (inWrite && (inChannelsIn <= abs(info[i].inChannels))) {
546 						return true;
547 					}
548 				}
549 			}
550 		}
551 
552 			// special meaning on output, specific num on input
553 		else if (info[i].outChannels < 0) {
554 			if (info[i].inChannels == inChannelsIn)
555 			{
556 					// can do any out channels
557 				if (info[i].outChannels == -1) {
558 					return true;
559 				}
560 					// total chans on output
561 				else {
562 					bool outWrite = false;
563 					IsElementCountWritable (kAudioUnitScope_Output, outWrite);
564 					if (outWrite && (inChannelsOut <= abs(info[i].outChannels))) {
565 						return true;
566 					}
567 				}
568 			}
569 		}
570 
571 			// both chans in struct >= 0 - thus has to explicitly match
572 		else if ((info[i].inChannels == inChannelsIn) && (info[i].outChannels == inChannelsOut)) {
573 			return true;
574 		}
575 
576 			// now check to see if a wild card on the args (inChannelsIn or inChannelsOut chans is zero) is found
577 			// tells us to match just one side of the scopes
578 		else if (inChannelsIn == 0) {
579 			if (info[i].outChannels == inChannelsOut) {
580 				return true;
581 			}
582 		}
583 		else if (inChannelsOut == 0) {
584 			if (info[i].inChannels == inChannelsIn) {
585 				return true;
586 			}
587 		}
588 	}
589 
590 	return false;
591 }
592 
593 static
CheckDynCount(SInt32 inTotalChans,const CAAUChanHelper & inHelper)594 bool CheckDynCount (SInt32 inTotalChans, const CAAUChanHelper &inHelper)
595 {
596 	int totalChans = 0;
597 	for (unsigned int i = 0; i < inHelper.mNumEls; ++i)
598 		totalChans += inHelper.mChans[i];
599 	return (totalChans <= inTotalChans);
600 }
601 
CheckOneSide(const CAAUChanHelper & inHelper,bool checkOutput,const AUChannelInfo * info,UInt32 numInfo) const602 bool	CAAudioUnit::CheckOneSide (const CAAUChanHelper		&inHelper,
603 									bool					checkOutput,
604 									const AUChannelInfo		*info,
605 									UInt32					numInfo) const
606 {
607 		// now we can use the wildcard option (see above impl) to see if this matches
608 	for (unsigned int el = 0; el < inHelper.mNumEls; ++el) {
609 		bool testAlready = false;
610 		for (unsigned int i = 0; i < el; ++i) {
611 			if (inHelper.mChans[i] == inHelper.mChans[el]) {
612 				testAlready = true;
613 				break;
614 			}
615 		}
616 		if (!testAlready) {
617 			if (checkOutput) {
618 				if (!ValidateChannelPair (0, inHelper.mChans[el], info, numInfo)) return false;
619 			} else {
620 				if (!ValidateChannelPair (inHelper.mChans[el], 0, info, numInfo)) return false;
621 			}
622 		}
623 	}
624 	return true;
625 }
626 
CanDo(const CAAUChanHelper & inputs,const CAAUChanHelper & outputs) const627 bool		CAAudioUnit::CanDo (const CAAUChanHelper		&inputs,
628 								const CAAUChanHelper		&outputs) const
629 
630 {
631 // first check our state
632 		// huh!
633 	if (inputs.mNumEls == 0 && outputs.mNumEls == 0) return false;
634 
635 	UInt32 elCount;
636 	if (GetElementCount (kAudioUnitScope_Input, elCount)) { return false; }
637 	if (elCount != inputs.mNumEls) return false;
638 
639 	if (GetElementCount (kAudioUnitScope_Output, elCount)) { return false; }
640 	if (elCount != outputs.mNumEls) return false;
641 
642 // (1) special cases (effects and sources (generators and instruments) only)
643 	UInt32	dataSize = 0;
644 	if (GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
645 									kAudioUnitScope_Global, 0, &dataSize, NULL) != noErr)
646 	{
647 		if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline()) {
648 			UInt32 numChan = outputs.mNumEls > 0 ? outputs.mChans[0] : inputs.mChans[0];
649 			for (unsigned int in = 0; in < inputs.mNumEls; ++in)
650 				if (numChan != inputs.mChans[in]) return false;
651 			for (unsigned int out = 0; out < outputs.mNumEls; ++out)
652 				if (numChan != outputs.mChans[out]) return false;
653 			return true;
654 		}
655 
656 			// in this case, all the channels have to match the current config
657 		if (Comp().Desc().IsGenerator() || Comp().Desc().IsMusicDevice()) {
658 			for (unsigned int in = 0; in < inputs.mNumEls; ++in) {
659 				UInt32 chan;
660 				if (NumberChannels (kAudioUnitScope_Input, in, chan)) return false;
661 				if (chan != UInt32(inputs.mChans[in])) return false;
662 			}
663 			for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
664 				UInt32 chan;
665 				if (NumberChannels (kAudioUnitScope_Output, out, chan)) return false;
666 				if (chan != UInt32(outputs.mChans[out])) return false;
667 			}
668 			return true;
669 		}
670 
671 			// if we get here we can't determine anything about channel capabilities
672 		return false;
673 	}
674 
675 	StackAUChannelInfo info (dataSize);
676 
677 	if (GetProperty (kAudioUnitProperty_SupportedNumChannels,
678 							kAudioUnitScope_Global, 0,
679 							info.mChanInfo, &dataSize) != noErr)
680 	{
681 		return false;
682 	}
683 
684 	int numInfo = dataSize / sizeof(AUChannelInfo);
685 
686 // (2) Test for dynamic capability (or no elements on that scope)
687 	SInt32 dynInChans = 0;
688 	if (ValidateDynamicScope (kAudioUnitScope_Input, dynInChans, info.mChanInfo, numInfo)) {
689 		if (CheckDynCount (dynInChans, inputs) == false) return false;
690 	}
691 
692 	SInt32 dynOutChans = 0;
693 	if (ValidateDynamicScope (kAudioUnitScope_Output, dynOutChans, info.mChanInfo, numInfo)) {
694 		if (CheckDynCount (dynOutChans, outputs) == false) return false;
695 	}
696 
697 	if (dynOutChans && dynInChans) { return true; }
698 
699 // (3)	Just need to test one side
700 	if (dynInChans || (inputs.mNumEls == 0)) {
701 		return CheckOneSide (outputs, true, info.mChanInfo, numInfo);
702 	}
703 
704 	if (dynOutChans || (outputs.mNumEls == 0)) {
705 		return CheckOneSide (inputs, false, info.mChanInfo, numInfo);
706 	}
707 
708 // (4) - not a dynamic AU, has ins and outs, and has channel constraints so we test every possible pairing
709 	for (unsigned int in = 0; in < inputs.mNumEls; ++in)
710 	{
711 		bool testInAlready = false;
712 		for (unsigned int i = 0; i < in; ++i) {
713 			if (inputs.mChans[i] == inputs.mChans[in]) {
714 				testInAlready = true;
715 				break;
716 			}
717 		}
718 		if (!testInAlready) {
719 			for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
720 					// try to save a little bit and not test the same pairing multiple times...
721 				bool testOutAlready = false;
722 				for (unsigned int i = 0; i < out; ++i) {
723 					if (outputs.mChans[i] == outputs.mChans[out]) {
724 						testOutAlready = true;
725 						break;
726 					}
727 				}
728 				if (!testOutAlready) {
729 					if (!ValidateChannelPair (inputs.mChans[in], outputs.mChans[out],info.mChanInfo, numInfo)) {
730 						return false;
731 					}
732 				}
733 			}
734 		}
735 	}
736 
737 	return true;
738 }
739 
SupportsNumChannels() const740 bool		CAAudioUnit::SupportsNumChannels () const
741 {
742 	// this is the default assumption of an audio effect unit
743 	Boolean* isWritable = 0;
744 	UInt32	dataSize = 0;
745 		// lets see if the unit has any channel restrictions
746 	OSStatus result = AudioUnitGetPropertyInfo (AU(),
747 									kAudioUnitProperty_SupportedNumChannels,
748 									kAudioUnitScope_Global, 0,
749 									&dataSize, isWritable); //don't care if this is writable
750 
751 		// if this property is NOT implemented an FX unit
752 		// is expected to deal with same channel valance in and out
753 	if (result) {
754 		if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline())
755 			return true;
756 	}
757 	return result == noErr;
758 }
759 
GetChannelLayoutTags(AudioUnitScope inScope,AudioUnitElement inEl,ChannelTagVector & outChannelVector) const760 OSStatus		CAAudioUnit::GetChannelLayoutTags (AudioUnitScope 	inScope,
761 										AudioUnitElement 			inEl,
762 										ChannelTagVector			&outChannelVector) const
763 {
764 	if (HasChannelLayouts (inScope, inEl) == false) return kAudioUnitErr_InvalidProperty;
765 
766 	UInt32 dataSize;
767 	OSStatus result = AudioUnitGetPropertyInfo (AU(),
768 								kAudioUnitProperty_SupportedChannelLayoutTags,
769 								inScope, inEl,
770 								&dataSize, NULL);
771 
772 	if (result) return result;
773 
774 		// OK lets get our channel layouts and see if the one we want is present
775 	AudioChannelLayoutTag* info = (AudioChannelLayoutTag*)malloc (dataSize);
776 	result = AudioUnitGetProperty (AU(),
777 							kAudioUnitProperty_SupportedChannelLayoutTags,
778 							inScope, inEl,
779 							info, &dataSize);
780 	if (result) goto home;
781 
782 	outChannelVector.erase (outChannelVector.begin(), outChannelVector.end());
783 	for (unsigned int i = 0; i < (dataSize / sizeof (AudioChannelLayoutTag)); ++i)
784 		outChannelVector.push_back (info[i]);
785 
786 home:
787 	free (info);
788 	return result;
789 }
790 
HasChannelLayouts(AudioUnitScope inScope,AudioUnitElement inEl) const791 bool		CAAudioUnit::HasChannelLayouts (AudioUnitScope 		inScope,
792 										AudioUnitElement 		inEl) const
793 {
794 	OSStatus result = AudioUnitGetPropertyInfo (AU(),
795 									kAudioUnitProperty_SupportedChannelLayoutTags,
796 									inScope, inEl,
797 									NULL, NULL);
798 	return !result;
799 }
800 
HasChannelLayout(AudioUnitScope inScope,AudioUnitElement inEl) const801 bool		CAAudioUnit::HasChannelLayout (AudioUnitScope 		inScope,
802 											AudioUnitElement 		inEl) const
803 {
804 	Boolean writable;
805 	UInt32 size;
806 
807 	return AudioUnitGetPropertyInfo (AU(),
808 									kAudioUnitProperty_AudioChannelLayout,
809 									inScope, inEl,
810 									&size, &writable) == noErr;
811 }
812 
GetChannelLayout(AudioUnitScope inScope,AudioUnitElement inEl,CAAudioChannelLayout & outLayout) const813 OSStatus	CAAudioUnit::GetChannelLayout (AudioUnitScope 		inScope,
814 										AudioUnitElement 		inEl,
815 										CAAudioChannelLayout	&outLayout) const
816 {
817 	UInt32 size;
818 	OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_AudioChannelLayout,
819 									inScope, inEl, &size, NULL);
820 	if (result) return result;
821 
822 	AudioChannelLayout *layout = (AudioChannelLayout*)malloc (size);
823 
824 	ca_require_noerr (result = AudioUnitGetProperty (AU(), kAudioUnitProperty_AudioChannelLayout,
825 									inScope, inEl, layout, &size), home);
826 
827 	outLayout = CAAudioChannelLayout (layout);
828 
829 home:
830 	free (layout);
831 	return result;
832 }
833 
SetChannelLayout(AudioUnitScope inScope,AudioUnitElement inEl,const CAAudioChannelLayout & inLayout)834 OSStatus	CAAudioUnit::SetChannelLayout (AudioUnitScope 		inScope,
835 									AudioUnitElement 			inEl,
836 									const CAAudioChannelLayout 		&inLayout)
837 {
838 	OSStatus result = AudioUnitSetProperty (AU(),
839 									kAudioUnitProperty_AudioChannelLayout,
840 									inScope, inEl,
841 									inLayout, inLayout.Size());
842 	return result;
843 }
844 
SetChannelLayout(AudioUnitScope inScope,AudioUnitElement inEl,const AudioChannelLayout & inLayout,UInt32 inSize)845 OSStatus	CAAudioUnit::SetChannelLayout (AudioUnitScope 			inScope,
846 											AudioUnitElement 		inEl,
847 											const AudioChannelLayout		&inLayout,
848 											UInt32					inSize)
849 {
850 	OSStatus result = AudioUnitSetProperty (AU(),
851 									kAudioUnitProperty_AudioChannelLayout,
852 									inScope, inEl,
853 									&inLayout, inSize);
854 	return result;
855 }
856 
ClearChannelLayout(AudioUnitScope inScope,AudioUnitElement inEl)857 OSStatus		CAAudioUnit::ClearChannelLayout (AudioUnitScope	inScope,
858 											AudioUnitElement	inEl)
859 {
860 	return AudioUnitSetProperty (AU(),
861 							kAudioUnitProperty_AudioChannelLayout,
862 							inScope, inEl, NULL, 0);
863 }
864 
GetFormat(AudioUnitScope inScope,AudioUnitElement inEl,AudioStreamBasicDescription & outFormat) const865 OSStatus	CAAudioUnit::GetFormat (AudioUnitScope				inScope,
866 									AudioUnitElement			inEl,
867 									AudioStreamBasicDescription	&outFormat) const
868 {
869 	UInt32 dataSize = sizeof (AudioStreamBasicDescription);
870 	return AudioUnitGetProperty (AU(), kAudioUnitProperty_StreamFormat,
871 								inScope, inEl,
872 								&outFormat, &dataSize);
873 }
874 
SetFormat(AudioUnitScope inScope,AudioUnitElement inEl,const AudioStreamBasicDescription & inFormat)875 OSStatus	CAAudioUnit::SetFormat (AudioUnitScope						inScope,
876 									AudioUnitElement					inEl,
877 									const AudioStreamBasicDescription	&inFormat)
878 {
879 	return AudioUnitSetProperty (AU(), kAudioUnitProperty_StreamFormat,
880 								inScope, inEl,
881 								const_cast<AudioStreamBasicDescription*>(&inFormat),
882 								sizeof (AudioStreamBasicDescription));
883 }
884 
GetSampleRate(AudioUnitScope inScope,AudioUnitElement inEl,Float64 & outRate) const885 OSStatus	CAAudioUnit::GetSampleRate (AudioUnitScope		inScope,
886 										AudioUnitElement	inEl,
887 										Float64				&outRate) const
888 {
889 	UInt32 dataSize = sizeof (Float64);
890 	return AudioUnitGetProperty (AU(), kAudioUnitProperty_SampleRate,
891 								inScope, inEl,
892 								&outRate, &dataSize);
893 }
894 
SetSampleRate(AudioUnitScope inScope,AudioUnitElement inEl,Float64 inRate)895 OSStatus	CAAudioUnit::SetSampleRate (AudioUnitScope		inScope,
896 										AudioUnitElement	inEl,
897 										Float64				inRate)
898 {
899 	AudioStreamBasicDescription desc;
900 	OSStatus result = GetFormat (inScope, inEl, desc);
901 	if (result) return result;
902 	desc.mSampleRate = inRate;
903 	return SetFormat (inScope, inEl, desc);
904 }
905 
SetSampleRate(Float64 inSampleRate)906 OSStatus	CAAudioUnit::SetSampleRate (Float64			inSampleRate)
907 {
908 	OSStatus result;
909 
910 	UInt32 elCount;
911 	ca_require_noerr (result = GetElementCount(kAudioUnitScope_Input, elCount), home);
912 	if (elCount) {
913 		for (unsigned int i = 0; i < elCount; ++i) {
914 			ca_require_noerr (result = SetSampleRate (kAudioUnitScope_Input, i, inSampleRate), home);
915 		}
916 	}
917 
918 	ca_require_noerr (result = GetElementCount(kAudioUnitScope_Output, elCount), home);
919 	if (elCount) {
920 		for (unsigned int i = 0; i < elCount; ++i) {
921 			ca_require_noerr (result = SetSampleRate (kAudioUnitScope_Output, i, inSampleRate), home);
922 		}
923 	}
924 
925 home:
926 	return result;
927 }
928 
NumberChannels(AudioUnitScope inScope,AudioUnitElement inEl,UInt32 & outChans) const929 OSStatus	CAAudioUnit::NumberChannels (AudioUnitScope		inScope,
930 										AudioUnitElement	inEl,
931 										UInt32				&outChans) const
932 {
933 	AudioStreamBasicDescription desc;
934 	OSStatus result = GetFormat (inScope, inEl, desc);
935 	if (!result)
936 		outChans = desc.mChannelsPerFrame;
937 	return result;
938 }
939 
SetNumberChannels(AudioUnitScope inScope,AudioUnitElement inEl,UInt32 inChans)940 OSStatus	CAAudioUnit::SetNumberChannels (AudioUnitScope	inScope,
941 										AudioUnitElement	inEl,
942 										UInt32				inChans)
943 {
944 			// set this as the output of the AU
945 	CAStreamBasicDescription desc;
946 	OSStatus result = GetFormat (inScope, inEl, desc);
947 		if (result) return result;
948 	desc.ChangeNumberChannels (inChans, desc.IsInterleaved());
949 	result = SetFormat (inScope, inEl, desc);
950 	return result;
951 }
952 
IsElementCountWritable(AudioUnitScope inScope,bool & outWritable) const953 OSStatus		CAAudioUnit::IsElementCountWritable (AudioUnitScope inScope, bool &outWritable) const
954 {
955 	Boolean isWritable;
956 	UInt32 outDataSize;
957 	OSStatus result = GetPropertyInfo (kAudioUnitProperty_ElementCount, inScope, 0, &outDataSize, &isWritable);
958 	if (result)
959 		return result;
960 	outWritable = isWritable ? true : false;
961 	return noErr;
962 }
963 
GetElementCount(AudioUnitScope inScope,UInt32 & outCount) const964 OSStatus		CAAudioUnit::GetElementCount (AudioUnitScope inScope, UInt32 &outCount) const
965 {
966 	UInt32 propSize = sizeof(outCount);
967 	return GetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &outCount, &propSize);
968 }
969 
SetElementCount(AudioUnitScope inScope,UInt32 inCount)970 OSStatus		CAAudioUnit::SetElementCount (AudioUnitScope inScope, UInt32 inCount)
971 {
972 	return SetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &inCount, sizeof(inCount));
973 }
974 
HasDynamicScope(AudioUnitScope inScope,SInt32 & outTotalNumChannels) const975 bool			CAAudioUnit::HasDynamicScope (AudioUnitScope inScope, SInt32 &outTotalNumChannels) const
976 {
977 	// ok - now we need to check the AU's capability here.
978 	// this is the default assumption of an audio effect unit
979 	Boolean* isWritable = 0;
980 	UInt32	dataSize = 0;
981 	OSStatus result = GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
982 								kAudioUnitScope_Global, 0,
983 								&dataSize, isWritable); //don't care if this is writable
984 
985 		// AU has to explicitly tell us about this.
986 	if (result) return false;
987 
988 	StackAUChannelInfo info (dataSize);
989 
990 	result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
991 							kAudioUnitScope_Global, 0,
992 							info.mChanInfo, &dataSize);
993 	if (result) return false;
994 
995 	return ValidateDynamicScope (inScope, outTotalNumChannels, info.mChanInfo, (dataSize / sizeof(AUChannelInfo)));
996 }
997 
998 // as we've already checked that the element count is writable
999 // the following conditions will match this..
1000 /*
1001 -1, -2 ->	signifies no restrictions
1002 -2, -1 ->	signifies no restrictions -> in this case outTotalNumChannels == -1 (any num channels)
1003 
1004 -N 	(where N is less than -2), signifies the total channel count on the scope side (in or out)
1005 */
ValidateDynamicScope(AudioUnitScope inScope,SInt32 & outTotalNumChannels,const AUChannelInfo * info,UInt32 numInfo) const1006 bool	CAAudioUnit::ValidateDynamicScope (AudioUnitScope		inScope,
1007 											SInt32				&outTotalNumChannels,
1008 											const AUChannelInfo *info,
1009 											UInt32				numInfo) const
1010 {
1011 	bool writable = false;
1012 	OSStatus result = IsElementCountWritable (inScope, writable);
1013 	if (result || (writable == false))
1014 		return false;
1015 
1016 	//now chan layout can contain -1 for either scope (ie. doesn't care)
1017 	for (unsigned int i = 0; i < numInfo; ++i)
1018 	{
1019 		// lets test the special wild card case first...
1020 		// this says the AU can do any num channels on input or output - for eg. Matrix Mixer
1021 		if (((info[i].inChannels == -1) && (info[i].outChannels == -2))
1022 			|| ((info[i].inChannels == -2) && (info[i].outChannels == -1)))
1023 		{
1024 			outTotalNumChannels = -1;
1025 			return true;
1026 		}
1027 
1028 		// ok lets now test our special case....
1029 		if (inScope == kAudioUnitScope_Input) {
1030 				// isn't dynamic on this side at least
1031 			if (info[i].inChannels >= 0)
1032 				continue;
1033 
1034 			if (info[i].inChannels < -2) {
1035 				outTotalNumChannels = abs (info[i].inChannels);
1036 				return true;
1037 			}
1038 		}
1039 
1040 		else if (inScope == kAudioUnitScope_Output) {
1041 				// isn't dynamic on this side at least
1042 			if (info[i].outChannels >= 0)
1043 				continue;
1044 
1045 			if (info[i].outChannels < -2) {
1046 				outTotalNumChannels = abs (info[i].outChannels);
1047 				return true;
1048 			}
1049 		}
1050 
1051 		else {
1052 			break; // wrong scope was specified
1053 		}
1054 	}
1055 
1056 	return false;
1057 }
1058 
ConfigureDynamicScope(AudioUnitScope inScope,UInt32 inNumElements,UInt32 * inChannelsPerElement,Float64 inSampleRate)1059 OSStatus	CAAudioUnit::ConfigureDynamicScope (AudioUnitScope 		inScope,
1060 											UInt32 					inNumElements,
1061 											UInt32 					*inChannelsPerElement,
1062 											Float64 				inSampleRate)
1063 {
1064 	SInt32 numChannels = 0;
1065 	bool isDyamic = HasDynamicScope (inScope, numChannels);
1066 	if (isDyamic == false)
1067 		return kAudioUnitErr_InvalidProperty;
1068 
1069 	//lets to a sanity check...
1070 	// if numChannels == -1, then it can do "any"...
1071 	if (numChannels > 0) {
1072 		SInt32 count = 0;
1073 		for (unsigned int i = 0; i < inNumElements; ++i)
1074 			count += inChannelsPerElement[i];
1075 		if (count > numChannels)
1076 			return kAudioUnitErr_InvalidPropertyValue;
1077 	}
1078 
1079 	OSStatus result = SetElementCount (inScope, inNumElements);
1080 	if (result)
1081 		return result;
1082 
1083 	for (unsigned int i = 0; i < inNumElements; ++i) {
1084 		CAStreamBasicDescription desc;
1085 		result = GetFormat (inScope, i, desc);
1086 			if (result) return result;
1087 		desc.ChangeNumberChannels (inChannelsPerElement[i], desc.IsInterleaved());
1088 		desc.mSampleRate = inSampleRate;
1089 		result = SetFormat (inScope, i, desc);
1090 		if (result)
1091 			return result;
1092 	}
1093 	return noErr;
1094 }
1095 
1096 #pragma mark __Properties
1097 
CanBypass() const1098 bool		CAAudioUnit::CanBypass () const
1099 {
1100 	Boolean outWritable;
1101 	OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_BypassEffect,
1102 									kAudioUnitScope_Global, 0,
1103 									NULL, &outWritable);
1104 	return (!result && outWritable);
1105 }
1106 
GetBypass() const1107 bool		CAAudioUnit::GetBypass 		() const
1108 {
1109 	UInt32 dataSize = sizeof (UInt32);
1110 	UInt32 outBypass;
1111 	OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_BypassEffect,
1112 								kAudioUnitScope_Global, 0,
1113 								&outBypass, &dataSize);
1114 	return (result ? false : outBypass);
1115 }
1116 
SetBypass(bool inBypass) const1117 OSStatus	CAAudioUnit::SetBypass 		(bool	inBypass) const
1118 {
1119 	UInt32 bypass = inBypass ? 1 : 0;
1120 	return AudioUnitSetProperty (AU(), kAudioUnitProperty_BypassEffect,
1121 								kAudioUnitScope_Global, 0,
1122 								&bypass, sizeof (UInt32));
1123 }
1124 
GetMaxFramesPerSlice(UInt32 & outMaxFrames) const1125 OSStatus	CAAudioUnit::GetMaxFramesPerSlice (UInt32& outMaxFrames) const
1126 {
1127 	UInt32 dataSize = sizeof(outMaxFrames);
1128 	return AudioUnitGetProperty (AU(), kAudioUnitProperty_MaximumFramesPerSlice,
1129 								kAudioUnitScope_Global, 0,
1130 								&outMaxFrames, &dataSize);
1131 }
1132 
SetMaxFramesPerSlice(UInt32 inMaxFrames)1133 OSStatus	CAAudioUnit::SetMaxFramesPerSlice (UInt32 inMaxFrames)
1134 {
1135 	return AudioUnitSetProperty (AU(), kAudioUnitProperty_MaximumFramesPerSlice,
1136 								 kAudioUnitScope_Global, 0,
1137 								 &inMaxFrames, sizeof (UInt32));
1138 }
1139 
Latency() const1140 Float64		CAAudioUnit::Latency () const
1141 {
1142 	Float64 secs;
1143 	UInt32 size = sizeof(secs);
1144 	if (GetProperty (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &secs, &size))
1145 		return 0;
1146 	return secs;
1147 }
1148 
GetAUPreset(CFPropertyListRef & outData) const1149 OSStatus	CAAudioUnit::GetAUPreset (CFPropertyListRef &outData) const
1150 {
1151 	UInt32 dataSize = sizeof(outData);
1152 	return AudioUnitGetProperty (AU(), kAudioUnitProperty_ClassInfo,
1153 								kAudioUnitScope_Global, 0,
1154 								&outData, &dataSize);
1155 }
1156 
SetAUPreset(CFPropertyListRef & inData)1157 OSStatus	CAAudioUnit::SetAUPreset (CFPropertyListRef &inData)
1158 {
1159 	return AudioUnitSetProperty (AU(), kAudioUnitProperty_ClassInfo,
1160 								kAudioUnitScope_Global, 0,
1161 								&inData, sizeof (CFPropertyListRef));
1162 }
1163 
1164 #if !TARGET_OS_IPHONE
SetAUPresetFromDocument(CFPropertyListRef & inData)1165 OSStatus	CAAudioUnit::SetAUPresetFromDocument (CFPropertyListRef &inData)
1166 {
1167 	return AudioUnitSetProperty (AU(), kAudioUnitProperty_ClassInfoFromDocument,
1168 								kAudioUnitScope_Global, 0,
1169 								&inData, sizeof (CFPropertyListRef));
1170 }
1171 #endif
1172 
GetPresentPreset(AUPreset & outData) const1173 OSStatus	CAAudioUnit::GetPresentPreset (AUPreset &outData) const
1174 {
1175 	UInt32 dataSize = sizeof(outData);
1176 	OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_PresentPreset,
1177 								kAudioUnitScope_Global, 0,
1178 								&outData, &dataSize);
1179 #if !TARGET_OS_IPHONE
1180 #ifndef __LP64__
1181 	if (result == kAudioUnitErr_InvalidProperty) {
1182 		dataSize = sizeof(outData);
1183 		result = AudioUnitGetProperty (AU(), kAudioUnitProperty_CurrentPreset,
1184 									kAudioUnitScope_Global, 0,
1185 									&outData, &dataSize);
1186 		if (result == noErr) {
1187 			// we now retain the CFString in the preset so for the client of this API
1188 			// it is consistent (ie. the string should be released when done)
1189 			if (outData.presetName)
1190 				CFRetain (outData.presetName);
1191 		}
1192 	}
1193 #endif
1194 #endif
1195 	return result;
1196 }
1197 
SetPresentPreset(AUPreset & inData)1198 OSStatus	CAAudioUnit::SetPresentPreset (AUPreset &inData)
1199 {
1200 	OSStatus result = AudioUnitSetProperty (AU(), kAudioUnitProperty_PresentPreset,
1201 								kAudioUnitScope_Global, 0,
1202 								&inData, sizeof (AUPreset));
1203 #if !TARGET_OS_IPHONE
1204 #ifndef __LP64__
1205 	if (result == kAudioUnitErr_InvalidProperty) {
1206 		result = AudioUnitSetProperty (AU(), kAudioUnitProperty_CurrentPreset,
1207 								kAudioUnitScope_Global, 0,
1208 								&inData, sizeof (AUPreset));
1209 	}
1210 #endif
1211 #endif
1212 	return result;
1213 }
1214 
HasCustomView() const1215 bool		CAAudioUnit::HasCustomView () const
1216 {
1217 #if !TARGET_OS_IPHONE
1218 	UInt32 dataSize = 0;
1219 	OSStatus result = -4/*unimpErr*/;
1220 #ifndef __LP64__
1221 	result = GetPropertyInfo(kAudioUnitProperty_GetUIComponentList,
1222                                         kAudioUnitScope_Global, 0,
1223                                         &dataSize, NULL);
1224 #endif
1225 	if (result || !dataSize) {
1226 		dataSize = 0;
1227 		result = GetPropertyInfo(kAudioUnitProperty_CocoaUI,
1228                                         kAudioUnitScope_Global, 0,
1229                                         &dataSize, NULL);
1230 		if (result || !dataSize)
1231 			return false;
1232 	}
1233 	return true;
1234 #else
1235 	return false;
1236 #endif
1237 
1238 }
1239 
GetParameter(AudioUnitParameterID inID,AudioUnitScope scope,AudioUnitElement element,Float32 & outValue) const1240 OSStatus		CAAudioUnit::GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1241 											Float32 &outValue) const
1242 {
1243 	return mDataPtr ? mDataPtr->GetParameter (inID, scope, element, outValue) : static_cast<OSStatus>(paramErr);
1244 }
1245 
SetParameter(AudioUnitParameterID inID,AudioUnitScope scope,AudioUnitElement element,Float32 value,UInt32 bufferOffsetFrames)1246 OSStatus		CAAudioUnit::SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1247 											Float32 value, UInt32 bufferOffsetFrames)
1248 {
1249 	return mDataPtr ? mDataPtr->SetParameter (inID, scope, element, value, bufferOffsetFrames) : static_cast<OSStatus>(paramErr);
1250 }
1251 
MIDIEvent(UInt32 inStatus,UInt32 inData1,UInt32 inData2,UInt32 inOffsetSampleFrame)1252 OSStatus		CAAudioUnit::MIDIEvent (UInt32			inStatus,
1253 								UInt32					inData1,
1254 								UInt32					inData2,
1255 								UInt32					inOffsetSampleFrame)
1256 {
1257 	return mDataPtr ? mDataPtr->MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame) : paramErr;
1258 }
1259 
StartNote(MusicDeviceInstrumentID inInstrument,MusicDeviceGroupID inGroupID,NoteInstanceID * outNoteInstanceID,UInt32 inOffsetSampleFrame,const MusicDeviceNoteParams * inParams)1260 OSStatus	CAAudioUnit::StartNote (MusicDeviceInstrumentID		inInstrument,
1261 									MusicDeviceGroupID			inGroupID,
1262 									NoteInstanceID *			outNoteInstanceID,
1263 									UInt32						inOffsetSampleFrame,
1264 									const MusicDeviceNoteParams * inParams)
1265 {
1266 	return mDataPtr ? mDataPtr->StartNote (inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams)
1267 					: paramErr;
1268 }
1269 
StopNote(MusicDeviceGroupID inGroupID,NoteInstanceID inNoteInstanceID,UInt32 inOffsetSampleFrame)1270 OSStatus	CAAudioUnit::StopNote (MusicDeviceGroupID		inGroupID,
1271 									NoteInstanceID				inNoteInstanceID,
1272 									UInt32						inOffsetSampleFrame)
1273 {
1274 	return mDataPtr ? mDataPtr->StopNote (inGroupID, inNoteInstanceID, inOffsetSampleFrame) : paramErr;
1275 }
1276 
1277 
1278 #pragma mark __Render
1279 
Render(AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inOutputBusNumber,UInt32 inNumberFrames,AudioBufferList * ioData)1280 OSStatus		CAAudioUnit::Render (AudioUnitRenderActionFlags 			* ioActionFlags,
1281 												const AudioTimeStamp 		* inTimeStamp,
1282 												UInt32						inOutputBusNumber,
1283 												UInt32						inNumberFrames,
1284 												AudioBufferList				* ioData)
1285 {
1286 	return mDataPtr ? mDataPtr->Render (ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData) : static_cast<OSStatus>(paramErr);
1287 }
1288 
1289 extern "C" OSStatus
1290 AudioUnitProcess (					AudioUnit						inUnit,
1291 									AudioUnitRenderActionFlags *	ioActionFlags,
1292 									const AudioTimeStamp *			inTimeStamp,
1293 									UInt32							inNumberFrames,
1294 									AudioBufferList *				ioData);
1295 
Process(AudioUnitRenderActionFlags & ioActionFlags,const AudioTimeStamp & inTimeStamp,UInt32 inNumberFrames,AudioBufferList & ioData)1296 OSStatus		CAAudioUnit::Process (AudioUnitRenderActionFlags 			& ioActionFlags,
1297 												const AudioTimeStamp 		& inTimeStamp,
1298 												UInt32						inNumberFrames,
1299 												AudioBufferList				& ioData)
1300 {
1301 #if defined(__MAC_10_7) || defined(__IPHONE_4_0)
1302 	return AudioUnitProcess (AU(), &ioActionFlags, &inTimeStamp, inNumberFrames, &ioData);
1303 #else
1304 	return -4/*unimpErr*/;
1305 #endif
1306 }
1307 
1308 extern "C" OSStatus
1309 AudioUnitProcessMultiple (			AudioUnit						inUnit,
1310 									AudioUnitRenderActionFlags *	ioActionFlags,
1311 									const AudioTimeStamp *			inTimeStamp,
1312 									UInt32							inNumberFrames,
1313 									UInt32							inNumberInputBufferLists,
1314 									const AudioBufferList **		inInputBufferLists,
1315 									UInt32							inNumberOutputBufferLists,
1316 									AudioBufferList **				ioOutputBufferLists);
1317 
ProcessMultiple(AudioUnitRenderActionFlags & ioActionFlags,const AudioTimeStamp & inTimeStamp,UInt32 inNumberFrames,UInt32 inNumberInputBufferLists,const AudioBufferList ** inInputBufferLists,UInt32 inNumberOutputBufferLists,AudioBufferList ** ioOutputBufferLists)1318 OSStatus		CAAudioUnit::ProcessMultiple (AudioUnitRenderActionFlags 	& ioActionFlags,
1319 									const AudioTimeStamp					& inTimeStamp,
1320 									UInt32									inNumberFrames,
1321 									UInt32									inNumberInputBufferLists,
1322 									const AudioBufferList **				inInputBufferLists,
1323 									UInt32									inNumberOutputBufferLists,
1324 									AudioBufferList **						ioOutputBufferLists)
1325 {
1326 #if defined(__MAC_10_7) || defined(__IPHONE_4_0)
1327 	return AudioUnitProcessMultiple (AU(), &ioActionFlags, &inTimeStamp, inNumberFrames,
1328 				inNumberInputBufferLists, inInputBufferLists, inNumberOutputBufferLists, ioOutputBufferLists);
1329 #else
1330 	return -4/*unimpErr*/;
1331 #endif
1332 }
1333 
1334 #pragma mark __CAAUChanHelper
1335 
CAAUChanHelper(const CAAudioUnit & inAU,AudioUnitScope inScope)1336 CAAUChanHelper::CAAUChanHelper(const CAAudioUnit &inAU, AudioUnitScope inScope)
1337 	:mChans(NULL), mNumEls(0), mDidAllocate(false)
1338 {
1339 	UInt32 elCount;
1340 	if (inAU.GetElementCount (inScope, elCount)) return;
1341 	if (elCount > kStaticElCount) {
1342 		mChans = new UInt32[elCount];
1343 		mDidAllocate = true;
1344 		memset (mChans, 0, sizeof(int) * elCount);
1345 	} else {
1346 		mChans = mStaticChans;
1347 		memset (mChans, 0, sizeof(int) * kStaticElCount);
1348 	}
1349 	for (unsigned int i = 0; i < elCount; ++i) {
1350 		UInt32 numChans;
1351 		if (inAU.NumberChannels (inScope, i, numChans)) return;
1352 		mChans[i] = numChans;
1353 	}
1354 	mNumEls = elCount;
1355 }
1356 
CAAUChanHelper(UInt32 inMaxElems)1357 CAAUChanHelper::CAAUChanHelper(UInt32 inMaxElems)
1358 	: mNumEls(inMaxElems), mDidAllocate(false)
1359 {
1360 	if (inMaxElems > kStaticElCount) {
1361 		mChans = new UInt32[inMaxElems];
1362 		mDidAllocate = true;
1363 		memset (mChans, 0, sizeof(int) * inMaxElems);
1364 	} else {
1365 		mChans = mStaticChans;
1366 		memset (mChans, 0, sizeof(int) * kStaticElCount);
1367 	}
1368 }
1369 
~CAAUChanHelper()1370 CAAUChanHelper::~CAAUChanHelper()
1371 {
1372 	if (mDidAllocate) delete [] mChans;
1373 }
1374 
operator =(const CAAUChanHelper & c)1375 CAAUChanHelper&		CAAUChanHelper::operator= (const CAAUChanHelper &c)
1376 {
1377 	if (mDidAllocate) delete [] mChans;
1378 	if (c.mDidAllocate) {
1379 		mChans = new UInt32[c.mNumEls];
1380 		mDidAllocate = true;
1381 	} else {
1382 		mDidAllocate = false;
1383 		mChans = mStaticChans;
1384 	}
1385 	memcpy (mChans, c.mChans, c.mNumEls * sizeof(int));
1386 
1387 	return *this;
1388 }
1389 
1390 
1391 #pragma mark __Print Utilities
1392 
Print(FILE * file) const1393 void		CAAudioUnit::Print (FILE* file) const
1394 {
1395 	fprintf (file, "AudioUnit:%p\n", AU());
1396 	if (IsValid()) {
1397 		fprintf (file, "\tnode=%ld\t", (long)GetAUNode()); Comp().Print (file);
1398 	}
1399 }
1400 
1401 #if CA_AU_USE_FAST_DISPATCH
1402 // Handle  GetComponentInstanceStorage(ComponentInstance aComponentInstance)
LoadGetComponentInstanceStorage(void * inst)1403 static void *LoadGetComponentInstanceStorage (void *inst)
1404 {
1405 	typedef void* (*GetComponentInstanceStorageProc)(void* aComponentInstance);
1406 	static GetComponentInstanceStorageProc sGetComponentInstanceStorageProc = NULL;
1407 
1408 	static int sDoCSLoad = 1;
1409 	if (sDoCSLoad) {
1410 		sDoCSLoad = 0;
1411 		void *theImage = dlopen("/System/Library/Frameworks/CoreServices.framework/CoreServices", RTLD_LAZY);
1412 		if (!theImage) return NULL;
1413 
1414 		sGetComponentInstanceStorageProc = (GetComponentInstanceStorageProc) dlsym(theImage, "GetComponentInstanceStorage");
1415 	}
1416 	if (sGetComponentInstanceStorageProc)
1417 		return (*sGetComponentInstanceStorageProc)(inst);
1418 	return NULL;
1419 }
1420 #endif
1421 
1422