1 /*
2 Copyright (C) 2016 Apple Inc. All Rights Reserved.
3 See LICENSE.txt for this sample’s licensing information
4
5 Abstract:
6 Part of Core Audio AUBase Classes
7 */
8
9 #include "AUScopeElement.h"
10 #include <AudioUnit/AudioUnitProperties.h>
11 #include "AUBase.h"
12
13 //_____________________________________________________________________________
14 //
15 // By default, parameterIDs may be arbitrarily spaced, and an STL map
16 // will be used for access. Calling UseIndexedParameters() will
17 // instead use an STL vector for faster indexed access.
18 // This assumes the paramIDs are numbered 0.....inNumberOfParameters-1
19 // Call this before defining/adding any parameters with SetParameter()
20 //
UseIndexedParameters(int inNumberOfParameters)21 void AUElement::UseIndexedParameters(int inNumberOfParameters)
22 {
23 mIndexedParameters.resize (inNumberOfParameters);
24 mUseIndexedParameters = true;
25 }
26
27 //_____________________________________________________________________________
28 //
29 // Helper method.
30 // returns the ParameterMapEvent object associated with the paramID
31 //
GetParamEvent(AudioUnitParameterID paramID)32 inline ParameterMapEvent& AUElement::GetParamEvent(AudioUnitParameterID paramID)
33 {
34 ParameterMapEvent *event;
35
36 if(mUseIndexedParameters)
37 {
38 if(paramID >= mIndexedParameters.size() )
39 COMPONENT_THROW(kAudioUnitErr_InvalidParameter);
40
41 event = &mIndexedParameters[paramID];
42 }
43 else
44 {
45 ParameterMap::iterator i = mParameters.find(paramID);
46 if (i == mParameters.end())
47 COMPONENT_THROW(kAudioUnitErr_InvalidParameter);
48
49 event = &(*i).second;
50 }
51
52 return *event;
53 }
54
55 //_____________________________________________________________________________
56 //
57 // Helper method.
58 // returns whether the specified paramID is known to the element
59 //
HasParameterID(AudioUnitParameterID paramID) const60 bool AUElement::HasParameterID (AudioUnitParameterID paramID) const
61 {
62 if(mUseIndexedParameters)
63 {
64 if(paramID >= mIndexedParameters.size() )
65 return false;
66
67 return true;
68 }
69
70 ParameterMap::const_iterator i = mParameters.find(paramID);
71 if (i == mParameters.end())
72 return false;
73
74 return true;
75 }
76
77 //_____________________________________________________________________________
78 //
79 // caller assumes that this is actually an immediate parameter
80 //
GetParameter(AudioUnitParameterID paramID)81 AudioUnitParameterValue AUElement::GetParameter(AudioUnitParameterID paramID)
82 {
83 ParameterMapEvent &event = GetParamEvent(paramID);
84
85 return event.GetValue();
86 }
87
88
89 //_____________________________________________________________________________
90 //
GetRampSliceStartEnd(AudioUnitParameterID paramID,AudioUnitParameterValue & outStartValue,AudioUnitParameterValue & outEndValue,AudioUnitParameterValue & outValuePerFrameDelta)91 void AUElement::GetRampSliceStartEnd( AudioUnitParameterID paramID,
92 AudioUnitParameterValue & outStartValue,
93 AudioUnitParameterValue & outEndValue,
94 AudioUnitParameterValue & outValuePerFrameDelta )
95
96 {
97 ParameterMapEvent &event = GetParamEvent(paramID);
98
99 // works even if the value is constant (immediate parameter value)
100 event.GetRampSliceStartEnd(outStartValue, outEndValue, outValuePerFrameDelta );
101 }
102
103 //_____________________________________________________________________________
104 //
GetEndValue(AudioUnitParameterID paramID)105 AudioUnitParameterValue AUElement::GetEndValue( AudioUnitParameterID paramID)
106
107 {
108 ParameterMapEvent &event = GetParamEvent(paramID);
109
110 // works even if the value is constant (immediate parameter value)
111 return event.GetEndValue();
112 }
113
114 //_____________________________________________________________________________
115 //
SetParameter(AudioUnitParameterID paramID,AudioUnitParameterValue inValue,bool okWhenInitialized)116 void AUElement::SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue inValue, bool okWhenInitialized)
117 {
118 if(mUseIndexedParameters)
119 {
120 ParameterMapEvent &event = GetParamEvent(paramID);
121 event.SetValue(inValue);
122 }
123 else
124 {
125 ParameterMap::iterator i = mParameters.find(paramID);
126
127 if (i == mParameters.end())
128 {
129 if (mAudioUnit->IsInitialized() && !okWhenInitialized) {
130 // The AU should not be creating new parameters once initialized.
131 // If a client tries to set an undefined parameter, we could throw as follows,
132 // but this might cause a regression. So it is better to just fail silently.
133 // COMPONENT_THROW(kAudioUnitErr_InvalidParameter);
134 #if DEBUG
135 fprintf(stderr, "WARNING: %s SetParameter for undefined param ID %d while initialized. Ignoring..\n",
136 mAudioUnit->GetLoggingString(), (int)paramID);
137 #endif
138 } else {
139 // create new entry in map for the paramID (only happens first time)
140 ParameterMapEvent event(inValue);
141 mParameters[paramID] = event;
142 }
143 }
144 else
145 {
146 // paramID already exists in map so simply change its value
147 ParameterMapEvent &event = (*i).second;
148 event.SetValue(inValue);
149 }
150 }
151 }
152
153 //_____________________________________________________________________________
154 //
SetScheduledEvent(AudioUnitParameterID paramID,const AudioUnitParameterEvent & inEvent,UInt32 inSliceOffsetInBuffer,UInt32 inSliceDurationFrames,bool okWhenInitialized)155 void AUElement::SetScheduledEvent( AudioUnitParameterID paramID,
156 const AudioUnitParameterEvent &inEvent,
157 UInt32 inSliceOffsetInBuffer,
158 UInt32 inSliceDurationFrames,
159 bool okWhenInitialized )
160 {
161 if(mUseIndexedParameters)
162 {
163 ParameterMapEvent &event = GetParamEvent(paramID);
164 event.SetScheduledEvent(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames );
165 }
166 else
167 {
168 ParameterMap::iterator i = mParameters.find(paramID);
169
170 if (i == mParameters.end())
171 {
172 if (mAudioUnit->IsInitialized() && !okWhenInitialized) {
173 // The AU should not be creating new parameters once initialized.
174 // If a client tries to set an undefined parameter, we could throw as follows,
175 // but this might cause a regression. So it is better to just fail silently.
176 // COMPONENT_THROW(kAudioUnitErr_InvalidParameter);
177 #if DEBUG
178 fprintf(stderr, "WARNING: %s SetScheduledEvent for undefined param ID %d while initialized. Ignoring..\n",
179 mAudioUnit->GetLoggingString(), (int)paramID);
180 #endif
181 } else {
182 // create new entry in map for the paramID (only happens first time)
183 ParameterMapEvent event(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames);
184 mParameters[paramID] = event;
185 }
186 }
187 else
188 {
189 // paramID already exists in map so simply change its value
190 ParameterMapEvent &event = (*i).second;
191
192 event.SetScheduledEvent(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames );
193 }
194 }
195 }
196
197
198
199 //_____________________________________________________________________________
200 //
GetParameterList(AudioUnitParameterID * outList)201 void AUElement::GetParameterList(AudioUnitParameterID *outList)
202 {
203 if(mUseIndexedParameters)
204 {
205 UInt32 nparams = static_cast<UInt32>(mIndexedParameters.size());
206 for (UInt32 i = 0; i < nparams; i++ )
207 *outList++ = (AudioUnitParameterID)i;
208 }
209 else
210 {
211 for (ParameterMap::iterator i = mParameters.begin(); i != mParameters.end(); ++i)
212 *outList++ = (*i).first;
213 }
214 }
215
216 //_____________________________________________________________________________
217 //
SaveState(AudioUnitScope scope,CFMutableDataRef data)218 void AUElement::SaveState(AudioUnitScope scope, CFMutableDataRef data)
219 {
220 AudioUnitParameterInfo paramInfo;
221 CFIndex countOffset = CFDataGetLength(data);
222 UInt32 nparams, nOmitted = 0, theData;
223
224 if(mUseIndexedParameters)
225 {
226 nparams = static_cast<UInt32>(mIndexedParameters.size());
227 theData = CFSwapInt32HostToBig(nparams);
228 CFDataAppendBytes(data, (UInt8 *)&theData, sizeof(nparams));
229
230 for (UInt32 i = 0; i < nparams; i++)
231 {
232 struct {
233 UInt32 paramID;
234 //CFSwappedFloat32 value; crashes gcc3 PFE
235 UInt32 value; // really a big-endian float
236 } entry;
237
238 if (mAudioUnit->GetParameterInfo(scope, i, paramInfo) == noErr) {
239 if ((paramInfo.flags & kAudioUnitParameterFlag_CFNameRelease) && paramInfo.cfNameString)
240 CFRelease(paramInfo.cfNameString);
241 if (paramInfo.flags & kAudioUnitParameterFlag_OmitFromPresets) {
242 ++nOmitted;
243 continue;
244 }
245 }
246
247 entry.paramID = CFSwapInt32HostToBig(i);
248
249 AudioUnitParameterValue v = mIndexedParameters[i].GetValue();
250 entry.value = CFSwapInt32HostToBig(*(UInt32 *)&v );
251
252 CFDataAppendBytes(data, (UInt8 *)&entry, sizeof(entry));
253 }
254 }
255 else
256 {
257 nparams = static_cast<uint32_t>(mParameters.size());
258 theData = CFSwapInt32HostToBig(nparams);
259 CFDataAppendBytes(data, (UInt8 *)&theData, sizeof(nparams));
260
261 for (ParameterMap::iterator i = mParameters.begin(); i != mParameters.end(); ++i) {
262 struct {
263 UInt32 paramID;
264 //CFSwappedFloat32 value; crashes gcc3 PFE
265 UInt32 value; // really a big-endian float
266 } entry;
267
268 if (mAudioUnit->GetParameterInfo(scope, (*i).first, paramInfo) == noErr) {
269 if ((paramInfo.flags & kAudioUnitParameterFlag_CFNameRelease) && paramInfo.cfNameString)
270 CFRelease(paramInfo.cfNameString);
271 if (paramInfo.flags & kAudioUnitParameterFlag_OmitFromPresets) {
272 ++nOmitted;
273 continue;
274 }
275 }
276
277 entry.paramID = CFSwapInt32HostToBig((*i).first);
278
279 AudioUnitParameterValue v = (*i).second.GetValue();
280 entry.value = CFSwapInt32HostToBig(*(UInt32 *)&v );
281
282 CFDataAppendBytes(data, (UInt8 *)&entry, sizeof(entry));
283 }
284 }
285 if (nOmitted > 0) {
286 theData = CFSwapInt32HostToBig(nparams - nOmitted);
287 *(UInt32 *)(CFDataGetBytePtr(data) + countOffset) = theData;
288 }
289 }
290
291 //_____________________________________________________________________________
292 //
RestoreState(const UInt8 * state)293 const UInt8 * AUElement::RestoreState(const UInt8 *state)
294 {
295 union FloatInt32 { UInt32 i; AudioUnitParameterValue f; };
296 const UInt8 *p = state;
297 UInt32 nparams = CFSwapInt32BigToHost(*(UInt32 *)p);
298 p += sizeof(UInt32);
299
300 for (UInt32 i = 0; i < nparams; ++i) {
301 struct {
302 AudioUnitParameterID paramID;
303 AudioUnitParameterValue value;
304 } entry;
305
306 entry.paramID = CFSwapInt32BigToHost(*(UInt32 *)p);
307 p += sizeof(UInt32);
308 FloatInt32 temp;
309 temp.i = CFSwapInt32BigToHost(*(UInt32 *)p);
310 entry.value = temp.f;
311 p += sizeof(AudioUnitParameterValue);
312
313 SetParameter(entry.paramID, entry.value);
314 }
315 return p;
316 }
317
318 //_____________________________________________________________________________
319 //
SetName(CFStringRef inName)320 void AUElement::SetName (CFStringRef inName)
321 {
322 if (mElementName) CFRelease (mElementName);
323 mElementName = inName;
324 if (mElementName) CFRetain (mElementName);
325 }
326
327
328 //_____________________________________________________________________________
329 //
AUIOElement(AUBase * audioUnit)330 AUIOElement::AUIOElement(AUBase *audioUnit) :
331 AUElement(audioUnit),
332 mWillAllocate (true)
333 {
334 mStreamFormat = CAStreamBasicDescription(kAUDefaultSampleRate, 2, CAStreamBasicDescription::kPCMFormatFloat32, audioUnit->AudioUnitAPIVersion() == 1);
335 // stereo
336 // interleaved if API version 1, deinterleaved if version 2
337 }
338
339 //_____________________________________________________________________________
340 //
SetStreamFormat(const CAStreamBasicDescription & desc)341 OSStatus AUIOElement::SetStreamFormat(const CAStreamBasicDescription &desc)
342 {
343 mStreamFormat = desc;
344 return AUBase::noErr;
345 }
346
347 //_____________________________________________________________________________
348 // inFramesToAllocate == 0 implies the AudioUnit's max-frames-per-slice will be used
AllocateBuffer(UInt32 inFramesToAllocate)349 void AUIOElement::AllocateBuffer(UInt32 inFramesToAllocate)
350 {
351 if (GetAudioUnit()->HasBegunInitializing())
352 {
353 UInt32 framesToAllocate = inFramesToAllocate > 0 ? inFramesToAllocate : GetAudioUnit()->GetMaxFramesPerSlice();
354
355 // printf ("will allocate: %d\n", (int)((mWillAllocate && NeedsBufferSpace()) ? framesToAllocate : 0));
356
357 mIOBuffer.Allocate(mStreamFormat, (mWillAllocate && NeedsBufferSpace()) ? framesToAllocate : 0);
358 }
359 }
360
361 //_____________________________________________________________________________
362 //
DeallocateBuffer()363 void AUIOElement::DeallocateBuffer()
364 {
365 mIOBuffer.Deallocate();
366 }
367
368 //_____________________________________________________________________________
369 //
370 // AudioChannelLayout support
371
372 // outLayoutTagsPtr WILL be NULL if called to find out how many
373 // layouts that Audio Unit will report
374 // return 0 (ie. NO channel layouts) if the AU doesn't require channel layout knowledge
GetChannelLayoutTags(AudioChannelLayoutTag * outLayoutTagsPtr)375 UInt32 AUIOElement::GetChannelLayoutTags (AudioChannelLayoutTag *outLayoutTagsPtr)
376 {
377 return 0;
378 }
379
380 // As the AudioChannelLayout can be a variable length structure
381 // (though in most cases it won't be!!!)
382 // The size of the ACL is always returned by the method
383 // if outMapPtr is NOT-NULL, then AU should copy into this pointer (outMapPtr) the current ACL that it has in use.
384 // the AU should also return whether the property is writable (that is the client can provide any arbitrary ACL that the audio unit will then honour)
385 // or if the property is read only - which is the generally preferred mode.
386 // If the AU doesn't require an AudioChannelLayout, then just return 0.
GetAudioChannelLayout(AudioChannelLayout * outMapPtr,Boolean & outWritable)387 UInt32 AUIOElement::GetAudioChannelLayout (AudioChannelLayout *outMapPtr,
388 Boolean &outWritable)
389 {
390 return 0;
391 }
392
393 // the incoming channel map will be at least as big as a basic AudioChannelLayout
394 // but its contents will determine its actual size
395 // Subclass should overide if channel map is writable
SetAudioChannelLayout(const AudioChannelLayout & inData)396 OSStatus AUIOElement::SetAudioChannelLayout (const AudioChannelLayout &inData)
397 {
398 return kAudioUnitErr_InvalidProperty;
399 }
400
401 // Some units support optional usage of channel maps - typically converter units
402 // that can do channel remapping between different maps. In that optional case
403 // the user should be able to remove a channel map if that is possible.
404 // Typically this is NOT the case (e.g., the 3DMixer even in the stereo case
405 // needs to know if it is rendering to speakers or headphones)
RemoveAudioChannelLayout()406 OSStatus AUIOElement::RemoveAudioChannelLayout ()
407 {
408 return kAudioUnitErr_InvalidPropertyValue;
409 }
410
411
412 //_____________________________________________________________________________
413 //
~AUScope()414 AUScope::~AUScope()
415 {
416 for (ElementVector::iterator it = mElements.begin(); it != mElements.end(); ++it)
417 delete *it;
418 }
419
420 //_____________________________________________________________________________
421 //
SetNumberOfElements(UInt32 numElements)422 void AUScope::SetNumberOfElements(UInt32 numElements)
423 {
424 if (mDelegate)
425 return mDelegate->SetNumberOfElements(numElements);
426
427 if (numElements > mElements.size()) {
428 mElements.reserve(numElements);
429 while (numElements > mElements.size()) {
430 AUElement *elem = mCreator->CreateElement(GetScope(), static_cast<UInt32>(mElements.size()));
431 mElements.push_back(elem);
432 }
433 } else
434 while (numElements < mElements.size()) {
435 AUElement *elem = mElements.back();
436 mElements.pop_back();
437 delete elem;
438 }
439 }
440
441 //_____________________________________________________________________________
442 //
HasElementWithName() const443 bool AUScope::HasElementWithName () const
444 {
445 for (UInt32 i = 0; i < GetNumberOfElements(); ++i) {
446 AUElement * el = const_cast<AUScope*>(this)->GetElement (i);
447 if (el && el->HasName()) {
448 return true;
449 }
450 }
451 return false;
452 }
453
454 //_____________________________________________________________________________
455 //
456
AddElementNamesToDict(CFMutableDictionaryRef & inNameDict)457 void AUScope::AddElementNamesToDict (CFMutableDictionaryRef & inNameDict)
458 {
459 if (HasElementWithName())
460 {
461 static char string[32];
462 CFMutableDictionaryRef elementDict = CFDictionaryCreateMutable (NULL, 0,
463 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
464 CFStringRef str;
465 for (UInt32 i = 0; i < GetNumberOfElements(); ++i) {
466 AUElement * el = GetElement (i);
467 if (el && el->HasName()) {
468 snprintf (string, sizeof(string), "%d", int(i));
469 str = CFStringCreateWithCString (NULL, string, kCFStringEncodingASCII);
470 CFDictionarySetValue (elementDict, str, el->GetName());
471 CFRelease (str);
472 }
473 }
474
475 snprintf (string, sizeof(string), "%d", int(mScope));
476 str = CFStringCreateWithCString (NULL, string, kCFStringEncodingASCII);
477 CFDictionarySetValue (inNameDict, str, elementDict);
478 CFRelease (str);
479 CFRelease (elementDict);
480 }
481 }
482
483 //_____________________________________________________________________________
484 //
RestoreElementNames(CFDictionaryRef & inNameDict)485 bool AUScope::RestoreElementNames (CFDictionaryRef& inNameDict)
486 {
487 static char string[32];
488
489 //first we have to see if we have enough elements
490 bool didAddElements = false;
491 unsigned int maxElNum = GetNumberOfElements();
492
493 int dictSize = static_cast<int>(CFDictionaryGetCount(inNameDict));
494 CFStringRef * keys = (CFStringRef*)CA_malloc (dictSize * sizeof (CFStringRef));
495 CFDictionaryGetKeysAndValues (inNameDict, reinterpret_cast<const void**>(keys), NULL);
496 for (int i = 0; i < dictSize; i++)
497 {
498 unsigned int intKey = 0;
499 CFStringGetCString (keys[i], string, 32, kCFStringEncodingASCII);
500 int result = sscanf (string, "%u", &intKey);
501 // check if sscanf succeeded and element index is less than max elements.
502 if (result && UInt32(intKey) < maxElNum)
503 {
504 CFStringRef elName = reinterpret_cast<CFStringRef>(CFDictionaryGetValue (inNameDict, keys[i]));
505 AUElement* element = GetElement (intKey);
506 if (element)
507 element->SetName (elName);
508 }
509 }
510 free (keys);
511
512 return didAddElements;
513 }
514
SaveState(CFMutableDataRef data)515 void AUScope::SaveState(CFMutableDataRef data)
516 {
517 AudioUnitElement nElems = GetNumberOfElements();
518 for (AudioUnitElement ielem = 0; ielem < nElems; ++ielem) {
519 AUElement *element = GetElement(ielem);
520 UInt32 nparams = element->GetNumberOfParameters();
521 if (nparams > 0) {
522 struct {
523 UInt32 scope;
524 UInt32 element;
525 } hdr;
526
527 hdr.scope = CFSwapInt32HostToBig(GetScope());
528 hdr.element = CFSwapInt32HostToBig(ielem);
529 CFDataAppendBytes(data, (UInt8 *)&hdr, sizeof(hdr));
530
531 element->SaveState(mScope, data);
532 }
533 }
534 }
535
RestoreState(const UInt8 * state)536 const UInt8 * AUScope::RestoreState(const UInt8 *state)
537 {
538 const UInt8 *p = state;
539 UInt32 elementIdx = CFSwapInt32BigToHost(*(UInt32 *)p); p += sizeof(UInt32);
540 AUElement *element = GetElement(elementIdx);
541 if (!element) {
542 struct {
543 AudioUnitParameterID paramID;
544 AudioUnitParameterValue value;
545 } entry;
546 UInt32 nparams = CFSwapInt32BigToHost(*(UInt32 *)p);
547 p += sizeof(UInt32);
548
549 p += nparams * sizeof(entry);
550 } else
551 p = element->RestoreState(p);
552
553 return p;
554 }
555