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