Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / platform / mac / SuperColliderAU / AUSDK / AUBase.cpp
blob5412fe13ff4b48538bf71c164cb669324bb8d6b8
1 /* Copyright � 2007 Apple Inc. All Rights Reserved.
3 Disclaimer: IMPORTANT: This Apple software is supplied to you by
4 Apple Inc. ("Apple") in consideration of your agreement to the
5 following terms, and your use, installation, modification or
6 redistribution of this Apple software constitutes acceptance of these
7 terms. If you do not agree with these terms, please do not use,
8 install, modify or redistribute this Apple software.
10 In consideration of your agreement to abide by the following terms, and
11 subject to these terms, Apple grants you a personal, non-exclusive
12 license, under Apple's copyrights in this original Apple software (the
13 "Apple Software"), to use, reproduce, modify and redistribute the Apple
14 Software, with or without modifications, in source and/or binary forms;
15 provided that if you redistribute the Apple Software in its entirety and
16 without modifications, you must retain this notice and the following
17 text and disclaimers in all such redistributions of the Apple Software.
18 Neither the name, trademarks, service marks or logos of Apple Inc.
19 may be used to endorse or promote products derived from the Apple
20 Software without specific prior written permission from Apple. Except
21 as expressly stated in this notice, no other rights or licenses, express
22 or implied, are granted by Apple herein, including but not limited to
23 any patent rights that may be infringed by your derivative works or by
24 other works in which the Apple Software may be incorporated.
26 The Apple Software is provided by Apple on an "AS IS" basis. APPLE
27 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
28 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
29 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
30 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
32 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
33 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
36 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
37 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
38 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
39 POSSIBILITY OF SUCH DAMAGE.
41 /*=============================================================================
42 AUBase.cpp
44 =============================================================================*/
46 #include "AUBase.h"
47 #include "AUDispatch.h"
48 #include "AUInputElement.h"
49 #include "AUOutputElement.h"
50 #include <algorithm>
51 #include "CAAudioChannelLayout.h"
52 #include "CAHostTimeBase.h"
53 #include "CAVectorUnit.h"
55 #if TARGET_OS_MAC && (TARGET_CPU_X86 || TARGET_CPU_X86_64)
56 // our compiler does ALL floating point with SSE
57 #define GETCSR() ({ int _result; asm volatile ("stmxcsr %0" : "=m" (*&_result) ); /*return*/ _result; })
58 #define SETCSR( a ) { int _temp = a; asm volatile( "ldmxcsr %0" : : "m" (*&_temp ) ); }
60 #define DISABLE_DENORMALS int _savemxcsr = GETCSR(); SETCSR(_savemxcsr | 0x8040);
61 #define RESTORE_DENORMALS SETCSR(_savemxcsr);
62 #else
63 #define DISABLE_DENORMALS
64 #define RESTORE_DENORMALS
65 #endif
67 #define kAudioUnitRenderAction_PostRenderError (1 << 8)
70 static bool sAUBaseCFStringsInitialized = false;
71 // this is used for the presets
72 static CFStringRef kUntitledString = NULL;
73 //these are the current keys for the class info document
74 static CFStringRef kVersionString = NULL;
75 static CFStringRef kTypeString = NULL;
76 static CFStringRef kSubtypeString = NULL;
77 static CFStringRef kManufacturerString = NULL;
78 static CFStringRef kDataString = NULL;
79 static CFStringRef kNameString = NULL;
80 static CFStringRef kRenderQualityString = NULL;
81 static CFStringRef kCPULoadString = NULL;
82 static CFStringRef kVSTDataString = NULL;
83 static CFStringRef kElementNameString = NULL;
84 static CFStringRef kPartString = NULL;
86 SInt32 AUBase::sVectorUnitType = kVecUninitialized;
88 //_____________________________________________________________________________
90 AUBase::AUBase( AudioUnit inInstance,
91 UInt32 numInputElements,
92 UInt32 numOutputElements,
93 UInt32 numGroupElements,
94 UInt32 numPartElements) :
95 ComponentBase(inInstance),
96 mElementsCreated(false),
97 mInitialized(false),
98 mInitNumInputEls(numInputElements), mInitNumOutputEls(numOutputElements),
99 mInitNumGroupEls(numGroupElements), mInitNumPartEls(numPartElements),
100 mRenderThreadID (NULL),
101 mWantsRenderThreadID (false),
102 mLastRenderedSampleTime(kNoLastRenderedSampleTime),
103 mLastRenderError(0),
104 mContextName(NULL),
105 mBuffersAllocated(false),
106 mLogString (NULL),
107 mDebugDispatcher (NULL)
111 if(!sAUBaseCFStringsInitialized)
113 kUntitledString = CFSTR("Untitled");
114 kVersionString = CFSTR(kAUPresetVersionKey);
115 kTypeString = CFSTR(kAUPresetTypeKey);
116 kSubtypeString = CFSTR(kAUPresetSubtypeKey);
117 kManufacturerString = CFSTR(kAUPresetManufacturerKey);
118 kDataString = CFSTR(kAUPresetDataKey);
119 kNameString = CFSTR(kAUPresetNameKey);
120 kRenderQualityString = CFSTR(kAUPresetRenderQualityKey);
121 kCPULoadString = CFSTR(kAUPresetCPULoadKey);
122 kVSTDataString = CFSTR(kAUPresetVSTDataKey);
123 kElementNameString = CFSTR(kAUPresetElementNameKey);
124 kPartString = CFSTR(kAUPresetPartKey);
125 sAUBaseCFStringsInitialized = true;
128 if (sVectorUnitType == kVecUninitialized) {
129 sVectorUnitType = CAVectorUnit::GetVectorUnitType() ;
132 mAudioUnitAPIVersion = 2;
134 SetMaxFramesPerSlice(kAUDefaultMaxFramesPerSlice);
136 GlobalScope().Initialize(this, kAudioUnitScope_Global, 1);
138 if (mAudioUnitAPIVersion > 1)
139 mParamList.reserve (24);
141 memset (&mHostCallbackInfo, 0, sizeof (mHostCallbackInfo));
143 mCurrentPreset.presetNumber = -1;
144 mCurrentPreset.presetName = kUntitledString;
145 CFRetain (mCurrentPreset.presetName);
148 //_____________________________________________________________________________
150 AUBase::~AUBase()
152 if (mCurrentPreset.presetName) CFRelease (mCurrentPreset.presetName);
153 if (mContextName) CFRelease (mContextName);
154 if (mLogString) delete [] mLogString;
157 //_____________________________________________________________________________
159 void AUBase::CreateElements()
161 if (!mElementsCreated) {
162 Inputs().Initialize(this, kAudioUnitScope_Input, mInitNumInputEls);
163 Outputs().Initialize(this, kAudioUnitScope_Output, mInitNumOutputEls);
164 Groups().Initialize(this, kAudioUnitScope_Group, mInitNumGroupEls);
165 Parts().Initialize(this, kAudioUnitScope_Part, mInitNumPartEls);
166 mElementsCreated = true;
170 //_____________________________________________________________________________
172 void AUBase::SetMaxFramesPerSlice(UInt32 nFrames)
174 mMaxFramesPerSlice = nFrames;
175 if (mBuffersAllocated)
176 ReallocateBuffers();
177 PropertyChanged(kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0);
180 //_____________________________________________________________________________
182 OSStatus AUBase::CanSetMaxFrames() const
184 return IsInitialized() ? kAudioUnitErr_Initialized : OSStatus(noErr);
187 //_____________________________________________________________________________
189 void AUBase::ReallocateBuffers()
191 CreateElements();
193 int i;
194 int nOutputs = Outputs().GetNumberOfElements();
195 for (i = 0; i < nOutputs; ++i) {
196 AUOutputElement *output = GetOutput(i);
197 output->AllocateBuffer(); // does no work if already allocated
199 int nInputs = Inputs().GetNumberOfElements();
200 for (i = 0; i < nInputs; ++i) {
201 AUInputElement *input = GetInput(i);
202 input->AllocateBuffer(); // does no work if already allocated
204 mBuffersAllocated = true;
207 //_____________________________________________________________________________
209 ComponentResult AUBase::DoInitialize()
211 ComponentResult result = noErr;
213 if (!mInitialized) {
214 result = Initialize();
215 if (result == noErr) {
216 mInitialized = true; // signal that it's okay to initialize buffers
217 ReallocateBuffers(); // calls CreateElements()
221 return result;
224 //_____________________________________________________________________________
226 ComponentResult AUBase::Initialize()
228 return noErr;
231 //_____________________________________________________________________________
233 void AUBase::PreDestructor()
235 DoCleanup();
238 //_____________________________________________________________________________
240 void AUBase::DoCleanup()
242 if (mInitialized)
243 Cleanup();
244 mInitialized = false;
247 //_____________________________________________________________________________
249 void AUBase::Cleanup()
253 //_____________________________________________________________________________
255 ComponentResult AUBase::Reset( AudioUnitScope inScope,
256 AudioUnitElement inElement)
258 mLastRenderedSampleTime = -1;
259 return noErr;
262 //_____________________________________________________________________________
264 ComponentResult AUBase::DispatchGetPropertyInfo(AudioUnitPropertyID inID,
265 AudioUnitScope inScope,
266 AudioUnitElement inElement,
267 UInt32 & outDataSize,
268 Boolean & outWritable)
270 ComponentResult result = noErr;
271 bool validateElement = true;
273 switch (inID) {
274 case kAudioUnitProperty_MakeConnection:
275 require(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Global, InvalidScope);
276 outDataSize = sizeof(AudioUnitConnection);
277 outWritable = true;
278 break;
281 case kAudioUnitProperty_SetRenderCallback:
282 require(AudioUnitAPIVersion() > 1, InvalidProperty);
283 require(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Global, InvalidScope);
284 outDataSize = sizeof(AURenderCallbackStruct);
285 outWritable = true;
286 break;
289 case kAudioUnitProperty_FastDispatch:
290 require(inScope == kAudioUnitScope_Global, InvalidScope);
291 outDataSize = sizeof(void *);
292 outWritable = false;
293 validateElement = false;
294 break;
296 case kAudioUnitProperty_StreamFormat:
297 outDataSize = sizeof(CAStreamBasicDescription);
298 outWritable = IsStreamFormatWritable(inScope, inElement);
299 break;
301 case kAudioUnitProperty_SampleRate:
302 outDataSize = sizeof(Float64);
303 outWritable = IsStreamFormatWritable(inScope, inElement);
304 break;
306 case kAudioUnitProperty_ClassInfo:
307 require(inScope == kAudioUnitScope_Global, InvalidScope);
308 outDataSize = sizeof(CFPropertyListRef);
309 outWritable = true;
310 break;
312 case kAudioUnitProperty_FactoryPresets:
313 require(inScope == kAudioUnitScope_Global, InvalidScope);
314 result = GetPresets(NULL);
315 if (!result) {
316 outDataSize = sizeof(CFArrayRef);
317 outWritable = false;
319 break;
321 case kAudioUnitProperty_PresentPreset:
322 #ifndef __LP64__
323 case kAudioUnitProperty_CurrentPreset:
324 #endif
325 require(inScope == kAudioUnitScope_Global, InvalidScope);
326 outDataSize = sizeof(AUPreset);
327 outWritable = true;
328 break;
330 case kAudioUnitProperty_ContextName:
331 require(inScope == kAudioUnitScope_Global, InvalidScope);
332 outDataSize = sizeof(CFStringRef);
333 outWritable = true;
334 break;
336 case kAudioUnitProperty_ElementName:
337 outDataSize = sizeof (CFStringRef);
338 outWritable = true;
339 break;
341 case kAudioUnitProperty_GetUIComponentList:
342 require(inScope == kAudioUnitScope_Global, InvalidScope);
343 outDataSize = GetNumCustomUIComponents();
344 if (outDataSize == 0)
345 goto InvalidProperty;
346 outDataSize *= sizeof (ComponentDescription);
348 outWritable = false;
349 break;
351 case kAudioUnitProperty_ParameterList:
353 UInt32 nparams = 0;
354 result = GetParameterList(inScope, NULL, nparams);
356 outDataSize = sizeof(AudioUnitParameterID) * nparams;
357 outWritable = false;
358 validateElement = false;
360 break;
362 case kAudioUnitProperty_ParameterInfo:
363 outDataSize = sizeof(AudioUnitParameterInfo);
364 outWritable = false;
365 validateElement = false;
366 break;
368 case kAudioUnitProperty_ParameterValueStrings:
369 result = GetParameterValueStrings(inScope, inElement, NULL);
370 if (result == noErr) {
371 outDataSize = sizeof(CFArrayRef);
372 outWritable = false;
373 validateElement = false;
375 break;
377 case kAudioUnitProperty_ElementCount:
378 outDataSize = sizeof(UInt32);
379 outWritable = BusCountWritable(inScope);
380 validateElement = false;
381 break;
383 case kAudioUnitProperty_Latency:
384 require(inScope == kAudioUnitScope_Global, InvalidScope);
385 outDataSize = sizeof(Float64);
386 outWritable = false;
387 break;
389 case kAudioUnitProperty_TailTime:
390 require(inScope == kAudioUnitScope_Global, InvalidScope);
391 if (SupportsTail()) {
392 outDataSize = sizeof(Float64);
393 outWritable = false;
394 } else
395 goto InvalidProperty;
396 break;
398 case kAudioUnitProperty_MaximumFramesPerSlice:
399 require(inScope == kAudioUnitScope_Global, InvalidScope);
400 outDataSize = sizeof(UInt32);
401 outWritable = true;
402 break;
404 case kAudioUnitProperty_LastRenderError:
405 require(inScope == kAudioUnitScope_Global, InvalidScope);
406 outDataSize = sizeof(OSStatus);
407 outWritable = false;
408 break;
410 #if 0
411 case kAudioUnitProperty_SetExternalBuffer:
412 require(inScope == kAudioUnitScope_Global, InvalidScope);
413 outDataSize = sizeof(AudioUnitExternalBuffer);
414 outWritable = true;
415 break;
416 #endif
418 case kAudioUnitProperty_SupportedNumChannels:
420 require(inScope == kAudioUnitScope_Global, InvalidScope);
421 UInt32 num = SupportedNumChannels (NULL);
422 if (num) {
423 outDataSize = sizeof (AUChannelInfo) * num;
424 result = noErr;
425 } else
426 goto InvalidProperty;
427 outWritable = false;
428 break;
431 case kAudioUnitProperty_SupportedChannelLayoutTags:
433 UInt32 numLayouts = GetChannelLayoutTags(inScope, inElement, NULL);
434 if (numLayouts) {
435 outDataSize = numLayouts * sizeof(AudioChannelLayoutTag);
436 result = noErr;
437 } else
438 goto InvalidProperty;
439 outWritable = false;
440 validateElement = false; //already done it
441 break;
444 case kAudioUnitProperty_AudioChannelLayout:
446 outWritable = false;
447 outDataSize = GetAudioChannelLayout(inScope, inElement, NULL, outWritable);
448 if (outDataSize) {
449 result = noErr;
450 } else {
451 if (GetChannelLayoutTags(inScope, inElement, NULL) == 0)
452 goto InvalidProperty;
453 else
454 result = kAudioUnitErr_InvalidPropertyValue;
456 validateElement = false; //already done it
457 break;
460 case kAudioUnitProperty_HostCallbacks:
461 require(inScope == kAudioUnitScope_Global, InvalidScope);
462 outDataSize = sizeof (HostCallbackInfo);
463 outWritable = true;
464 break;
466 case kAudioUnitProperty_IconLocation:
467 require(inScope == kAudioUnitScope_Global, InvalidScope);
468 outWritable = false;
469 if (!HasIcon())
470 goto InvalidProperty;
471 outDataSize = sizeof(CFURLRef);
472 break;
474 default:
475 result = GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable);
476 validateElement = false;
477 break;
480 if (result == noErr && validateElement) {
481 require(GetElement(inScope, inElement) != NULL, InvalidElement);
484 return result;
485 InvalidProperty:
486 return kAudioUnitErr_InvalidProperty;
487 InvalidScope:
488 return kAudioUnitErr_InvalidScope;
489 InvalidElement:
490 return kAudioUnitErr_InvalidElement;
493 //_____________________________________________________________________________
495 ComponentResult AUBase::DispatchGetProperty( AudioUnitPropertyID inID,
496 AudioUnitScope inScope,
497 AudioUnitElement inElement,
498 void * outData)
500 // NOTE: We're currently only called from AUBase::ComponentEntryDispatch, which
501 // calls DispatchGetPropertyInfo first, which performs validation of the scope/element,
502 // and ensures that the outData buffer is non-null and large enough.
503 ComponentResult result = noErr;
505 switch (inID) {
506 case kAudioUnitProperty_FastDispatch:
507 switch (inElement) {
508 case kAudioUnitGetParameterSelect:
509 *(AudioUnitGetParameterProc *)outData = (AudioUnitGetParameterProc)AudioUnitBaseGetParameter;
510 break;
511 case kAudioUnitSetParameterSelect:
512 *(AudioUnitSetParameterProc *)outData = (AudioUnitSetParameterProc)AudioUnitBaseSetParameter;
513 break;
514 case kAudioUnitRenderSelect:
515 if (AudioUnitAPIVersion() > 1)
516 *(AudioUnitRenderProc *)outData = (AudioUnitRenderProc)AudioUnitBaseRender;
517 else result = kAudioUnitErr_InvalidElement;
518 break;
519 default:
520 result = GetProperty(inID, inScope, inElement, outData);
521 break;
523 break;
525 case kAudioUnitProperty_StreamFormat:
526 *(CAStreamBasicDescription *)outData = GetStreamFormat(inScope, inElement);
527 break;
529 case kAudioUnitProperty_SampleRate:
530 *(Float64 *)outData = GetStreamFormat(inScope, inElement).mSampleRate;
531 break;
533 case kAudioUnitProperty_ParameterList:
535 UInt32 nparams = 0;
536 result = GetParameterList(inScope, (AudioUnitParameterID *)outData, nparams);
538 break;
540 case kAudioUnitProperty_ParameterInfo:
541 result = GetParameterInfo(inScope, inElement, *(AudioUnitParameterInfo *)outData);
542 break;
544 case kAudioUnitProperty_ClassInfo:
546 *(CFPropertyListRef *)outData = NULL;
547 result = SaveState((CFPropertyListRef *)outData);
549 break;
551 case kAudioUnitProperty_ParameterValueStrings:
552 result = GetParameterValueStrings(inScope, inElement, (CFArrayRef *)outData);
553 break;
555 case kAudioUnitProperty_FactoryPresets:
557 *(CFArrayRef *)outData = NULL;
558 result = GetPresets ((CFArrayRef *)outData);
560 break;
562 case kAudioUnitProperty_PresentPreset:
563 #ifndef __LP64__
564 case kAudioUnitProperty_CurrentPreset:
565 #endif
567 *(AUPreset *)outData = mCurrentPreset;
569 // retain current string (as client owns a reference to it and will release it)
570 if (inID == kAudioUnitProperty_PresentPreset && mCurrentPreset.presetName)
571 CFRetain (mCurrentPreset.presetName);
573 result = noErr;
575 break;
577 case kAudioUnitProperty_ContextName:
578 *(CFStringRef *)outData = mContextName;
579 if (mContextName) {
580 CFRetain(mContextName);
581 // retain CFString (if exists) since client will be responsible for its release
582 result = noErr;
583 } else {
584 result = kAudioUnitErr_InvalidPropertyValue;
586 break;
588 case kAudioUnitProperty_ElementName:
590 AUElement * element = GetElement(inScope, inElement);
591 if (element->HasName()) {
592 *(CFStringRef *)outData = element->GetName();
593 CFRetain (element->GetName());
594 result = noErr;
595 } else
596 result = kAudioUnitErr_InvalidPropertyValue;
598 break;
600 case kAudioUnitProperty_ElementCount:
601 *(UInt32 *)outData = GetScope(inScope).GetNumberOfElements();
602 break;
604 case kAudioUnitProperty_Latency:
605 *(Float64 *)outData = GetLatency();
606 break;
608 case kAudioUnitProperty_TailTime:
609 if (SupportsTail())
610 *(Float64 *)outData = GetTailTime();
611 else
612 result = kAudioUnitErr_InvalidProperty;
613 break;
615 case kAudioUnitProperty_MaximumFramesPerSlice:
616 *(UInt32 *)outData = mMaxFramesPerSlice;
617 break;
619 case kAudioUnitProperty_LastRenderError:
620 *(OSStatus *)outData = mLastRenderError;
621 mLastRenderError = 0;
622 break;
624 case kAudioUnitProperty_SupportedNumChannels:
626 const AUChannelInfo* infoPtr;
627 UInt32 num = SupportedNumChannels (&infoPtr);
628 memcpy (outData, infoPtr, num * sizeof (AUChannelInfo));
630 break;
632 case kAudioUnitProperty_SupportedChannelLayoutTags:
634 AudioChannelLayoutTag* ptr = outData ? static_cast<AudioChannelLayoutTag*>(outData) : NULL;
635 UInt32 numLayouts = GetChannelLayoutTags (inScope, inElement, ptr);
636 if (numLayouts == 0)
637 result = kAudioUnitErr_InvalidProperty;
639 break;
641 case kAudioUnitProperty_AudioChannelLayout:
643 AudioChannelLayout* ptr = outData ? static_cast<AudioChannelLayout*>(outData) : NULL;
644 Boolean writable;
645 UInt32 dataSize = GetAudioChannelLayout(inScope, inElement, ptr, writable);
646 if (!dataSize) {
647 result = kAudioUnitErr_InvalidProperty;
649 break;
652 case kAudioUnitProperty_HostCallbacks:
653 *(HostCallbackInfo *)outData = mHostCallbackInfo;
654 break;
656 case kAudioUnitProperty_GetUIComponentList:
657 GetUIComponentDescs ((ComponentDescription*)outData);
658 break;
660 case kAudioUnitProperty_IconLocation:
662 CFURLRef iconLocation = CopyIconLocation();
663 if (iconLocation) {
664 *(CFURLRef*)outData = iconLocation;
665 } else
666 result = kAudioUnitErr_InvalidProperty;
668 break;
670 default:
671 result = GetProperty(inID, inScope, inElement, outData);
672 break;
674 return result;
678 //_____________________________________________________________________________
680 ComponentResult AUBase::DispatchSetProperty( AudioUnitPropertyID inID,
681 AudioUnitScope inScope,
682 AudioUnitElement inElement,
683 const void * inData,
684 UInt32 inDataSize)
686 ComponentResult result = noErr;
688 switch (inID) {
689 case kAudioUnitProperty_MakeConnection:
690 require(inDataSize >= sizeof(AudioUnitConnection), InvalidPropertyValue);
692 AudioUnitConnection &connection = *(AudioUnitConnection *)inData;
693 //printf("Making connection AU %08lX output %ld => AU %08lX input %ld\n",
694 //connection.sourceAudioUnit, connection.sourceOutputNumber, GetComponentInstance(), connection.destInputNumber);
695 result = SetConnection(connection);
697 break;
700 case kAudioUnitProperty_SetRenderCallback:
702 require(inDataSize >= sizeof(AURenderCallbackStruct), InvalidPropertyValue);
703 require(AudioUnitAPIVersion() > 1, InvalidProperty);
704 AURenderCallbackStruct &callback = *(AURenderCallbackStruct*)inData;
705 result = SetInputCallback(kAudioUnitProperty_SetRenderCallback, inElement,
706 reinterpret_cast<ProcPtr>(callback.inputProc), callback.inputProcRefCon);
708 break;
710 case kAudioUnitProperty_ElementCount:
711 require(inDataSize == sizeof(UInt32), InvalidPropertyValue);
712 require(BusCountWritable(inScope), NotWritable);
713 result = SetBusCount(inScope, *(UInt32*)inData);
714 if (result == noErr) {
715 PropertyChanged(inID, inScope, inElement);
717 break;
719 case kAudioUnitProperty_MaximumFramesPerSlice:
720 require(inDataSize == sizeof(UInt32), InvalidPropertyValue);
721 result = CanSetMaxFrames();
722 if (result) return result;
723 SetMaxFramesPerSlice(*(UInt32 *)inData);
724 break;
726 case kAudioUnitProperty_StreamFormat:
728 if (inDataSize < 36) goto InvalidPropertyValue;
729 require(GetElement(inScope, inElement) != NULL, InvalidElement);
731 CAStreamBasicDescription newDesc;
732 // now we're going to be ultra conservative! because of discrepancies between
733 // sizes of this struct based on aligment padding inconsistencies
734 memset (&newDesc, 0, sizeof(newDesc));
735 memcpy (&newDesc, inData, 36);
737 require(ValidFormat(inScope, inElement, newDesc), InvalidFormat);
739 const CAStreamBasicDescription curDesc = GetStreamFormat(inScope, inElement);
741 if ( !(curDesc == newDesc) ) {
742 require(IsStreamFormatWritable(inScope, inElement), NotWritable);
743 result = ChangeStreamFormat(inScope, inElement, curDesc, newDesc);
746 break;
748 case kAudioUnitProperty_SampleRate:
750 require(inDataSize == sizeof(Float64), InvalidPropertyValue);
751 require(GetElement(inScope, inElement) != NULL, InvalidElement);
753 const CAStreamBasicDescription curDesc = GetStreamFormat(inScope, inElement);
754 CAStreamBasicDescription newDesc = curDesc;
755 newDesc.mSampleRate = *(Float64 *)inData;
757 require(ValidFormat(inScope, inElement, newDesc), InvalidFormat);
759 if ( !(curDesc == newDesc) ) {
760 require(IsStreamFormatWritable(inScope, inElement), NotWritable);
761 result = ChangeStreamFormat(inScope, inElement, curDesc, newDesc);
764 break;
766 case kAudioUnitProperty_AudioChannelLayout:
768 const AudioChannelLayout *layout = static_cast<const AudioChannelLayout *>(inData);
769 require(inDataSize >= offsetof(AudioChannelLayout, mChannelDescriptions) + layout->mNumberChannelDescriptions * sizeof(AudioChannelDescription), InvalidPropertyValue);
770 result = SetAudioChannelLayout(inScope, inElement, layout);
771 if (result == noErr)
772 PropertyChanged(inID, inScope, inElement);
773 break;
776 case kAudioUnitProperty_SetExternalBuffer:
777 require(inDataSize >= sizeof(AudioUnitExternalBuffer), InvalidPropertyValue);
778 require(IsInitialized(), Uninitialized);
780 AudioUnitExternalBuffer &buf = *(AudioUnitExternalBuffer*)inData;
781 if (intptr_t(buf.buffer) & 0x0F) result = paramErr;
782 else if (inScope == kAudioUnitScope_Input) {
783 AUInputElement *input = GetInput(inElement);
784 input->UseExternalBuffer(buf);
785 } else {
786 AUOutputElement *output = GetOutput(inElement);
787 output->UseExternalBuffer(buf);
790 break;
792 case kAudioUnitProperty_ClassInfo:
793 require(inDataSize == sizeof(CFPropertyListRef *), InvalidPropertyValue);
794 require(inScope == kAudioUnitScope_Global, InvalidScope);
795 result = RestoreState(*(CFPropertyListRef *)inData);
796 break;
798 case kAudioUnitProperty_PresentPreset:
799 #ifndef __LP64__
800 case kAudioUnitProperty_CurrentPreset:
801 #endif
803 require(inDataSize == sizeof(AUPreset), InvalidPropertyValue);
804 require(inScope == kAudioUnitScope_Global, InvalidScope);
805 AUPreset & newPreset = *(AUPreset *)inData;
807 if (newPreset.presetNumber >= 0)
809 result = NewFactoryPresetSet(newPreset);
810 // NewFactoryPresetSet SHOULD call SetAFactoryPreset if the preset is valid
811 // from its own list of preset number->name
812 if (!result)
813 PropertyChanged(inID, inScope, inElement);
815 else if (newPreset.presetName)
817 CFRelease (mCurrentPreset.presetName);
818 mCurrentPreset = newPreset;
819 CFRetain (mCurrentPreset.presetName);
820 PropertyChanged(inID, inScope, inElement);
822 else
823 result = kAudioUnitErr_InvalidPropertyValue;
825 break;
827 case kAudioUnitProperty_ContextName:
829 require(inDataSize == sizeof(CFStringRef), InvalidPropertyValue);
830 require(inScope == kAudioUnitScope_Global, InvalidScope);
831 CFStringRef inStr = *(CFStringRef *)inData;
832 if (mContextName) CFRelease(mContextName);
833 if (inStr) CFRetain(inStr);
834 mContextName = inStr;
835 PropertyChanged(inID, inScope, inElement);
837 break;
839 case kAudioUnitProperty_ElementName:
841 require(GetElement(inScope, inElement) != NULL, InvalidElement);
842 require(inDataSize == sizeof(CFStringRef), InvalidPropertyValue);
843 AUElement * element = GetScope(inScope).GetElement (inElement);
844 element->SetName (*(CFStringRef *)inData);
845 PropertyChanged(inID, inScope, inElement);
847 break;
849 case kAudioUnitProperty_HostCallbacks:
851 require(inScope == kAudioUnitScope_Global, InvalidScope);
852 UInt32 availSize = (inDataSize < sizeof (mHostCallbackInfo) ? inDataSize : sizeof (mHostCallbackInfo));
853 bool hasChanged = !memcmp (&mHostCallbackInfo, inData, availSize);
854 memset (&mHostCallbackInfo, 0, sizeof (mHostCallbackInfo));
855 memcpy (&mHostCallbackInfo, inData, availSize);
856 if (hasChanged)
857 PropertyChanged(inID, inScope, inElement);
858 break;
861 default:
862 result = SetProperty(inID, inScope, inElement, inData, inDataSize);
863 if (result == noErr)
864 PropertyChanged(inID, inScope, inElement);
866 break;
868 return result;
869 NotWritable:
870 return kAudioUnitErr_PropertyNotWritable;
871 InvalidFormat:
872 return kAudioUnitErr_FormatNotSupported;
873 Uninitialized:
874 return kAudioUnitErr_Uninitialized;
875 InvalidScope:
876 return kAudioUnitErr_InvalidScope;
877 InvalidProperty:
878 return kAudioUnitErr_InvalidProperty;
879 InvalidPropertyValue:
880 return kAudioUnitErr_InvalidPropertyValue;
881 InvalidElement:
882 return kAudioUnitErr_InvalidElement;
885 //_____________________________________________________________________________
887 ComponentResult AUBase::DispatchRemovePropertyValue (AudioUnitPropertyID inID,
888 AudioUnitScope inScope,
889 AudioUnitElement inElement)
891 ComponentResult result = noErr;
892 switch (inID)
894 case kAudioUnitProperty_AudioChannelLayout:
896 result = RemoveAudioChannelLayout(inScope, inElement);
897 if (result == noErr)
898 PropertyChanged(inID, inScope, inElement);
899 break;
902 case kAudioUnitProperty_ContextName:
903 if (mContextName) CFRelease(mContextName);
904 mContextName = NULL;
905 result = noErr;
906 break;
908 case kAudioUnitProperty_HostCallbacks:
910 require(inScope == kAudioUnitScope_Global, InvalidScope);
911 bool hasValue = false;
912 void* ptr = &mHostCallbackInfo;
913 for (unsigned int i = 0; i < sizeof (HostCallbackInfo); ++i) {
914 if (static_cast<char*>(ptr)[i]) {
915 hasValue = true;
916 break;
919 if (hasValue) {
920 memset (&mHostCallbackInfo, 0, sizeof (HostCallbackInfo));
921 PropertyChanged(inID, inScope, inElement);
923 break;
926 default:
927 result = RemovePropertyValue (inID, inScope, inElement);
928 break;
931 return result;
932 InvalidScope:
933 return kAudioUnitErr_InvalidScope;
936 //_____________________________________________________________________________
938 ComponentResult AUBase::GetPropertyInfo( AudioUnitPropertyID inID,
939 AudioUnitScope inScope,
940 AudioUnitElement inElement,
941 UInt32 & outDataSize,
942 Boolean & outWritable)
944 return kAudioUnitErr_InvalidProperty;
948 //_____________________________________________________________________________
950 ComponentResult AUBase::GetProperty( AudioUnitPropertyID inID,
951 AudioUnitScope inScope,
952 AudioUnitElement inElement,
953 void * outData)
955 return kAudioUnitErr_InvalidProperty;
959 //_____________________________________________________________________________
961 ComponentResult AUBase::SetProperty( AudioUnitPropertyID inID,
962 AudioUnitScope inScope,
963 AudioUnitElement inElement,
964 const void * inData,
965 UInt32 inDataSize)
967 return kAudioUnitErr_InvalidProperty;
970 //_____________________________________________________________________________
972 ComponentResult AUBase::RemovePropertyValue ( AudioUnitPropertyID inID,
973 AudioUnitScope inScope,
974 AudioUnitElement inElement)
976 return kAudioUnitErr_InvalidPropertyValue;
979 //_____________________________________________________________________________
981 ComponentResult AUBase::AddPropertyListener( AudioUnitPropertyID inID,
982 AudioUnitPropertyListenerProc inProc,
983 void * inProcRefCon)
985 PropertyListener pl;
987 pl.propertyID = inID;
988 pl.listenerProc = inProc;
989 pl.listenerRefCon = inProcRefCon;
991 if (mPropertyListeners.empty())
992 mPropertyListeners.reserve(32);
993 mPropertyListeners.push_back(pl);
995 return noErr;
998 //_____________________________________________________________________________
1000 ComponentResult AUBase::RemovePropertyListener( AudioUnitPropertyID inID,
1001 AudioUnitPropertyListenerProc inProc,
1002 void * inProcRefCon,
1003 bool refConSpecified)
1005 // iterate in reverse so that it's safe to erase in the middle of the vector
1006 for (int i = mPropertyListeners.size(); --i >=0; ) {
1007 PropertyListeners::iterator it = mPropertyListeners.begin() + i;
1008 if ((*it).propertyID == inID && (*it).listenerProc == inProc && (!refConSpecified || (*it).listenerRefCon == inProcRefCon))
1009 mPropertyListeners.erase(it);
1011 return noErr;
1014 //_____________________________________________________________________________
1016 void AUBase::PropertyChanged( AudioUnitPropertyID inID,
1017 AudioUnitScope inScope,
1018 AudioUnitElement inElement)
1020 for (PropertyListeners::iterator it = mPropertyListeners.begin(); it != mPropertyListeners.end(); ++it)
1021 if ((*it).propertyID == inID)
1022 ((*it).listenerProc)((*it).listenerRefCon, mComponentInstance, inID, inScope, inElement);
1025 //_____________________________________________________________________________
1027 ComponentResult AUBase::SetRenderNotification( ProcPtr inProc,
1028 void * inRefCon)
1030 if (inProc == NULL) {
1031 return paramErr;
1032 } else {
1033 mRenderCallbacks.deferred_add(RenderCallback(inProc, inRefCon));
1034 // this will do nothing if it's already in the list
1036 return noErr;
1039 //_____________________________________________________________________________
1041 ComponentResult AUBase::RemoveRenderNotification( ProcPtr inProc,
1042 void * inRefCon)
1044 mRenderCallbacks.deferred_remove(RenderCallback(inProc, inRefCon));
1045 return noErr; // error?
1048 //_____________________________________________________________________________
1050 ComponentResult AUBase::GetParameter( AudioUnitParameterID inID,
1051 AudioUnitScope inScope,
1052 AudioUnitElement inElement,
1053 AudioUnitParameterValue & outValue)
1055 if (inScope == kAudioUnitScope_Group) {
1056 return GetGroupParameter (inID, inElement, outValue);
1059 AUElement *elem = SafeGetElement(inScope, inElement);
1060 outValue = elem->GetParameter(inID);
1061 return noErr;
1065 //_____________________________________________________________________________
1067 ComponentResult AUBase::SetParameter( AudioUnitParameterID inID,
1068 AudioUnitScope inScope,
1069 AudioUnitElement inElement,
1070 AudioUnitParameterValue inValue,
1071 UInt32 inBufferOffsetInFrames)
1073 if (inScope == kAudioUnitScope_Group) {
1074 return SetGroupParameter (inID, inElement, inValue, inBufferOffsetInFrames);
1077 AUElement *elem = SafeGetElement(inScope, inElement);
1078 elem->SetParameter(inID, inValue);
1079 return noErr;
1082 //_____________________________________________________________________________
1084 ComponentResult AUBase::SetGroupParameter( AudioUnitParameterID inID,
1085 AudioUnitElement inElement,
1086 AudioUnitParameterValue inValue,
1087 UInt32 inBufferOffsetInFrames)
1089 return kAudioUnitErr_InvalidScope;
1092 //_____________________________________________________________________________
1094 ComponentResult AUBase::GetGroupParameter( AudioUnitParameterID inID,
1095 AudioUnitElement inElement,
1096 AudioUnitParameterValue & outValue)
1098 return kAudioUnitErr_InvalidScope;
1101 //_____________________________________________________________________________
1103 ComponentResult AUBase::ScheduleParameter ( const AudioUnitParameterEvent *inParameterEvent,
1104 UInt32 inNumEvents)
1106 for (UInt32 i = 0; i < inNumEvents; ++i)
1108 if (inParameterEvent[i].eventType == kParameterEvent_Immediate)
1110 SetParameter (inParameterEvent[i].parameter,
1111 inParameterEvent[i].scope,
1112 inParameterEvent[i].element,
1113 inParameterEvent[i].eventValues.immediate.value,
1114 inParameterEvent[i].eventValues.immediate.bufferOffset);
1116 mParamList.push_back (inParameterEvent[i]);
1119 return noErr;
1122 // ____________________________________________________________________________
1124 static bool SortParameterEventList(const AudioUnitParameterEvent &ev1, const AudioUnitParameterEvent &ev2 )
1126 int offset1 = ev1.eventType == kParameterEvent_Immediate ? ev1.eventValues.immediate.bufferOffset : ev1.eventValues.ramp.startBufferOffset;
1127 int offset2 = ev2.eventType == kParameterEvent_Immediate ? ev2.eventValues.immediate.bufferOffset : ev2.eventValues.ramp.startBufferOffset;
1129 if(offset1 < offset2) return true;
1130 return false;
1134 // ____________________________________________________________________________
1136 ComponentResult AUBase::ProcessForScheduledParams( ParameterEventList &inParamList,
1137 UInt32 inFramesToProcess,
1138 void *inUserData )
1140 ComponentResult result = noErr;
1142 int totalFramesToProcess = inFramesToProcess;
1144 int framesRemaining = totalFramesToProcess;
1146 unsigned int currentStartFrame = 0; // start of the whole buffer
1150 // sort the ParameterEventList by startBufferOffset
1151 std::sort(inParamList.begin(), inParamList.end(), SortParameterEventList);
1153 ParameterEventList::iterator iter = inParamList.begin();
1156 while(framesRemaining > 0 )
1158 // first of all, go through the ramped automation events and find out where the next
1159 // division of our whole buffer will be
1161 int currentEndFrame = totalFramesToProcess; // start out assuming we'll process all the way to
1162 // the end of the buffer
1164 iter = inParamList.begin();
1166 // find the next break point
1167 while(iter != inParamList.end() )
1169 AudioUnitParameterEvent &event = *iter;
1171 int offset = event.eventType == kParameterEvent_Immediate ? event.eventValues.immediate.bufferOffset : event.eventValues.ramp.startBufferOffset;
1173 if(offset > (int)currentStartFrame && offset < currentEndFrame )
1175 currentEndFrame = offset;
1176 break;
1179 // consider ramp end to be a possible choice (there may be gaps in the supplied ramp events)
1180 if(event.eventType == kParameterEvent_Ramped )
1182 offset = event.eventValues.ramp.startBufferOffset + event.eventValues.ramp.durationInFrames;
1184 if(offset > (int)currentStartFrame && offset < currentEndFrame )
1186 currentEndFrame = offset;
1190 iter++;
1193 int framesThisTime = currentEndFrame - currentStartFrame;
1195 // next, setup the parameter maps to be current for the ramp parameters active during
1196 // this time segment...
1198 for(ParameterEventList::iterator iter2 = inParamList.begin(); iter2 != inParamList.end(); iter2++ )
1200 AudioUnitParameterEvent &event = *iter2;
1202 bool eventFallsInSlice;
1205 if(event.eventType == kParameterEvent_Ramped)
1206 eventFallsInSlice = event.eventValues.ramp.startBufferOffset < currentEndFrame
1207 && event.eventValues.ramp.startBufferOffset + event.eventValues.ramp.durationInFrames > currentStartFrame;
1208 else /* kParameterEvent_Immediate */
1209 // actually, for the same parameter, there may be future immediate events which override this one,
1210 // but it's OK since the event list is sorted in time order, we're guaranteed to end up with the current one
1211 eventFallsInSlice = event.eventValues.immediate.bufferOffset <= currentStartFrame;
1213 if(eventFallsInSlice)
1215 AUElement *element = GetElement(event.scope, event.element );
1217 if(element) element->SetScheduledEvent( event.parameter,
1218 event,
1219 currentStartFrame,
1220 currentEndFrame - currentStartFrame );
1226 // Finally, actually do the processing for this slice.....
1228 result = ProcessScheduledSlice( inUserData,
1229 currentStartFrame,
1230 framesThisTime,
1231 inFramesToProcess );
1233 if(result != noErr) break;
1235 framesRemaining -= framesThisTime;
1236 currentStartFrame = currentEndFrame; // now start from where we left off last time
1239 return result;
1242 //_____________________________________________________________________________
1244 void AUBase::SetWantsRenderThreadID (bool inFlag)
1246 if (inFlag == mWantsRenderThreadID)
1247 return;
1249 mWantsRenderThreadID = inFlag;
1250 if (!mWantsRenderThreadID)
1251 mRenderThreadID = NULL;
1254 //_____________________________________________________________________________
1257 //_____________________________________________________________________________
1259 ComponentResult AUBase::DoRender( AudioUnitRenderActionFlags & ioActionFlags,
1260 const AudioTimeStamp & inTimeStamp,
1261 UInt32 inBusNumber,
1262 UInt32 inFramesToProcess,
1263 AudioBufferList & ioData)
1265 ComponentResult theError;
1266 RenderCallbackList::iterator rcit;
1268 DISABLE_DENORMALS
1270 try {
1271 if (!IsInitialized())
1272 COMPONENT_THROW(kAudioUnitErr_Uninitialized);
1274 check(mAudioUnitAPIVersion >= 2);
1276 if (inFramesToProcess > mMaxFramesPerSlice)
1277 COMPONENT_THROW(kAudioUnitErr_TooManyFramesToProcess);
1279 AUOutputElement *output = GetOutput(inBusNumber); // will throw if non-existant
1280 if (output->GetStreamFormat().NumberChannelStreams() != ioData.mNumberBuffers) {
1281 debug_string("DoRender: output stream format does not match ioData.mNumberBuffers");
1282 COMPONENT_THROW(paramErr);
1285 if (WantsRenderThreadID())
1287 #if TARGET_OS_MAC
1288 mRenderThreadID = pthread_self();
1289 #elif TARGET_OS_WIN32
1290 mRenderThreadID = GetCurrentThreadId();
1291 #endif
1294 mRenderCallbacks.update();
1295 AudioUnitRenderActionFlags flags = ioActionFlags | kAudioUnitRenderAction_PreRender;
1296 for (rcit = mRenderCallbacks.begin(); rcit != mRenderCallbacks.end(); ++rcit) {
1297 RenderCallback &rc = *rcit;
1298 (*(AURenderCallback)rc.mRenderNotify)(rc.mRenderNotifyRefCon,
1299 &flags,
1300 &inTimeStamp, inBusNumber, inFramesToProcess, &ioData);
1303 theError = DoRenderBus(ioActionFlags, inTimeStamp, inBusNumber, output, inFramesToProcess, ioData);
1305 flags = ioActionFlags | kAudioUnitRenderAction_PostRender;
1307 if (SetRenderError (theError)) {
1308 flags |= kAudioUnitRenderAction_PostRenderError; //commentd to compile under 10.4
1311 for (rcit = mRenderCallbacks.begin(); rcit != mRenderCallbacks.end(); ++rcit) {
1312 RenderCallback &rc = *rcit;
1313 (*(AURenderCallback)rc.mRenderNotify)(rc.mRenderNotifyRefCon,
1314 &flags,
1315 &inTimeStamp, inBusNumber, inFramesToProcess, &ioData);
1318 // The vector's being emptied
1319 // because these events should only apply to this Render cycle, so anything
1320 // left over is from a preceding cycle and should be dumped. New scheduled
1321 // parameters must be scheduled from the next pre-render callback.
1322 if (!mParamList.empty())
1323 mParamList.clear();
1326 catch (OSStatus err) {
1327 DebugMessageN1 (" from %s", GetLoggingString());
1328 theError = SetRenderError (err);
1331 RESTORE_DENORMALS
1333 return theError;
1336 //_____________________________________________________________________________
1338 ComponentResult AUBase::SetInputCallback( UInt32 inPropertyID,
1339 AudioUnitElement inElement,
1340 ProcPtr inProc,
1341 void * inRefCon)
1343 AUInputElement *input = GetInput(inElement); // may throw
1345 input->SetInputCallback(inProc, inRefCon);
1346 PropertyChanged(inPropertyID, kAudioUnitScope_Input, inElement);
1348 return noErr;
1351 //_____________________________________________________________________________
1353 ComponentResult AUBase::SetConnection( const AudioUnitConnection & inConnection)
1355 OSStatus err;
1356 AUInputElement *input = GetInput(inConnection.destInputNumber); // may throw
1358 if (inConnection.sourceAudioUnit) {
1359 // connecting, not disconnecting
1360 CAStreamBasicDescription sourceDesc;
1361 UInt32 size = sizeof(CAStreamBasicDescription);
1362 require_noerr(err = AudioUnitGetProperty(
1363 inConnection.sourceAudioUnit,
1364 kAudioUnitProperty_StreamFormat,
1365 kAudioUnitScope_Output,
1366 inConnection.sourceOutputNumber,
1367 &sourceDesc,
1368 &size), errexit);
1369 require_noerr(err = DispatchSetProperty (kAudioUnitProperty_StreamFormat,
1370 kAudioUnitScope_Input, inConnection.destInputNumber,
1371 &sourceDesc, sizeof(CAStreamBasicDescription)), errexit);
1373 input->SetConnection(inConnection);
1375 PropertyChanged(kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, inConnection.destInputNumber);
1376 return noErr;
1378 errexit:
1379 return err;
1382 //_____________________________________________________________________________
1384 UInt32 AUBase::SupportedNumChannels ( const AUChannelInfo** outInfo)
1386 return 0;
1389 //_____________________________________________________________________________
1391 bool AUBase::ValidFormat( AudioUnitScope inScope,
1392 AudioUnitElement inElement,
1393 const CAStreamBasicDescription & inNewFormat)
1395 return FormatIsCanonical(inNewFormat);
1398 //_____________________________________________________________________________
1400 bool AUBase::IsStreamFormatWritable( AudioUnitScope scope,
1401 AudioUnitElement element)
1403 switch (scope) {
1404 case kAudioUnitScope_Input:
1406 AUInputElement *input = GetInput(element);
1407 if (input->HasConnection()) return false; // can't write format when input comes from connection
1409 // ... fall ...
1410 case kAudioUnitScope_Output:
1411 return StreamFormatWritable(scope, element);
1413 //#warning "aliasing of global scope format should be pushed to subclasses"
1414 case kAudioUnitScope_Global:
1415 return StreamFormatWritable(kAudioUnitScope_Output, 0);
1417 return false;
1420 //_____________________________________________________________________________
1422 const CAStreamBasicDescription &
1423 AUBase::GetStreamFormat( AudioUnitScope inScope,
1424 AudioUnitElement inElement)
1426 //#warning "aliasing of global scope format should be pushed to subclasses"
1427 AUIOElement *element;
1429 switch (inScope) {
1430 case kAudioUnitScope_Input:
1431 element = Inputs().GetIOElement(inElement);
1432 break;
1433 case kAudioUnitScope_Output:
1434 element = Outputs().GetIOElement(inElement);
1435 break;
1436 case kAudioUnitScope_Global: // global stream description is an alias for that of output 0
1437 element = Outputs().GetIOElement(0);
1438 break;
1439 default:
1440 COMPONENT_THROW(kAudioUnitErr_InvalidScope);
1442 return element->GetStreamFormat();
1445 ComponentResult AUBase::SetBusCount( AudioUnitScope inScope,
1446 UInt32 inCount)
1448 GetScope(inScope).SetNumberOfElements(inCount);
1449 return noErr;
1452 //_____________________________________________________________________________
1454 ComponentResult AUBase::ChangeStreamFormat( AudioUnitScope inScope,
1455 AudioUnitElement inElement,
1456 const CAStreamBasicDescription & inPrevFormat,
1457 const CAStreamBasicDescription & inNewFormat)
1459 //#warning "aliasing of global scope format should be pushed to subclasses"
1460 AUIOElement *element;
1462 switch (inScope) {
1463 case kAudioUnitScope_Input:
1464 element = Inputs().GetIOElement(inElement);
1465 break;
1466 case kAudioUnitScope_Output:
1467 element = Outputs().GetIOElement(inElement);
1468 break;
1469 case kAudioUnitScope_Global:
1470 element = Outputs().GetIOElement(0);
1471 break;
1472 default:
1473 COMPONENT_THROW(kAudioUnitErr_InvalidScope);
1475 element->SetStreamFormat(inNewFormat);
1476 PropertyChanged(kAudioUnitProperty_StreamFormat, inScope, inElement);
1477 return noErr;
1480 UInt32 AUBase::GetChannelLayoutTags( AudioUnitScope inScope,
1481 AudioUnitElement inElement,
1482 AudioChannelLayoutTag * outLayoutTags)
1484 return GetIOElement(inScope, inElement)->GetChannelLayoutTags(outLayoutTags);
1487 UInt32 AUBase::GetAudioChannelLayout( AudioUnitScope scope,
1488 AudioUnitElement element,
1489 AudioChannelLayout * outLayoutPtr,
1490 Boolean & outWritable)
1492 AUIOElement * el = GetIOElement(scope, element);
1493 return el->GetAudioChannelLayout(outLayoutPtr, outWritable);
1496 OSStatus AUBase::RemoveAudioChannelLayout( AudioUnitScope inScope,
1497 AudioUnitElement inElement)
1499 OSStatus result = noErr;
1500 AUIOElement * el = GetIOElement(inScope, inElement);
1501 Boolean writable;
1502 if (el->GetAudioChannelLayout(NULL, writable)) {
1503 result = el->RemoveAudioChannelLayout();
1505 return result;
1508 OSStatus AUBase::SetAudioChannelLayout( AudioUnitScope inScope,
1509 AudioUnitElement inElement,
1510 const AudioChannelLayout * inLayout)
1512 AUIOElement* ioEl = GetIOElement (inScope, inElement);
1514 // the num channels of the layout HAS TO MATCH the current channels of the Element's stream format
1515 UInt32 currentChannels = ioEl->GetStreamFormat().NumberChannels();
1516 UInt32 numChannelsInLayout = CAAudioChannelLayout::NumberChannels(*inLayout);
1517 if (currentChannels != numChannelsInLayout)
1518 return kAudioUnitErr_InvalidPropertyValue;
1520 UInt32 numLayouts = GetChannelLayoutTags (inScope, inElement, NULL);
1521 if (numLayouts == 0)
1522 return kAudioUnitErr_InvalidProperty;
1523 AudioChannelLayoutTag *tags = (AudioChannelLayoutTag *)malloc (numLayouts * sizeof (AudioChannelLayoutTag));
1524 GetChannelLayoutTags (inScope, inElement, tags);
1525 bool foundTag = false;
1526 for (unsigned int i = 0; i < numLayouts; ++i) {
1527 if (tags[i] == inLayout->mChannelLayoutTag || tags[i] == kAudioChannelLayoutTag_UseChannelDescriptions) {
1528 foundTag = true;
1529 break;
1532 free(tags);
1534 if (foundTag == false)
1535 return kAudioUnitErr_InvalidPropertyValue;
1537 return ioEl->SetAudioChannelLayout(*inLayout);
1540 static void AddNumToDictionary (CFMutableDictionaryRef dict, CFStringRef key, SInt32 value)
1542 CFNumberRef num = CFNumberCreate (NULL, kCFNumberSInt32Type, &value);
1543 CFDictionarySetValue (dict, key, num);
1544 CFRelease (num);
1547 #define kCurrentSavedStateVersion 0
1549 ComponentResult AUBase::SaveState( CFPropertyListRef * outData)
1551 ComponentDescription desc;
1552 OSStatus result = GetComponentInfo((Component)GetComponentInstance(), &desc, NULL, NULL, NULL);
1553 if (result) return result;
1555 CFMutableDictionaryRef dict = CFDictionaryCreateMutable (NULL, 0,
1556 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1558 // first step -> save the version to the data ref
1559 SInt32 value = kCurrentSavedStateVersion;
1560 AddNumToDictionary (dict, kVersionString, value);
1562 // second step -> save the component type, subtype, manu to the data ref
1563 value = desc.componentType;
1564 AddNumToDictionary (dict, kTypeString, value);
1566 value = desc.componentSubType;
1567 AddNumToDictionary (dict, kSubtypeString, value);
1569 value = desc.componentManufacturer;
1570 AddNumToDictionary (dict, kManufacturerString, value);
1572 // fourth step -> save the state of all parameters on all scopes and elements
1573 CFMutableDataRef data = CFDataCreateMutable(NULL, 0);
1574 for (AudioUnitScope iscope = 0; iscope < 4; ++iscope) {
1575 if (iscope == kAudioUnitScope_Group)
1576 continue;
1577 AUScope &scope = GetScope(iscope);
1578 AudioUnitElement nElems = scope.GetNumberOfElements();
1579 for (AudioUnitElement ielem = 0; ielem < nElems; ++ielem) {
1580 AUElement *element = scope.GetElement(ielem);
1581 UInt32 nparams = element->GetNumberOfParameters();
1582 if (nparams > 0) {
1583 struct {
1584 UInt32 scope;
1585 UInt32 element;
1586 } hdr;
1588 hdr.scope = CFSwapInt32HostToBig(iscope);
1589 hdr.element = CFSwapInt32HostToBig(ielem);
1590 CFDataAppendBytes(data, (UInt8 *)&hdr, sizeof(hdr));
1592 element->SaveState(data);
1597 // save all this in the data section of the dictionary
1598 CFDictionarySetValue(dict, kDataString, data);
1599 CFRelease (data);
1601 //OK - now we're going to do some properties
1602 //save the preset name...
1603 CFDictionarySetValue (dict, kNameString, mCurrentPreset.presetName);
1605 // Does the unit support the RenderQuality property - if so, save it...
1606 value = 0;
1607 result = DispatchGetProperty (kAudioUnitProperty_RenderQuality,
1608 kAudioUnitScope_Global,
1610 &value);
1612 if (result == noErr) {
1613 AddNumToDictionary (dict, kRenderQualityString, value);
1616 // Does the unit support the CPULoad Quality property - if so, save it...
1617 Float32 cpuLoad;
1618 result = DispatchGetProperty (kAudioUnitProperty_CPULoad,
1619 kAudioUnitScope_Global,
1621 &cpuLoad);
1623 if (result == noErr) {
1624 CFNumberRef num = CFNumberCreate (NULL, kCFNumberFloatType, &cpuLoad);
1625 CFDictionarySetValue (dict, kCPULoadString, num);
1626 CFRelease (num);
1629 // Do we have any element names for any of our scopes?
1630 // first check to see if we have any names...
1631 bool foundName = false;
1632 for (AudioUnitScope i = 0; i < kNumScopes; ++i) {
1633 foundName = GetScope (i).HasElementWithName();
1634 if (foundName)
1635 break;
1637 // OK - we found a name away we go...
1638 if (foundName) {
1639 CFMutableDictionaryRef nameDict = CFDictionaryCreateMutable (NULL, 0,
1640 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1641 for (AudioUnitScope i = 0; i < kNumScopes; ++i) {
1642 GetScope (i).AddElementNamesToDict (nameDict);
1645 CFDictionarySetValue (dict, kElementNameString, nameDict);
1646 CFRelease (nameDict);
1649 // we're done!!!
1650 *outData = dict;
1652 return noErr;
1655 //_____________________________________________________________________________
1657 ComponentResult AUBase::RestoreState( CFPropertyListRef plist)
1659 if (CFGetTypeID(plist) != CFDictionaryGetTypeID()) return kAudioUnitErr_InvalidPropertyValue;
1661 ComponentDescription desc;
1662 OSStatus result = GetComponentInfo((Component)GetComponentInstance(), &desc, NULL, NULL, NULL);
1663 if (result) return result;
1665 CFDictionaryRef dict = static_cast<CFDictionaryRef>(plist);
1667 // zeroeth step - make sure the Part key is NOT present, as this method is used
1668 // to restore the GLOBAL state of the dictionary
1669 if (CFDictionaryContainsKey (dict, kPartString))
1670 return kAudioUnitErr_InvalidPropertyValue;
1672 // first step -> check the saved version in the data ref
1673 // at this point we're only dealing with version==0
1674 CFNumberRef cfnum = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue (dict, kVersionString));
1675 if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue;
1676 SInt32 value;
1677 CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value);
1678 if (value != kCurrentSavedStateVersion) return kAudioUnitErr_InvalidPropertyValue;
1680 // second step -> check that this data belongs to this kind of audio unit
1681 // by checking the component subtype and manuID
1682 // We're not checking the type, since there may be different versions (effect, format-converter, offline)
1683 // of essentially the same AU
1684 cfnum = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue (dict, kSubtypeString));
1685 if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue;
1686 CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value);
1687 if (UInt32(value) != desc.componentSubType) return kAudioUnitErr_InvalidPropertyValue;
1689 cfnum = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue (dict, kManufacturerString));
1690 if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue;
1691 CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value);
1692 if (UInt32(value) != desc.componentManufacturer) return kAudioUnitErr_InvalidPropertyValue;
1694 // fourth step -> restore the state of all of the parameters for each scope and element
1695 CFDataRef data = reinterpret_cast<CFDataRef>(CFDictionaryGetValue (dict, kDataString));
1696 if (data != NULL)
1698 const UInt8 *p, *pend;
1700 p = CFDataGetBytePtr(data);
1701 pend = p + CFDataGetLength(data);
1703 // we have a zero length data, which may just mean there were no parameters to save!
1704 // if (p >= pend) return noErr;
1706 while (p < pend) {
1707 struct {
1708 UInt32 scope;
1709 UInt32 element;
1710 } hdr;
1712 hdr.scope = CFSwapInt32BigToHost(*(UInt32 *)p); p += sizeof(UInt32);
1713 hdr.element = CFSwapInt32BigToHost(*(UInt32 *)p); p += sizeof(UInt32);
1715 AUScope &scope = GetScope(hdr.scope);
1716 AUElement *element = scope.GetElement(hdr.element);
1717 // $$$ order of operations issue: what if the element does not yet exist?
1718 // then we just break out of the loop
1719 if (!element)
1720 break;
1721 p = element->RestoreState(p);
1724 #if 0
1725 // we're not going to do anything here - we'll assume that the subclass can handle this case
1726 // if translating from either VST or MAS formats
1727 // If an AU want this method to validate that the preset is valid and contains either/or a vstdata
1728 // or msdata key, then this can be done here as below for the vstdata example.
1729 else // no data key
1731 if (!CFDictionaryContainsKey (dict, kVSTDataString))
1732 return kAudioUnitErr_InvalidPropertyValue;
1734 #endif
1736 //OK - now we're going to do some properties
1737 //restore the preset name...
1738 CFStringRef name = reinterpret_cast<CFStringRef>(CFDictionaryGetValue (dict, kNameString));
1739 if (mCurrentPreset.presetName) CFRelease (mCurrentPreset.presetName);
1740 if (name)
1742 mCurrentPreset.presetName = name;
1743 mCurrentPreset.presetNumber = -1;
1745 else { // no name entry make the default one
1746 mCurrentPreset.presetName = kUntitledString;
1747 mCurrentPreset.presetNumber = -1;
1750 CFRetain (mCurrentPreset.presetName);
1751 #ifndef __LP64__
1752 PropertyChanged(kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global, 0);
1753 #endif
1754 PropertyChanged(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
1756 // Does the dict contain render quality information?
1757 if (CFDictionaryGetValueIfPresent (dict, kRenderQualityString, reinterpret_cast<const void**>(&cfnum)))
1759 CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value);
1760 DispatchSetProperty (kAudioUnitProperty_RenderQuality,
1761 kAudioUnitScope_Global,
1763 &value,
1764 sizeof(value));
1767 // Does the unit support the CPULoad Quality property - if so, save it...
1768 if (CFDictionaryGetValueIfPresent (dict, kCPULoadString, reinterpret_cast<const void**>(&cfnum)))
1770 Float32 floatValue;
1771 CFNumberGetValue (cfnum, kCFNumberFloatType, &floatValue);
1772 DispatchSetProperty (kAudioUnitProperty_CPULoad,
1773 kAudioUnitScope_Global,
1775 &floatValue,
1776 sizeof(floatValue));
1779 // Do we have any element names for any of our scopes?
1780 CFDictionaryRef nameDict;
1781 if (CFDictionaryGetValueIfPresent (dict, kElementNameString, reinterpret_cast<const void**>(&nameDict)))
1783 char string[64];
1784 for (int i = 0; i < kNumScopes; ++i)
1786 sprintf (string, "%d", i);
1787 CFStringRef key = CFStringCreateWithCString (NULL, string, kCFStringEncodingASCII);
1788 CFDictionaryRef elementDict;
1789 if (CFDictionaryGetValueIfPresent (nameDict, key, reinterpret_cast<const void**>(&elementDict)))
1791 bool didAddElements = GetScope (i).RestoreElementNames (elementDict);
1792 if (didAddElements)
1793 PropertyChanged (kAudioUnitProperty_ElementCount, i, 0);
1795 CFRelease (key);
1799 return noErr;
1802 ComponentResult AUBase::GetPresets ( CFArrayRef * outData) const
1804 return kAudioUnitErr_InvalidProperty;
1807 OSStatus AUBase::NewFactoryPresetSet (const AUPreset & inNewFactoryPreset)
1809 return kAudioUnitErr_InvalidProperty;
1812 // set the default preset for the unit -> the number of the preset MUST be >= 0
1813 // and the name should be valid, or the preset WON'T take
1814 bool AUBase::SetAFactoryPresetAsCurrent (const AUPreset & inPreset)
1816 if (inPreset.presetNumber < 0 || inPreset.presetName == NULL) return false;
1817 CFRelease (mCurrentPreset.presetName);
1818 mCurrentPreset = inPreset;
1819 CFRetain (mCurrentPreset.presetName);
1820 return true;
1823 int AUBase::GetNumCustomUIComponents ()
1825 return 0;
1828 void AUBase::GetUIComponentDescs (ComponentDescription* inDescArray) {}
1830 bool AUBase::HasIcon ()
1832 CFURLRef url = CopyIconLocation();
1833 if (url) {
1834 CFRelease (url);
1835 return true;
1837 return false;
1840 CFURLRef AUBase::CopyIconLocation ()
1842 return NULL;
1845 //_____________________________________________________________________________
1847 ComponentResult AUBase::GetParameterList( AudioUnitScope inScope,
1848 AudioUnitParameterID * outParameterList,
1849 UInt32 & outNumParameters)
1851 AUScope &scope = GetScope(inScope);
1852 AUElement *elementWithMostParameters = NULL;
1853 UInt32 maxNumParams = 0;
1855 int nElems = scope.GetNumberOfElements();
1856 for (int ielem = 0; ielem < nElems; ++ielem) {
1857 AUElement *element = scope.GetElement(ielem);
1858 UInt32 nParams = element->GetNumberOfParameters();
1859 if (nParams > maxNumParams) {
1860 maxNumParams = nParams;
1861 elementWithMostParameters = element;
1865 if (outParameterList != NULL && elementWithMostParameters != NULL)
1866 elementWithMostParameters->GetParameterList(outParameterList);
1868 outNumParameters = maxNumParams;
1869 return noErr;
1872 //_____________________________________________________________________________
1874 ComponentResult AUBase::GetParameterInfo( AudioUnitScope inScope,
1875 AudioUnitParameterID inParameterID,
1876 AudioUnitParameterInfo &outParameterInfo )
1878 return kAudioUnitErr_InvalidParameter;
1881 //_____________________________________________________________________________
1883 ComponentResult AUBase::GetParameterValueStrings(AudioUnitScope inScope,
1884 AudioUnitParameterID inParameterID,
1885 CFArrayRef * outStrings)
1887 return kAudioUnitErr_InvalidProperty; // this AU doesn't know how
1890 //_____________________________________________________________________________
1892 void AUBase::SetNumberOfElements( AudioUnitScope inScope,
1893 UInt32 numElements)
1895 if (inScope == kAudioUnitScope_Global && numElements != 1)
1896 COMPONENT_THROW(kAudioUnitErr_InvalidScope);
1898 GetScope(inScope).SetNumberOfElements(numElements);
1901 //_____________________________________________________________________________
1903 AUElement * AUBase::CreateElement( AudioUnitScope scope,
1904 AudioUnitElement element)
1906 switch (scope) {
1907 case kAudioUnitScope_Global:
1908 return new AUGlobalElement(this);
1909 case kAudioUnitScope_Input:
1910 return new AUInputElement(this);
1911 case kAudioUnitScope_Output:
1912 return new AUOutputElement(this);
1913 case kAudioUnitScope_Group:
1914 return new AUGroupElement(this);
1915 case kAudioUnitScope_Part:
1916 return new AUPartElement(this);
1918 COMPONENT_THROW(kAudioUnitErr_InvalidScope);
1920 return NULL; // get rid of compiler warning
1923 //_____________________________________________________________________________
1925 bool AUBase::FormatIsCanonical( const CAStreamBasicDescription &f)
1927 return (f.mFormatID == kAudioFormatLinearPCM
1928 && f.mFramesPerPacket == 1
1929 && f.mBytesPerPacket == f.mBytesPerFrame
1930 // && f.mChannelsPerFrame >= 0 -- this is always true since it's unsigned
1931 // so far, it's a valid PCM format
1932 && (f.mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0
1933 && ((f.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0) == (mAudioUnitAPIVersion == 1)
1934 #if TARGET_RT_BIG_ENDIAN
1935 && (f.mFormatFlags & kLinearPCMFormatFlagIsBigEndian) != 0
1936 #else
1937 && (f.mFormatFlags & kLinearPCMFormatFlagIsBigEndian) == 0
1938 #endif
1939 && f.mBitsPerChannel == 32
1940 && f.mBytesPerFrame == f.NumberInterleavedChannels() * sizeof(AudioSampleType)
1944 //_____________________________________________________________________________
1946 void AUBase::MakeCanonicalFormat( CAStreamBasicDescription & f,
1947 int nChannels)
1949 f.SetCanonical(nChannels, mAudioUnitAPIVersion < 2); // interleaved for v1, non for v2
1950 f.mSampleRate = 0.0;
1953 const Float64 AUBase::kNoLastRenderedSampleTime = -1.;
1955 //_____________________________________________________________________________
1957 char *StringForOSType (OSType t, char *writeLocation)
1959 char *p = writeLocation;
1960 unsigned char str[4], *q = str;
1961 *(UInt32 *)str = CFSwapInt32HostToBig(t);
1963 bool hasNonPrint = false;
1964 for (int i = 0; i < 4; ++i) {
1965 if (!(isprint(*q) && *q != '\\')) {
1966 hasNonPrint = true;
1967 break;
1971 if (hasNonPrint)
1972 p += sprintf (p, "0x");
1974 for (int i = 0; i < 4; ++i) {
1975 if (hasNonPrint) {
1976 p += sprintf(p, "%02X", *q++);
1977 } else {
1978 *p++ = *q++;
1981 *p = '\0';
1982 return writeLocation;
1985 char* AUBase::GetLoggingString () const
1987 if (mLogString) return mLogString;
1989 ComponentDescription desc;
1990 OSStatus err = GetComponentInfo((Component)GetComponentInstance(), &desc, NULL, NULL, NULL);
1991 if (err) return "";
1993 const_cast<AUBase*>(this)->mLogString = new char[128];
1994 char str[24];
1995 char str1[24];
1996 char str2[24];
1997 sprintf (const_cast<AUBase*>(this)->mLogString, "AU (%p): \'%s - %s - %s\'",
1998 GetComponentInstance(),
1999 StringForOSType(desc.componentType, str),
2000 StringForOSType(desc.componentSubType, str1),
2001 StringForOSType(desc.componentManufacturer, str2));
2003 return mLogString;