AUPublic/AUBase/AUScopeElement.cpp
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
Part of Core Audio AUBase Classes |
*/ |
#include "AUScopeElement.h" |
#include <AudioUnit/AudioUnitProperties.h> |
#include "AUBase.h" |
//_____________________________________________________________________________ |
// |
// By default, parameterIDs may be arbitrarily spaced, and an STL map |
// will be used for access. Calling UseIndexedParameters() will |
// instead use an STL vector for faster indexed access. |
// This assumes the paramIDs are numbered 0.....inNumberOfParameters-1 |
// Call this before defining/adding any parameters with SetParameter() |
// |
void AUElement::UseIndexedParameters(int inNumberOfParameters) |
{ |
mIndexedParameters.resize (inNumberOfParameters); |
mUseIndexedParameters = true; |
} |
//_____________________________________________________________________________ |
// |
// Helper method. |
// returns the ParameterMapEvent object associated with the paramID |
// |
inline ParameterMapEvent& AUElement::GetParamEvent(AudioUnitParameterID paramID) |
{ |
ParameterMapEvent *event; |
if(mUseIndexedParameters) |
{ |
if(paramID >= mIndexedParameters.size() ) |
COMPONENT_THROW(kAudioUnitErr_InvalidParameter); |
event = &mIndexedParameters[paramID]; |
} |
else |
{ |
ParameterMap::iterator i = mParameters.find(paramID); |
if (i == mParameters.end()) |
COMPONENT_THROW(kAudioUnitErr_InvalidParameter); |
event = &(*i).second; |
} |
return *event; |
} |
//_____________________________________________________________________________ |
// |
// Helper method. |
// returns whether the specified paramID is known to the element |
// |
bool AUElement::HasParameterID (AudioUnitParameterID paramID) const |
{ |
if(mUseIndexedParameters) |
{ |
if(paramID >= mIndexedParameters.size() ) |
return false; |
return true; |
} |
ParameterMap::const_iterator i = mParameters.find(paramID); |
if (i == mParameters.end()) |
return false; |
return true; |
} |
//_____________________________________________________________________________ |
// |
// caller assumes that this is actually an immediate parameter |
// |
AudioUnitParameterValue AUElement::GetParameter(AudioUnitParameterID paramID) |
{ |
ParameterMapEvent &event = GetParamEvent(paramID); |
return event.GetValue(); |
} |
//_____________________________________________________________________________ |
// |
void AUElement::GetRampSliceStartEnd( AudioUnitParameterID paramID, |
AudioUnitParameterValue & outStartValue, |
AudioUnitParameterValue & outEndValue, |
AudioUnitParameterValue & outValuePerFrameDelta ) |
{ |
ParameterMapEvent &event = GetParamEvent(paramID); |
// works even if the value is constant (immediate parameter value) |
event.GetRampSliceStartEnd(outStartValue, outEndValue, outValuePerFrameDelta ); |
} |
//_____________________________________________________________________________ |
// |
AudioUnitParameterValue AUElement::GetEndValue( AudioUnitParameterID paramID) |
{ |
ParameterMapEvent &event = GetParamEvent(paramID); |
// works even if the value is constant (immediate parameter value) |
return event.GetEndValue(); |
} |
//_____________________________________________________________________________ |
// |
void AUElement::SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue inValue, bool okWhenInitialized) |
{ |
if(mUseIndexedParameters) |
{ |
ParameterMapEvent &event = GetParamEvent(paramID); |
event.SetValue(inValue); |
} |
else |
{ |
ParameterMap::iterator i = mParameters.find(paramID); |
if (i == mParameters.end()) |
{ |
if (mAudioUnit->IsInitialized() && !okWhenInitialized) { |
// The AU should not be creating new parameters once initialized. |
// If a client tries to set an undefined parameter, we could throw as follows, |
// but this might cause a regression. So it is better to just fail silently. |
// COMPONENT_THROW(kAudioUnitErr_InvalidParameter); |
#if DEBUG |
fprintf(stderr, "WARNING: %s SetParameter for undefined param ID %d while initialized. Ignoring..\n", |
mAudioUnit->GetLoggingString(), (int)paramID); |
#endif |
} else { |
// create new entry in map for the paramID (only happens first time) |
ParameterMapEvent event(inValue); |
mParameters[paramID] = event; |
} |
} |
else |
{ |
// paramID already exists in map so simply change its value |
ParameterMapEvent &event = (*i).second; |
event.SetValue(inValue); |
} |
} |
} |
//_____________________________________________________________________________ |
// |
void AUElement::SetScheduledEvent( AudioUnitParameterID paramID, |
const AudioUnitParameterEvent &inEvent, |
UInt32 inSliceOffsetInBuffer, |
UInt32 inSliceDurationFrames, |
bool okWhenInitialized ) |
{ |
if(mUseIndexedParameters) |
{ |
ParameterMapEvent &event = GetParamEvent(paramID); |
event.SetScheduledEvent(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames ); |
} |
else |
{ |
ParameterMap::iterator i = mParameters.find(paramID); |
if (i == mParameters.end()) |
{ |
if (mAudioUnit->IsInitialized() && !okWhenInitialized) { |
// The AU should not be creating new parameters once initialized. |
// If a client tries to set an undefined parameter, we could throw as follows, |
// but this might cause a regression. So it is better to just fail silently. |
// COMPONENT_THROW(kAudioUnitErr_InvalidParameter); |
#if DEBUG |
fprintf(stderr, "WARNING: %s SetScheduledEvent for undefined param ID %d while initialized. Ignoring..\n", |
mAudioUnit->GetLoggingString(), (int)paramID); |
#endif |
} else { |
// create new entry in map for the paramID (only happens first time) |
ParameterMapEvent event(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames); |
mParameters[paramID] = event; |
} |
} |
else |
{ |
// paramID already exists in map so simply change its value |
ParameterMapEvent &event = (*i).second; |
event.SetScheduledEvent(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames ); |
} |
} |
} |
//_____________________________________________________________________________ |
// |
void AUElement::GetParameterList(AudioUnitParameterID *outList) |
{ |
if(mUseIndexedParameters) |
{ |
UInt32 nparams = static_cast<UInt32>(mIndexedParameters.size()); |
for (UInt32 i = 0; i < nparams; i++ ) |
*outList++ = (AudioUnitParameterID)i; |
} |
else |
{ |
for (ParameterMap::iterator i = mParameters.begin(); i != mParameters.end(); ++i) |
*outList++ = (*i).first; |
} |
} |
//_____________________________________________________________________________ |
// |
void AUElement::SaveState(AudioUnitScope scope, CFMutableDataRef data) |
{ |
AudioUnitParameterInfo paramInfo; |
CFIndex countOffset = CFDataGetLength(data); |
UInt32 nparams, nOmitted = 0, theData; |
if(mUseIndexedParameters) |
{ |
nparams = static_cast<UInt32>(mIndexedParameters.size()); |
theData = CFSwapInt32HostToBig(nparams); |
CFDataAppendBytes(data, (UInt8 *)&theData, sizeof(nparams)); |
for (UInt32 i = 0; i < nparams; i++) |
{ |
struct { |
UInt32 paramID; |
//CFSwappedFloat32 value; crashes gcc3 PFE |
UInt32 value; // really a big-endian float |
} entry; |
if (mAudioUnit->GetParameterInfo(scope, i, paramInfo) == noErr) { |
if ((paramInfo.flags & kAudioUnitParameterFlag_CFNameRelease) && paramInfo.cfNameString) |
CFRelease(paramInfo.cfNameString); |
if (paramInfo.flags & kAudioUnitParameterFlag_OmitFromPresets) { |
++nOmitted; |
continue; |
} |
} |
entry.paramID = CFSwapInt32HostToBig(i); |
AudioUnitParameterValue v = mIndexedParameters[i].GetValue(); |
entry.value = CFSwapInt32HostToBig(*(UInt32 *)&v ); |
CFDataAppendBytes(data, (UInt8 *)&entry, sizeof(entry)); |
} |
} |
else |
{ |
nparams = static_cast<uint32_t>(mParameters.size()); |
theData = CFSwapInt32HostToBig(nparams); |
CFDataAppendBytes(data, (UInt8 *)&theData, sizeof(nparams)); |
for (ParameterMap::iterator i = mParameters.begin(); i != mParameters.end(); ++i) { |
struct { |
UInt32 paramID; |
//CFSwappedFloat32 value; crashes gcc3 PFE |
UInt32 value; // really a big-endian float |
} entry; |
if (mAudioUnit->GetParameterInfo(scope, (*i).first, paramInfo) == noErr) { |
if ((paramInfo.flags & kAudioUnitParameterFlag_CFNameRelease) && paramInfo.cfNameString) |
CFRelease(paramInfo.cfNameString); |
if (paramInfo.flags & kAudioUnitParameterFlag_OmitFromPresets) { |
++nOmitted; |
continue; |
} |
} |
entry.paramID = CFSwapInt32HostToBig((*i).first); |
AudioUnitParameterValue v = (*i).second.GetValue(); |
entry.value = CFSwapInt32HostToBig(*(UInt32 *)&v ); |
CFDataAppendBytes(data, (UInt8 *)&entry, sizeof(entry)); |
} |
} |
if (nOmitted > 0) { |
theData = CFSwapInt32HostToBig(nparams - nOmitted); |
*(UInt32 *)(CFDataGetBytePtr(data) + countOffset) = theData; |
} |
} |
//_____________________________________________________________________________ |
// |
const UInt8 * AUElement::RestoreState(const UInt8 *state) |
{ |
union FloatInt32 { UInt32 i; AudioUnitParameterValue f; }; |
const UInt8 *p = state; |
UInt32 nparams = CFSwapInt32BigToHost(*(UInt32 *)p); |
p += sizeof(UInt32); |
for (UInt32 i = 0; i < nparams; ++i) { |
struct { |
AudioUnitParameterID paramID; |
AudioUnitParameterValue value; |
} entry; |
entry.paramID = CFSwapInt32BigToHost(*(UInt32 *)p); |
p += sizeof(UInt32); |
FloatInt32 temp; |
temp.i = CFSwapInt32BigToHost(*(UInt32 *)p); |
entry.value = temp.f; |
p += sizeof(AudioUnitParameterValue); |
SetParameter(entry.paramID, entry.value); |
} |
return p; |
} |
//_____________________________________________________________________________ |
// |
void AUElement::SetName (CFStringRef inName) |
{ |
if (mElementName) CFRelease (mElementName); |
mElementName = inName; |
if (mElementName) CFRetain (mElementName); |
} |
//_____________________________________________________________________________ |
// |
AUIOElement::AUIOElement(AUBase *audioUnit) : |
AUElement(audioUnit), |
mWillAllocate (true) |
{ |
mStreamFormat = CAStreamBasicDescription(kAUDefaultSampleRate, 2, CAStreamBasicDescription::kPCMFormatFloat32, audioUnit->AudioUnitAPIVersion() == 1); |
// stereo |
// interleaved if API version 1, deinterleaved if version 2 |
} |
//_____________________________________________________________________________ |
// |
OSStatus AUIOElement::SetStreamFormat(const CAStreamBasicDescription &desc) |
{ |
mStreamFormat = desc; |
return AUBase::noErr; |
} |
//_____________________________________________________________________________ |
// inFramesToAllocate == 0 implies the AudioUnit's max-frames-per-slice will be used |
void AUIOElement::AllocateBuffer(UInt32 inFramesToAllocate) |
{ |
if (GetAudioUnit()->HasBegunInitializing()) |
{ |
UInt32 framesToAllocate = inFramesToAllocate > 0 ? inFramesToAllocate : GetAudioUnit()->GetMaxFramesPerSlice(); |
// printf ("will allocate: %d\n", (int)((mWillAllocate && NeedsBufferSpace()) ? framesToAllocate : 0)); |
mIOBuffer.Allocate(mStreamFormat, (mWillAllocate && NeedsBufferSpace()) ? framesToAllocate : 0); |
} |
} |
//_____________________________________________________________________________ |
// |
void AUIOElement::DeallocateBuffer() |
{ |
mIOBuffer.Deallocate(); |
} |
//_____________________________________________________________________________ |
// |
// AudioChannelLayout support |
// outLayoutTagsPtr WILL be NULL if called to find out how many |
// layouts that Audio Unit will report |
// return 0 (ie. NO channel layouts) if the AU doesn't require channel layout knowledge |
UInt32 AUIOElement::GetChannelLayoutTags (AudioChannelLayoutTag *outLayoutTagsPtr) |
{ |
return 0; |
} |
// As the AudioChannelLayout can be a variable length structure |
// (though in most cases it won't be!!!) |
// The size of the ACL is always returned by the method |
// if outMapPtr is NOT-NULL, then AU should copy into this pointer (outMapPtr) the current ACL that it has in use. |
// 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) |
// or if the property is read only - which is the generally preferred mode. |
// If the AU doesn't require an AudioChannelLayout, then just return 0. |
UInt32 AUIOElement::GetAudioChannelLayout (AudioChannelLayout *outMapPtr, |
Boolean &outWritable) |
{ |
return 0; |
} |
// the incoming channel map will be at least as big as a basic AudioChannelLayout |
// but its contents will determine its actual size |
// Subclass should overide if channel map is writable |
OSStatus AUIOElement::SetAudioChannelLayout (const AudioChannelLayout &inData) |
{ |
return kAudioUnitErr_InvalidProperty; |
} |
// Some units support optional usage of channel maps - typically converter units |
// that can do channel remapping between different maps. In that optional case |
// the user should be able to remove a channel map if that is possible. |
// Typically this is NOT the case (e.g., the 3DMixer even in the stereo case |
// needs to know if it is rendering to speakers or headphones) |
OSStatus AUIOElement::RemoveAudioChannelLayout () |
{ |
return kAudioUnitErr_InvalidPropertyValue; |
} |
//_____________________________________________________________________________ |
// |
AUScope::~AUScope() |
{ |
for (ElementVector::iterator it = mElements.begin(); it != mElements.end(); ++it) |
delete *it; |
} |
//_____________________________________________________________________________ |
// |
void AUScope::SetNumberOfElements(UInt32 numElements) |
{ |
if (mDelegate) |
return mDelegate->SetNumberOfElements(numElements); |
if (numElements > mElements.size()) { |
mElements.reserve(numElements); |
while (numElements > mElements.size()) { |
AUElement *elem = mCreator->CreateElement(GetScope(), static_cast<UInt32>(mElements.size())); |
mElements.push_back(elem); |
} |
} else |
while (numElements < mElements.size()) { |
AUElement *elem = mElements.back(); |
mElements.pop_back(); |
delete elem; |
} |
} |
//_____________________________________________________________________________ |
// |
bool AUScope::HasElementWithName () const |
{ |
for (UInt32 i = 0; i < GetNumberOfElements(); ++i) { |
AUElement * el = const_cast<AUScope*>(this)->GetElement (i); |
if (el && el->HasName()) { |
return true; |
} |
} |
return false; |
} |
//_____________________________________________________________________________ |
// |
void AUScope::AddElementNamesToDict (CFMutableDictionaryRef & inNameDict) |
{ |
if (HasElementWithName()) |
{ |
static char string[32]; |
CFMutableDictionaryRef elementDict = CFDictionaryCreateMutable (NULL, 0, |
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
CFStringRef str; |
for (UInt32 i = 0; i < GetNumberOfElements(); ++i) { |
AUElement * el = GetElement (i); |
if (el && el->HasName()) { |
snprintf (string, sizeof(string), "%d", int(i)); |
str = CFStringCreateWithCString (NULL, string, kCFStringEncodingASCII); |
CFDictionarySetValue (elementDict, str, el->GetName()); |
CFRelease (str); |
} |
} |
snprintf (string, sizeof(string), "%d", int(mScope)); |
str = CFStringCreateWithCString (NULL, string, kCFStringEncodingASCII); |
CFDictionarySetValue (inNameDict, str, elementDict); |
CFRelease (str); |
CFRelease (elementDict); |
} |
} |
//_____________________________________________________________________________ |
// |
bool AUScope::RestoreElementNames (CFDictionaryRef& inNameDict) |
{ |
static char string[32]; |
//first we have to see if we have enough elements |
bool didAddElements = false; |
unsigned int maxElNum = GetNumberOfElements(); |
int dictSize = static_cast<int>(CFDictionaryGetCount(inNameDict)); |
CFStringRef * keys = (CFStringRef*)CA_malloc (dictSize * sizeof (CFStringRef)); |
CFDictionaryGetKeysAndValues (inNameDict, reinterpret_cast<const void**>(keys), NULL); |
for (int i = 0; i < dictSize; i++) |
{ |
unsigned int intKey = 0; |
CFStringGetCString (keys[i], string, 32, kCFStringEncodingASCII); |
int result = sscanf (string, "%u", &intKey); |
// check if sscanf succeeded and element index is less than max elements. |
if (result && UInt32(intKey) < maxElNum) |
{ |
CFStringRef elName = reinterpret_cast<CFStringRef>(CFDictionaryGetValue (inNameDict, keys[i])); |
AUElement* element = GetElement (intKey); |
if (element) |
element->SetName (elName); |
} |
} |
free (keys); |
return didAddElements; |
} |
void AUScope::SaveState(CFMutableDataRef data) |
{ |
AudioUnitElement nElems = GetNumberOfElements(); |
for (AudioUnitElement ielem = 0; ielem < nElems; ++ielem) { |
AUElement *element = GetElement(ielem); |
UInt32 nparams = element->GetNumberOfParameters(); |
if (nparams > 0) { |
struct { |
UInt32 scope; |
UInt32 element; |
} hdr; |
hdr.scope = CFSwapInt32HostToBig(GetScope()); |
hdr.element = CFSwapInt32HostToBig(ielem); |
CFDataAppendBytes(data, (UInt8 *)&hdr, sizeof(hdr)); |
element->SaveState(mScope, data); |
} |
} |
} |
const UInt8 * AUScope::RestoreState(const UInt8 *state) |
{ |
const UInt8 *p = state; |
UInt32 elementIdx = CFSwapInt32BigToHost(*(UInt32 *)p); p += sizeof(UInt32); |
AUElement *element = GetElement(elementIdx); |
if (!element) { |
struct { |
AudioUnitParameterID paramID; |
AudioUnitParameterValue value; |
} entry; |
UInt32 nparams = CFSwapInt32BigToHost(*(UInt32 *)p); |
p += sizeof(UInt32); |
p += nparams * sizeof(entry); |
} else |
p = element->RestoreState(p); |
return p; |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-02-19