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