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 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())
353 // the au should either really tell us about this
354 // or we will assume the worst
359 *chaninfo
= (AUChannelInfo
*) malloc (dataSize
);
360 cnt
= dataSize
/ sizeof (AUChannelInfo
);
362 result
= GetProperty (kAudioUnitProperty_SupportedNumChannels
,
363 kAudioUnitScope_Global
, 0,
364 *chaninfo
, &dataSize
);
366 if (result
) { return -1; }
371 bool CAAudioUnit::ValidateChannelPair (int inChannelsIn
,
373 const AUChannelInfo
* info
,
374 UInt32 numChanInfo
) const
376 // we've the following cases (some combinations) to test here:
378 >0 An explicit number of channels on either side
379 0 that side (generally input!) has no elements
381 -1,-1 any num channels as long as same channels on in and out
382 -1,-2 any num channels channels on in and out - special meaning
383 -2+ indicates total num channs AU can handle
384 - elements configurable to any num channels,
385 - element count in scope must be writable
388 //now chan layout can contain -1 for either scope (ie. doesn't care)
389 for (unsigned int i
= 0; i
< numChanInfo
; ++i
)
391 //less than zero on both sides - check for special attributes
392 if ((info
[i
].inChannels
< 0) && (info
[i
].outChannels
< 0))
394 // these are our wild card matches
395 if (info
[i
].inChannels
== -1 && info
[i
].outChannels
== -1) {
396 if (inChannelsOut
== inChannelsIn
) {
400 else if ((info
[i
].inChannels
== -1 && info
[i
].outChannels
== -2)
401 || (info
[i
].inChannels
== -2 && info
[i
].outChannels
== -1))
405 // these are our total num channels matches
406 // element count MUST be writable
408 bool outWrite
= false; bool inWrite
= false;
409 IsElementCountWritable (kAudioUnitScope_Output
, outWrite
);
410 IsElementCountWritable (kAudioUnitScope_Input
, inWrite
);
411 if (inWrite
&& outWrite
) {
412 if ((inChannelsOut
<= abs(info
[i
].outChannels
))
413 && (inChannelsIn
<= abs(info
[i
].inChannels
)))
421 // special meaning on input, specific num on output
422 else if (info
[i
].inChannels
< 0) {
423 if (info
[i
].outChannels
== inChannelsOut
)
425 // can do any in channels
426 if (info
[i
].inChannels
== -1) {
429 // total chans on input
431 bool inWrite
= false;
432 IsElementCountWritable (kAudioUnitScope_Input
, inWrite
);
433 if (inWrite
&& (inChannelsIn
<= abs(info
[i
].inChannels
))) {
440 // special meaning on output, specific num on input
441 else if (info
[i
].outChannels
< 0) {
442 if (info
[i
].inChannels
== inChannelsIn
)
444 // can do any out channels
445 if (info
[i
].outChannels
== -1) {
448 // total chans on output
450 bool outWrite
= false;
451 IsElementCountWritable (kAudioUnitScope_Output
, outWrite
);
452 if (outWrite
&& (inChannelsOut
<= abs(info
[i
].outChannels
))) {
459 // both chans in struct >= 0 - thus has to explicitly match
460 else if ((info
[i
].inChannels
== inChannelsIn
) && (info
[i
].outChannels
== inChannelsOut
)) {
464 // now check to see if a wild card on the args (inChannelsIn or inChannelsOut chans is zero) is found
465 // tells us to match just one side of the scopes
466 else if (inChannelsIn
== 0) {
467 if (info
[i
].outChannels
== inChannelsOut
) {
471 else if (inChannelsOut
== 0) {
472 if (info
[i
].inChannels
== inChannelsIn
) {
481 bool CheckDynCount (SInt32 inTotalChans
, const CAAUChanHelper
&inHelper
)
484 for (unsigned int i
= 0; i
< inHelper
.mNumEls
; ++i
)
485 totalChans
+= inHelper
.mChans
[i
];
486 return (totalChans
<= inTotalChans
);
489 bool CAAudioUnit::CheckOneSide (const CAAUChanHelper
&inHelper
,
491 const AUChannelInfo
*info
,
492 UInt32 numInfo
) const
494 // now we can use the wildcard option (see above impl) to see if this matches
495 for (unsigned int el
= 0; el
< inHelper
.mNumEls
; ++el
) {
496 bool testAlready
= false;
497 for (unsigned int i
= 0; i
< el
; ++i
) {
498 if (inHelper
.mChans
[i
] == inHelper
.mChans
[el
]) {
505 if (!ValidateChannelPair (0, inHelper
.mChans
[el
], info
, numInfo
)) return false;
507 if (!ValidateChannelPair (inHelper
.mChans
[el
], 0, info
, numInfo
)) return false;
514 bool CAAudioUnit::CanDo (const CAAUChanHelper
&inputs
,
515 const CAAUChanHelper
&outputs
) const
518 // first check our state
520 if (inputs
.mNumEls
== 0 && outputs
.mNumEls
== 0) return false;
523 if (GetElementCount (kAudioUnitScope_Input
, elCount
)) { return false; }
524 if (elCount
!= inputs
.mNumEls
) return false;
526 if (GetElementCount (kAudioUnitScope_Output
, elCount
)) { return false; }
527 if (elCount
!= outputs
.mNumEls
) return false;
529 // (1) special cases (effects and sources (generators and instruments) only)
531 if (GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels
,
532 kAudioUnitScope_Global
, 0, &dataSize
, NULL
) != noErr
)
534 if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline()) {
535 UInt32 numChan
= outputs
.mNumEls
> 0 ? outputs
.mChans
[0] : inputs
.mChans
[0];
536 for (unsigned int in
= 0; in
< inputs
.mNumEls
; ++in
)
537 if (numChan
!= inputs
.mChans
[in
]) return false;
538 for (unsigned int out
= 0; out
< outputs
.mNumEls
; ++out
)
539 if (numChan
!= outputs
.mChans
[out
]) return false;
543 // in this case, all the channels have to match the current config
544 if (Comp().Desc().IsGenerator() || Comp().Desc().IsMusicDevice()) {
545 for (unsigned int in
= 0; in
< inputs
.mNumEls
; ++in
) {
547 if (NumberChannels (kAudioUnitScope_Input
, in
, chan
)) return false;
548 if (chan
!= UInt32(inputs
.mChans
[in
])) return false;
550 for (unsigned int out
= 0; out
< outputs
.mNumEls
; ++out
) {
552 if (NumberChannels (kAudioUnitScope_Output
, out
, chan
)) return false;
553 if (chan
!= UInt32(outputs
.mChans
[out
])) return false;
558 // if we get here we can't determine anything about channel capabilities
562 StackAUChannelInfo
info (dataSize
);
564 if (GetProperty (kAudioUnitProperty_SupportedNumChannels
,
565 kAudioUnitScope_Global
, 0,
566 info
.mChanInfo
, &dataSize
) != noErr
)
571 int numInfo
= dataSize
/ sizeof(AUChannelInfo
);
573 // (2) Test for dynamic capability (or no elements on that scope)
574 SInt32 dynInChans
= 0;
575 if (ValidateDynamicScope (kAudioUnitScope_Input
, dynInChans
, info
.mChanInfo
, numInfo
)) {
576 if (CheckDynCount (dynInChans
, inputs
) == false) return false;
579 SInt32 dynOutChans
= 0;
580 if (ValidateDynamicScope (kAudioUnitScope_Output
, dynOutChans
, info
.mChanInfo
, numInfo
)) {
581 if (CheckDynCount (dynOutChans
, outputs
) == false) return false;
584 if (dynOutChans
&& dynInChans
) { return true; }
586 // (3) Just need to test one side
587 if (dynInChans
|| (inputs
.mNumEls
== 0)) {
588 return CheckOneSide (outputs
, true, info
.mChanInfo
, numInfo
);
591 if (dynOutChans
|| (outputs
.mNumEls
== 0)) {
592 return CheckOneSide (inputs
, false, info
.mChanInfo
, numInfo
);
595 // (4) - not a dynamic AU, has ins and outs, and has channel constraints so we test every possible pairing
596 for (unsigned int in
= 0; in
< inputs
.mNumEls
; ++in
)
598 bool testInAlready
= false;
599 for (unsigned int i
= 0; i
< in
; ++i
) {
600 if (inputs
.mChans
[i
] == inputs
.mChans
[in
]) {
601 testInAlready
= true;
605 if (!testInAlready
) {
606 for (unsigned int out
= 0; out
< outputs
.mNumEls
; ++out
) {
607 // try to save a little bit and not test the same pairing multiple times...
608 bool testOutAlready
= false;
609 for (unsigned int i
= 0; i
< out
; ++i
) {
610 if (outputs
.mChans
[i
] == outputs
.mChans
[out
]) {
611 testOutAlready
= true;
615 if (!testOutAlready
) {
616 if (!ValidateChannelPair (inputs
.mChans
[in
], outputs
.mChans
[out
],info
.mChanInfo
, numInfo
)) {
627 bool CAAudioUnit::SupportsNumChannels () const
629 // this is the default assumption of an audio effect unit
630 Boolean
* isWritable
= 0;
632 // lets see if the unit has any channel restrictions
633 OSStatus result
= AudioUnitGetPropertyInfo (AU(),
634 kAudioUnitProperty_SupportedNumChannels
,
635 kAudioUnitScope_Global
, 0,
636 &dataSize
, isWritable
); //don't care if this is writable
638 // if this property is NOT implemented an FX unit
639 // is expected to deal with same channel valance in and out
641 if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline())
644 return result
== noErr
;
647 bool CAAudioUnit::GetChannelLayouts (AudioUnitScope inScope
,
648 AudioUnitElement inEl
,
649 ChannelTagVector
&outChannelVector
) const
651 if (HasChannelLayouts (inScope
, inEl
) == false) return false;
654 OSStatus result
= AudioUnitGetPropertyInfo (AU(),
655 kAudioUnitProperty_SupportedChannelLayoutTags
,
659 if (result
== kAudioUnitErr_InvalidProperty
) {
660 // if we get here we can do layouts but we've got the speaker config property
661 outChannelVector
.erase (outChannelVector
.begin(), outChannelVector
.end());
662 outChannelVector
.push_back (kAudioChannelLayoutTag_Stereo
);
663 outChannelVector
.push_back (kAudioChannelLayoutTag_StereoHeadphones
);
664 outChannelVector
.push_back (kAudioChannelLayoutTag_Quadraphonic
);
665 outChannelVector
.push_back (kAudioChannelLayoutTag_AudioUnit_5_0
);
669 if (result
) return false;
672 // OK lets get our channel layouts and see if the one we want is present
673 AudioChannelLayoutTag
* info
= (AudioChannelLayoutTag
*)malloc (dataSize
);
674 result
= AudioUnitGetProperty (AU(),
675 kAudioUnitProperty_SupportedChannelLayoutTags
,
678 if (result
) goto home
;
680 outChannelVector
.erase (outChannelVector
.begin(), outChannelVector
.end());
681 for (unsigned int i
= 0; i
< (dataSize
/ sizeof (AudioChannelLayoutTag
)); ++i
)
682 outChannelVector
.push_back (info
[i
]);
689 bool CAAudioUnit::HasChannelLayouts (AudioUnitScope inScope
,
690 AudioUnitElement inEl
) const
692 OSStatus result
= AudioUnitGetPropertyInfo (AU(),
693 kAudioUnitProperty_SupportedChannelLayoutTags
,
699 OSStatus
CAAudioUnit::GetChannelLayout (AudioUnitScope inScope
,
700 AudioUnitElement inEl
,
701 CAAudioChannelLayout
&outLayout
) const
704 OSStatus result
= AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_AudioChannelLayout
,
705 inScope
, inEl
, &size
, NULL
);
706 if (result
) return result
;
708 AudioChannelLayout
*layout
= (AudioChannelLayout
*)malloc (size
);
710 require_noerr (result
= AudioUnitGetProperty (AU(), kAudioUnitProperty_AudioChannelLayout
,
711 inScope
, inEl
, layout
, &size
), home
);
713 outLayout
= CAAudioChannelLayout (layout
);
720 OSStatus
CAAudioUnit::SetChannelLayout (AudioUnitScope inScope
,
721 AudioUnitElement inEl
,
722 CAAudioChannelLayout
&inLayout
)
724 OSStatus result
= AudioUnitSetProperty (AU(),
725 kAudioUnitProperty_AudioChannelLayout
,
727 inLayout
, inLayout
.Size());
731 OSStatus
CAAudioUnit::SetChannelLayout (AudioUnitScope inScope
,
732 AudioUnitElement inEl
,
733 AudioChannelLayout
&inLayout
,
736 OSStatus result
= AudioUnitSetProperty (AU(),
737 kAudioUnitProperty_AudioChannelLayout
,
743 OSStatus
CAAudioUnit::ClearChannelLayout (AudioUnitScope inScope
,
744 AudioUnitElement inEl
)
746 return AudioUnitSetProperty (AU(),
747 kAudioUnitProperty_AudioChannelLayout
,
748 inScope
, inEl
, NULL
, 0);
751 OSStatus
CAAudioUnit::GetFormat (AudioUnitScope inScope
,
752 AudioUnitElement inEl
,
753 AudioStreamBasicDescription
&outFormat
) const
755 UInt32 dataSize
= sizeof (AudioStreamBasicDescription
);
756 return AudioUnitGetProperty (AU(), kAudioUnitProperty_StreamFormat
,
758 &outFormat
, &dataSize
);
761 OSStatus
CAAudioUnit::SetFormat (AudioUnitScope inScope
,
762 AudioUnitElement inEl
,
763 const AudioStreamBasicDescription
&inFormat
)
765 return AudioUnitSetProperty (AU(), kAudioUnitProperty_StreamFormat
,
767 const_cast<AudioStreamBasicDescription
*>(&inFormat
),
768 sizeof (AudioStreamBasicDescription
));
771 OSStatus
CAAudioUnit::GetSampleRate (AudioUnitScope inScope
,
772 AudioUnitElement inEl
,
773 Float64
&outRate
) const
775 UInt32 dataSize
= sizeof (Float64
);
776 return AudioUnitGetProperty (AU(), kAudioUnitProperty_SampleRate
,
778 &outRate
, &dataSize
);
781 OSStatus
CAAudioUnit::SetSampleRate (AudioUnitScope inScope
,
782 AudioUnitElement inEl
,
785 AudioStreamBasicDescription desc
;
786 OSStatus result
= GetFormat (inScope
, inEl
, desc
);
787 if (result
) return result
;
788 desc
.mSampleRate
= inRate
;
789 return SetFormat (inScope
, inEl
, desc
);
792 OSStatus
CAAudioUnit::SetSampleRate (Float64 inSampleRate
)
797 require_noerr (result
= GetElementCount(kAudioUnitScope_Input
, elCount
), home
);
799 for (unsigned int i
= 0; i
< elCount
; ++i
) {
800 require_noerr (result
= SetSampleRate (kAudioUnitScope_Input
, i
, inSampleRate
), home
);
804 require_noerr (result
= GetElementCount(kAudioUnitScope_Output
, elCount
), home
);
806 for (unsigned int i
= 0; i
< elCount
; ++i
) {
807 require_noerr (result
= SetSampleRate (kAudioUnitScope_Output
, i
, inSampleRate
), home
);
815 OSStatus
CAAudioUnit::NumberChannels (AudioUnitScope inScope
,
816 AudioUnitElement inEl
,
817 UInt32
&outChans
) const
819 AudioStreamBasicDescription desc
;
820 OSStatus result
= GetFormat (inScope
, inEl
, desc
);
822 outChans
= desc
.mChannelsPerFrame
;
826 OSStatus
CAAudioUnit::SetNumberChannels (AudioUnitScope inScope
,
827 AudioUnitElement inEl
,
830 // set this as the output of the AU
831 CAStreamBasicDescription desc
;
832 OSStatus result
= GetFormat (inScope
, inEl
, desc
);
833 if (result
) return result
;
834 desc
.SetCanonical (inChans
, desc
.IsInterleaved());
835 result
= SetFormat (inScope
, inEl
, desc
);
839 OSStatus
CAAudioUnit::IsElementCountWritable (AudioUnitScope inScope
, bool &outWritable
) const
843 OSStatus result
= GetPropertyInfo (kAudioUnitProperty_ElementCount
, inScope
, 0, &outDataSize
, &isWritable
);
846 outWritable
= isWritable
? true : false;
850 OSStatus
CAAudioUnit::GetElementCount (AudioUnitScope inScope
, UInt32
&outCount
) const
852 UInt32 propSize
= sizeof(outCount
);
853 return GetProperty (kAudioUnitProperty_ElementCount
, inScope
, 0, &outCount
, &propSize
);
856 OSStatus
CAAudioUnit::SetElementCount (AudioUnitScope inScope
, UInt32 inCount
)
858 return SetProperty (kAudioUnitProperty_ElementCount
, inScope
, 0, &inCount
, sizeof(inCount
));
861 bool CAAudioUnit::HasDynamicScope (AudioUnitScope inScope
, SInt32
&outTotalNumChannels
) const
863 // ok - now we need to check the AU's capability here.
864 // this is the default assumption of an audio effect unit
865 Boolean
* isWritable
= 0;
867 OSStatus result
= GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels
,
868 kAudioUnitScope_Global
, 0,
869 &dataSize
, isWritable
); //don't care if this is writable
871 // AU has to explicitly tell us about this.
872 if (result
) return false;
874 StackAUChannelInfo
info (dataSize
);
876 result
= GetProperty (kAudioUnitProperty_SupportedNumChannels
,
877 kAudioUnitScope_Global
, 0,
878 info
.mChanInfo
, &dataSize
);
879 if (result
) return false;
881 return ValidateDynamicScope (inScope
, outTotalNumChannels
, info
.mChanInfo
, (dataSize
/ sizeof(AUChannelInfo
)));
884 // as we've already checked that the element count is writable
885 // the following conditions will match this..
887 -1, -2 -> signifies no restrictions
888 -2, -1 -> signifies no restrictions -> in this case outTotalNumChannels == -1 (any num channels)
890 -N (where N is less than -2), signifies the total channel count on the scope side (in or out)
892 bool CAAudioUnit::ValidateDynamicScope (AudioUnitScope inScope
,
893 SInt32
&outTotalNumChannels
,
894 const AUChannelInfo
*info
,
895 UInt32 numInfo
) const
897 bool writable
= false;
898 OSStatus result
= IsElementCountWritable (inScope
, writable
);
899 if (result
|| (writable
== false))
902 //now chan layout can contain -1 for either scope (ie. doesn't care)
903 for (unsigned int i
= 0; i
< numInfo
; ++i
)
905 // lets test the special wild card case first...
906 // this says the AU can do any num channels on input or output - for eg. Matrix Mixer
907 if (((info
[i
].inChannels
== -1) && (info
[i
].outChannels
== -2))
908 || ((info
[i
].inChannels
== -2) && (info
[i
].outChannels
== -1)))
910 outTotalNumChannels
= -1;
914 // ok lets now test our special case....
915 if (inScope
== kAudioUnitScope_Input
) {
916 // isn't dynamic on this side at least
917 if (info
[i
].inChannels
>= 0)
920 if (info
[i
].inChannels
< -2) {
921 outTotalNumChannels
= abs (info
[i
].inChannels
);
926 else if (inScope
== kAudioUnitScope_Output
) {
927 // isn't dynamic on this side at least
928 if (info
[i
].outChannels
>= 0)
931 if (info
[i
].outChannels
< -2) {
932 outTotalNumChannels
= abs (info
[i
].outChannels
);
938 break; // wrong scope was specified
945 OSStatus
CAAudioUnit::ConfigureDynamicScope (AudioUnitScope inScope
,
946 UInt32 inNumElements
,
947 UInt32
*inChannelsPerElement
,
948 Float64 inSampleRate
)
950 SInt32 numChannels
= 0;
951 bool isDyamic
= HasDynamicScope (inScope
, numChannels
);
952 if (isDyamic
== false)
953 return kAudioUnitErr_InvalidProperty
;
955 //lets to a sanity check...
956 // if numChannels == -1, then it can do "any"...
957 if (numChannels
> 0) {
959 for (unsigned int i
= 0; i
< inNumElements
; ++i
)
960 count
+= inChannelsPerElement
[i
];
961 if (count
> numChannels
)
962 return kAudioUnitErr_InvalidPropertyValue
;
965 OSStatus result
= SetElementCount (inScope
, inNumElements
);
969 CAStreamBasicDescription desc
;
970 desc
.mSampleRate
= inSampleRate
;
971 for (unsigned int i
= 0; i
< inNumElements
; ++i
) {
972 desc
.SetCanonical (inChannelsPerElement
[i
], false);
973 result
= SetFormat (inScope
, i
, desc
);
980 #pragma mark __Properties
982 bool CAAudioUnit::CanBypass () const
985 OSStatus result
= AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_BypassEffect
,
986 kAudioUnitScope_Global
, 0,
988 return (!result
&& outWritable
);
991 bool CAAudioUnit::GetBypass () const
993 UInt32 dataSize
= sizeof (UInt32
);
995 OSStatus result
= AudioUnitGetProperty (AU(), kAudioUnitProperty_BypassEffect
,
996 kAudioUnitScope_Global
, 0,
997 &outBypass
, &dataSize
);
998 return (result
? false : outBypass
);
1001 OSStatus
CAAudioUnit::SetBypass (bool inBypass
) const
1003 UInt32 bypass
= inBypass
? 1 : 0;
1004 return AudioUnitSetProperty (AU(), kAudioUnitProperty_BypassEffect
,
1005 kAudioUnitScope_Global
, 0,
1006 &bypass
, sizeof (UInt32
));
1009 Float64
CAAudioUnit::Latency () const
1012 UInt32 size
= sizeof(secs
);
1013 if (GetProperty (kAudioUnitProperty_Latency
, kAudioUnitScope_Global
, 0, &secs
, &size
))
1018 OSStatus
CAAudioUnit::GetAUPreset (CFPropertyListRef
&outData
) const
1020 UInt32 dataSize
= sizeof(outData
);
1021 return AudioUnitGetProperty (AU(), kAudioUnitProperty_ClassInfo
,
1022 kAudioUnitScope_Global
, 0,
1023 &outData
, &dataSize
);
1026 OSStatus
CAAudioUnit::SetAUPreset (CFPropertyListRef
&inData
)
1028 return AudioUnitSetProperty (AU(), kAudioUnitProperty_ClassInfo
,
1029 kAudioUnitScope_Global
, 0,
1030 &inData
, sizeof (CFPropertyListRef
));
1033 OSStatus
CAAudioUnit::GetPresentPreset (AUPreset
&outData
) const
1035 UInt32 dataSize
= sizeof(outData
);
1036 OSStatus result
= AudioUnitGetProperty (AU(), kAudioUnitProperty_PresentPreset
,
1037 kAudioUnitScope_Global
, 0,
1038 &outData
, &dataSize
);
1039 if (result
== kAudioUnitErr_InvalidProperty
) {
1040 dataSize
= sizeof(outData
);
1041 result
= AudioUnitGetProperty (AU(), kAudioUnitProperty_CurrentPreset
,
1042 kAudioUnitScope_Global
, 0,
1043 &outData
, &dataSize
);
1044 if (result
== noErr
) {
1045 // we now retain the CFString in the preset so for the client of this API
1046 // it is consistent (ie. the string should be released when done)
1047 if (outData
.presetName
)
1048 CFRetain (outData
.presetName
);
1054 OSStatus
CAAudioUnit::SetPresentPreset (AUPreset
&inData
)
1056 OSStatus result
= AudioUnitSetProperty (AU(), kAudioUnitProperty_PresentPreset
,
1057 kAudioUnitScope_Global
, 0,
1058 &inData
, sizeof (AUPreset
));
1059 if (result
== kAudioUnitErr_InvalidProperty
) {
1060 result
= AudioUnitSetProperty (AU(), kAudioUnitProperty_CurrentPreset
,
1061 kAudioUnitScope_Global
, 0,
1062 &inData
, sizeof (AUPreset
));
1067 bool CAAudioUnit::HasCustomView () const
1069 UInt32 dataSize
= 0;
1070 OSStatus result
= GetPropertyInfo(kAudioUnitProperty_GetUIComponentList
,
1071 kAudioUnitScope_Global
, 0,
1073 if (result
|| !dataSize
) {
1075 result
= GetPropertyInfo(kAudioUnitProperty_CocoaUI
,
1076 kAudioUnitScope_Global
, 0,
1078 if (result
|| !dataSize
)
1084 OSStatus
CAAudioUnit::GetParameter(AudioUnitParameterID inID
, AudioUnitScope scope
, AudioUnitElement element
,
1085 Float32
&outValue
) const
1087 return mDataPtr
? mDataPtr
->GetParameter (inID
, scope
, element
, outValue
) : paramErr
;
1090 OSStatus
CAAudioUnit::SetParameter(AudioUnitParameterID inID
, AudioUnitScope scope
, AudioUnitElement element
,
1091 Float32 value
, UInt32 bufferOffsetFrames
)
1093 return mDataPtr
? mDataPtr
->SetParameter (inID
, scope
, element
, value
, bufferOffsetFrames
) : paramErr
;
1096 OSStatus
CAAudioUnit::MIDIEvent (UInt32 inStatus
,
1099 UInt32 inOffsetSampleFrame
)
1101 return mDataPtr
? mDataPtr
->MIDIEvent (inStatus
, inData1
, inData2
, inOffsetSampleFrame
) : paramErr
;
1104 OSStatus
CAAudioUnit::StartNote (MusicDeviceInstrumentID inInstrument
,
1105 MusicDeviceGroupID inGroupID
,
1106 NoteInstanceID
* outNoteInstanceID
,
1107 UInt32 inOffsetSampleFrame
,
1108 const MusicDeviceNoteParams
* inParams
)
1110 return mDataPtr
? mDataPtr
->StartNote (inInstrument
, inGroupID
, outNoteInstanceID
, inOffsetSampleFrame
, inParams
)
1114 OSStatus
CAAudioUnit::StopNote (MusicDeviceGroupID inGroupID
,
1115 NoteInstanceID inNoteInstanceID
,
1116 UInt32 inOffsetSampleFrame
)
1118 return mDataPtr
? mDataPtr
->StopNote (inGroupID
, inNoteInstanceID
, inOffsetSampleFrame
) : paramErr
;
1121 #pragma mark __Render
1123 OSStatus
CAAudioUnit::Render (AudioUnitRenderActionFlags
* ioActionFlags
,
1124 const AudioTimeStamp
* inTimeStamp
,
1125 UInt32 inOutputBusNumber
,
1126 UInt32 inNumberFrames
,
1127 AudioBufferList
* ioData
)
1129 return mDataPtr
? mDataPtr
->Render (ioActionFlags
, inTimeStamp
, inOutputBusNumber
, inNumberFrames
, ioData
) : paramErr
;
1132 static AURenderCallbackStruct sRenderCallback
;
1133 static OSStatus
PrerollRenderProc ( void * /*inRefCon*/,
1134 AudioUnitRenderActionFlags
* /*inActionFlags*/,
1135 const AudioTimeStamp
* /*inTimeStamp*/,
1136 UInt32
/*inBusNumber*/,
1137 UInt32
/*inNumFrames*/,
1138 AudioBufferList
*ioData
)
1140 AudioBuffer
*buf
= ioData
->mBuffers
;
1141 for (UInt32 i
= ioData
->mNumberBuffers
; i
--; ++buf
)
1142 memset((Byte
*)buf
->mData
, 0, buf
->mDataByteSize
);
1147 OSStatus
CAAudioUnit::Preroll (UInt32 inFrameSize
)
1149 CAStreamBasicDescription desc
;
1150 OSStatus result
= GetFormat (kAudioUnitScope_Input
, 0, desc
);
1151 bool hasInput
= false;
1153 if (result
== noErr
)
1155 sRenderCallback
.inputProc
= PrerollRenderProc
;
1156 sRenderCallback
.inputProcRefCon
= 0;
1158 result
= SetProperty (kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
,
1159 0, &sRenderCallback
, sizeof(sRenderCallback
));
1160 if (result
) return result
;
1164 AudioUnitRenderActionFlags flags
= 0;
1165 AudioTimeStamp time
;
1166 memset (&time
, 0, sizeof(time
));
1167 time
.mFlags
= kAudioTimeStampSampleTimeValid
;
1169 CAStreamBasicDescription outputFormat
;
1170 require_noerr (result
= GetFormat (kAudioUnitScope_Output
, 0, outputFormat
), home
);
1172 AUOutputBL
list (outputFormat
, inFrameSize
);
1175 require_noerr (result
= Render (&flags
, &time
, 0, inFrameSize
, list
.ABL()), home
);
1176 require_noerr (result
= GlobalReset(), home
);
1181 // remove our installed callback
1182 sRenderCallback
.inputProc
= 0;
1183 sRenderCallback
.inputProcRefCon
= 0;
1185 SetProperty (kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
,
1186 0, &sRenderCallback
, sizeof(sRenderCallback
));
1191 #pragma mark __CAAUChanHelper
1193 CAAUChanHelper::CAAUChanHelper(const CAAudioUnit
&inAU
, AudioUnitScope inScope
)
1194 :mChans(NULL
), mNumEls(0), mDidAllocate(false)
1197 if (inAU
.GetElementCount (inScope
, elCount
)) return;
1199 mChans
= new UInt32
[elCount
];
1200 mDidAllocate
= true;
1201 memset (mChans
, 0, sizeof(int) * elCount
);
1203 mChans
= mStaticChans
;
1204 memset (mChans
, 0, sizeof(int) * 8);
1206 for (unsigned int i
= 0; i
< elCount
; ++i
) {
1208 if (inAU
.NumberChannels (inScope
, i
, numChans
)) return;
1209 mChans
[i
] = numChans
;
1214 CAAUChanHelper::~CAAUChanHelper()
1216 if (mDidAllocate
) delete [] mChans
;
1219 CAAUChanHelper
& CAAUChanHelper::operator= (const CAAUChanHelper
&c
)
1221 if (mDidAllocate
) delete [] mChans
;
1222 if (c
.mDidAllocate
) {
1223 mChans
= new UInt32
[c
.mNumEls
];
1224 mDidAllocate
= true;
1226 mDidAllocate
= false;
1227 mChans
= mStaticChans
;
1229 memcpy (mChans
, c
.mChans
, c
.mNumEls
* sizeof(int));
1234 #pragma mark __Print Utilities
1236 void CAAudioUnit::Print (FILE* file
) const
1238 fprintf (file
, "AudioUnit:%p\n", AU());
1240 fprintf (file
, "\tnode=%ld\t", (long)GetAUNode()); Comp().Print (file
);