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 /*=============================================================================
41 =============================================================================*/
43 #include "CAAudioUnit.h"
45 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
46 #include <AudioUnit/MusicDevice.h>
48 #include <MusicDevice.h>
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 {
66 AUState (Component inComp)
69 OSStatus result = ::OpenAComponent (inComp, &mUnit);
75 AUState (const AUNode &inNode, const AudioUnit& inUnit)
76 : mUnit (inUnit), mNode (inNode)
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,
122 UInt32 inOffsetSampleFrame)
125 if (mMIDIEventProc != NULL) {
126 return reinterpret_cast<MusicDeviceMIDIEventProc>(mMIDIEventProc) (mConnInstanceStorage,
127 inStatus, inData1, inData2, inOffsetSampleFrame);
129 return MusicDeviceMIDIEvent (mUnit, inStatus, inData1, inData2, inOffsetSampleFrame);
135 OSStatus StartNote (MusicDeviceInstrumentID inInstrument,
136 MusicDeviceGroupID inGroupID,
137 NoteInstanceID * outNoteInstanceID,
138 UInt32 inOffsetSampleFrame,
139 const MusicDeviceNoteParams * inParams)
142 return MusicDeviceStartNote (mUnit, inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams);
147 OSStatus StopNote (MusicDeviceGroupID inGroupID,
148 NoteInstanceID inNoteInstanceID,
149 UInt32 inOffsetSampleFrame)
152 return MusicDeviceStopNote (mUnit, inGroupID, inNoteInstanceID, inOffsetSampleFrame);
159 // get the fast dispatch pointers
162 UInt32 size = sizeof(AudioUnitRenderProc);
163 if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
164 kAudioUnitScope_Global, kAudioUnitRenderSelect,
165 &mRenderProc, &size) != noErr)
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);
184 mConnInstanceStorage = NULL;
187 ProcPtr mRenderProc, mGetParamProc, mSetParamProc, mMIDIEventProc;
189 void * mConnInstanceStorage;
192 // get the compiler to tell us when we do a bad thing!!!
194 AUState (const AUState& other) : CAReferenceCounted (other) {}
195 AUState& operator= (const AUState&) { return *this; }
199 CAAudioUnit::AUState::~AUState ()
201 if (mUnit && (mNode == 0)) {
202 ::CloseComponent (mUnit);
208 OSStatus CAAudioUnit::Open (const CAComponent& inComp, CAAudioUnit &outUnit)
213 } catch (OSStatus res) {
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 ()
244 CAAudioUnit& CAAudioUnit::operator= (const CAAudioUnit &a)
246 if (mDataPtr != a.mDataPtr) {
250 if ((mDataPtr = a.mDataPtr) != NULL)
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;
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;
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
308 if ((Comp().Desc().IsEffect() && (inChannelsIn == inChannelsOut))
309 || (Comp().Desc().IsOffline() && (inChannelsIn == inChannelsOut)))
315 // the au should either really tell us about this
316 // or we will assume the worst
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;
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
347 if (Comp().Desc().IsEffect())
351 else if (Comp().Desc().IsGenerator() || Comp().Desc().IsMusicDevice()) {
352 // directly query Bus Formats
353 // Note that that these may refer to different subBusses
354 // (eg. Kick, Snare,.. on a Drummachine)
355 // eventually the Bus-Name for each configuration should be exposed
356 // for the User to select..
358 UInt32 elCountIn, elCountOut;
360 if (GetElementCount (kAudioUnitScope_Input, elCountIn)) return -1;
361 if (GetElementCount (kAudioUnitScope_Output, elCountOut)) return -1;
363 cnt = std::max(elCountIn, elCountOut);
365 *chaninfo = (AUChannelInfo*) malloc (sizeof (AUChannelInfo) * cnt);
367 for (unsigned int i = 0; i < elCountIn; ++i) {
369 if (NumberChannels (kAudioUnitScope_Input, i, numChans)) return -1;
370 (*chaninfo)[i].inChannels = numChans;
372 for (unsigned int i = elCountIn; i < cnt; ++i) {
373 (*chaninfo)[i].inChannels = 0;
376 for (unsigned int i = 0; i < elCountOut; ++i) {
378 if (NumberChannels (kAudioUnitScope_Output, i, numChans)) return -1;
379 (*chaninfo)[i].outChannels = numChans;
381 for (unsigned int i = elCountOut; i < cnt; ++i) {
382 (*chaninfo)[i].outChannels = 0;
388 // the au should either really tell us about this
389 // or we will assume the worst
394 *chaninfo = (AUChannelInfo*) malloc (dataSize);
395 cnt = dataSize / sizeof (AUChannelInfo);
397 result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
398 kAudioUnitScope_Global, 0,
399 *chaninfo, &dataSize);
401 if (result) { return -1; }
406 bool CAAudioUnit::ValidateChannelPair (int inChannelsIn,
408 const AUChannelInfo * info,
409 UInt32 numChanInfo) const
411 // we've the following cases (some combinations) to test here:
413 >0 An explicit number of channels on either side
414 0 that side (generally input!) has no elements
416 -1,-1 any num channels as long as same channels on in and out
417 -1,-2 any num channels channels on in and out - special meaning
418 -2+ indicates total num channs AU can handle
419 - elements configurable to any num channels,
420 - element count in scope must be writable
423 //now chan layout can contain -1 for either scope (ie. doesn't care)
424 for (unsigned int i = 0; i < numChanInfo; ++i)
426 //less than zero on both sides - check for special attributes
427 if ((info[i].inChannels < 0) && (info[i].outChannels < 0))
429 // these are our wild card matches
430 if (info[i].inChannels == -1 && info[i].outChannels == -1) {
431 if (inChannelsOut == inChannelsIn) {
435 else if ((info[i].inChannels == -1 && info[i].outChannels == -2)
436 || (info[i].inChannels == -2 && info[i].outChannels == -1))
440 // these are our total num channels matches
441 // element count MUST be writable
443 bool outWrite = false; bool inWrite = false;
444 IsElementCountWritable (kAudioUnitScope_Output, outWrite);
445 IsElementCountWritable (kAudioUnitScope_Input, inWrite);
446 if (inWrite && outWrite) {
447 if ((inChannelsOut <= abs(info[i].outChannels))
448 && (inChannelsIn <= abs(info[i].inChannels)))
456 // special meaning on input, specific num on output
457 else if (info[i].inChannels < 0) {
458 if (info[i].outChannels == inChannelsOut)
460 // can do any in channels
461 if (info[i].inChannels == -1) {
464 // total chans on input
466 bool inWrite = false;
467 IsElementCountWritable (kAudioUnitScope_Input, inWrite);
468 if (inWrite && (inChannelsIn <= abs(info[i].inChannels))) {
475 // special meaning on output, specific num on input
476 else if (info[i].outChannels < 0) {
477 if (info[i].inChannels == inChannelsIn)
479 // can do any out channels
480 if (info[i].outChannels == -1) {
483 // total chans on output
485 bool outWrite = false;
486 IsElementCountWritable (kAudioUnitScope_Output, outWrite);
487 if (outWrite && (inChannelsOut <= abs(info[i].outChannels))) {
494 // both chans in struct >= 0 - thus has to explicitly match
495 else if ((info[i].inChannels == inChannelsIn) && (info[i].outChannels == inChannelsOut)) {
499 // now check to see if a wild card on the args (inChannelsIn or inChannelsOut chans is zero) is found
500 // tells us to match just one side of the scopes
501 else if (inChannelsIn == 0) {
502 if (info[i].outChannels == inChannelsOut) {
506 else if (inChannelsOut == 0) {
507 if (info[i].inChannels == inChannelsIn) {
516 bool CheckDynCount (SInt32 inTotalChans, const CAAUChanHelper &inHelper)
519 for (unsigned int i = 0; i < inHelper.mNumEls; ++i)
520 totalChans += inHelper.mChans[i];
521 return (totalChans <= inTotalChans);
524 bool CAAudioUnit::CheckOneSide (const CAAUChanHelper &inHelper,
526 const AUChannelInfo *info,
527 UInt32 numInfo) const
529 // now we can use the wildcard option (see above impl) to see if this matches
530 for (unsigned int el = 0; el < inHelper.mNumEls; ++el) {
531 bool testAlready = false;
532 for (unsigned int i = 0; i < el; ++i) {
533 if (inHelper.mChans[i] == inHelper.mChans[el]) {
540 if (!ValidateChannelPair (0, inHelper.mChans[el], info, numInfo)) return false;
542 if (!ValidateChannelPair (inHelper.mChans[el], 0, info, numInfo)) return false;
549 bool CAAudioUnit::CanDo (const CAAUChanHelper &inputs,
550 const CAAUChanHelper &outputs) const
553 // first check our state
555 if (inputs.mNumEls == 0 && outputs.mNumEls == 0) return false;
558 if (GetElementCount (kAudioUnitScope_Input, elCount)) { return false; }
559 if (elCount != inputs.mNumEls) return false;
561 if (GetElementCount (kAudioUnitScope_Output, elCount)) { return false; }
562 if (elCount != outputs.mNumEls) return false;
564 // (1) special cases (effects and sources (generators and instruments) only)
566 if (GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
567 kAudioUnitScope_Global, 0, &dataSize, NULL) != noErr)
569 if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline()) {
570 UInt32 numChan = outputs.mNumEls > 0 ? outputs.mChans[0] : inputs.mChans[0];
571 for (unsigned int in = 0; in < inputs.mNumEls; ++in)
572 if (numChan != inputs.mChans[in]) return false;
573 for (unsigned int out = 0; out < outputs.mNumEls; ++out)
574 if (numChan != outputs.mChans[out]) return false;
578 // in this case, all the channels have to match the current config
579 if (Comp().Desc().IsGenerator() || Comp().Desc().IsMusicDevice()) {
580 for (unsigned int in = 0; in < inputs.mNumEls; ++in) {
582 if (NumberChannels (kAudioUnitScope_Input, in, chan)) return false;
583 if (chan != UInt32(inputs.mChans[in])) return false;
585 for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
587 if (NumberChannels (kAudioUnitScope_Output, out, chan)) return false;
588 if (chan != UInt32(outputs.mChans[out])) return false;
593 // if we get here we can't determine anything about channel capabilities
597 StackAUChannelInfo info (dataSize);
599 if (GetProperty (kAudioUnitProperty_SupportedNumChannels,
600 kAudioUnitScope_Global, 0,
601 info.mChanInfo, &dataSize) != noErr)
606 int numInfo = dataSize / sizeof(AUChannelInfo);
608 // (2) Test for dynamic capability (or no elements on that scope)
609 SInt32 dynInChans = 0;
610 if (ValidateDynamicScope (kAudioUnitScope_Input, dynInChans, info.mChanInfo, numInfo)) {
611 if (CheckDynCount (dynInChans, inputs) == false) return false;
614 SInt32 dynOutChans = 0;
615 if (ValidateDynamicScope (kAudioUnitScope_Output, dynOutChans, info.mChanInfo, numInfo)) {
616 if (CheckDynCount (dynOutChans, outputs) == false) return false;
619 if (dynOutChans && dynInChans) { return true; }
621 // (3) Just need to test one side
622 if (dynInChans || (inputs.mNumEls == 0)) {
623 return CheckOneSide (outputs, true, info.mChanInfo, numInfo);
626 if (dynOutChans || (outputs.mNumEls == 0)) {
627 return CheckOneSide (inputs, false, info.mChanInfo, numInfo);
630 // (4) - not a dynamic AU, has ins and outs, and has channel constraints so we test every possible pairing
631 for (unsigned int in = 0; in < inputs.mNumEls; ++in)
633 bool testInAlready = false;
634 for (unsigned int i = 0; i < in; ++i) {
635 if (inputs.mChans[i] == inputs.mChans[in]) {
636 testInAlready = true;
640 if (!testInAlready) {
641 for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
642 // try to save a little bit and not test the same pairing multiple times...
643 bool testOutAlready = false;
644 for (unsigned int i = 0; i < out; ++i) {
645 if (outputs.mChans[i] == outputs.mChans[out]) {
646 testOutAlready = true;
650 if (!testOutAlready) {
651 if (!ValidateChannelPair (inputs.mChans[in], outputs.mChans[out],info.mChanInfo, numInfo)) {
662 bool CAAudioUnit::SupportsNumChannels () const
664 // this is the default assumption of an audio effect unit
665 Boolean* isWritable = 0;
667 // lets see if the unit has any channel restrictions
668 OSStatus result = AudioUnitGetPropertyInfo (AU(),
669 kAudioUnitProperty_SupportedNumChannels,
670 kAudioUnitScope_Global, 0,
671 &dataSize, isWritable); //don't care if this is writable
673 // if this property is NOT implemented an FX unit
674 // is expected to deal with same channel valance in and out
676 if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline())
679 return result == noErr;
682 bool CAAudioUnit::GetChannelLayouts (AudioUnitScope inScope,
683 AudioUnitElement inEl,
684 ChannelTagVector &outChannelVector) const
686 if (HasChannelLayouts (inScope, inEl) == false) return false;
689 OSStatus result = AudioUnitGetPropertyInfo (AU(),
690 kAudioUnitProperty_SupportedChannelLayoutTags,
694 if (result == kAudioUnitErr_InvalidProperty) {
695 // if we get here we can do layouts but we've got the speaker config property
696 outChannelVector.erase (outChannelVector.begin(), outChannelVector.end());
697 outChannelVector.push_back (kAudioChannelLayoutTag_Stereo);
698 outChannelVector.push_back (kAudioChannelLayoutTag_StereoHeadphones);
699 outChannelVector.push_back (kAudioChannelLayoutTag_Quadraphonic);
700 outChannelVector.push_back (kAudioChannelLayoutTag_AudioUnit_5_0);
704 if (result) return false;
707 // OK lets get our channel layouts and see if the one we want is present
708 AudioChannelLayoutTag* info = (AudioChannelLayoutTag*)malloc (dataSize);
709 result = AudioUnitGetProperty (AU(),
710 kAudioUnitProperty_SupportedChannelLayoutTags,
713 if (result) goto home;
715 outChannelVector.erase (outChannelVector.begin(), outChannelVector.end());
716 for (unsigned int i = 0; i < (dataSize / sizeof (AudioChannelLayoutTag)); ++i)
717 outChannelVector.push_back (info[i]);
724 bool CAAudioUnit::HasChannelLayouts (AudioUnitScope inScope,
725 AudioUnitElement inEl) const
727 OSStatus result = AudioUnitGetPropertyInfo (AU(),
728 kAudioUnitProperty_SupportedChannelLayoutTags,
734 OSStatus CAAudioUnit::GetChannelLayout (AudioUnitScope inScope,
735 AudioUnitElement inEl,
736 CAAudioChannelLayout &outLayout) const
739 OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_AudioChannelLayout,
740 inScope, inEl, &size, NULL);
741 if (result) return result;
743 AudioChannelLayout *layout = (AudioChannelLayout*)malloc (size);
745 require_noerr (result = AudioUnitGetProperty (AU(), kAudioUnitProperty_AudioChannelLayout,
746 inScope, inEl, layout, &size), home);
748 outLayout = CAAudioChannelLayout (layout);
755 OSStatus CAAudioUnit::SetChannelLayout (AudioUnitScope inScope,
756 AudioUnitElement inEl,
757 CAAudioChannelLayout &inLayout)
759 OSStatus result = AudioUnitSetProperty (AU(),
760 kAudioUnitProperty_AudioChannelLayout,
762 inLayout, inLayout.Size());
766 OSStatus CAAudioUnit::SetChannelLayout (AudioUnitScope inScope,
767 AudioUnitElement inEl,
768 AudioChannelLayout &inLayout,
771 OSStatus result = AudioUnitSetProperty (AU(),
772 kAudioUnitProperty_AudioChannelLayout,
778 OSStatus CAAudioUnit::ClearChannelLayout (AudioUnitScope inScope,
779 AudioUnitElement inEl)
781 return AudioUnitSetProperty (AU(),
782 kAudioUnitProperty_AudioChannelLayout,
783 inScope, inEl, NULL, 0);
786 OSStatus CAAudioUnit::GetFormat (AudioUnitScope inScope,
787 AudioUnitElement inEl,
788 AudioStreamBasicDescription &outFormat) const
790 UInt32 dataSize = sizeof (AudioStreamBasicDescription);
791 return AudioUnitGetProperty (AU(), kAudioUnitProperty_StreamFormat,
793 &outFormat, &dataSize);
796 OSStatus CAAudioUnit::SetFormat (AudioUnitScope inScope,
797 AudioUnitElement inEl,
798 const AudioStreamBasicDescription &inFormat)
800 return AudioUnitSetProperty (AU(), kAudioUnitProperty_StreamFormat,
802 const_cast<AudioStreamBasicDescription*>(&inFormat),
803 sizeof (AudioStreamBasicDescription));
806 OSStatus CAAudioUnit::GetSampleRate (AudioUnitScope inScope,
807 AudioUnitElement inEl,
808 Float64 &outRate) const
810 UInt32 dataSize = sizeof (Float64);
811 return AudioUnitGetProperty (AU(), kAudioUnitProperty_SampleRate,
813 &outRate, &dataSize);
816 OSStatus CAAudioUnit::SetSampleRate (AudioUnitScope inScope,
817 AudioUnitElement inEl,
820 AudioStreamBasicDescription desc;
821 OSStatus result = GetFormat (inScope, inEl, desc);
822 if (result) return result;
823 desc.mSampleRate = inRate;
824 return SetFormat (inScope, inEl, desc);
827 OSStatus CAAudioUnit::SetSampleRate (Float64 inSampleRate)
832 require_noerr (result = GetElementCount(kAudioUnitScope_Input, elCount), home);
834 for (unsigned int i = 0; i < elCount; ++i) {
835 require_noerr (result = SetSampleRate (kAudioUnitScope_Input, i, inSampleRate), home);
839 require_noerr (result = GetElementCount(kAudioUnitScope_Output, elCount), home);
841 for (unsigned int i = 0; i < elCount; ++i) {
842 require_noerr (result = SetSampleRate (kAudioUnitScope_Output, i, inSampleRate), home);
850 OSStatus CAAudioUnit::NumberChannels (AudioUnitScope inScope,
851 AudioUnitElement inEl,
852 UInt32 &outChans) const
854 AudioStreamBasicDescription desc;
855 OSStatus result = GetFormat (inScope, inEl, desc);
857 outChans = desc.mChannelsPerFrame;
861 OSStatus CAAudioUnit::SetNumberChannels (AudioUnitScope inScope,
862 AudioUnitElement inEl,
865 // set this as the output of the AU
866 CAStreamBasicDescription desc;
867 OSStatus result = GetFormat (inScope, inEl, desc);
868 if (result) return result;
869 desc.SetCanonical (inChans, desc.IsInterleaved());
870 result = SetFormat (inScope, inEl, desc);
874 OSStatus CAAudioUnit::IsElementCountWritable (AudioUnitScope inScope, bool &outWritable) const
878 OSStatus result = GetPropertyInfo (kAudioUnitProperty_ElementCount, inScope, 0, &outDataSize, &isWritable);
881 outWritable = isWritable ? true : false;
885 OSStatus CAAudioUnit::GetElementCount (AudioUnitScope inScope, UInt32 &outCount) const
887 UInt32 propSize = sizeof(outCount);
888 return GetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &outCount, &propSize);
891 OSStatus CAAudioUnit::SetElementCount (AudioUnitScope inScope, UInt32 inCount)
893 return SetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &inCount, sizeof(inCount));
896 bool CAAudioUnit::HasDynamicScope (AudioUnitScope inScope, SInt32 &outTotalNumChannels) const
898 // ok - now we need to check the AU's capability here.
899 // this is the default assumption of an audio effect unit
900 Boolean* isWritable = 0;
902 OSStatus result = GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
903 kAudioUnitScope_Global, 0,
904 &dataSize, isWritable); //don't care if this is writable
906 // AU has to explicitly tell us about this.
907 if (result) return false;
909 StackAUChannelInfo info (dataSize);
911 result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
912 kAudioUnitScope_Global, 0,
913 info.mChanInfo, &dataSize);
914 if (result) return false;
916 return ValidateDynamicScope (inScope, outTotalNumChannels, info.mChanInfo, (dataSize / sizeof(AUChannelInfo)));
919 // as we've already checked that the element count is writable
920 // the following conditions will match this..
922 -1, -2 -> signifies no restrictions
923 -2, -1 -> signifies no restrictions -> in this case outTotalNumChannels == -1 (any num channels)
925 -N (where N is less than -2), signifies the total channel count on the scope side (in or out)
927 bool CAAudioUnit::ValidateDynamicScope (AudioUnitScope inScope,
928 SInt32 &outTotalNumChannels,
929 const AUChannelInfo *info,
930 UInt32 numInfo) const
932 bool writable = false;
933 OSStatus result = IsElementCountWritable (inScope, writable);
934 if (result || (writable == false))
937 //now chan layout can contain -1 for either scope (ie. doesn't care)
938 for (unsigned int i = 0; i < numInfo; ++i)
940 // lets test the special wild card case first...
941 // this says the AU can do any num channels on input or output - for eg. Matrix Mixer
942 if (((info[i].inChannels == -1) && (info[i].outChannels == -2))
943 || ((info[i].inChannels == -2) && (info[i].outChannels == -1)))
945 outTotalNumChannels = -1;
949 // ok lets now test our special case....
950 if (inScope == kAudioUnitScope_Input) {
951 // isn't dynamic on this side at least
952 if (info[i].inChannels >= 0)
955 if (info[i].inChannels < -2) {
956 outTotalNumChannels = abs (info[i].inChannels);
961 else if (inScope == kAudioUnitScope_Output) {
962 // isn't dynamic on this side at least
963 if (info[i].outChannels >= 0)
966 if (info[i].outChannels < -2) {
967 outTotalNumChannels = abs (info[i].outChannels);
973 break; // wrong scope was specified
980 OSStatus CAAudioUnit::ConfigureDynamicScope (AudioUnitScope inScope,
981 UInt32 inNumElements,
982 UInt32 *inChannelsPerElement,
983 Float64 inSampleRate)
985 SInt32 numChannels = 0;
986 bool isDyamic = HasDynamicScope (inScope, numChannels);
987 if (isDyamic == false)
988 return kAudioUnitErr_InvalidProperty;
990 //lets to a sanity check...
991 // if numChannels == -1, then it can do "any"...
992 if (numChannels > 0) {
994 for (unsigned int i = 0; i < inNumElements; ++i)
995 count += inChannelsPerElement[i];
996 if (count > numChannels)
997 return kAudioUnitErr_InvalidPropertyValue;
1000 OSStatus result = SetElementCount (inScope, inNumElements);
1004 CAStreamBasicDescription desc;
1005 desc.mSampleRate = inSampleRate;
1006 for (unsigned int i = 0; i < inNumElements; ++i) {
1007 desc.SetCanonical (inChannelsPerElement[i], false);
1008 result = SetFormat (inScope, i, desc);
1015 #pragma mark __Properties
1017 bool CAAudioUnit::CanBypass () const
1019 Boolean outWritable;
1020 OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_BypassEffect,
1021 kAudioUnitScope_Global, 0,
1022 NULL, &outWritable);
1023 return (!result && outWritable);
1026 bool CAAudioUnit::GetBypass () const
1028 UInt32 dataSize = sizeof (UInt32);
1030 OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_BypassEffect,
1031 kAudioUnitScope_Global, 0,
1032 &outBypass, &dataSize);
1033 return (result ? false : outBypass);
1036 OSStatus CAAudioUnit::SetBypass (bool inBypass) const
1038 UInt32 bypass = inBypass ? 1 : 0;
1039 return AudioUnitSetProperty (AU(), kAudioUnitProperty_BypassEffect,
1040 kAudioUnitScope_Global, 0,
1041 &bypass, sizeof (UInt32));
1044 Float64 CAAudioUnit::Latency () const
1047 UInt32 size = sizeof(secs);
1048 if (GetProperty (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &secs, &size))
1053 OSStatus CAAudioUnit::GetAUPreset (CFPropertyListRef &outData) const
1055 UInt32 dataSize = sizeof(outData);
1056 return AudioUnitGetProperty (AU(), kAudioUnitProperty_ClassInfo,
1057 kAudioUnitScope_Global, 0,
1058 &outData, &dataSize);
1061 OSStatus CAAudioUnit::SetAUPreset (CFPropertyListRef &inData)
1063 return AudioUnitSetProperty (AU(), kAudioUnitProperty_ClassInfo,
1064 kAudioUnitScope_Global, 0,
1065 &inData, sizeof (CFPropertyListRef));
1068 OSStatus CAAudioUnit::GetPresentPreset (AUPreset &outData) const
1070 UInt32 dataSize = sizeof(outData);
1071 OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_PresentPreset,
1072 kAudioUnitScope_Global, 0,
1073 &outData, &dataSize);
1074 if (result == kAudioUnitErr_InvalidProperty) {
1075 dataSize = sizeof(outData);
1076 result = AudioUnitGetProperty (AU(), kAudioUnitProperty_CurrentPreset,
1077 kAudioUnitScope_Global, 0,
1078 &outData, &dataSize);
1079 if (result == noErr) {
1080 // we now retain the CFString in the preset so for the client of this API
1081 // it is consistent (ie. the string should be released when done)
1082 if (outData.presetName)
1083 CFRetain (outData.presetName);
1089 OSStatus CAAudioUnit::SetPresentPreset (AUPreset &inData)
1091 OSStatus result = AudioUnitSetProperty (AU(), kAudioUnitProperty_PresentPreset,
1092 kAudioUnitScope_Global, 0,
1093 &inData, sizeof (AUPreset));
1094 if (result == kAudioUnitErr_InvalidProperty) {
1095 result = AudioUnitSetProperty (AU(), kAudioUnitProperty_CurrentPreset,
1096 kAudioUnitScope_Global, 0,
1097 &inData, sizeof (AUPreset));
1102 bool CAAudioUnit::HasCustomView () const
1104 UInt32 dataSize = 0;
1105 OSStatus result = GetPropertyInfo(kAudioUnitProperty_GetUIComponentList,
1106 kAudioUnitScope_Global, 0,
1108 if (result || !dataSize) {
1110 result = GetPropertyInfo(kAudioUnitProperty_CocoaUI,
1111 kAudioUnitScope_Global, 0,
1113 if (result || !dataSize)
1119 OSStatus CAAudioUnit::GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1120 Float32 &outValue) const
1122 return mDataPtr ? (OSStatus) mDataPtr->GetParameter (inID, scope, element, outValue) : paramErr;
1125 OSStatus CAAudioUnit::SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1126 Float32 value, UInt32 bufferOffsetFrames)
1128 return mDataPtr ? (OSStatus) mDataPtr->SetParameter (inID, scope, element, value, bufferOffsetFrames) : paramErr;
1131 OSStatus CAAudioUnit::MIDIEvent (UInt32 inStatus,
1134 UInt32 inOffsetSampleFrame)
1136 return mDataPtr ? (OSStatus) mDataPtr->MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame) : paramErr;
1139 OSStatus CAAudioUnit::StartNote (MusicDeviceInstrumentID inInstrument,
1140 MusicDeviceGroupID inGroupID,
1141 NoteInstanceID * outNoteInstanceID,
1142 UInt32 inOffsetSampleFrame,
1143 const MusicDeviceNoteParams * inParams)
1145 return mDataPtr ? (OSStatus) mDataPtr->StartNote (inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams)
1149 OSStatus CAAudioUnit::StopNote (MusicDeviceGroupID inGroupID,
1150 NoteInstanceID inNoteInstanceID,
1151 UInt32 inOffsetSampleFrame)
1153 return mDataPtr ? (OSStatus) mDataPtr->StopNote (inGroupID, inNoteInstanceID, inOffsetSampleFrame) : paramErr;
1156 #pragma mark __Render
1158 OSStatus CAAudioUnit::Render (AudioUnitRenderActionFlags * ioActionFlags,
1159 const AudioTimeStamp * inTimeStamp,
1160 UInt32 inOutputBusNumber,
1161 UInt32 inNumberFrames,
1162 AudioBufferList * ioData)
1164 return mDataPtr ? (OSStatus) mDataPtr->Render (ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData) : paramErr;
1167 static AURenderCallbackStruct sRenderCallback;
1168 static OSStatus PrerollRenderProc ( void * /*inRefCon*/,
1169 AudioUnitRenderActionFlags * /*inActionFlags*/,
1170 const AudioTimeStamp * /*inTimeStamp*/,
1171 UInt32 /*inBusNumber*/,
1172 UInt32 /*inNumFrames*/,
1173 AudioBufferList *ioData)
1175 AudioBuffer *buf = ioData->mBuffers;
1176 for (UInt32 i = ioData->mNumberBuffers; i--; ++buf)
1177 memset((Byte *)buf->mData, 0, buf->mDataByteSize);
1182 OSStatus CAAudioUnit::Preroll (UInt32 inFrameSize)
1184 CAStreamBasicDescription desc;
1185 OSStatus result = GetFormat (kAudioUnitScope_Input, 0, desc);
1186 bool hasInput = false;
1188 if (result == noErr)
1190 sRenderCallback.inputProc = PrerollRenderProc;
1191 sRenderCallback.inputProcRefCon = 0;
1193 result = SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1194 0, &sRenderCallback, sizeof(sRenderCallback));
1195 if (result) return result;
1199 AudioUnitRenderActionFlags flags = 0;
1200 AudioTimeStamp time;
1201 memset (&time, 0, sizeof(time));
1202 time.mFlags = kAudioTimeStampSampleTimeValid;
1204 CAStreamBasicDescription outputFormat;
1205 require_noerr (result = GetFormat (kAudioUnitScope_Output, 0, outputFormat), home);
1207 AUOutputBL list (outputFormat, inFrameSize);
1210 require_noerr (result = Render (&flags, &time, 0, inFrameSize, list.ABL()), home);
1211 require_noerr (result = GlobalReset(), home);
1216 // remove our installed callback
1217 sRenderCallback.inputProc = 0;
1218 sRenderCallback.inputProcRefCon = 0;
1220 SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1221 0, &sRenderCallback, sizeof(sRenderCallback));
1226 #pragma mark __CAAUChanHelper
1228 CAAUChanHelper::CAAUChanHelper(const CAAudioUnit &inAU, AudioUnitScope inScope)
1229 :mChans(NULL), mNumEls(0), mDidAllocate(false)
1232 if (inAU.GetElementCount (inScope, elCount)) return;
1234 mChans = new UInt32[elCount];
1235 mDidAllocate = true;
1236 memset (mChans, 0, sizeof(int) * elCount);
1238 mChans = mStaticChans;
1239 memset (mChans, 0, sizeof(int) * 8);
1241 for (unsigned int i = 0; i < elCount; ++i) {
1243 if (inAU.NumberChannels (inScope, i, numChans)) return;
1244 mChans[i] = numChans;
1249 CAAUChanHelper::~CAAUChanHelper()
1251 if (mDidAllocate) delete [] mChans;
1254 CAAUChanHelper& CAAUChanHelper::operator= (const CAAUChanHelper &c)
1256 if (mDidAllocate) delete [] mChans;
1257 if (c.mDidAllocate) {
1258 mChans = new UInt32[c.mNumEls];
1259 mDidAllocate = true;
1261 mDidAllocate = false;
1262 mChans = mStaticChans;
1264 memcpy (mChans, c.mChans, c.mNumEls * sizeof(int));
1269 #pragma mark __Print Utilities
1271 void CAAudioUnit::Print (FILE* file) const
1273 fprintf (file, "AudioUnit:%p\n", AU());
1275 fprintf (file, "\tnode=%ld\t", (long)GetAUNode()); Comp().Print (file);