Remove erroneous assert which I added earlier.
[ardour2.git] / libs / appleutility / CAAudioUnit.cpp
blobaaf57f233de261515c033c9bcc0728017c540e64
1 /* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved.
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.
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.
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.
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.
38 /*=============================================================================
39 CAAudioUnit.cpp
41 =============================================================================*/
43 #include "CAAudioUnit.h"
45 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
46 #include <AudioUnit/MusicDevice.h>
47 #else
48 #include <MusicDevice.h>
49 #endif
51 #include "CAReferenceCounted.h"
52 #include "AUOutputBL.h" //this is for the Preroll only
55 struct StackAUChannelInfo {
56 StackAUChannelInfo (UInt32 inSize) : mChanInfo ((AUChannelInfo*)malloc (inSize)) {}
57 ~StackAUChannelInfo() { free (mChanInfo); }
59 AUChannelInfo* mChanInfo;
64 class CAAudioUnit::AUState : public CAReferenceCounted {
65 public:
66 AUState (Component inComp)
67 : mUnit(0), mNode (0)
69 OSStatus result = ::OpenAComponent (inComp, &mUnit);
70 if (result)
71 throw result;
72 Init();
75 AUState (const AUNode &inNode, const AudioUnit& inUnit)
76 : mUnit (inUnit), mNode (inNode)
78 Init();
81 ~AUState();
83 AudioUnit mUnit;
84 AUNode mNode;
86 OSStatus GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
87 Float32 &outValue) const
89 if (mGetParamProc != NULL) {
90 return reinterpret_cast<AudioUnitGetParameterProc>(mGetParamProc) (mConnInstanceStorage,
91 inID, scope, element, &outValue);
93 return AudioUnitGetParameter(mUnit, inID, scope, element, &outValue);
96 OSStatus SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
97 Float32 value, UInt32 bufferOffsetFrames)
99 if (mSetParamProc != NULL) {
100 return reinterpret_cast<AudioUnitSetParameterProc>(mSetParamProc) (mConnInstanceStorage,
101 inID, scope, element, value, bufferOffsetFrames);
103 return AudioUnitSetParameter(mUnit, inID, scope, element, value, bufferOffsetFrames);
106 OSStatus Render (AudioUnitRenderActionFlags * ioActionFlags,
107 const AudioTimeStamp * inTimeStamp,
108 UInt32 inOutputBusNumber,
109 UInt32 inNumberFrames,
110 AudioBufferList * ioData)
112 if (mRenderProc != NULL) {
113 return reinterpret_cast<AudioUnitRenderProc>(mRenderProc) (mConnInstanceStorage,
114 ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
116 return AudioUnitRender(mUnit, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
119 OSStatus MIDIEvent (UInt32 inStatus,
120 UInt32 inData1,
121 UInt32 inData2,
122 UInt32 inOffsetSampleFrame)
124 #if !TARGET_OS_WIN32
125 if (mMIDIEventProc != NULL) {
126 return reinterpret_cast<MusicDeviceMIDIEventProc>(mMIDIEventProc) (mConnInstanceStorage,
127 inStatus, inData1, inData2, inOffsetSampleFrame);
129 return MusicDeviceMIDIEvent (mUnit, inStatus, inData1, inData2, inOffsetSampleFrame);
130 #else
131 return paramErr;
132 #endif
135 OSStatus StartNote (MusicDeviceInstrumentID inInstrument,
136 MusicDeviceGroupID inGroupID,
137 NoteInstanceID * outNoteInstanceID,
138 UInt32 inOffsetSampleFrame,
139 const MusicDeviceNoteParams * inParams)
141 #if !TARGET_OS_WIN32
142 return MusicDeviceStartNote (mUnit, inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams);
143 #else
144 return paramErr;
145 #endif
147 OSStatus StopNote (MusicDeviceGroupID inGroupID,
148 NoteInstanceID inNoteInstanceID,
149 UInt32 inOffsetSampleFrame)
151 #if !TARGET_OS_WIN32
152 return MusicDeviceStopNote (mUnit, inGroupID, inNoteInstanceID, inOffsetSampleFrame);
153 #else
154 return paramErr;
155 #endif
158 private:
159 // get the fast dispatch pointers
160 void Init()
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;
176 if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
177 kAudioUnitScope_Global, kMusicDeviceMIDIEventSelect,
178 &mMIDIEventProc, &size) != noErr)
179 mMIDIEventProc = NULL;
181 if (mRenderProc || mGetParamProc || mSetParamProc || mMIDIEventProc)
182 mConnInstanceStorage = GetComponentInstanceStorage(mUnit);
183 else
184 mConnInstanceStorage = NULL;
187 ProcPtr mRenderProc, mGetParamProc, mSetParamProc, mMIDIEventProc;
189 void * mConnInstanceStorage;
191 private:
192 // get the compiler to tell us when we do a bad thing!!!
193 AUState () {}
194 AUState (const AUState&) {}
195 AUState& operator= (const AUState&) { return *this; }
199 CAAudioUnit::AUState::~AUState ()
201 if (mUnit && (mNode == 0)) {
202 ::CloseComponent (mUnit);
204 mNode = 0;
205 mUnit = 0;
208 OSStatus CAAudioUnit::Open (const CAComponent& inComp, CAAudioUnit &outUnit)
210 try {
211 outUnit = inComp;
212 return noErr;
213 } catch (OSStatus res) {
214 return res;
215 } catch (...) {
216 return -1;
220 CAAudioUnit::CAAudioUnit (const AudioUnit& inUnit)
221 : mComp (inUnit), mDataPtr (new AUState (-1, inUnit))
225 CAAudioUnit::CAAudioUnit (const CAComponent& inComp)
226 : mComp (inComp), mDataPtr (0)
228 mDataPtr = new AUState (mComp.Comp());
231 CAAudioUnit::CAAudioUnit (const AUNode &inNode, const AudioUnit& inUnit)
232 : mComp (inUnit), mDataPtr(new AUState (inNode, inUnit))
236 CAAudioUnit::~CAAudioUnit ()
238 if (mDataPtr) {
239 mDataPtr->release();
240 mDataPtr = NULL;
244 CAAudioUnit& CAAudioUnit::operator= (const CAAudioUnit &a)
246 if (mDataPtr != a.mDataPtr) {
247 if (mDataPtr)
248 mDataPtr->release();
250 if ((mDataPtr = a.mDataPtr) != NULL)
251 mDataPtr->retain();
253 mComp = a.mComp;
256 return *this;
259 bool CAAudioUnit::operator== (const CAAudioUnit& y) const
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;
267 bool CAAudioUnit::operator== (const AudioUnit& y) const
269 if (!mDataPtr) return false;
270 return mDataPtr->mUnit == y;
273 #pragma mark __State Management
275 bool CAAudioUnit::IsValid () const
277 return mDataPtr ? mDataPtr->mUnit != 0 : false;
280 AudioUnit CAAudioUnit::AU() const
282 return mDataPtr ? mDataPtr->mUnit : 0;
285 AUNode CAAudioUnit::GetAUNode () const
287 return mDataPtr ? mDataPtr->mNode : 0;
290 #pragma mark __Format Handling
292 bool CAAudioUnit::CanDo ( int inChannelsIn,
293 int inChannelsOut) const
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
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)
308 if (Comp().Desc().IsEffect() && (inChannelsIn == inChannelsOut)
309 || Comp().Desc().IsOffline() && (inChannelsIn == inChannelsOut))
311 return true;
313 else
315 // the au should either really tell us about this
316 // or we will assume the worst
317 return false;
321 StackAUChannelInfo info (dataSize);
323 result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
324 kAudioUnitScope_Global, 0,
325 info.mChanInfo, &dataSize);
326 if (result) { return false; }
328 return ValidateChannelPair (inChannelsIn, inChannelsOut, info.mChanInfo, (dataSize / sizeof (AUChannelInfo)));
331 int CAAudioUnit::GetChannelInfo (AUChannelInfo** chaninfo, UInt32& cnt)
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
342 // if this property is NOT implemented an FX unit
343 // is expected to deal with same channel valance in and out
345 if (result)
347 if (Comp().Desc().IsEffect())
349 return 1;
351 else
353 // the au should either really tell us about this
354 // or we will assume the worst
355 return -1;
359 *chaninfo = (AUChannelInfo*) malloc (dataSize);
360 cnt = dataSize / sizeof (AUChannelInfo);
362 result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
363 kAudioUnitScope_Global, 0,
364 *chaninfo, &dataSize);
366 if (result) { return -1; }
367 return 0;
371 bool CAAudioUnit::ValidateChannelPair (int inChannelsIn,
372 int inChannelsOut,
373 const AUChannelInfo * info,
374 UInt32 numChanInfo) const
376 // we've the following cases (some combinations) to test here:
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
388 //now chan layout can contain -1 for either scope (ie. doesn't care)
389 for (unsigned int i = 0; i < numChanInfo; ++i)
391 //less than zero on both sides - check for special attributes
392 if ((info[i].inChannels < 0) && (info[i].outChannels < 0))
394 // these are our wild card matches
395 if (info[i].inChannels == -1 && info[i].outChannels == -1) {
396 if (inChannelsOut == inChannelsIn) {
397 return true;
400 else if ((info[i].inChannels == -1 && info[i].outChannels == -2)
401 || (info[i].inChannels == -2 && info[i].outChannels == -1))
403 return true;
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)))
415 return true;
421 // special meaning on input, specific num on output
422 else if (info[i].inChannels < 0) {
423 if (info[i].outChannels == inChannelsOut)
425 // can do any in channels
426 if (info[i].inChannels == -1) {
427 return true;
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;
440 // special meaning on output, specific num on input
441 else if (info[i].outChannels < 0) {
442 if (info[i].inChannels == inChannelsIn)
444 // can do any out channels
445 if (info[i].outChannels == -1) {
446 return true;
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;
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;
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;
471 else if (inChannelsOut == 0) {
472 if (info[i].inChannels == inChannelsIn) {
473 return true;
478 return false;
481 bool CheckDynCount (SInt32 inTotalChans, const CAAUChanHelper &inHelper)
483 int totalChans = 0;
484 for (unsigned int i = 0; i < inHelper.mNumEls; ++i)
485 totalChans += inHelper.mChans[i];
486 return (totalChans <= inTotalChans);
489 bool CAAudioUnit::CheckOneSide (const CAAUChanHelper &inHelper,
490 bool checkOutput,
491 const AUChannelInfo *info,
492 UInt32 numInfo) const
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;
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;
511 return true;
514 bool CAAudioUnit::CanDo (const CAAUChanHelper &inputs,
515 const CAAUChanHelper &outputs) const
518 // first check our state
519 // huh!
520 if (inputs.mNumEls == 0 && outputs.mNumEls == 0) return false;
522 UInt32 elCount;
523 if (GetElementCount (kAudioUnitScope_Input, elCount)) { return false; }
524 if (elCount != inputs.mNumEls) return false;
526 if (GetElementCount (kAudioUnitScope_Output, elCount)) { return false; }
527 if (elCount != outputs.mNumEls) return false;
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)
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;
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;
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;
555 return true;
558 // if we get here we can't determine anything about channel capabilities
559 return false;
562 StackAUChannelInfo info (dataSize);
564 if (GetProperty (kAudioUnitProperty_SupportedNumChannels,
565 kAudioUnitScope_Global, 0,
566 info.mChanInfo, &dataSize) != noErr)
568 return false;
571 int numInfo = dataSize / sizeof(AUChannelInfo);
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;
579 SInt32 dynOutChans = 0;
580 if (ValidateDynamicScope (kAudioUnitScope_Output, dynOutChans, info.mChanInfo, numInfo)) {
581 if (CheckDynCount (dynOutChans, outputs) == false) return false;
584 if (dynOutChans && dynInChans) { return true; }
586 // (3) Just need to test one side
587 if (dynInChans || (inputs.mNumEls == 0)) {
588 return CheckOneSide (outputs, true, info.mChanInfo, numInfo);
591 if (dynOutChans || (outputs.mNumEls == 0)) {
592 return CheckOneSide (inputs, false, info.mChanInfo, numInfo);
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)
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;
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;
615 if (!testOutAlready) {
616 if (!ValidateChannelPair (inputs.mChans[in], outputs.mChans[out],info.mChanInfo, numInfo)) {
617 return false;
624 return true;
627 bool CAAudioUnit::SupportsNumChannels () const
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
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;
644 return result == noErr;
647 bool CAAudioUnit::GetChannelLayouts (AudioUnitScope inScope,
648 AudioUnitElement inEl,
649 ChannelTagVector &outChannelVector) const
651 if (HasChannelLayouts (inScope, inEl) == false) return false;
653 UInt32 dataSize;
654 OSStatus result = AudioUnitGetPropertyInfo (AU(),
655 kAudioUnitProperty_SupportedChannelLayoutTags,
656 inScope, inEl,
657 &dataSize, NULL);
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;
669 if (result) return false;
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;
680 outChannelVector.erase (outChannelVector.begin(), outChannelVector.end());
681 for (unsigned int i = 0; i < (dataSize / sizeof (AudioChannelLayoutTag)); ++i)
682 outChannelVector.push_back (info[i]);
684 home:
685 free (info);
686 return canDo;
689 bool CAAudioUnit::HasChannelLayouts (AudioUnitScope inScope,
690 AudioUnitElement inEl) const
692 OSStatus result = AudioUnitGetPropertyInfo (AU(),
693 kAudioUnitProperty_SupportedChannelLayoutTags,
694 inScope, inEl,
695 NULL, NULL);
696 return !result;
699 OSStatus CAAudioUnit::GetChannelLayout (AudioUnitScope inScope,
700 AudioUnitElement inEl,
701 CAAudioChannelLayout &outLayout) const
703 UInt32 size;
704 OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_AudioChannelLayout,
705 inScope, inEl, &size, NULL);
706 if (result) return result;
708 AudioChannelLayout *layout = (AudioChannelLayout*)malloc (size);
710 require_noerr (result = AudioUnitGetProperty (AU(), kAudioUnitProperty_AudioChannelLayout,
711 inScope, inEl, layout, &size), home);
713 outLayout = CAAudioChannelLayout (layout);
715 home:
716 free (layout);
717 return result;
720 OSStatus CAAudioUnit::SetChannelLayout (AudioUnitScope inScope,
721 AudioUnitElement inEl,
722 CAAudioChannelLayout &inLayout)
724 OSStatus result = AudioUnitSetProperty (AU(),
725 kAudioUnitProperty_AudioChannelLayout,
726 inScope, inEl,
727 inLayout, inLayout.Size());
728 return result;
731 OSStatus CAAudioUnit::SetChannelLayout (AudioUnitScope inScope,
732 AudioUnitElement inEl,
733 AudioChannelLayout &inLayout,
734 UInt32 inSize)
736 OSStatus result = AudioUnitSetProperty (AU(),
737 kAudioUnitProperty_AudioChannelLayout,
738 inScope, inEl,
739 &inLayout, inSize);
740 return result;
743 OSStatus CAAudioUnit::ClearChannelLayout (AudioUnitScope inScope,
744 AudioUnitElement inEl)
746 return AudioUnitSetProperty (AU(),
747 kAudioUnitProperty_AudioChannelLayout,
748 inScope, inEl, NULL, 0);
751 OSStatus CAAudioUnit::GetFormat (AudioUnitScope inScope,
752 AudioUnitElement inEl,
753 AudioStreamBasicDescription &outFormat) const
755 UInt32 dataSize = sizeof (AudioStreamBasicDescription);
756 return AudioUnitGetProperty (AU(), kAudioUnitProperty_StreamFormat,
757 inScope, inEl,
758 &outFormat, &dataSize);
761 OSStatus CAAudioUnit::SetFormat (AudioUnitScope inScope,
762 AudioUnitElement inEl,
763 const AudioStreamBasicDescription &inFormat)
765 return AudioUnitSetProperty (AU(), kAudioUnitProperty_StreamFormat,
766 inScope, inEl,
767 const_cast<AudioStreamBasicDescription*>(&inFormat),
768 sizeof (AudioStreamBasicDescription));
771 OSStatus CAAudioUnit::GetSampleRate (AudioUnitScope inScope,
772 AudioUnitElement inEl,
773 Float64 &outRate) const
775 UInt32 dataSize = sizeof (Float64);
776 return AudioUnitGetProperty (AU(), kAudioUnitProperty_SampleRate,
777 inScope, inEl,
778 &outRate, &dataSize);
781 OSStatus CAAudioUnit::SetSampleRate (AudioUnitScope inScope,
782 AudioUnitElement inEl,
783 Float64 inRate)
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);
792 OSStatus CAAudioUnit::SetSampleRate (Float64 inSampleRate)
794 OSStatus result;
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);
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);
811 home:
812 return result;
815 OSStatus CAAudioUnit::NumberChannels (AudioUnitScope inScope,
816 AudioUnitElement inEl,
817 UInt32 &outChans) const
819 AudioStreamBasicDescription desc;
820 OSStatus result = GetFormat (inScope, inEl, desc);
821 if (!result)
822 outChans = desc.mChannelsPerFrame;
823 return result;
826 OSStatus CAAudioUnit::SetNumberChannels (AudioUnitScope inScope,
827 AudioUnitElement inEl,
828 UInt32 inChans)
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;
839 OSStatus CAAudioUnit::IsElementCountWritable (AudioUnitScope inScope, bool &outWritable) const
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;
850 OSStatus CAAudioUnit::GetElementCount (AudioUnitScope inScope, UInt32 &outCount) const
852 UInt32 propSize = sizeof(outCount);
853 return GetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &outCount, &propSize);
856 OSStatus CAAudioUnit::SetElementCount (AudioUnitScope inScope, UInt32 inCount)
858 return SetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &inCount, sizeof(inCount));
861 bool CAAudioUnit::HasDynamicScope (AudioUnitScope inScope, SInt32 &outTotalNumChannels) const
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
871 // AU has to explicitly tell us about this.
872 if (result) return false;
874 StackAUChannelInfo info (dataSize);
876 result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
877 kAudioUnitScope_Global, 0,
878 info.mChanInfo, &dataSize);
879 if (result) return false;
881 return ValidateDynamicScope (inScope, outTotalNumChannels, info.mChanInfo, (dataSize / sizeof(AUChannelInfo)));
884 // as we've already checked that the element count is writable
885 // the following conditions will match this..
887 -1, -2 -> signifies no restrictions
888 -2, -1 -> signifies no restrictions -> in this case outTotalNumChannels == -1 (any num channels)
890 -N (where N is less than -2), signifies the total channel count on the scope side (in or out)
892 bool CAAudioUnit::ValidateDynamicScope (AudioUnitScope inScope,
893 SInt32 &outTotalNumChannels,
894 const AUChannelInfo *info,
895 UInt32 numInfo) const
897 bool writable = false;
898 OSStatus result = IsElementCountWritable (inScope, writable);
899 if (result || (writable == false))
900 return false;
902 //now chan layout can contain -1 for either scope (ie. doesn't care)
903 for (unsigned int i = 0; i < numInfo; ++i)
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)))
910 outTotalNumChannels = -1;
911 return true;
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;
920 if (info[i].inChannels < -2) {
921 outTotalNumChannels = abs (info[i].inChannels);
922 return true;
926 else if (inScope == kAudioUnitScope_Output) {
927 // isn't dynamic on this side at least
928 if (info[i].outChannels >= 0)
929 continue;
931 if (info[i].outChannels < -2) {
932 outTotalNumChannels = abs (info[i].outChannels);
933 return true;
937 else {
938 break; // wrong scope was specified
942 return false;
945 OSStatus CAAudioUnit::ConfigureDynamicScope (AudioUnitScope inScope,
946 UInt32 inNumElements,
947 UInt32 *inChannelsPerElement,
948 Float64 inSampleRate)
950 SInt32 numChannels = 0;
951 bool isDyamic = HasDynamicScope (inScope, numChannels);
952 if (isDyamic == false)
953 return kAudioUnitErr_InvalidProperty;
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;
965 OSStatus result = SetElementCount (inScope, inNumElements);
966 if (result)
967 return result;
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;
977 return noErr;
980 #pragma mark __Properties
982 bool CAAudioUnit::CanBypass () const
984 Boolean outWritable;
985 OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_BypassEffect,
986 kAudioUnitScope_Global, 0,
987 NULL, &outWritable);
988 return (!result && outWritable);
991 bool CAAudioUnit::GetBypass () const
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);
1001 OSStatus CAAudioUnit::SetBypass (bool inBypass) const
1003 UInt32 bypass = inBypass ? 1 : 0;
1004 return AudioUnitSetProperty (AU(), kAudioUnitProperty_BypassEffect,
1005 kAudioUnitScope_Global, 0,
1006 &bypass, sizeof (UInt32));
1009 Float64 CAAudioUnit::Latency () const
1011 Float64 secs;
1012 UInt32 size = sizeof(secs);
1013 if (GetProperty (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &secs, &size))
1014 return 0;
1015 return secs;
1018 OSStatus CAAudioUnit::GetAUPreset (CFPropertyListRef &outData) const
1020 UInt32 dataSize = sizeof(outData);
1021 return AudioUnitGetProperty (AU(), kAudioUnitProperty_ClassInfo,
1022 kAudioUnitScope_Global, 0,
1023 &outData, &dataSize);
1026 OSStatus CAAudioUnit::SetAUPreset (CFPropertyListRef &inData)
1028 return AudioUnitSetProperty (AU(), kAudioUnitProperty_ClassInfo,
1029 kAudioUnitScope_Global, 0,
1030 &inData, sizeof (CFPropertyListRef));
1033 OSStatus CAAudioUnit::GetPresentPreset (AUPreset &outData) const
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);
1051 return result;
1054 OSStatus CAAudioUnit::SetPresentPreset (AUPreset &inData)
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));
1064 return result;
1067 bool CAAudioUnit::HasCustomView () const
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;
1081 return true;
1084 OSStatus CAAudioUnit::GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1085 Float32 &outValue) const
1087 return mDataPtr ? mDataPtr->GetParameter (inID, scope, element, outValue) : paramErr;
1090 OSStatus CAAudioUnit::SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1091 Float32 value, UInt32 bufferOffsetFrames)
1093 return mDataPtr ? mDataPtr->SetParameter (inID, scope, element, value, bufferOffsetFrames) : paramErr;
1096 OSStatus CAAudioUnit::MIDIEvent (UInt32 inStatus,
1097 UInt32 inData1,
1098 UInt32 inData2,
1099 UInt32 inOffsetSampleFrame)
1101 return mDataPtr ? mDataPtr->MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame) : paramErr;
1104 OSStatus CAAudioUnit::StartNote (MusicDeviceInstrumentID inInstrument,
1105 MusicDeviceGroupID inGroupID,
1106 NoteInstanceID * outNoteInstanceID,
1107 UInt32 inOffsetSampleFrame,
1108 const MusicDeviceNoteParams * inParams)
1110 return mDataPtr ? mDataPtr->StartNote (inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams)
1111 : paramErr;
1114 OSStatus CAAudioUnit::StopNote (MusicDeviceGroupID inGroupID,
1115 NoteInstanceID inNoteInstanceID,
1116 UInt32 inOffsetSampleFrame)
1118 return mDataPtr ? mDataPtr->StopNote (inGroupID, inNoteInstanceID, inOffsetSampleFrame) : paramErr;
1121 #pragma mark __Render
1123 OSStatus CAAudioUnit::Render (AudioUnitRenderActionFlags * ioActionFlags,
1124 const AudioTimeStamp * inTimeStamp,
1125 UInt32 inOutputBusNumber,
1126 UInt32 inNumberFrames,
1127 AudioBufferList * ioData)
1129 return mDataPtr ? mDataPtr->Render (ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData) : paramErr;
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)
1140 AudioBuffer *buf = ioData->mBuffers;
1141 for (UInt32 i = ioData->mNumberBuffers; i--; ++buf)
1142 memset((Byte *)buf->mData, 0, buf->mDataByteSize);
1144 return noErr;
1147 OSStatus CAAudioUnit::Preroll (UInt32 inFrameSize)
1149 CAStreamBasicDescription desc;
1150 OSStatus result = GetFormat (kAudioUnitScope_Input, 0, desc);
1151 bool hasInput = false;
1152 //we have input
1153 if (result == noErr)
1155 sRenderCallback.inputProc = PrerollRenderProc;
1156 sRenderCallback.inputProcRefCon = 0;
1158 result = SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1159 0, &sRenderCallback, sizeof(sRenderCallback));
1160 if (result) return result;
1161 hasInput = true;
1164 AudioUnitRenderActionFlags flags = 0;
1165 AudioTimeStamp time;
1166 memset (&time, 0, sizeof(time));
1167 time.mFlags = kAudioTimeStampSampleTimeValid;
1169 CAStreamBasicDescription outputFormat;
1170 require_noerr (result = GetFormat (kAudioUnitScope_Output, 0, outputFormat), home);
1172 AUOutputBL list (outputFormat, inFrameSize);
1173 list.Prepare ();
1175 require_noerr (result = Render (&flags, &time, 0, inFrameSize, list.ABL()), home);
1176 require_noerr (result = GlobalReset(), home);
1179 home:
1180 if (hasInput) {
1181 // remove our installed callback
1182 sRenderCallback.inputProc = 0;
1183 sRenderCallback.inputProcRefCon = 0;
1185 SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1186 0, &sRenderCallback, sizeof(sRenderCallback));
1188 return result;
1191 #pragma mark __CAAUChanHelper
1193 CAAUChanHelper::CAAUChanHelper(const CAAudioUnit &inAU, AudioUnitScope inScope)
1194 :mChans(NULL), mNumEls(0), mDidAllocate(false)
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);
1206 for (unsigned int i = 0; i < elCount; ++i) {
1207 UInt32 numChans;
1208 if (inAU.NumberChannels (inScope, i, numChans)) return;
1209 mChans[i] = numChans;
1211 mNumEls = elCount;
1214 CAAUChanHelper::~CAAUChanHelper()
1216 if (mDidAllocate) delete [] mChans;
1219 CAAUChanHelper& CAAUChanHelper::operator= (const CAAUChanHelper &c)
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;
1229 memcpy (mChans, c.mChans, c.mNumEls * sizeof(int));
1231 return *this;
1234 #pragma mark __Print Utilities
1236 void CAAudioUnit::Print (FILE* file) const
1238 fprintf (file, "AudioUnit:%p\n", AU());
1239 if (IsValid()) {
1240 fprintf (file, "\tnode=%ld\t", (long)GetAUNode()); Comp().Print (file);