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&) {}
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 bool CAAudioUnit::ValidateChannelPair (int inChannelsIn,
333 const AUChannelInfo * info,
334 UInt32 numChanInfo) const
336 // we've the following cases (some combinations) to test here:
338 >0 An explicit number of channels on either side
339 0 that side (generally input!) has no elements
341 -1,-1 any num channels as long as same channels on in and out
342 -1,-2 any num channels channels on in and out - special meaning
343 -2+ indicates total num channs AU can handle
344 - elements configurable to any num channels,
345 - element count in scope must be writable
348 //now chan layout can contain -1 for either scope (ie. doesn't care)
349 for (unsigned int i = 0; i < numChanInfo; ++i)
351 //less than zero on both sides - check for special attributes
352 if ((info[i].inChannels < 0) && (info[i].outChannels < 0))
354 // these are our wild card matches
355 if (info[i].inChannels == -1 && info[i].outChannels == -1) {
356 if (inChannelsOut == inChannelsIn) {
360 else if ((info[i].inChannels == -1 && info[i].outChannels == -2)
361 || (info[i].inChannels == -2 && info[i].outChannels == -1))
365 // these are our total num channels matches
366 // element count MUST be writable
368 bool outWrite = false; bool inWrite = false;
369 IsElementCountWritable (kAudioUnitScope_Output, outWrite);
370 IsElementCountWritable (kAudioUnitScope_Input, inWrite);
371 if (inWrite && outWrite) {
372 if ((inChannelsOut <= abs(info[i].outChannels))
373 && (inChannelsIn <= abs(info[i].inChannels)))
381 // special meaning on input, specific num on output
382 else if (info[i].inChannels < 0) {
383 if (info[i].outChannels == inChannelsOut)
385 // can do any in channels
386 if (info[i].inChannels == -1) {
389 // total chans on input
391 bool inWrite = false;
392 IsElementCountWritable (kAudioUnitScope_Input, inWrite);
393 if (inWrite && (inChannelsIn <= abs(info[i].inChannels))) {
400 // special meaning on output, specific num on input
401 else if (info[i].outChannels < 0) {
402 if (info[i].inChannels == inChannelsIn)
404 // can do any out channels
405 if (info[i].outChannels == -1) {
408 // total chans on output
410 bool outWrite = false;
411 IsElementCountWritable (kAudioUnitScope_Output, outWrite);
412 if (outWrite && (inChannelsOut <= abs(info[i].outChannels))) {
419 // both chans in struct >= 0 - thus has to explicitly match
420 else if ((info[i].inChannels == inChannelsIn) && (info[i].outChannels == inChannelsOut)) {
424 // now check to see if a wild card on the args (inChannelsIn or inChannelsOut chans is zero) is found
425 // tells us to match just one side of the scopes
426 else if (inChannelsIn == 0) {
427 if (info[i].outChannels == inChannelsOut) {
431 else if (inChannelsOut == 0) {
432 if (info[i].inChannels == inChannelsIn) {
441 bool CheckDynCount (SInt32 inTotalChans, const CAAUChanHelper &inHelper)
444 for (unsigned int i = 0; i < inHelper.mNumEls; ++i)
445 totalChans += inHelper.mChans[i];
446 return (totalChans <= inTotalChans);
449 bool CAAudioUnit::CheckOneSide (const CAAUChanHelper &inHelper,
451 const AUChannelInfo *info,
452 UInt32 numInfo) const
454 // now we can use the wildcard option (see above impl) to see if this matches
455 for (unsigned int el = 0; el < inHelper.mNumEls; ++el) {
456 bool testAlready = false;
457 for (unsigned int i = 0; i < el; ++i) {
458 if (inHelper.mChans[i] == inHelper.mChans[el]) {
465 if (!ValidateChannelPair (0, inHelper.mChans[el], info, numInfo)) return false;
467 if (!ValidateChannelPair (inHelper.mChans[el], 0, info, numInfo)) return false;
474 bool CAAudioUnit::CanDo (const CAAUChanHelper &inputs,
475 const CAAUChanHelper &outputs) const
478 // first check our state
480 if (inputs.mNumEls == 0 && outputs.mNumEls == 0) return false;
483 if (GetElementCount (kAudioUnitScope_Input, elCount)) { return false; }
484 if (elCount != inputs.mNumEls) return false;
486 if (GetElementCount (kAudioUnitScope_Output, elCount)) { return false; }
487 if (elCount != outputs.mNumEls) return false;
489 // (1) special cases (effects and sources (generators and instruments) only)
491 if (GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
492 kAudioUnitScope_Global, 0, &dataSize, NULL) != noErr)
494 if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline()) {
495 UInt32 numChan = outputs.mNumEls > 0 ? outputs.mChans[0] : inputs.mChans[0];
496 for (unsigned int in = 0; in < inputs.mNumEls; ++in)
497 if (numChan != inputs.mChans[in]) return false;
498 for (unsigned int out = 0; out < outputs.mNumEls; ++out)
499 if (numChan != outputs.mChans[out]) return false;
503 // in this case, all the channels have to match the current config
504 if (Comp().Desc().IsGenerator() || Comp().Desc().IsMusicDevice()) {
505 for (unsigned int in = 0; in < inputs.mNumEls; ++in) {
507 if (NumberChannels (kAudioUnitScope_Input, in, chan)) return false;
508 if (chan != UInt32(inputs.mChans[in])) return false;
510 for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
512 if (NumberChannels (kAudioUnitScope_Output, out, chan)) return false;
513 if (chan != UInt32(outputs.mChans[out])) return false;
518 // if we get here we can't determine anything about channel capabilities
522 StackAUChannelInfo info (dataSize);
524 if (GetProperty (kAudioUnitProperty_SupportedNumChannels,
525 kAudioUnitScope_Global, 0,
526 info.mChanInfo, &dataSize) != noErr)
531 int numInfo = dataSize / sizeof(AUChannelInfo);
533 // (2) Test for dynamic capability (or no elements on that scope)
534 SInt32 dynInChans = 0;
535 if (ValidateDynamicScope (kAudioUnitScope_Input, dynInChans, info.mChanInfo, numInfo)) {
536 if (CheckDynCount (dynInChans, inputs) == false) return false;
539 SInt32 dynOutChans = 0;
540 if (ValidateDynamicScope (kAudioUnitScope_Output, dynOutChans, info.mChanInfo, numInfo)) {
541 if (CheckDynCount (dynOutChans, outputs) == false) return false;
544 if (dynOutChans && dynInChans) { return true; }
546 // (3) Just need to test one side
547 if (dynInChans || (inputs.mNumEls == 0)) {
548 return CheckOneSide (outputs, true, info.mChanInfo, numInfo);
551 if (dynOutChans || (outputs.mNumEls == 0)) {
552 return CheckOneSide (inputs, false, info.mChanInfo, numInfo);
555 // (4) - not a dynamic AU, has ins and outs, and has channel constraints so we test every possible pairing
556 for (unsigned int in = 0; in < inputs.mNumEls; ++in)
558 bool testInAlready = false;
559 for (unsigned int i = 0; i < in; ++i) {
560 if (inputs.mChans[i] == inputs.mChans[in]) {
561 testInAlready = true;
565 if (!testInAlready) {
566 for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
567 // try to save a little bit and not test the same pairing multiple times...
568 bool testOutAlready = false;
569 for (unsigned int i = 0; i < out; ++i) {
570 if (outputs.mChans[i] == outputs.mChans[out]) {
571 testOutAlready = true;
575 if (!testOutAlready) {
576 if (!ValidateChannelPair (inputs.mChans[in], outputs.mChans[out],info.mChanInfo, numInfo)) {
587 bool CAAudioUnit::SupportsNumChannels () const
589 // this is the default assumption of an audio effect unit
590 Boolean* isWritable = 0;
592 // lets see if the unit has any channel restrictions
593 OSStatus result = AudioUnitGetPropertyInfo (AU(),
594 kAudioUnitProperty_SupportedNumChannels,
595 kAudioUnitScope_Global, 0,
596 &dataSize, isWritable); //don't care if this is writable
598 // if this property is NOT implemented an FX unit
599 // is expected to deal with same channel valance in and out
601 if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline())
604 return result == noErr;
607 bool CAAudioUnit::GetChannelLayouts (AudioUnitScope inScope,
608 AudioUnitElement inEl,
609 ChannelTagVector &outChannelVector) const
611 if (HasChannelLayouts (inScope, inEl) == false) return false;
614 OSStatus result = AudioUnitGetPropertyInfo (AU(),
615 kAudioUnitProperty_SupportedChannelLayoutTags,
619 if (result == kAudioUnitErr_InvalidProperty) {
620 // if we get here we can do layouts but we've got the speaker config property
621 outChannelVector.erase (outChannelVector.begin(), outChannelVector.end());
622 outChannelVector.push_back (kAudioChannelLayoutTag_Stereo);
623 outChannelVector.push_back (kAudioChannelLayoutTag_StereoHeadphones);
624 outChannelVector.push_back (kAudioChannelLayoutTag_Quadraphonic);
625 outChannelVector.push_back (kAudioChannelLayoutTag_AudioUnit_5_0);
629 if (result) return false;
632 // OK lets get our channel layouts and see if the one we want is present
633 AudioChannelLayoutTag* info = (AudioChannelLayoutTag*)malloc (dataSize);
634 result = AudioUnitGetProperty (AU(),
635 kAudioUnitProperty_SupportedChannelLayoutTags,
638 if (result) goto home;
640 outChannelVector.erase (outChannelVector.begin(), outChannelVector.end());
641 for (unsigned int i = 0; i < (dataSize / sizeof (AudioChannelLayoutTag)); ++i)
642 outChannelVector.push_back (info[i]);
649 bool CAAudioUnit::HasChannelLayouts (AudioUnitScope inScope,
650 AudioUnitElement inEl) const
652 OSStatus result = AudioUnitGetPropertyInfo (AU(),
653 kAudioUnitProperty_SupportedChannelLayoutTags,
659 OSStatus CAAudioUnit::GetChannelLayout (AudioUnitScope inScope,
660 AudioUnitElement inEl,
661 CAAudioChannelLayout &outLayout) const
664 OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_AudioChannelLayout,
665 inScope, inEl, &size, NULL);
666 if (result) return result;
668 AudioChannelLayout *layout = (AudioChannelLayout*)malloc (size);
670 require_noerr (result = AudioUnitGetProperty (AU(), kAudioUnitProperty_AudioChannelLayout,
671 inScope, inEl, layout, &size), home);
673 outLayout = CAAudioChannelLayout (layout);
680 OSStatus CAAudioUnit::SetChannelLayout (AudioUnitScope inScope,
681 AudioUnitElement inEl,
682 CAAudioChannelLayout &inLayout)
684 OSStatus result = AudioUnitSetProperty (AU(),
685 kAudioUnitProperty_AudioChannelLayout,
687 inLayout, inLayout.Size());
691 OSStatus CAAudioUnit::SetChannelLayout (AudioUnitScope inScope,
692 AudioUnitElement inEl,
693 AudioChannelLayout &inLayout,
696 OSStatus result = AudioUnitSetProperty (AU(),
697 kAudioUnitProperty_AudioChannelLayout,
703 OSStatus CAAudioUnit::ClearChannelLayout (AudioUnitScope inScope,
704 AudioUnitElement inEl)
706 return AudioUnitSetProperty (AU(),
707 kAudioUnitProperty_AudioChannelLayout,
708 inScope, inEl, NULL, 0);
711 OSStatus CAAudioUnit::GetFormat (AudioUnitScope inScope,
712 AudioUnitElement inEl,
713 AudioStreamBasicDescription &outFormat) const
715 UInt32 dataSize = sizeof (AudioStreamBasicDescription);
716 return AudioUnitGetProperty (AU(), kAudioUnitProperty_StreamFormat,
718 &outFormat, &dataSize);
721 OSStatus CAAudioUnit::SetFormat (AudioUnitScope inScope,
722 AudioUnitElement inEl,
723 const AudioStreamBasicDescription &inFormat)
725 return AudioUnitSetProperty (AU(), kAudioUnitProperty_StreamFormat,
727 const_cast<AudioStreamBasicDescription*>(&inFormat),
728 sizeof (AudioStreamBasicDescription));
731 OSStatus CAAudioUnit::GetSampleRate (AudioUnitScope inScope,
732 AudioUnitElement inEl,
733 Float64 &outRate) const
735 UInt32 dataSize = sizeof (Float64);
736 return AudioUnitGetProperty (AU(), kAudioUnitProperty_SampleRate,
738 &outRate, &dataSize);
741 OSStatus CAAudioUnit::SetSampleRate (AudioUnitScope inScope,
742 AudioUnitElement inEl,
745 AudioStreamBasicDescription desc;
746 OSStatus result = GetFormat (inScope, inEl, desc);
747 if (result) return result;
748 desc.mSampleRate = inRate;
749 return SetFormat (inScope, inEl, desc);
752 OSStatus CAAudioUnit::SetSampleRate (Float64 inSampleRate)
757 require_noerr (result = GetElementCount(kAudioUnitScope_Input, elCount), home);
759 for (unsigned int i = 0; i < elCount; ++i) {
760 require_noerr (result = SetSampleRate (kAudioUnitScope_Input, i, inSampleRate), home);
764 require_noerr (result = GetElementCount(kAudioUnitScope_Output, elCount), home);
766 for (unsigned int i = 0; i < elCount; ++i) {
767 require_noerr (result = SetSampleRate (kAudioUnitScope_Output, i, inSampleRate), home);
775 OSStatus CAAudioUnit::NumberChannels (AudioUnitScope inScope,
776 AudioUnitElement inEl,
777 UInt32 &outChans) const
779 AudioStreamBasicDescription desc;
780 OSStatus result = GetFormat (inScope, inEl, desc);
782 outChans = desc.mChannelsPerFrame;
786 OSStatus CAAudioUnit::SetNumberChannels (AudioUnitScope inScope,
787 AudioUnitElement inEl,
790 // set this as the output of the AU
791 CAStreamBasicDescription desc;
792 OSStatus result = GetFormat (inScope, inEl, desc);
793 if (result) return result;
794 desc.SetCanonical (inChans, desc.IsInterleaved());
795 result = SetFormat (inScope, inEl, desc);
799 OSStatus CAAudioUnit::IsElementCountWritable (AudioUnitScope inScope, bool &outWritable) const
803 OSStatus result = GetPropertyInfo (kAudioUnitProperty_ElementCount, inScope, 0, &outDataSize, &isWritable);
806 outWritable = isWritable ? true : false;
810 OSStatus CAAudioUnit::GetElementCount (AudioUnitScope inScope, UInt32 &outCount) const
812 UInt32 propSize = sizeof(outCount);
813 return GetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &outCount, &propSize);
816 OSStatus CAAudioUnit::SetElementCount (AudioUnitScope inScope, UInt32 inCount)
818 return SetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &inCount, sizeof(inCount));
821 bool CAAudioUnit::HasDynamicScope (AudioUnitScope inScope, SInt32 &outTotalNumChannels) const
823 // ok - now we need to check the AU's capability here.
824 // this is the default assumption of an audio effect unit
825 Boolean* isWritable = 0;
827 OSStatus result = GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
828 kAudioUnitScope_Global, 0,
829 &dataSize, isWritable); //don't care if this is writable
831 // AU has to explicitly tell us about this.
832 if (result) return false;
834 StackAUChannelInfo info (dataSize);
836 result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
837 kAudioUnitScope_Global, 0,
838 info.mChanInfo, &dataSize);
839 if (result) return false;
841 return ValidateDynamicScope (inScope, outTotalNumChannels, info.mChanInfo, (dataSize / sizeof(AUChannelInfo)));
844 // as we've already checked that the element count is writable
845 // the following conditions will match this..
847 -1, -2 -> signifies no restrictions
848 -2, -1 -> signifies no restrictions -> in this case outTotalNumChannels == -1 (any num channels)
850 -N (where N is less than -2), signifies the total channel count on the scope side (in or out)
852 bool CAAudioUnit::ValidateDynamicScope (AudioUnitScope inScope,
853 SInt32 &outTotalNumChannels,
854 const AUChannelInfo *info,
855 UInt32 numInfo) const
857 bool writable = false;
858 OSStatus result = IsElementCountWritable (inScope, writable);
859 if (result || (writable == false))
862 //now chan layout can contain -1 for either scope (ie. doesn't care)
863 for (unsigned int i = 0; i < numInfo; ++i)
865 // lets test the special wild card case first...
866 // this says the AU can do any num channels on input or output - for eg. Matrix Mixer
867 if (((info[i].inChannels == -1) && (info[i].outChannels == -2))
868 || ((info[i].inChannels == -2) && (info[i].outChannels == -1)))
870 outTotalNumChannels = -1;
874 // ok lets now test our special case....
875 if (inScope == kAudioUnitScope_Input) {
876 // isn't dynamic on this side at least
877 if (info[i].inChannels >= 0)
880 if (info[i].inChannels < -2) {
881 outTotalNumChannels = abs (info[i].inChannels);
886 else if (inScope == kAudioUnitScope_Output) {
887 // isn't dynamic on this side at least
888 if (info[i].outChannels >= 0)
891 if (info[i].outChannels < -2) {
892 outTotalNumChannels = abs (info[i].outChannels);
898 break; // wrong scope was specified
905 OSStatus CAAudioUnit::ConfigureDynamicScope (AudioUnitScope inScope,
906 UInt32 inNumElements,
907 UInt32 *inChannelsPerElement,
908 Float64 inSampleRate)
910 SInt32 numChannels = 0;
911 bool isDyamic = HasDynamicScope (inScope, numChannels);
912 if (isDyamic == false)
913 return kAudioUnitErr_InvalidProperty;
915 //lets to a sanity check...
916 // if numChannels == -1, then it can do "any"...
917 if (numChannels > 0) {
919 for (unsigned int i = 0; i < inNumElements; ++i)
920 count += inChannelsPerElement[i];
921 if (count > numChannels)
922 return kAudioUnitErr_InvalidPropertyValue;
925 OSStatus result = SetElementCount (inScope, inNumElements);
929 CAStreamBasicDescription desc;
930 desc.mSampleRate = inSampleRate;
931 for (unsigned int i = 0; i < inNumElements; ++i) {
932 desc.SetCanonical (inChannelsPerElement[i], false);
933 result = SetFormat (inScope, i, desc);
940 #pragma mark __Properties
942 bool CAAudioUnit::CanBypass () const
945 OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_BypassEffect,
946 kAudioUnitScope_Global, 0,
948 return (!result && outWritable);
951 bool CAAudioUnit::GetBypass () const
953 UInt32 dataSize = sizeof (UInt32);
955 OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_BypassEffect,
956 kAudioUnitScope_Global, 0,
957 &outBypass, &dataSize);
958 return (result ? false : outBypass);
961 OSStatus CAAudioUnit::SetBypass (bool inBypass) const
963 UInt32 bypass = inBypass ? 1 : 0;
964 return AudioUnitSetProperty (AU(), kAudioUnitProperty_BypassEffect,
965 kAudioUnitScope_Global, 0,
966 &bypass, sizeof (UInt32));
969 Float64 CAAudioUnit::Latency () const
972 UInt32 size = sizeof(secs);
973 if (GetProperty (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &secs, &size))
978 OSStatus CAAudioUnit::GetAUPreset (CFPropertyListRef &outData) const
980 UInt32 dataSize = sizeof(outData);
981 return AudioUnitGetProperty (AU(), kAudioUnitProperty_ClassInfo,
982 kAudioUnitScope_Global, 0,
983 &outData, &dataSize);
986 OSStatus CAAudioUnit::SetAUPreset (CFPropertyListRef &inData)
988 return AudioUnitSetProperty (AU(), kAudioUnitProperty_ClassInfo,
989 kAudioUnitScope_Global, 0,
990 &inData, sizeof (CFPropertyListRef));
993 OSStatus CAAudioUnit::GetPresentPreset (AUPreset &outData) const
995 UInt32 dataSize = sizeof(outData);
996 OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_PresentPreset,
997 kAudioUnitScope_Global, 0,
998 &outData, &dataSize);
999 if (result == kAudioUnitErr_InvalidProperty) {
1000 dataSize = sizeof(outData);
1001 result = AudioUnitGetProperty (AU(), kAudioUnitProperty_CurrentPreset,
1002 kAudioUnitScope_Global, 0,
1003 &outData, &dataSize);
1004 if (result == noErr) {
1005 // we now retain the CFString in the preset so for the client of this API
1006 // it is consistent (ie. the string should be released when done)
1007 if (outData.presetName)
1008 CFRetain (outData.presetName);
1014 OSStatus CAAudioUnit::SetPresentPreset (AUPreset &inData)
1016 OSStatus result = AudioUnitSetProperty (AU(), kAudioUnitProperty_PresentPreset,
1017 kAudioUnitScope_Global, 0,
1018 &inData, sizeof (AUPreset));
1019 if (result == kAudioUnitErr_InvalidProperty) {
1020 result = AudioUnitSetProperty (AU(), kAudioUnitProperty_CurrentPreset,
1021 kAudioUnitScope_Global, 0,
1022 &inData, sizeof (AUPreset));
1027 bool CAAudioUnit::HasCustomView () const
1029 UInt32 dataSize = 0;
1030 OSStatus result = GetPropertyInfo(kAudioUnitProperty_GetUIComponentList,
1031 kAudioUnitScope_Global, 0,
1033 if (result || !dataSize) {
1035 result = GetPropertyInfo(kAudioUnitProperty_CocoaUI,
1036 kAudioUnitScope_Global, 0,
1038 if (result || !dataSize)
1044 OSStatus CAAudioUnit::GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1045 Float32 &outValue) const
1047 return mDataPtr ? mDataPtr->GetParameter (inID, scope, element, outValue) : paramErr;
1050 OSStatus CAAudioUnit::SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1051 Float32 value, UInt32 bufferOffsetFrames)
1053 return mDataPtr ? mDataPtr->SetParameter (inID, scope, element, value, bufferOffsetFrames) : paramErr;
1056 OSStatus CAAudioUnit::MIDIEvent (UInt32 inStatus,
1059 UInt32 inOffsetSampleFrame)
1061 return mDataPtr ? mDataPtr->MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame) : paramErr;
1064 OSStatus CAAudioUnit::StartNote (MusicDeviceInstrumentID inInstrument,
1065 MusicDeviceGroupID inGroupID,
1066 NoteInstanceID * outNoteInstanceID,
1067 UInt32 inOffsetSampleFrame,
1068 const MusicDeviceNoteParams * inParams)
1070 return mDataPtr ? mDataPtr->StartNote (inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams)
1074 OSStatus CAAudioUnit::StopNote (MusicDeviceGroupID inGroupID,
1075 NoteInstanceID inNoteInstanceID,
1076 UInt32 inOffsetSampleFrame)
1078 return mDataPtr ? mDataPtr->StopNote (inGroupID, inNoteInstanceID, inOffsetSampleFrame) : paramErr;
1081 #pragma mark __Render
1083 OSStatus CAAudioUnit::Render (AudioUnitRenderActionFlags * ioActionFlags,
1084 const AudioTimeStamp * inTimeStamp,
1085 UInt32 inOutputBusNumber,
1086 UInt32 inNumberFrames,
1087 AudioBufferList * ioData)
1089 return mDataPtr ? mDataPtr->Render (ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData) : paramErr;
1092 static AURenderCallbackStruct sRenderCallback;
1093 static OSStatus PrerollRenderProc ( void * /*inRefCon*/,
1094 AudioUnitRenderActionFlags * /*inActionFlags*/,
1095 const AudioTimeStamp * /*inTimeStamp*/,
1096 UInt32 /*inBusNumber*/,
1097 UInt32 /*inNumFrames*/,
1098 AudioBufferList *ioData)
1100 AudioBuffer *buf = ioData->mBuffers;
1101 for (UInt32 i = ioData->mNumberBuffers; i--; ++buf)
1102 memset((Byte *)buf->mData, 0, buf->mDataByteSize);
1107 OSStatus CAAudioUnit::Preroll (UInt32 inFrameSize)
1109 CAStreamBasicDescription desc;
1110 OSStatus result = GetFormat (kAudioUnitScope_Input, 0, desc);
1111 bool hasInput = false;
1113 if (result == noErr)
1115 sRenderCallback.inputProc = PrerollRenderProc;
1116 sRenderCallback.inputProcRefCon = 0;
1118 result = SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1119 0, &sRenderCallback, sizeof(sRenderCallback));
1120 if (result) return result;
1124 AudioUnitRenderActionFlags flags = 0;
1125 AudioTimeStamp time;
1126 memset (&time, 0, sizeof(time));
1127 time.mFlags = kAudioTimeStampSampleTimeValid;
1129 CAStreamBasicDescription outputFormat;
1130 require_noerr (result = GetFormat (kAudioUnitScope_Output, 0, outputFormat), home);
1132 AUOutputBL list (outputFormat, inFrameSize);
1135 require_noerr (result = Render (&flags, &time, 0, inFrameSize, list.ABL()), home);
1136 require_noerr (result = GlobalReset(), home);
1141 // remove our installed callback
1142 sRenderCallback.inputProc = 0;
1143 sRenderCallback.inputProcRefCon = 0;
1145 SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1146 0, &sRenderCallback, sizeof(sRenderCallback));
1151 #pragma mark __CAAUChanHelper
1153 CAAUChanHelper::CAAUChanHelper(const CAAudioUnit &inAU, AudioUnitScope inScope)
1154 :mChans(NULL), mNumEls(0), mDidAllocate(false)
1157 if (inAU.GetElementCount (inScope, elCount)) return;
1159 mChans = new UInt32[elCount];
1160 mDidAllocate = true;
1161 memset (mChans, 0, sizeof(int) * elCount);
1163 mChans = mStaticChans;
1164 memset (mChans, 0, sizeof(int) * 8);
1166 for (unsigned int i = 0; i < elCount; ++i) {
1168 if (inAU.NumberChannels (inScope, i, numChans)) return;
1169 mChans[i] = numChans;
1174 CAAUChanHelper::~CAAUChanHelper()
1176 if (mDidAllocate) delete [] mChans;
1179 CAAUChanHelper& CAAUChanHelper::operator= (const CAAUChanHelper &c)
1181 if (mDidAllocate) delete [] mChans;
1182 if (c.mDidAllocate) {
1183 mChans = new UInt32[c.mNumEls];
1184 mDidAllocate = true;
1186 mDidAllocate = false;
1187 mChans = mStaticChans;
1189 memcpy (mChans, c.mChans, c.mNumEls * sizeof(int));
1194 #pragma mark __Print Utilities
1196 void CAAudioUnit::Print (FILE* file) const
1198 fprintf (file, "AudioUnit:%p\n", AU());
1200 fprintf (file, "\tnode=%ld\t", (long)GetAUNode()); Comp().Print (file);