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, elCount;
360 if (GetElementCount (kAudioUnitScope_Input, elCountIn)) return 1;
361 if (GetElementCount (kAudioUnitScope_Output, elCountOut)) return 1;
363 elCount = std::max(elCountIn, elCountOut);
365 *chaninfo = (AUChannelInfo*) malloc (sizeof (AUChannelInfo) * elCount);
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 < elCount; ++i) {
373 (*chaninfo)[i].inChannels = 0;
377 for (unsigned int i = 0; i < elCountOut; ++i) {
379 if (NumberChannels (kAudioUnitScope_Output, i, numChans)) return 1;
380 (*chaninfo)[i].outChannels = numChans;
382 for (unsigned int i = elCountOut; i < elCount; ++i) {
383 (*chaninfo)[i].outChannels = 0;
389 // the au should either really tell us about this
390 // or we will assume the worst
395 *chaninfo = (AUChannelInfo*) malloc (dataSize);
396 cnt = dataSize / sizeof (AUChannelInfo);
398 result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
399 kAudioUnitScope_Global, 0,
400 *chaninfo, &dataSize);
402 if (result) { return -1; }
407 bool CAAudioUnit::ValidateChannelPair (int inChannelsIn,
409 const AUChannelInfo * info,
410 UInt32 numChanInfo) const
412 // we've the following cases (some combinations) to test here:
414 >0 An explicit number of channels on either side
415 0 that side (generally input!) has no elements
417 -1,-1 any num channels as long as same channels on in and out
418 -1,-2 any num channels channels on in and out - special meaning
419 -2+ indicates total num channs AU can handle
420 - elements configurable to any num channels,
421 - element count in scope must be writable
424 //now chan layout can contain -1 for either scope (ie. doesn't care)
425 for (unsigned int i = 0; i < numChanInfo; ++i)
427 //less than zero on both sides - check for special attributes
428 if ((info[i].inChannels < 0) && (info[i].outChannels < 0))
430 // these are our wild card matches
431 if (info[i].inChannels == -1 && info[i].outChannels == -1) {
432 if (inChannelsOut == inChannelsIn) {
436 else if ((info[i].inChannels == -1 && info[i].outChannels == -2)
437 || (info[i].inChannels == -2 && info[i].outChannels == -1))
441 // these are our total num channels matches
442 // element count MUST be writable
444 bool outWrite = false; bool inWrite = false;
445 IsElementCountWritable (kAudioUnitScope_Output, outWrite);
446 IsElementCountWritable (kAudioUnitScope_Input, inWrite);
447 if (inWrite && outWrite) {
448 if ((inChannelsOut <= abs(info[i].outChannels))
449 && (inChannelsIn <= abs(info[i].inChannels)))
457 // special meaning on input, specific num on output
458 else if (info[i].inChannels < 0) {
459 if (info[i].outChannels == inChannelsOut)
461 // can do any in channels
462 if (info[i].inChannels == -1) {
465 // total chans on input
467 bool inWrite = false;
468 IsElementCountWritable (kAudioUnitScope_Input, inWrite);
469 if (inWrite && (inChannelsIn <= abs(info[i].inChannels))) {
476 // special meaning on output, specific num on input
477 else if (info[i].outChannels < 0) {
478 if (info[i].inChannels == inChannelsIn)
480 // can do any out channels
481 if (info[i].outChannels == -1) {
484 // total chans on output
486 bool outWrite = false;
487 IsElementCountWritable (kAudioUnitScope_Output, outWrite);
488 if (outWrite && (inChannelsOut <= abs(info[i].outChannels))) {
495 // both chans in struct >= 0 - thus has to explicitly match
496 else if ((info[i].inChannels == inChannelsIn) && (info[i].outChannels == inChannelsOut)) {
500 // now check to see if a wild card on the args (inChannelsIn or inChannelsOut chans is zero) is found
501 // tells us to match just one side of the scopes
502 else if (inChannelsIn == 0) {
503 if (info[i].outChannels == inChannelsOut) {
507 else if (inChannelsOut == 0) {
508 if (info[i].inChannels == inChannelsIn) {
517 bool CheckDynCount (SInt32 inTotalChans, const CAAUChanHelper &inHelper)
520 for (unsigned int i = 0; i < inHelper.mNumEls; ++i)
521 totalChans += inHelper.mChans[i];
522 return (totalChans <= inTotalChans);
525 bool CAAudioUnit::CheckOneSide (const CAAUChanHelper &inHelper,
527 const AUChannelInfo *info,
528 UInt32 numInfo) const
530 // now we can use the wildcard option (see above impl) to see if this matches
531 for (unsigned int el = 0; el < inHelper.mNumEls; ++el) {
532 bool testAlready = false;
533 for (unsigned int i = 0; i < el; ++i) {
534 if (inHelper.mChans[i] == inHelper.mChans[el]) {
541 if (!ValidateChannelPair (0, inHelper.mChans[el], info, numInfo)) return false;
543 if (!ValidateChannelPair (inHelper.mChans[el], 0, info, numInfo)) return false;
550 bool CAAudioUnit::CanDo (const CAAUChanHelper &inputs,
551 const CAAUChanHelper &outputs) const
554 // first check our state
556 if (inputs.mNumEls == 0 && outputs.mNumEls == 0) return false;
559 if (GetElementCount (kAudioUnitScope_Input, elCount)) { return false; }
560 if (elCount != inputs.mNumEls) return false;
562 if (GetElementCount (kAudioUnitScope_Output, elCount)) { return false; }
563 if (elCount != outputs.mNumEls) return false;
565 // (1) special cases (effects and sources (generators and instruments) only)
567 if (GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
568 kAudioUnitScope_Global, 0, &dataSize, NULL) != noErr)
570 if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline()) {
571 UInt32 numChan = outputs.mNumEls > 0 ? outputs.mChans[0] : inputs.mChans[0];
572 for (unsigned int in = 0; in < inputs.mNumEls; ++in)
573 if (numChan != inputs.mChans[in]) return false;
574 for (unsigned int out = 0; out < outputs.mNumEls; ++out)
575 if (numChan != outputs.mChans[out]) return false;
579 // in this case, all the channels have to match the current config
580 if (Comp().Desc().IsGenerator() || Comp().Desc().IsMusicDevice()) {
581 for (unsigned int in = 0; in < inputs.mNumEls; ++in) {
583 if (NumberChannels (kAudioUnitScope_Input, in, chan)) return false;
584 if (chan != UInt32(inputs.mChans[in])) return false;
586 for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
588 if (NumberChannels (kAudioUnitScope_Output, out, chan)) return false;
589 if (chan != UInt32(outputs.mChans[out])) return false;
594 // if we get here we can't determine anything about channel capabilities
598 StackAUChannelInfo info (dataSize);
600 if (GetProperty (kAudioUnitProperty_SupportedNumChannels,
601 kAudioUnitScope_Global, 0,
602 info.mChanInfo, &dataSize) != noErr)
607 int numInfo = dataSize / sizeof(AUChannelInfo);
609 // (2) Test for dynamic capability (or no elements on that scope)
610 SInt32 dynInChans = 0;
611 if (ValidateDynamicScope (kAudioUnitScope_Input, dynInChans, info.mChanInfo, numInfo)) {
612 if (CheckDynCount (dynInChans, inputs) == false) return false;
615 SInt32 dynOutChans = 0;
616 if (ValidateDynamicScope (kAudioUnitScope_Output, dynOutChans, info.mChanInfo, numInfo)) {
617 if (CheckDynCount (dynOutChans, outputs) == false) return false;
620 if (dynOutChans && dynInChans) { return true; }
622 // (3) Just need to test one side
623 if (dynInChans || (inputs.mNumEls == 0)) {
624 return CheckOneSide (outputs, true, info.mChanInfo, numInfo);
627 if (dynOutChans || (outputs.mNumEls == 0)) {
628 return CheckOneSide (inputs, false, info.mChanInfo, numInfo);
631 // (4) - not a dynamic AU, has ins and outs, and has channel constraints so we test every possible pairing
632 for (unsigned int in = 0; in < inputs.mNumEls; ++in)
634 bool testInAlready = false;
635 for (unsigned int i = 0; i < in; ++i) {
636 if (inputs.mChans[i] == inputs.mChans[in]) {
637 testInAlready = true;
641 if (!testInAlready) {
642 for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
643 // try to save a little bit and not test the same pairing multiple times...
644 bool testOutAlready = false;
645 for (unsigned int i = 0; i < out; ++i) {
646 if (outputs.mChans[i] == outputs.mChans[out]) {
647 testOutAlready = true;
651 if (!testOutAlready) {
652 if (!ValidateChannelPair (inputs.mChans[in], outputs.mChans[out],info.mChanInfo, numInfo)) {
663 bool CAAudioUnit::SupportsNumChannels () const
665 // this is the default assumption of an audio effect unit
666 Boolean* isWritable = 0;
668 // lets see if the unit has any channel restrictions
669 OSStatus result = AudioUnitGetPropertyInfo (AU(),
670 kAudioUnitProperty_SupportedNumChannels,
671 kAudioUnitScope_Global, 0,
672 &dataSize, isWritable); //don't care if this is writable
674 // if this property is NOT implemented an FX unit
675 // is expected to deal with same channel valance in and out
677 if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline())
680 return result == noErr;
683 bool CAAudioUnit::GetChannelLayouts (AudioUnitScope inScope,
684 AudioUnitElement inEl,
685 ChannelTagVector &outChannelVector) const
687 if (HasChannelLayouts (inScope, inEl) == false) return false;
690 OSStatus result = AudioUnitGetPropertyInfo (AU(),
691 kAudioUnitProperty_SupportedChannelLayoutTags,
695 if (result == kAudioUnitErr_InvalidProperty) {
696 // if we get here we can do layouts but we've got the speaker config property
697 outChannelVector.erase (outChannelVector.begin(), outChannelVector.end());
698 outChannelVector.push_back (kAudioChannelLayoutTag_Stereo);
699 outChannelVector.push_back (kAudioChannelLayoutTag_StereoHeadphones);
700 outChannelVector.push_back (kAudioChannelLayoutTag_Quadraphonic);
701 outChannelVector.push_back (kAudioChannelLayoutTag_AudioUnit_5_0);
705 if (result) return false;
708 // OK lets get our channel layouts and see if the one we want is present
709 AudioChannelLayoutTag* info = (AudioChannelLayoutTag*)malloc (dataSize);
710 result = AudioUnitGetProperty (AU(),
711 kAudioUnitProperty_SupportedChannelLayoutTags,
714 if (result) goto home;
716 outChannelVector.erase (outChannelVector.begin(), outChannelVector.end());
717 for (unsigned int i = 0; i < (dataSize / sizeof (AudioChannelLayoutTag)); ++i)
718 outChannelVector.push_back (info[i]);
725 bool CAAudioUnit::HasChannelLayouts (AudioUnitScope inScope,
726 AudioUnitElement inEl) const
728 OSStatus result = AudioUnitGetPropertyInfo (AU(),
729 kAudioUnitProperty_SupportedChannelLayoutTags,
735 OSStatus CAAudioUnit::GetChannelLayout (AudioUnitScope inScope,
736 AudioUnitElement inEl,
737 CAAudioChannelLayout &outLayout) const
740 OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_AudioChannelLayout,
741 inScope, inEl, &size, NULL);
742 if (result) return result;
744 AudioChannelLayout *layout = (AudioChannelLayout*)malloc (size);
746 require_noerr (result = AudioUnitGetProperty (AU(), kAudioUnitProperty_AudioChannelLayout,
747 inScope, inEl, layout, &size), home);
749 outLayout = CAAudioChannelLayout (layout);
756 OSStatus CAAudioUnit::SetChannelLayout (AudioUnitScope inScope,
757 AudioUnitElement inEl,
758 CAAudioChannelLayout &inLayout)
760 OSStatus result = AudioUnitSetProperty (AU(),
761 kAudioUnitProperty_AudioChannelLayout,
763 inLayout, inLayout.Size());
767 OSStatus CAAudioUnit::SetChannelLayout (AudioUnitScope inScope,
768 AudioUnitElement inEl,
769 AudioChannelLayout &inLayout,
772 OSStatus result = AudioUnitSetProperty (AU(),
773 kAudioUnitProperty_AudioChannelLayout,
779 OSStatus CAAudioUnit::ClearChannelLayout (AudioUnitScope inScope,
780 AudioUnitElement inEl)
782 return AudioUnitSetProperty (AU(),
783 kAudioUnitProperty_AudioChannelLayout,
784 inScope, inEl, NULL, 0);
787 OSStatus CAAudioUnit::GetFormat (AudioUnitScope inScope,
788 AudioUnitElement inEl,
789 AudioStreamBasicDescription &outFormat) const
791 UInt32 dataSize = sizeof (AudioStreamBasicDescription);
792 return AudioUnitGetProperty (AU(), kAudioUnitProperty_StreamFormat,
794 &outFormat, &dataSize);
797 OSStatus CAAudioUnit::SetFormat (AudioUnitScope inScope,
798 AudioUnitElement inEl,
799 const AudioStreamBasicDescription &inFormat)
801 return AudioUnitSetProperty (AU(), kAudioUnitProperty_StreamFormat,
803 const_cast<AudioStreamBasicDescription*>(&inFormat),
804 sizeof (AudioStreamBasicDescription));
807 OSStatus CAAudioUnit::GetSampleRate (AudioUnitScope inScope,
808 AudioUnitElement inEl,
809 Float64 &outRate) const
811 UInt32 dataSize = sizeof (Float64);
812 return AudioUnitGetProperty (AU(), kAudioUnitProperty_SampleRate,
814 &outRate, &dataSize);
817 OSStatus CAAudioUnit::SetSampleRate (AudioUnitScope inScope,
818 AudioUnitElement inEl,
821 AudioStreamBasicDescription desc;
822 OSStatus result = GetFormat (inScope, inEl, desc);
823 if (result) return result;
824 desc.mSampleRate = inRate;
825 return SetFormat (inScope, inEl, desc);
828 OSStatus CAAudioUnit::SetSampleRate (Float64 inSampleRate)
833 require_noerr (result = GetElementCount(kAudioUnitScope_Input, elCount), home);
835 for (unsigned int i = 0; i < elCount; ++i) {
836 require_noerr (result = SetSampleRate (kAudioUnitScope_Input, i, inSampleRate), home);
840 require_noerr (result = GetElementCount(kAudioUnitScope_Output, elCount), home);
842 for (unsigned int i = 0; i < elCount; ++i) {
843 require_noerr (result = SetSampleRate (kAudioUnitScope_Output, i, inSampleRate), home);
851 OSStatus CAAudioUnit::NumberChannels (AudioUnitScope inScope,
852 AudioUnitElement inEl,
853 UInt32 &outChans) const
855 AudioStreamBasicDescription desc;
856 OSStatus result = GetFormat (inScope, inEl, desc);
858 outChans = desc.mChannelsPerFrame;
862 OSStatus CAAudioUnit::SetNumberChannels (AudioUnitScope inScope,
863 AudioUnitElement inEl,
866 // set this as the output of the AU
867 CAStreamBasicDescription desc;
868 OSStatus result = GetFormat (inScope, inEl, desc);
869 if (result) return result;
870 desc.SetCanonical (inChans, desc.IsInterleaved());
871 result = SetFormat (inScope, inEl, desc);
875 OSStatus CAAudioUnit::IsElementCountWritable (AudioUnitScope inScope, bool &outWritable) const
879 OSStatus result = GetPropertyInfo (kAudioUnitProperty_ElementCount, inScope, 0, &outDataSize, &isWritable);
882 outWritable = isWritable ? true : false;
886 OSStatus CAAudioUnit::GetElementCount (AudioUnitScope inScope, UInt32 &outCount) const
888 UInt32 propSize = sizeof(outCount);
889 return GetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &outCount, &propSize);
892 OSStatus CAAudioUnit::SetElementCount (AudioUnitScope inScope, UInt32 inCount)
894 return SetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &inCount, sizeof(inCount));
897 bool CAAudioUnit::HasDynamicScope (AudioUnitScope inScope, SInt32 &outTotalNumChannels) const
899 // ok - now we need to check the AU's capability here.
900 // this is the default assumption of an audio effect unit
901 Boolean* isWritable = 0;
903 OSStatus result = GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
904 kAudioUnitScope_Global, 0,
905 &dataSize, isWritable); //don't care if this is writable
907 // AU has to explicitly tell us about this.
908 if (result) return false;
910 StackAUChannelInfo info (dataSize);
912 result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
913 kAudioUnitScope_Global, 0,
914 info.mChanInfo, &dataSize);
915 if (result) return false;
917 return ValidateDynamicScope (inScope, outTotalNumChannels, info.mChanInfo, (dataSize / sizeof(AUChannelInfo)));
920 // as we've already checked that the element count is writable
921 // the following conditions will match this..
923 -1, -2 -> signifies no restrictions
924 -2, -1 -> signifies no restrictions -> in this case outTotalNumChannels == -1 (any num channels)
926 -N (where N is less than -2), signifies the total channel count on the scope side (in or out)
928 bool CAAudioUnit::ValidateDynamicScope (AudioUnitScope inScope,
929 SInt32 &outTotalNumChannels,
930 const AUChannelInfo *info,
931 UInt32 numInfo) const
933 bool writable = false;
934 OSStatus result = IsElementCountWritable (inScope, writable);
935 if (result || (writable == false))
938 //now chan layout can contain -1 for either scope (ie. doesn't care)
939 for (unsigned int i = 0; i < numInfo; ++i)
941 // lets test the special wild card case first...
942 // this says the AU can do any num channels on input or output - for eg. Matrix Mixer
943 if (((info[i].inChannels == -1) && (info[i].outChannels == -2))
944 || ((info[i].inChannels == -2) && (info[i].outChannels == -1)))
946 outTotalNumChannels = -1;
950 // ok lets now test our special case....
951 if (inScope == kAudioUnitScope_Input) {
952 // isn't dynamic on this side at least
953 if (info[i].inChannels >= 0)
956 if (info[i].inChannels < -2) {
957 outTotalNumChannels = abs (info[i].inChannels);
962 else if (inScope == kAudioUnitScope_Output) {
963 // isn't dynamic on this side at least
964 if (info[i].outChannels >= 0)
967 if (info[i].outChannels < -2) {
968 outTotalNumChannels = abs (info[i].outChannels);
974 break; // wrong scope was specified
981 OSStatus CAAudioUnit::ConfigureDynamicScope (AudioUnitScope inScope,
982 UInt32 inNumElements,
983 UInt32 *inChannelsPerElement,
984 Float64 inSampleRate)
986 SInt32 numChannels = 0;
987 bool isDyamic = HasDynamicScope (inScope, numChannels);
988 if (isDyamic == false)
989 return kAudioUnitErr_InvalidProperty;
991 //lets to a sanity check...
992 // if numChannels == -1, then it can do "any"...
993 if (numChannels > 0) {
995 for (unsigned int i = 0; i < inNumElements; ++i)
996 count += inChannelsPerElement[i];
997 if (count > numChannels)
998 return kAudioUnitErr_InvalidPropertyValue;
1001 OSStatus result = SetElementCount (inScope, inNumElements);
1005 CAStreamBasicDescription desc;
1006 desc.mSampleRate = inSampleRate;
1007 for (unsigned int i = 0; i < inNumElements; ++i) {
1008 desc.SetCanonical (inChannelsPerElement[i], false);
1009 result = SetFormat (inScope, i, desc);
1016 #pragma mark __Properties
1018 bool CAAudioUnit::CanBypass () const
1020 Boolean outWritable;
1021 OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_BypassEffect,
1022 kAudioUnitScope_Global, 0,
1023 NULL, &outWritable);
1024 return (!result && outWritable);
1027 bool CAAudioUnit::GetBypass () const
1029 UInt32 dataSize = sizeof (UInt32);
1031 OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_BypassEffect,
1032 kAudioUnitScope_Global, 0,
1033 &outBypass, &dataSize);
1034 return (result ? false : outBypass);
1037 OSStatus CAAudioUnit::SetBypass (bool inBypass) const
1039 UInt32 bypass = inBypass ? 1 : 0;
1040 return AudioUnitSetProperty (AU(), kAudioUnitProperty_BypassEffect,
1041 kAudioUnitScope_Global, 0,
1042 &bypass, sizeof (UInt32));
1045 Float64 CAAudioUnit::Latency () const
1048 UInt32 size = sizeof(secs);
1049 if (GetProperty (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &secs, &size))
1054 OSStatus CAAudioUnit::GetAUPreset (CFPropertyListRef &outData) const
1056 UInt32 dataSize = sizeof(outData);
1057 return AudioUnitGetProperty (AU(), kAudioUnitProperty_ClassInfo,
1058 kAudioUnitScope_Global, 0,
1059 &outData, &dataSize);
1062 OSStatus CAAudioUnit::SetAUPreset (CFPropertyListRef &inData)
1064 return AudioUnitSetProperty (AU(), kAudioUnitProperty_ClassInfo,
1065 kAudioUnitScope_Global, 0,
1066 &inData, sizeof (CFPropertyListRef));
1069 OSStatus CAAudioUnit::GetPresentPreset (AUPreset &outData) const
1071 UInt32 dataSize = sizeof(outData);
1072 OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_PresentPreset,
1073 kAudioUnitScope_Global, 0,
1074 &outData, &dataSize);
1075 if (result == kAudioUnitErr_InvalidProperty) {
1076 dataSize = sizeof(outData);
1077 result = AudioUnitGetProperty (AU(), kAudioUnitProperty_CurrentPreset,
1078 kAudioUnitScope_Global, 0,
1079 &outData, &dataSize);
1080 if (result == noErr) {
1081 // we now retain the CFString in the preset so for the client of this API
1082 // it is consistent (ie. the string should be released when done)
1083 if (outData.presetName)
1084 CFRetain (outData.presetName);
1090 OSStatus CAAudioUnit::SetPresentPreset (AUPreset &inData)
1092 OSStatus result = AudioUnitSetProperty (AU(), kAudioUnitProperty_PresentPreset,
1093 kAudioUnitScope_Global, 0,
1094 &inData, sizeof (AUPreset));
1095 if (result == kAudioUnitErr_InvalidProperty) {
1096 result = AudioUnitSetProperty (AU(), kAudioUnitProperty_CurrentPreset,
1097 kAudioUnitScope_Global, 0,
1098 &inData, sizeof (AUPreset));
1103 bool CAAudioUnit::HasCustomView () const
1105 UInt32 dataSize = 0;
1106 OSStatus result = GetPropertyInfo(kAudioUnitProperty_GetUIComponentList,
1107 kAudioUnitScope_Global, 0,
1109 if (result || !dataSize) {
1111 result = GetPropertyInfo(kAudioUnitProperty_CocoaUI,
1112 kAudioUnitScope_Global, 0,
1114 if (result || !dataSize)
1120 OSStatus CAAudioUnit::GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1121 Float32 &outValue) const
1123 return mDataPtr ? (OSStatus) mDataPtr->GetParameter (inID, scope, element, outValue) : paramErr;
1126 OSStatus CAAudioUnit::SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1127 Float32 value, UInt32 bufferOffsetFrames)
1129 return mDataPtr ? (OSStatus) mDataPtr->SetParameter (inID, scope, element, value, bufferOffsetFrames) : paramErr;
1132 OSStatus CAAudioUnit::MIDIEvent (UInt32 inStatus,
1135 UInt32 inOffsetSampleFrame)
1137 return mDataPtr ? (OSStatus) mDataPtr->MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame) : paramErr;
1140 OSStatus CAAudioUnit::StartNote (MusicDeviceInstrumentID inInstrument,
1141 MusicDeviceGroupID inGroupID,
1142 NoteInstanceID * outNoteInstanceID,
1143 UInt32 inOffsetSampleFrame,
1144 const MusicDeviceNoteParams * inParams)
1146 return mDataPtr ? (OSStatus) mDataPtr->StartNote (inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams)
1150 OSStatus CAAudioUnit::StopNote (MusicDeviceGroupID inGroupID,
1151 NoteInstanceID inNoteInstanceID,
1152 UInt32 inOffsetSampleFrame)
1154 return mDataPtr ? (OSStatus) mDataPtr->StopNote (inGroupID, inNoteInstanceID, inOffsetSampleFrame) : paramErr;
1157 #pragma mark __Render
1159 OSStatus CAAudioUnit::Render (AudioUnitRenderActionFlags * ioActionFlags,
1160 const AudioTimeStamp * inTimeStamp,
1161 UInt32 inOutputBusNumber,
1162 UInt32 inNumberFrames,
1163 AudioBufferList * ioData)
1165 return mDataPtr ? (OSStatus) mDataPtr->Render (ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData) : paramErr;
1168 static AURenderCallbackStruct sRenderCallback;
1169 static OSStatus PrerollRenderProc ( void * /*inRefCon*/,
1170 AudioUnitRenderActionFlags * /*inActionFlags*/,
1171 const AudioTimeStamp * /*inTimeStamp*/,
1172 UInt32 /*inBusNumber*/,
1173 UInt32 /*inNumFrames*/,
1174 AudioBufferList *ioData)
1176 AudioBuffer *buf = ioData->mBuffers;
1177 for (UInt32 i = ioData->mNumberBuffers; i--; ++buf)
1178 memset((Byte *)buf->mData, 0, buf->mDataByteSize);
1183 OSStatus CAAudioUnit::Preroll (UInt32 inFrameSize)
1185 CAStreamBasicDescription desc;
1186 OSStatus result = GetFormat (kAudioUnitScope_Input, 0, desc);
1187 bool hasInput = false;
1189 if (result == noErr)
1191 sRenderCallback.inputProc = PrerollRenderProc;
1192 sRenderCallback.inputProcRefCon = 0;
1194 result = SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1195 0, &sRenderCallback, sizeof(sRenderCallback));
1196 if (result) return result;
1200 AudioUnitRenderActionFlags flags = 0;
1201 AudioTimeStamp time;
1202 memset (&time, 0, sizeof(time));
1203 time.mFlags = kAudioTimeStampSampleTimeValid;
1205 CAStreamBasicDescription outputFormat;
1206 require_noerr (result = GetFormat (kAudioUnitScope_Output, 0, outputFormat), home);
1208 AUOutputBL list (outputFormat, inFrameSize);
1211 require_noerr (result = Render (&flags, &time, 0, inFrameSize, list.ABL()), home);
1212 require_noerr (result = GlobalReset(), home);
1217 // remove our installed callback
1218 sRenderCallback.inputProc = 0;
1219 sRenderCallback.inputProcRefCon = 0;
1221 SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1222 0, &sRenderCallback, sizeof(sRenderCallback));
1227 #pragma mark __CAAUChanHelper
1229 CAAUChanHelper::CAAUChanHelper(const CAAudioUnit &inAU, AudioUnitScope inScope)
1230 :mChans(NULL), mNumEls(0), mDidAllocate(false)
1233 if (inAU.GetElementCount (inScope, elCount)) return;
1235 mChans = new UInt32[elCount];
1236 mDidAllocate = true;
1237 memset (mChans, 0, sizeof(int) * elCount);
1239 mChans = mStaticChans;
1240 memset (mChans, 0, sizeof(int) * 8);
1242 for (unsigned int i = 0; i < elCount; ++i) {
1244 if (inAU.NumberChannels (inScope, i, numChans)) return;
1245 mChans[i] = numChans;
1250 CAAUChanHelper::~CAAUChanHelper()
1252 if (mDidAllocate) delete [] mChans;
1255 CAAUChanHelper& CAAUChanHelper::operator= (const CAAUChanHelper &c)
1257 if (mDidAllocate) delete [] mChans;
1258 if (c.mDidAllocate) {
1259 mChans = new UInt32[c.mNumEls];
1260 mDidAllocate = true;
1262 mDidAllocate = false;
1263 mChans = mStaticChans;
1265 memcpy (mChans, c.mChans, c.mNumEls * sizeof(int));
1270 #pragma mark __Print Utilities
1272 void CAAudioUnit::Print (FILE* file) const
1274 fprintf (file, "AudioUnit:%p\n", AU());
1276 fprintf (file, "\tnode=%ld\t", (long)GetAUNode()); Comp().Print (file);