|
- /*!
- @file AudioUnitSDK/AUBase.cpp
- @copyright © 2000-2021 Apple Inc. All rights reserved.
- */
- #include <AudioUnitSDK/AUBase.h>
- #include <AudioUnitSDK/AUInputElement.h>
- #include <AudioUnitSDK/AUOutputElement.h>
- #include <AudioUnitSDK/AUUtility.h>
-
- #include <algorithm>
- #include <array>
- #include <cassert>
- #include <limits>
-
- namespace ausdk {
-
- #if TARGET_OS_MAC && (TARGET_CPU_X86 || TARGET_CPU_X86_64)
-
- class DenormalDisabler {
- public:
- DenormalDisabler() : mSavedMXCSR(GetCSR()) { SetCSR(mSavedMXCSR | 0x8040); }
-
- DenormalDisabler(const DenormalDisabler&) = delete;
- DenormalDisabler(DenormalDisabler&&) = delete;
- DenormalDisabler& operator=(const DenormalDisabler&) = delete;
- DenormalDisabler& operator=(DenormalDisabler&&) = delete;
-
- ~DenormalDisabler() { SetCSR(mSavedMXCSR); }
-
- private:
- #if 0 // not sure if this is right: // #if __has_include(<xmmintrin.h>)
- static unsigned GetCSR() { return _mm_getcsr(); }
- static void SetCSR(unsigned x) { _mm_setcsr(x); }
- #else
- // our compiler does ALL floating point with SSE
- static unsigned GetCSR()
- {
- unsigned result{};
- asm volatile("stmxcsr %0" : "=m"(*&result)); // NOLINT asm
- return result;
- }
- static void SetCSR(unsigned a)
- {
- unsigned temp = a;
- asm volatile("ldmxcsr %0" : : "m"(*&temp)); // NOLINT asm
- }
- #endif
-
- unsigned const mSavedMXCSR;
- };
-
- #else
- // while denormals can be flushed to zero on ARM processors, there is no performance benefit
- class DenormalDisabler {
- public:
- DenormalDisabler() = default;
- };
- #endif
-
- static std::once_flag sAUBaseCFStringsInitialized{}; // NOLINT non-const global
- // this is used for the presets
- static CFStringRef kUntitledString = nullptr; // NOLINT non-const global
- // these are the current keys for the class info document
- static CFStringRef kVersionString = nullptr; // NOLINT non-const global
- static CFStringRef kTypeString = nullptr; // NOLINT non-const global
- static CFStringRef kSubtypeString = nullptr; // NOLINT non-const global
- static CFStringRef kManufacturerString = nullptr; // NOLINT non-const global
- static CFStringRef kDataString = nullptr; // NOLINT non-const global
- static CFStringRef kNameString = nullptr; // NOLINT non-const global
- static CFStringRef kRenderQualityString = nullptr; // NOLINT non-const global
- static CFStringRef kElementNameString = nullptr; // NOLINT non-const global
- static CFStringRef kPartString = nullptr; // NOLINT non-const global
-
- static constexpr auto kNoLastRenderedSampleTime = std::numeric_limits<Float64>::lowest();
-
- //_____________________________________________________________________________
- //
- AUBase::AUBase(AudioComponentInstance inInstance, UInt32 numInputElements, UInt32 numOutputElements,
- UInt32 numGroupElements)
- : ComponentBase(inInstance), mInitNumInputEls(numInputElements),
- mInitNumOutputEls(numOutputElements), mInitNumGroupEls(numGroupElements),
- mLogString(CreateLoggingString())
- {
- ResetRenderTime();
-
- std::call_once(sAUBaseCFStringsInitialized, []() {
- kUntitledString = CFSTR("Untitled"); // NOLINT
- kVersionString = CFSTR(kAUPresetVersionKey); // NOLINT
- kTypeString = CFSTR(kAUPresetTypeKey); // NOLINT
- kSubtypeString = CFSTR(kAUPresetSubtypeKey); // NOLINT
- kManufacturerString = CFSTR(kAUPresetManufacturerKey); // NOLINT
- kDataString = CFSTR(kAUPresetDataKey); // NOLINT
- kNameString = CFSTR(kAUPresetNameKey); // NOLINT
- kRenderQualityString = CFSTR(kAUPresetRenderQualityKey); // NOLINT
- kElementNameString = CFSTR(kAUPresetElementNameKey); // NOLINT
- kPartString = CFSTR(kAUPresetPartKey); // NOLINT
- });
-
- GlobalScope().Initialize(this, kAudioUnitScope_Global, 1);
-
- mCurrentPreset.presetNumber = -1;
- CFRetain(mCurrentPreset.presetName = kUntitledString);
- }
-
- //_____________________________________________________________________________
- //
- AUBase::~AUBase()
- {
- if (mCurrentPreset.presetName != nullptr) {
- CFRelease(mCurrentPreset.presetName);
- }
- }
-
- //_____________________________________________________________________________
- //
- void AUBase::PostConstructorInternal()
- {
- // invoked after construction because they are virtual methods and/or call virtual methods
- if (mMaxFramesPerSlice == 0) {
- SetMaxFramesPerSlice(kAUDefaultMaxFramesPerSlice);
- }
- CreateElements();
- }
-
- //_____________________________________________________________________________
- //
- void AUBase::PreDestructorInternal()
- {
- // this is called from the ComponentBase dispatcher, which doesn't know anything about our
- // (optional) lock
- const AUEntryGuard guard(mAUMutex);
- DoCleanup();
- }
-
- //_____________________________________________________________________________
- //
- void AUBase::CreateElements()
- {
- if (!mElementsCreated) {
- Inputs().Initialize(this, kAudioUnitScope_Input, mInitNumInputEls);
- Outputs().Initialize(this, kAudioUnitScope_Output, mInitNumOutputEls);
- Groups().Initialize(this, kAudioUnitScope_Group, mInitNumGroupEls);
- CreateExtendedElements();
-
- mElementsCreated = true;
- }
- }
-
- //_____________________________________________________________________________
- //
- void AUBase::SetMaxFramesPerSlice(UInt32 nFrames)
- {
- if (nFrames == mMaxFramesPerSlice) {
- return;
- }
-
- mMaxFramesPerSlice = nFrames;
- if (mBuffersAllocated) {
- ReallocateBuffers();
- }
- PropertyChanged(kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0);
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::CanSetMaxFrames() const
- {
- return IsInitialized() ? kAudioUnitErr_Initialized : OSStatus(noErr);
- }
-
- //_____________________________________________________________________________
- //
- void AUBase::ReallocateBuffers()
- {
- CreateElements();
-
- const UInt32 nOutputs = Outputs().GetNumberOfElements();
- for (UInt32 i = 0; i < nOutputs; ++i) {
- Output(i).AllocateBuffer(); // does no work if already allocated
- }
- const UInt32 nInputs = Inputs().GetNumberOfElements();
- for (UInt32 i = 0; i < nInputs; ++i) {
- Input(i).AllocateBuffer(); // does no work if already allocated
- }
- mBuffersAllocated = true;
- }
-
- //_____________________________________________________________________________
- //
- void AUBase::DeallocateIOBuffers()
- {
- if (!mBuffersAllocated) {
- return;
- }
-
- const UInt32 nOutputs = Outputs().GetNumberOfElements();
- for (UInt32 i = 0; i < nOutputs; ++i) {
- Output(i).DeallocateBuffer();
- }
- const UInt32 nInputs = Inputs().GetNumberOfElements();
- for (UInt32 i = 0; i < nInputs; ++i) {
- Input(i).DeallocateBuffer();
- }
- mBuffersAllocated = false;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::DoInitialize()
- {
- OSStatus result = noErr;
-
- if (!mInitialized) {
- result = Initialize();
- if (result == noErr) {
- if (CanScheduleParameters()) {
- mParamEventList.reserve(24); // NOLINT magic #
- }
- mHasBegunInitializing = true;
- ReallocateBuffers(); // calls CreateElements()
- mInitialized = true; // signal that it's okay to render
- std::atomic_thread_fence(std::memory_order_seq_cst);
- }
- }
-
- return result;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::Initialize() { return noErr; }
-
- //_____________________________________________________________________________
- //
- void AUBase::DoCleanup()
- {
- if (mInitialized) {
- Cleanup();
- }
-
- DeallocateIOBuffers();
- ResetRenderTime();
-
- mInitialized = false;
- mHasBegunInitializing = false;
- }
-
- //_____________________________________________________________________________
- //
- void AUBase::Cleanup() {}
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::Reset(AudioUnitScope /*inScope*/, AudioUnitElement /*inElement*/)
- {
- ResetRenderTime();
- return noErr;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::DispatchGetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable)
- {
- OSStatus result = noErr;
- bool validateElement = true;
-
- switch (inID) {
- case kAudioUnitProperty_MakeConnection:
- AUSDK_Require(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Global,
- kAudioUnitErr_InvalidScope);
- outDataSize = sizeof(AudioUnitConnection);
- outWritable = true;
- break;
-
- case kAudioUnitProperty_SetRenderCallback:
- AUSDK_Require(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Global,
- kAudioUnitErr_InvalidScope);
- outDataSize = sizeof(AURenderCallbackStruct);
- outWritable = true;
- break;
-
- case kAudioUnitProperty_StreamFormat:
- outDataSize = sizeof(AudioStreamBasicDescription);
- outWritable = IsStreamFormatWritable(inScope, inElement);
- break;
-
- case kAudioUnitProperty_SampleRate:
- outDataSize = sizeof(Float64);
- outWritable = IsStreamFormatWritable(inScope, inElement);
- break;
-
- case kAudioUnitProperty_ClassInfo:
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- outDataSize = sizeof(CFPropertyListRef);
- outWritable = true;
- break;
-
- case kAudioUnitProperty_FactoryPresets:
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- AUSDK_Require_noerr(GetPresets(nullptr));
- outDataSize = sizeof(CFArrayRef);
- outWritable = false;
- break;
-
- case kAudioUnitProperty_PresentPreset:
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- outDataSize = sizeof(AUPreset);
- outWritable = true;
- break;
-
- case kAudioUnitProperty_ElementName:
- outDataSize = sizeof(CFStringRef);
- outWritable = true;
- break;
-
- case kAudioUnitProperty_ParameterList: {
- UInt32 nparams = 0;
- AUSDK_Require_noerr(GetParameterList(inScope, nullptr, nparams));
- outDataSize = sizeof(AudioUnitParameterID) * nparams;
- outWritable = false;
- validateElement = false;
- break;
- }
-
- case kAudioUnitProperty_ParameterInfo:
- outDataSize = sizeof(AudioUnitParameterInfo);
- outWritable = false;
- validateElement = false;
- break;
-
- case kAudioUnitProperty_ParameterHistoryInfo:
- outDataSize = sizeof(AudioUnitParameterHistoryInfo);
- outWritable = false;
- validateElement = false;
- break;
-
- case kAudioUnitProperty_ElementCount:
- outDataSize = sizeof(UInt32);
- outWritable = BusCountWritable(inScope);
- validateElement = false;
- break;
-
- case kAudioUnitProperty_Latency:
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- outDataSize = sizeof(Float64);
- outWritable = false;
- break;
-
- case kAudioUnitProperty_TailTime:
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- AUSDK_Require(SupportsTail(), kAudioUnitErr_InvalidProperty);
- outDataSize = sizeof(Float64);
- outWritable = false;
- break;
-
- case kAudioUnitProperty_MaximumFramesPerSlice:
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- outDataSize = sizeof(UInt32);
- outWritable = true;
- break;
-
- case kAudioUnitProperty_LastRenderError:
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- outDataSize = sizeof(OSStatus);
- outWritable = false;
- break;
-
- case kAudioUnitProperty_SupportedNumChannels: {
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- const UInt32 num = SupportedNumChannels(nullptr);
- AUSDK_Require(num != 0u, kAudioUnitErr_InvalidProperty);
- outDataSize = sizeof(AUChannelInfo) * num;
- outWritable = false;
- break;
- }
-
- case kAudioUnitProperty_SupportedChannelLayoutTags: {
- const auto tags = GetChannelLayoutTags(inScope, inElement);
- AUSDK_Require(!tags.empty(), kAudioUnitErr_InvalidProperty);
- outDataSize = static_cast<UInt32>(tags.size() * sizeof(AudioChannelLayoutTag));
- outWritable = false;
- validateElement = false; // already done it
- break;
- }
-
- case kAudioUnitProperty_AudioChannelLayout: {
- outWritable = false;
- outDataSize = GetAudioChannelLayout(inScope, inElement, nullptr, outWritable);
- if (outDataSize != 0u) {
- result = noErr;
- } else {
- const auto tags = GetChannelLayoutTags(inScope, inElement);
- return tags.empty() ? kAudioUnitErr_InvalidProperty
- : kAudioUnitErr_InvalidPropertyValue;
- }
- validateElement = false; // already done it
- break;
- }
-
- case kAudioUnitProperty_ShouldAllocateBuffer:
- AUSDK_Require((inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output),
- kAudioUnitErr_InvalidScope);
- outWritable = true;
- outDataSize = sizeof(UInt32);
- break;
-
- case kAudioUnitProperty_ParameterValueStrings:
- AUSDK_Require_noerr(GetParameterValueStrings(inScope, inElement, nullptr));
- outDataSize = sizeof(CFArrayRef);
- outWritable = false;
- validateElement = false;
- break;
-
- case kAudioUnitProperty_HostCallbacks:
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- outDataSize = sizeof(mHostCallbackInfo);
- outWritable = true;
- break;
-
- case kAudioUnitProperty_ContextName:
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- outDataSize = sizeof(CFStringRef);
- outWritable = true;
- break;
-
- #if !TARGET_OS_IPHONE
- case kAudioUnitProperty_IconLocation:
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- AUSDK_Require(HasIcon(), kAudioUnitErr_InvalidProperty);
- outWritable = false;
- outDataSize = sizeof(CFURLRef);
- break;
- #endif
-
- case kAudioUnitProperty_ParameterClumpName:
- outDataSize = sizeof(AudioUnitParameterNameInfo);
- outWritable = false;
- break;
-
- case 61: // kAudioUnitProperty_LastRenderSampleTime
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- outDataSize = sizeof(Float64);
- outWritable = false;
- break;
-
- case kAudioUnitProperty_NickName:
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- outDataSize = sizeof(CFStringRef);
- outWritable = true;
- break;
-
- default:
- result = GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable);
- validateElement = false;
- break;
- }
-
- if ((result == noErr) && validateElement) {
- AUSDK_Require(GetElement(inScope, inElement) != nullptr, kAudioUnitErr_InvalidElement);
- }
-
- return result;
- }
-
- //_____________________________________________________________________________
- //
- // NOLINTNEXTLINE(misc-no-recursion) with SaveState
- OSStatus AUBase::DispatchGetProperty(
- AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData)
- {
- // NOTE: We're currently only called from AUBase::ComponentEntryDispatch, which
- // calls DispatchGetPropertyInfo first, which performs validation of the scope/element,
- // and ensures that the outData buffer is non-null and large enough.
- OSStatus result = noErr;
-
- switch (inID) {
- case kAudioUnitProperty_StreamFormat:
- *static_cast<AudioStreamBasicDescription*>(outData) = GetStreamFormat(inScope, inElement);
- break;
-
- case kAudioUnitProperty_SampleRate:
- *static_cast<Float64*>(outData) = GetStreamFormat(inScope, inElement).mSampleRate;
- break;
-
- case kAudioUnitProperty_ParameterList: {
- UInt32 nparams = 0;
- result = GetParameterList(inScope, static_cast<AudioUnitParameterID*>(outData), nparams);
- break;
- }
-
- case kAudioUnitProperty_ParameterInfo:
- *static_cast<AudioUnitParameterInfo*>(outData) = {};
- result =
- GetParameterInfo(inScope, inElement, *static_cast<AudioUnitParameterInfo*>(outData));
- break;
-
- case kAudioUnitProperty_ParameterHistoryInfo: {
- auto* const info = static_cast<AudioUnitParameterHistoryInfo*>(outData);
- result = GetParameterHistoryInfo(
- inScope, inElement, info->updatesPerSecond, info->historyDurationInSeconds);
- break;
- }
-
- case kAudioUnitProperty_ClassInfo: {
- *static_cast<CFPropertyListRef*>(outData) = nullptr;
- result = SaveState(static_cast<CFPropertyListRef*>(outData));
- break;
- }
-
- case kAudioUnitProperty_FactoryPresets: {
- *static_cast<CFArrayRef*>(outData) = nullptr;
- result = GetPresets(static_cast<CFArrayRef*>(outData));
- break;
- }
-
- case kAudioUnitProperty_PresentPreset: {
- *static_cast<AUPreset*>(outData) = mCurrentPreset;
-
- // retain current string (as client owns a reference to it and will release it)
- if ((inID == kAudioUnitProperty_PresentPreset) && (mCurrentPreset.presetName != nullptr)) {
- CFRetain(mCurrentPreset.presetName);
- }
-
- result = noErr;
- break;
- }
-
- case kAudioUnitProperty_ElementName: {
- const AUElement* const element = GetElement(inScope, inElement);
- const CFStringRef name = *element->GetName();
- AUSDK_Require(name != nullptr, kAudioUnitErr_PropertyNotInUse);
- CFRetain(name); // must return a +1 reference
- *static_cast<CFStringRef*>(outData) = name;
- break;
- }
-
- case kAudioUnitProperty_ElementCount:
- *static_cast<UInt32*>(outData) = GetScope(inScope).GetNumberOfElements();
- break;
-
- case kAudioUnitProperty_Latency:
- *static_cast<Float64*>(outData) = GetLatency();
- break;
-
- case kAudioUnitProperty_TailTime:
- AUSDK_Require(SupportsTail(), kAudioUnitErr_InvalidProperty);
- *static_cast<Float64*>(outData) = GetTailTime();
- break;
-
- case kAudioUnitProperty_MaximumFramesPerSlice:
- *static_cast<UInt32*>(outData) = mMaxFramesPerSlice;
- break;
-
- case kAudioUnitProperty_LastRenderError:
- *static_cast<OSStatus*>(outData) = mLastRenderError;
- mLastRenderError = 0;
- break;
-
- case kAudioUnitProperty_SupportedNumChannels: {
- const AUChannelInfo* infoPtr = nullptr;
- const UInt32 num = SupportedNumChannels(&infoPtr);
- if (num != 0 && infoPtr != nullptr) {
- memcpy(outData, infoPtr, num * sizeof(AUChannelInfo));
- }
- break;
- }
-
- case kAudioUnitProperty_SupportedChannelLayoutTags: {
- const auto tags = GetChannelLayoutTags(inScope, inElement);
- AUSDK_Require(!tags.empty(), kAudioUnitErr_InvalidProperty);
- AudioChannelLayoutTag* const ptr =
- outData != nullptr ? static_cast<AudioChannelLayoutTag*>(outData) : nullptr;
- if (ptr != nullptr) {
- memcpy(ptr, tags.data(), tags.size() * sizeof(AudioChannelLayoutTag));
- }
- break;
- }
-
- case kAudioUnitProperty_AudioChannelLayout: {
- AudioChannelLayout* const ptr =
- outData != nullptr ? static_cast<AudioChannelLayout*>(outData) : nullptr;
- bool writable = false;
- const UInt32 dataSize = GetAudioChannelLayout(inScope, inElement, ptr, writable);
- AUSDK_Require(dataSize != 0, kAudioUnitErr_InvalidProperty);
- break;
- }
-
- case kAudioUnitProperty_ShouldAllocateBuffer: {
- const auto& element = IOElement(inScope, inElement);
- *static_cast<UInt32*>(outData) = static_cast<UInt32>(element.WillAllocateBuffer());
- break;
- }
-
- case kAudioUnitProperty_ParameterValueStrings:
- result = GetParameterValueStrings(inScope, inElement, static_cast<CFArrayRef*>(outData));
- break;
-
- case kAudioUnitProperty_HostCallbacks:
- memcpy(outData, &mHostCallbackInfo, sizeof(mHostCallbackInfo));
- break;
-
- case kAudioUnitProperty_ContextName:
- if (const CFStringRef name = *mContextName) {
- CFRetain(name); // must return a +1 reference
- *static_cast<CFStringRef*>(outData) = name;
- result = noErr;
- } else {
- *static_cast<CFStringRef*>(outData) = nullptr;
- result = kAudioUnitErr_PropertyNotInUse;
- }
- break;
-
- #if !TARGET_OS_IPHONE
- case kAudioUnitProperty_IconLocation: {
- const CFURLRef iconLocation = CopyIconLocation();
- AUSDK_Require(iconLocation != nullptr, kAudioUnitErr_InvalidProperty);
- *static_cast<CFURLRef*>(outData) = iconLocation;
- break;
- }
- #endif
-
- case kAudioUnitProperty_ParameterClumpName: {
- auto* const ioClumpInfo = static_cast<AudioUnitParameterNameInfo*>(outData);
- AUSDK_Require(ioClumpInfo->inID != kAudioUnitClumpID_System,
- kAudioUnitErr_InvalidPropertyValue); // this ID value is reserved
- result = CopyClumpName(inScope, ioClumpInfo->inID,
- static_cast<UInt32>(std::max(ioClumpInfo->inDesiredLength, SInt32(0))),
- &ioClumpInfo->outName);
-
- // this is provided for compatbility with existing implementations that don't know
- // about this new mechanism
- if (result == kAudioUnitErr_InvalidProperty) {
- result = GetProperty(inID, inScope, inElement, outData);
- }
- break;
- }
-
- case 61: // kAudioUnitProperty_LastRenderSampleTime
- *static_cast<Float64*>(outData) = mCurrentRenderTime.mSampleTime;
- break;
-
- case kAudioUnitProperty_NickName:
- // Ownership follows Core Foundation's 'Copy Rule'
- if (const auto* const name = *mNickName) {
- CFRetain(name);
- *static_cast<CFStringRef*>(outData) = name;
- } else {
- *static_cast<CFStringRef*>(outData) = nullptr;
- }
- break;
-
- default:
- result = GetProperty(inID, inScope, inElement, outData);
- break;
- }
- return result;
- }
-
-
- //_____________________________________________________________________________
- //
- // Note: We can be sure inData is non-null; otherwise RemoveProperty would have been called.
- // NOLINTNEXTLINE(misc-no-recursion) with RestoreState
- OSStatus AUBase::DispatchSetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, const void* inData, UInt32 inDataSize)
- {
- OSStatus result = noErr;
-
- switch (inID) {
- case kAudioUnitProperty_MakeConnection: {
- AUSDK_Require(
- inDataSize >= sizeof(AudioUnitConnection), kAudioUnitErr_InvalidPropertyValue);
- const auto& connection = *static_cast<const AudioUnitConnection*>(inData);
- result = SetConnection(connection);
- break;
- }
-
- case kAudioUnitProperty_SetRenderCallback: {
- AUSDK_Require(
- inDataSize >= sizeof(AURenderCallbackStruct), kAudioUnitErr_InvalidPropertyValue);
- const auto& callback = *static_cast<const AURenderCallbackStruct*>(inData);
- result = SetInputCallback(kAudioUnitProperty_SetRenderCallback, inElement,
- callback.inputProc, callback.inputProcRefCon);
- break;
- }
-
- case kAudioUnitProperty_ElementCount:
- AUSDK_Require(inDataSize == sizeof(UInt32), kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require(BusCountWritable(inScope), kAudioUnitErr_PropertyNotWritable);
- result = SetBusCount(inScope, *static_cast<const UInt32*>(inData));
- if (result == noErr) {
- PropertyChanged(inID, inScope, inElement);
- }
- break;
-
- case kAudioUnitProperty_MaximumFramesPerSlice:
- AUSDK_Require(inDataSize == sizeof(UInt32), kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require_noerr(CanSetMaxFrames());
- SetMaxFramesPerSlice(*static_cast<const UInt32*>(inData));
- break;
-
- case kAudioUnitProperty_StreamFormat: {
- constexpr static UInt32 kMinimumValidASBDSize = 36;
- AUSDK_Require(inDataSize >= kMinimumValidASBDSize, kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require(GetElement(inScope, inElement) != nullptr, kAudioUnitErr_InvalidElement);
-
- AudioStreamBasicDescription newDesc = {};
- // now we're going to be ultra conservative! because of discrepancies between
- // sizes of this struct based on aligment padding inconsistencies
- memcpy(&newDesc, inData, kMinimumValidASBDSize);
-
- AUSDK_Require(ASBD::MinimalSafetyCheck(newDesc), kAudioUnitErr_FormatNotSupported);
-
- AUSDK_Require(ValidFormat(inScope, inElement, newDesc), kAudioUnitErr_FormatNotSupported);
-
- const AudioStreamBasicDescription curDesc = GetStreamFormat(inScope, inElement);
-
- if (!ASBD::IsEqual(curDesc, newDesc)) {
- AUSDK_Require(
- IsStreamFormatWritable(inScope, inElement), kAudioUnitErr_PropertyNotWritable);
- result = ChangeStreamFormat(inScope, inElement, curDesc, newDesc);
- }
- break;
- }
-
- case kAudioUnitProperty_SampleRate: {
- AUSDK_Require(inDataSize == sizeof(Float64), kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require(GetElement(inScope, inElement) != nullptr, kAudioUnitErr_InvalidElement);
-
- const AudioStreamBasicDescription curDesc = GetStreamFormat(inScope, inElement);
- AudioStreamBasicDescription newDesc = curDesc;
- newDesc.mSampleRate = *static_cast<const Float64*>(inData);
-
- AUSDK_Require(ValidFormat(inScope, inElement, newDesc), kAudioUnitErr_FormatNotSupported);
-
- if (!ASBD::IsEqual(curDesc, newDesc)) {
- AUSDK_Require(
- IsStreamFormatWritable(inScope, inElement), kAudioUnitErr_PropertyNotWritable);
- result = ChangeStreamFormat(inScope, inElement, curDesc, newDesc);
- }
- break;
- }
-
- case kAudioUnitProperty_AudioChannelLayout: {
- const auto& layout = *static_cast<const AudioChannelLayout*>(inData);
- constexpr size_t headerSize = sizeof(AudioChannelLayout) - sizeof(AudioChannelDescription);
-
- AUSDK_Require(inDataSize >= offsetof(AudioChannelLayout, mNumberChannelDescriptions) +
- sizeof(AudioChannelLayout::mNumberChannelDescriptions),
- kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require(inDataSize >= headerSize + layout.mNumberChannelDescriptions *
- sizeof(AudioChannelDescription),
- kAudioUnitErr_InvalidPropertyValue);
- result = SetAudioChannelLayout(inScope, inElement, &layout);
- if (result == noErr) {
- PropertyChanged(inID, inScope, inElement);
- }
- break;
- }
-
- case kAudioUnitProperty_ClassInfo:
- AUSDK_Require(inDataSize == sizeof(CFPropertyListRef*), kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- result = RestoreState(*static_cast<const CFPropertyListRef*>(inData));
- break;
-
- case kAudioUnitProperty_PresentPreset: {
- AUSDK_Require(inDataSize == sizeof(AUPreset), kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- const auto& newPreset = *static_cast<const AUPreset*>(inData);
-
- if (newPreset.presetNumber >= 0) {
- result = NewFactoryPresetSet(newPreset);
- // NewFactoryPresetSet SHOULD call SetAFactoryPreset if the preset is valid
- // from its own list of preset number->name
- if (result == noErr) {
- PropertyChanged(inID, inScope, inElement);
- }
- } else if (newPreset.presetName != nullptr) {
- result = NewCustomPresetSet(newPreset);
- if (result == noErr) {
- PropertyChanged(inID, inScope, inElement);
- }
- } else {
- result = kAudioUnitErr_InvalidPropertyValue;
- }
- break;
- }
-
- case kAudioUnitProperty_ElementName: {
- AUSDK_Require(GetElement(inScope, inElement) != nullptr, kAudioUnitErr_InvalidElement);
- AUSDK_Require(inDataSize == sizeof(CFStringRef), kAudioUnitErr_InvalidPropertyValue);
- const auto element = GetScope(inScope).GetElement(inElement);
- const CFStringRef inStr = *static_cast<const CFStringRef*>(inData);
- element->SetName(inStr);
- PropertyChanged(inID, inScope, inElement);
- break;
- }
-
- case kAudioUnitProperty_ShouldAllocateBuffer: {
- AUSDK_Require((inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output),
- kAudioUnitErr_InvalidScope);
- AUSDK_Require(GetElement(inScope, inElement) != nullptr, kAudioUnitErr_InvalidElement);
- AUSDK_Require(inDataSize == sizeof(UInt32), kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require(!IsInitialized(), kAudioUnitErr_Initialized);
-
- auto& element = IOElement(inScope, inElement);
- element.SetWillAllocateBuffer(*static_cast<const UInt32*>(inData) != 0);
- break;
- }
-
- case kAudioUnitProperty_HostCallbacks: {
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- const UInt32 availSize =
- std::min(inDataSize, static_cast<UInt32>(sizeof(HostCallbackInfo)));
- const bool hasChanged = memcmp(&mHostCallbackInfo, inData, availSize) == 0;
- mHostCallbackInfo = {};
- memcpy(&mHostCallbackInfo, inData, availSize);
- if (hasChanged) {
- PropertyChanged(inID, inScope, inElement);
- }
- break;
- }
-
- case kAudioUnitProperty_ContextName: {
- AUSDK_Require(inDataSize == sizeof(CFStringRef), kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- const CFStringRef inStr = *static_cast<const CFStringRef*>(inData);
- mContextName = inStr;
- PropertyChanged(inID, inScope, inElement);
- break;
- }
-
-
- case kAudioUnitProperty_NickName: {
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- AUSDK_Require(inDataSize == sizeof(CFStringRef), kAudioUnitErr_InvalidPropertyValue);
- const CFStringRef inStr = *static_cast<const CFStringRef*>(inData);
- mNickName = inStr;
- PropertyChanged(inID, inScope, inElement);
- break;
- }
-
- default:
- result = SetProperty(inID, inScope, inElement, inData, inDataSize);
- if (result == noErr) {
- PropertyChanged(inID, inScope, inElement);
- }
-
- break;
- }
- return result;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::DispatchRemovePropertyValue(
- AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement)
- {
- OSStatus result = noErr;
- switch (inID) {
- case kAudioUnitProperty_AudioChannelLayout: {
- result = RemoveAudioChannelLayout(inScope, inElement);
- if (result == noErr) {
- PropertyChanged(inID, inScope, inElement);
- }
- break;
- }
-
- case kAudioUnitProperty_HostCallbacks: {
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- bool hasValue = false;
- const void* const ptr = &mHostCallbackInfo;
- for (size_t i = 0; i < sizeof(HostCallbackInfo); ++i) {
- if (static_cast<const char*>(ptr)[i] != 0) { // NOLINT
- hasValue = true;
- break;
- }
- }
- if (hasValue) {
- mHostCallbackInfo = {};
- PropertyChanged(inID, inScope, inElement);
- }
- break;
- }
-
- case kAudioUnitProperty_ContextName:
- mContextName = nullptr;
- result = noErr;
- break;
-
- case kAudioUnitProperty_NickName: {
- AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
- mNickName = nullptr;
- PropertyChanged(inID, inScope, inElement);
- break;
- }
-
- default:
- result = RemovePropertyValue(inID, inScope, inElement);
- break;
- }
-
- return result;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::GetPropertyInfo(AudioUnitPropertyID /*inID*/, AudioUnitScope /*inScope*/,
- AudioUnitElement /*inElement*/, UInt32& /*outDataSize*/, bool& /*outWritable*/)
- {
- return kAudioUnitErr_InvalidProperty;
- }
-
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::GetProperty(AudioUnitPropertyID /*inID*/, AudioUnitScope /*inScope*/,
- AudioUnitElement /*inElement*/, void* /*outData*/)
- {
- return kAudioUnitErr_InvalidProperty;
- }
-
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::SetProperty(AudioUnitPropertyID /*inID*/, AudioUnitScope /*inScope*/,
- AudioUnitElement /*inElement*/, const void* /*inData*/, UInt32 /*inDataSize*/)
- {
- return kAudioUnitErr_InvalidProperty;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::RemovePropertyValue(
- AudioUnitPropertyID /*inID*/, AudioUnitScope /*inScope*/, AudioUnitElement /*inElement*/)
- {
- return kAudioUnitErr_InvalidPropertyValue;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::AddPropertyListener(
- AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcRefCon)
- {
- const PropertyListener pl{
- .propertyID = inID, .listenerProc = inProc, .listenerRefCon = inProcRefCon
- };
-
- if (mPropertyListeners.empty()) {
- mPropertyListeners.reserve(32); // NOLINT magic#
- }
- mPropertyListeners.push_back(pl);
-
- return noErr;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::RemovePropertyListener(AudioUnitPropertyID inID,
- AudioUnitPropertyListenerProc inProc, void* inProcRefCon, bool refConSpecified)
- {
- const auto iter =
- std::remove_if(mPropertyListeners.begin(), mPropertyListeners.end(), [&](auto& item) {
- return item.propertyID == inID && item.listenerProc == inProc &&
- (!refConSpecified || item.listenerRefCon == inProcRefCon);
- });
- if (iter != mPropertyListeners.end()) {
- mPropertyListeners.erase(iter, mPropertyListeners.end());
- }
- return noErr;
- }
-
- //_____________________________________________________________________________
- //
- void AUBase::PropertyChanged(
- AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement)
- {
- for (const auto& pl : mPropertyListeners) {
- if (pl.propertyID == inID) {
- (pl.listenerProc)(pl.listenerRefCon, GetComponentInstance(), inID, inScope, inElement);
- }
- }
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::SetRenderNotification(AURenderCallback inProc, void* inRefCon)
- {
- if (inProc == nullptr) {
- return kAudio_ParamError;
- }
-
- mRenderCallbacksTouched = true;
- mRenderCallbacks.add(RenderCallback(inProc, inRefCon));
- // this will do nothing if it's already in the list
- return noErr;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::RemoveRenderNotification(AURenderCallback inProc, void* inRefCon)
- {
- mRenderCallbacks.remove(RenderCallback(inProc, inRefCon));
- return noErr; // error?
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::GetParameter(AudioUnitParameterID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, AudioUnitParameterValue& outValue)
- {
- const auto& elem = Element(inScope, inElement);
- outValue = elem.GetParameter(inID);
- return noErr;
- }
-
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::SetParameter(AudioUnitParameterID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 /*inBufferOffsetInFrames*/)
- {
- auto& elem = Element(inScope, inElement);
- elem.SetParameter(inID, inValue);
- return noErr;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::ScheduleParameter(
- const AudioUnitParameterEvent* inParameterEvent, UInt32 inNumEvents)
- {
- const bool canScheduleParameters = CanScheduleParameters();
-
- for (UInt32 i = 0; i < inNumEvents; ++i) {
- const auto& pe = inParameterEvent[i]; // NOLINT subscript
- if (pe.eventType == kParameterEvent_Immediate) {
- SetParameter(pe.parameter, pe.scope, pe.element,
- pe.eventValues.immediate.value, // NOLINT union
- pe.eventValues.immediate.bufferOffset); // NOLINT union
- }
- if (canScheduleParameters) {
- mParamEventList.push_back(pe);
- }
- }
-
- return noErr;
- }
-
- // ____________________________________________________________________________
- //
- constexpr bool ParameterEventListSortPredicate(
- const AudioUnitParameterEvent& ev1, const AudioUnitParameterEvent& ev2) noexcept
- {
- // ramp.startBufferOffset is signed
- const SInt32 offset1 =
- ev1.eventType == kParameterEvent_Immediate
- ? static_cast<SInt32>(ev1.eventValues.immediate.bufferOffset) // NOLINT union
- : ev1.eventValues.ramp.startBufferOffset; // NOLINT union
- const SInt32 offset2 =
- ev2.eventType == kParameterEvent_Immediate
- ? static_cast<SInt32>(ev2.eventValues.immediate.bufferOffset) // NOLINT union
- : ev2.eventValues.ramp.startBufferOffset; // NOLINT union
-
- return offset1 < offset2;
- }
-
-
- // ____________________________________________________________________________
- //
- OSStatus AUBase::ProcessForScheduledParams(
- ParameterEventList& inParamList, UInt32 inFramesToProcess, void* inUserData)
- {
- OSStatus result = noErr;
-
- UInt32 framesRemaining = inFramesToProcess;
-
- UInt32 currentStartFrame = 0; // start of the whole buffer
-
-
- // sort the ParameterEventList by startBufferOffset
- std::sort(inParamList.begin(), inParamList.end(), ParameterEventListSortPredicate);
-
- while (framesRemaining > 0) {
- // first of all, go through the ramped automation events and find out where the next
- // division of our whole buffer will be
-
- UInt32 currentEndFrame = inFramesToProcess; // start out assuming we'll process all the way
- // to the end of the buffer
-
- // find the next break point
- for (const auto& event : inParamList) {
- SInt32 offset =
- event.eventType == kParameterEvent_Immediate
- ? static_cast<SInt32>(event.eventValues.immediate.bufferOffset) // NOLINT
- : event.eventValues.ramp.startBufferOffset;
-
- if (offset > static_cast<SInt32>(currentStartFrame) &&
- offset < static_cast<SInt32>(currentEndFrame)) {
- currentEndFrame = static_cast<UInt32>(offset);
- break;
- }
-
- // consider ramp end to be a possible choice (there may be gaps in the supplied ramp
- // events)
- if (event.eventType == kParameterEvent_Ramped) {
- offset = event.eventValues.ramp.startBufferOffset +
- static_cast<SInt32>(event.eventValues.ramp.durationInFrames); // NOLINT
-
- if (offset > static_cast<SInt32>(currentStartFrame) &&
- offset < static_cast<SInt32>(currentEndFrame)) {
- currentEndFrame = static_cast<UInt32>(offset);
- }
- }
- }
-
- const UInt32 framesThisTime = currentEndFrame - currentStartFrame;
-
- // next, setup the parameter maps to be current for the ramp parameters active during
- // this time segment...
-
- for (const auto& event : inParamList) {
- bool eventFallsInSlice = false;
-
- if (event.eventType == kParameterEvent_Ramped) {
- const auto& ramp = event.eventValues.ramp;
- eventFallsInSlice =
- ramp.startBufferOffset < static_cast<SInt32>(currentEndFrame) &&
- (ramp.startBufferOffset + static_cast<SInt32>(ramp.durationInFrames)) >
- static_cast<SInt32>(currentStartFrame);
- } else { /* kParameterEvent_Immediate */
- // actually, for the same parameter, there may be future immediate events which
- // override this one, but it's OK since the event list is sorted in time order,
- // we're guaranteed to end up with the current one
- eventFallsInSlice = event.eventValues.immediate.bufferOffset <= currentStartFrame;
- }
-
- if (eventFallsInSlice) {
- AUElement* const element = GetElement(event.scope, event.element);
-
- if (element != nullptr) {
- element->SetScheduledEvent(event.parameter, event, currentStartFrame,
- currentEndFrame - currentStartFrame);
- }
- }
- }
-
-
- // Finally, actually do the processing for this slice.....
-
- result =
- ProcessScheduledSlice(inUserData, currentStartFrame, framesThisTime, inFramesToProcess);
-
- if (result != noErr) {
- break;
- }
-
- framesRemaining -= std::min(framesThisTime, framesRemaining);
- currentStartFrame = currentEndFrame; // now start from where we left off last time
- }
-
- return result;
- }
-
- //_____________________________________________________________________________
- //
- void AUBase::ResetRenderTime()
- {
- mCurrentRenderTime = {};
- mCurrentRenderTime.mSampleTime = kNoLastRenderedSampleTime;
- }
-
- //_____________________________________________________________________________
- //
- void AUBase::SetWantsRenderThreadID(bool inFlag)
- {
- if (inFlag == mWantsRenderThreadID) {
- return;
- }
-
- mWantsRenderThreadID = inFlag;
- if (!mWantsRenderThreadID) {
- mRenderThreadID = {};
- };
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::DoRender(AudioUnitRenderActionFlags& ioActionFlags,
- const AudioTimeStamp& inTimeStamp, UInt32 inBusNumber, UInt32 inFramesToProcess,
- AudioBufferList& ioData)
- {
- const auto errorExit = [this](OSStatus error) {
- AUSDK_LogError(" from %s, render err: %d", GetLoggingString(), static_cast<int>(error));
- SetRenderError(error);
- return error;
- };
-
- OSStatus theError = noErr;
-
- [[maybe_unused]] const DenormalDisabler denormalDisabler;
-
- try {
- AUSDK_Require(IsInitialized(), errorExit(kAudioUnitErr_Uninitialized));
- if (inFramesToProcess > mMaxFramesPerSlice) {
- #ifndef AUSDK_NO_LOGGING
- static UInt64 lastTimeMessagePrinted = 0;
- const UInt64 now = HostTime::Current();
- if (static_cast<double>(now - lastTimeMessagePrinted) >
- mHostTimeFrequency) { // not more than once per second.
- lastTimeMessagePrinted = now;
- AUSDK_LogError("kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=%u, "
- "mMaxFramesPerSlice=%u",
- static_cast<unsigned>(inFramesToProcess),
- static_cast<unsigned>(mMaxFramesPerSlice));
- }
- #endif
- return errorExit(kAudioUnitErr_TooManyFramesToProcess);
- }
- AUSDK_Require(!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(),
- errorExit(kAudio_ParamError));
-
- auto& output = Output(inBusNumber); // will throw if non-existant
- if (ASBD::NumberChannelStreams(output.GetStreamFormat()) != ioData.mNumberBuffers) {
- AUSDK_LogError(
- "ioData.mNumberBuffers=%u, "
- "ASBD::NumberChannelStreams(output.GetStreamFormat())=%u; kAudio_ParamError",
- static_cast<unsigned>(ioData.mNumberBuffers),
- static_cast<unsigned>(ASBD::NumberChannelStreams(output.GetStreamFormat())));
- return errorExit(kAudio_ParamError);
- }
-
- const unsigned expectedBufferByteSize =
- inFramesToProcess * output.GetStreamFormat().mBytesPerFrame;
- for (unsigned ibuf = 0; ibuf < ioData.mNumberBuffers; ++ibuf) {
- AudioBuffer& buf = ioData.mBuffers[ibuf]; // NOLINT
- if (buf.mData != nullptr) {
- // only care about the size if the buffer is non-null
- if (buf.mDataByteSize < expectedBufferByteSize) {
- // if the buffer is too small, we cannot render safely. kAudio_ParamError.
- AUSDK_LogError("%u frames, %u bytes/frame, expected %u-byte buffer; "
- "ioData.mBuffers[%u].mDataByteSize=%u; kAudio_ParamError",
- static_cast<unsigned>(inFramesToProcess),
- static_cast<unsigned>(output.GetStreamFormat().mBytesPerFrame),
- expectedBufferByteSize, ibuf, static_cast<unsigned>(buf.mDataByteSize));
- return errorExit(kAudio_ParamError);
- }
- // Some clients incorrectly pass bigger buffers than expectedBufferByteSize.
- // We will generally set the buffer size at the end of rendering, before we return.
- // However we should ensure that no one, DURING rendering, READS a
- // potentially incorrect size. This can lead to doing too much work, or
- // reading past the end of an input buffer into unmapped memory.
- buf.mDataByteSize = expectedBufferByteSize;
- }
- }
-
- if (WantsRenderThreadID()) {
- mRenderThreadID = std::this_thread::get_id();
- }
-
- if (mRenderCallbacksTouched) {
- AudioUnitRenderActionFlags flags = ioActionFlags | kAudioUnitRenderAction_PreRender;
- mRenderCallbacks.foreach ([&](const RenderCallback& rc) {
- (*static_cast<AURenderCallback>(rc.mRenderNotify))(rc.mRenderNotifyRefCon, &flags,
- &inTimeStamp, inBusNumber, inFramesToProcess, &ioData);
- });
- }
-
- theError =
- DoRenderBus(ioActionFlags, inTimeStamp, inBusNumber, output, inFramesToProcess, ioData);
-
- SetRenderError(theError);
-
- if (mRenderCallbacksTouched) {
- AudioUnitRenderActionFlags flags = ioActionFlags | kAudioUnitRenderAction_PostRender;
-
- if (theError != noErr) {
- flags |= kAudioUnitRenderAction_PostRenderError;
- }
-
- mRenderCallbacks.foreach ([&](const RenderCallback& rc) {
- (*static_cast<AURenderCallback>(rc.mRenderNotify))(rc.mRenderNotifyRefCon, &flags,
- &inTimeStamp, inBusNumber, inFramesToProcess, &ioData);
- });
- }
-
- // The vector's being emptied
- // because these events should only apply to this Render cycle, so anything
- // left over is from a preceding cycle and should be dumped. New scheduled
- // parameters must be scheduled from the next pre-render callback.
- if (!mParamEventList.empty()) {
- mParamEventList.clear();
- }
- } catch (const OSStatus& err) {
- return errorExit(err);
- } catch (...) {
- return errorExit(-1);
- }
- return theError;
- }
-
- inline bool CheckRenderArgs(AudioUnitRenderActionFlags flags)
- {
- return (flags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0u;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::DoProcess(AudioUnitRenderActionFlags& ioActionFlags,
- const AudioTimeStamp& inTimeStamp, UInt32 inFramesToProcess, AudioBufferList& ioData)
- {
- const auto errorExit = [this](OSStatus error) {
- AUSDK_LogError(" from %s, process err: %d", GetLoggingString(), static_cast<int>(error));
- SetRenderError(error);
- return error;
- };
-
- OSStatus theError = noErr;
-
- [[maybe_unused]] const DenormalDisabler denormalDisabler;
-
- try {
- if (CheckRenderArgs(ioActionFlags)) {
- AUSDK_Require(IsInitialized(), errorExit(kAudioUnitErr_Uninitialized));
- AUSDK_Require(inFramesToProcess <= mMaxFramesPerSlice,
- errorExit(kAudioUnitErr_TooManyFramesToProcess));
- AUSDK_Require(!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(),
- errorExit(kAudio_ParamError));
-
- const auto& input = Input(0); // will throw if non-existant
- if (ASBD::NumberChannelStreams(input.GetStreamFormat()) != ioData.mNumberBuffers) {
- AUSDK_LogError(
- "ioData.mNumberBuffers=%u, "
- "ASBD::NumberChannelStreams(input->GetStreamFormat())=%u; kAudio_ParamError",
- static_cast<unsigned>(ioData.mNumberBuffers),
- static_cast<unsigned>(ASBD::NumberChannelStreams(input.GetStreamFormat())));
- return errorExit(kAudio_ParamError);
- }
-
- const unsigned expectedBufferByteSize =
- inFramesToProcess * input.GetStreamFormat().mBytesPerFrame;
- for (unsigned ibuf = 0; ibuf < ioData.mNumberBuffers; ++ibuf) {
- AudioBuffer& buf = ioData.mBuffers[ibuf]; // NOLINT
- if (buf.mData != nullptr) {
- // only care about the size if the buffer is non-null
- if (buf.mDataByteSize < expectedBufferByteSize) {
- // if the buffer is too small, we cannot render safely. kAudio_ParamError.
- AUSDK_LogError("%u frames, %u bytes/frame, expected %u-byte buffer; "
- "ioData.mBuffers[%u].mDataByteSize=%u; kAudio_ParamError",
- static_cast<unsigned>(inFramesToProcess),
- static_cast<unsigned>(input.GetStreamFormat().mBytesPerFrame),
- expectedBufferByteSize, ibuf, static_cast<unsigned>(buf.mDataByteSize));
- return errorExit(kAudio_ParamError);
- }
- // Some clients incorrectly pass bigger buffers than expectedBufferByteSize.
- // We will generally set the buffer size at the end of rendering, before we
- // return. However we should ensure that no one, DURING rendering, READS a
- // potentially incorrect size. This can lead to doing too much work, or
- // reading past the end of an input buffer into unmapped memory.
- buf.mDataByteSize = expectedBufferByteSize;
- }
- }
- }
-
- if (WantsRenderThreadID()) {
- mRenderThreadID = std::this_thread::get_id();
- }
-
- if (NeedsToRender(inTimeStamp)) {
- theError = ProcessBufferLists(ioActionFlags, ioData, ioData, inFramesToProcess);
- } else {
- theError = noErr;
- }
-
- } catch (const OSStatus& err) {
- return errorExit(err);
- } catch (...) {
- return errorExit(-1);
- }
- return theError;
- }
-
- OSStatus AUBase::DoProcessMultiple(AudioUnitRenderActionFlags& ioActionFlags,
- const AudioTimeStamp& inTimeStamp, UInt32 inFramesToProcess, UInt32 inNumberInputBufferLists,
- const AudioBufferList** inInputBufferLists, UInt32 inNumberOutputBufferLists,
- AudioBufferList** ioOutputBufferLists)
- {
- const auto errorExit = [this](OSStatus error) {
- AUSDK_LogError(
- " from %s, processmultiple err: %d", GetLoggingString(), static_cast<int>(error));
- SetRenderError(error);
- return error;
- };
-
- OSStatus theError = noErr;
-
- [[maybe_unused]] const DenormalDisabler denormalDisabler;
-
- try {
- if (CheckRenderArgs(ioActionFlags)) {
- AUSDK_Require(IsInitialized(), errorExit(kAudioUnitErr_Uninitialized));
- AUSDK_Require(inFramesToProcess <= mMaxFramesPerSlice,
- errorExit(kAudioUnitErr_TooManyFramesToProcess));
- AUSDK_Require(!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(),
- errorExit(kAudio_ParamError));
-
- for (unsigned ibl = 0; ibl < inNumberInputBufferLists; ++ibl) {
- if (inInputBufferLists[ibl] != nullptr) { // NOLINT
- const auto& input = Input(ibl); // will throw if non-existant
- const unsigned expectedBufferByteSize =
- inFramesToProcess * input.GetStreamFormat().mBytesPerFrame;
-
- if (ASBD::NumberChannelStreams(input.GetStreamFormat()) !=
- inInputBufferLists[ibl]->mNumberBuffers) { // NOLINT
- AUSDK_LogError("inInputBufferLists[%u]->mNumberBuffers=%u, "
- "ASBD::NumberChannelStreams(input.GetStreamFormat())=%u; "
- "kAudio_ParamError",
- ibl, static_cast<unsigned>(inInputBufferLists[ibl]->mNumberBuffers),
- static_cast<unsigned>(
- ASBD::NumberChannelStreams(input.GetStreamFormat())));
- return errorExit(kAudio_ParamError);
- }
-
- for (unsigned ibuf = 0;
- ibuf < inInputBufferLists[ibl]->mNumberBuffers; // NOLINT
- ++ibuf) {
- const AudioBuffer& buf = inInputBufferLists[ibl]->mBuffers[ibuf]; // NOLINT
- if (buf.mData != nullptr) {
- if (buf.mDataByteSize < expectedBufferByteSize) {
- // the buffer is too small
- AUSDK_LogError(
- "%u frames, %u bytes/frame, expected %u-byte buffer; "
- "inInputBufferLists[%u].mBuffers[%u].mDataByteSize=%u; "
- "kAudio_ParamError",
- static_cast<unsigned>(inFramesToProcess),
- static_cast<unsigned>(input.GetStreamFormat().mBytesPerFrame),
- expectedBufferByteSize, ibl, ibuf,
- static_cast<unsigned>(buf.mDataByteSize));
- return errorExit(kAudio_ParamError);
- }
- } else {
- // the buffer must exist
- return errorExit(kAudio_ParamError);
- }
- }
- } else {
- // skip NULL input audio buffer list
- }
- }
-
- for (unsigned obl = 0; obl < inNumberOutputBufferLists; ++obl) {
- if (ioOutputBufferLists[obl] != nullptr) { // NOLINT
- const auto& output = Output(obl); // will throw if non-existant
- const unsigned expectedBufferByteSize =
- inFramesToProcess * output.GetStreamFormat().mBytesPerFrame;
-
- if (ASBD::NumberChannelStreams(output.GetStreamFormat()) !=
- ioOutputBufferLists[obl]->mNumberBuffers) { // NOLINT
- AUSDK_LogError("ioOutputBufferLists[%u]->mNumberBuffers=%u, "
- "ASBD::NumberChannelStreams(output.GetStreamFormat())=%u; "
- "kAudio_ParamError",
- obl, static_cast<unsigned>(ioOutputBufferLists[obl]->mNumberBuffers),
- static_cast<unsigned>(
- ASBD::NumberChannelStreams(output.GetStreamFormat())));
- return errorExit(kAudio_ParamError);
- }
-
- for (unsigned obuf = 0;
- obuf < ioOutputBufferLists[obl]->mNumberBuffers; // NOLINT
- ++obuf) {
- AudioBuffer& buf = ioOutputBufferLists[obl]->mBuffers[obuf]; // NOLINT
- if (buf.mData != nullptr) {
- // only care about the size if the buffer is non-null
- if (buf.mDataByteSize < expectedBufferByteSize) {
- // if the buffer is too small, we cannot render safely.
- // kAudio_ParamError.
- AUSDK_LogError(
- "%u frames, %u bytes/frame, expected %u-byte buffer; "
- "ioOutputBufferLists[%u]->mBuffers[%u].mDataByteSize=%u; "
- "kAudio_ParamError",
- static_cast<unsigned>(inFramesToProcess),
- static_cast<unsigned>(output.GetStreamFormat().mBytesPerFrame),
- expectedBufferByteSize, obl, obuf,
- static_cast<unsigned>(buf.mDataByteSize));
- return errorExit(kAudio_ParamError);
- }
- // Some clients incorrectly pass bigger buffers than
- // expectedBufferByteSize. We will generally set the buffer size at the
- // end of rendering, before we return. However we should ensure that no
- // one, DURING rendering, READS a potentially incorrect size. This can
- // lead to doing too much work, or reading past the end of an input
- // buffer into unmapped memory.
- buf.mDataByteSize = expectedBufferByteSize;
- }
- }
- } else {
- // skip NULL output audio buffer list
- }
- }
- }
-
- if (WantsRenderThreadID()) {
- mRenderThreadID = std::this_thread::get_id();
- }
-
- if (NeedsToRender(inTimeStamp)) {
- theError = ProcessMultipleBufferLists(ioActionFlags, inFramesToProcess,
- inNumberInputBufferLists, inInputBufferLists, inNumberOutputBufferLists,
- ioOutputBufferLists);
- } else {
- theError = noErr;
- }
- } catch (const OSStatus& err) {
- return errorExit(err);
- } catch (...) {
- return errorExit(-1);
- }
- return theError;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::SetInputCallback(
- UInt32 inPropertyID, AudioUnitElement inElement, AURenderCallback inProc, void* inRefCon)
- {
- auto& input = Input(inElement); // may throw
-
- input.SetInputCallback(inProc, inRefCon);
- PropertyChanged(inPropertyID, kAudioUnitScope_Input, inElement);
-
- return noErr;
- }
-
- //_____________________________________________________________________________
- //
- // NOLINTNEXTLINE(misc-no-recursion) with DispatchSetProperty
- OSStatus AUBase::SetConnection(const AudioUnitConnection& inConnection)
- {
- auto& input = Input(inConnection.destInputNumber); // may throw
-
- if (inConnection.sourceAudioUnit != nullptr) {
- // connecting, not disconnecting
- AudioStreamBasicDescription sourceDesc;
- UInt32 size = sizeof(AudioStreamBasicDescription);
- AUSDK_Require_noerr(
- AudioUnitGetProperty(inConnection.sourceAudioUnit, kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Output, inConnection.sourceOutputNumber, &sourceDesc, &size));
- AUSDK_Require_noerr(
- DispatchSetProperty(kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
- inConnection.destInputNumber, &sourceDesc, sizeof(AudioStreamBasicDescription)));
- }
- input.SetConnection(inConnection);
-
- PropertyChanged(
- kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, inConnection.destInputNumber);
- return noErr;
- }
-
- //_____________________________________________________________________________
- //
- UInt32 AUBase::SupportedNumChannels(const AUChannelInfo** /*outInfo*/) { return 0; }
-
- //_____________________________________________________________________________
- //
- bool AUBase::ValidFormat(AudioUnitScope /*inScope*/, AudioUnitElement /*inElement*/,
- const AudioStreamBasicDescription& inNewFormat)
- {
- return ASBD::IsCommonFloat32(inNewFormat) &&
- (!ASBD::IsInterleaved(inNewFormat) || inNewFormat.mChannelsPerFrame == 1);
- }
-
- //_____________________________________________________________________________
- //
- bool AUBase::IsStreamFormatWritable(AudioUnitScope scope, AudioUnitElement element)
- {
- switch (scope) {
- case kAudioUnitScope_Input: {
- const auto& input = Input(element);
- if (input.HasConnection()) {
- return false; // can't write format when input comes from connection
- }
- [[fallthrough]];
- }
- case kAudioUnitScope_Output:
- return StreamFormatWritable(scope, element);
-
- //#warning "aliasing of global scope format should be pushed to subclasses"
- case kAudioUnitScope_Global:
- return StreamFormatWritable(kAudioUnitScope_Output, 0);
- default:
- break;
- }
- return false;
- }
-
- //_____________________________________________________________________________
- //
- AudioStreamBasicDescription AUBase::GetStreamFormat(
- AudioUnitScope inScope, AudioUnitElement inElement)
- {
- //#warning "aliasing of global scope format should be pushed to subclasses"
- AUIOElement* element = nullptr;
-
- switch (inScope) {
- case kAudioUnitScope_Input:
- element = Inputs().GetIOElement(inElement);
- break;
- case kAudioUnitScope_Output:
- element = Outputs().GetIOElement(inElement);
- break;
- case kAudioUnitScope_Global: // global stream description is an alias for that of output 0
- element = Outputs().GetIOElement(0);
- break;
- default:
- Throw(kAudioUnitErr_InvalidScope);
- }
- return element->GetStreamFormat();
- }
-
- OSStatus AUBase::SetBusCount(AudioUnitScope inScope, UInt32 inCount)
- {
- if (IsInitialized()) {
- return kAudioUnitErr_Initialized;
- }
-
- GetScope(inScope).SetNumberOfElements(inCount);
- return noErr;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::ChangeStreamFormat(AudioUnitScope inScope, AudioUnitElement inElement,
- const AudioStreamBasicDescription& inPrevFormat, const AudioStreamBasicDescription& inNewFormat)
- {
- if (ASBD::IsEqual(inNewFormat, inPrevFormat)) {
- return noErr;
- }
-
- //#warning "aliasing of global scope format should be pushed to subclasses"
- AUIOElement* element = nullptr;
-
- switch (inScope) {
- case kAudioUnitScope_Input:
- element = Inputs().GetIOElement(inElement);
- break;
- case kAudioUnitScope_Output:
- element = Outputs().GetIOElement(inElement);
- break;
- case kAudioUnitScope_Global:
- element = Outputs().GetIOElement(0);
- break;
- default:
- Throw(kAudioUnitErr_InvalidScope);
- }
- element->SetStreamFormat(inNewFormat);
- PropertyChanged(kAudioUnitProperty_StreamFormat, inScope, inElement);
- return noErr;
- }
-
- std::vector<AudioChannelLayoutTag> AUBase::GetChannelLayoutTags(
- AudioUnitScope inScope, AudioUnitElement inElement)
- {
- return IOElement(inScope, inElement).GetChannelLayoutTags();
- }
-
- UInt32 AUBase::GetAudioChannelLayout(AudioUnitScope scope, AudioUnitElement element,
- AudioChannelLayout* outLayoutPtr, bool& outWritable)
- {
- auto& el = IOElement(scope, element);
- return el.GetAudioChannelLayout(outLayoutPtr, outWritable);
- }
-
- OSStatus AUBase::RemoveAudioChannelLayout(AudioUnitScope inScope, AudioUnitElement inElement)
- {
- OSStatus result = noErr;
- auto& el = IOElement(inScope, inElement);
- bool writable = false;
- if (el.GetAudioChannelLayout(nullptr, writable) > 0) {
- result = el.RemoveAudioChannelLayout();
- }
- return result;
- }
-
- OSStatus AUBase::SetAudioChannelLayout(
- AudioUnitScope inScope, AudioUnitElement inElement, const AudioChannelLayout* inLayout)
- {
- auto& ioEl = IOElement(inScope, inElement);
-
- // the num channels of the layout HAS TO MATCH the current channels of the Element's stream
- // format
- const UInt32 currentChannels = ioEl.GetStreamFormat().mChannelsPerFrame;
- const UInt32 numChannelsInLayout = AUChannelLayout::NumberChannels(*inLayout);
- if (currentChannels != numChannelsInLayout) {
- return kAudioUnitErr_InvalidPropertyValue;
- }
-
- const auto tags = GetChannelLayoutTags(inScope, inElement);
- if (tags.empty()) {
- return kAudioUnitErr_InvalidProperty;
- }
- const auto inTag = inLayout->mChannelLayoutTag;
- const auto iter = std::find_if(tags.begin(), tags.end(), [&inTag](auto& tag) {
- return tag == inTag || tag == kAudioChannelLayoutTag_UseChannelDescriptions;
- });
-
- if (iter == tags.end()) {
- return kAudioUnitErr_InvalidPropertyValue;
- }
-
- return ioEl.SetAudioChannelLayout(*inLayout);
- }
-
- constexpr int kCurrentSavedStateVersion = 0;
-
- static void AddNumToDictionary(CFMutableDictionaryRef dict, CFStringRef key, SInt32 value)
- {
- const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value);
- CFDictionarySetValue(dict, key, num);
- CFRelease(num);
- }
-
- // NOLINTNEXTLINE(misc-no-recursion) with DispatchGetProperty
- OSStatus AUBase::SaveState(CFPropertyListRef* outData)
- {
- const AudioComponentDescription desc = GetComponentDescription();
-
- auto dict = Owned<CFMutableDictionaryRef>::from_create(CFDictionaryCreateMutable(
- nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
-
- // first step -> save the version to the data ref
- SInt32 value = kCurrentSavedStateVersion;
- AddNumToDictionary(*dict, kVersionString, value);
-
- // second step -> save the component type, subtype, manu to the data ref
- value = static_cast<SInt32>(desc.componentType);
- AddNumToDictionary(*dict, kTypeString, value);
-
- value = static_cast<SInt32>(desc.componentSubType);
- AddNumToDictionary(*dict, kSubtypeString, value);
-
- value = static_cast<SInt32>(desc.componentManufacturer);
- AddNumToDictionary(*dict, kManufacturerString, value);
-
- // fourth step -> save the state of all parameters on all scopes and elements
- auto data = Owned<CFMutableDataRef>::from_create(CFDataCreateMutable(nullptr, 0));
- for (AudioUnitScope iscope = 0; iscope < 3; ++iscope) {
- const auto& scope = GetScope(iscope);
- scope.SaveState(*data);
- }
-
- SaveExtendedScopes(*data);
-
- // save all this in the data section of the dictionary
- CFDictionarySetValue(*dict, kDataString, *data);
- data = nullptr; // data can be large-ish, so destroy it now.
-
- // OK - now we're going to do some properties
- // save the preset name...
- CFDictionarySetValue(*dict, kNameString, mCurrentPreset.presetName);
-
- // Does the unit support the RenderQuality property - if so, save it...
- OSStatus result =
- DispatchGetProperty(kAudioUnitProperty_RenderQuality, kAudioUnitScope_Global, 0, &value);
-
- if (result == noErr) {
- AddNumToDictionary(*dict, kRenderQualityString, value);
- }
-
- // Do we have any element names for any of our scopes?
- // first check to see if we have any names...
- bool foundName = false;
- for (AudioUnitScope i = 0; i < kNumScopes; ++i) {
- foundName = GetScope(i).HasElementWithName();
- if (foundName) {
- break;
- }
- }
- // OK - we found a name away we go...
- if (foundName) {
- auto nameDict = Owned<CFMutableDictionaryRef>::from_create(CFDictionaryCreateMutable(
- nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
- for (AudioUnitScope i = 0; i < kNumScopes; ++i) {
- GetScope(i).AddElementNamesToDict(*nameDict);
- }
-
- CFDictionarySetValue(*dict, kElementNameString, *nameDict);
- }
-
- // we're done!!!
- *outData = static_cast<CFPropertyListRef>(dict.release()); // transfer ownership
-
- return noErr;
- }
-
- //_____________________________________________________________________________
- //
- // NOLINTNEXTLINE(misc-no-recursion) with DispatchSetProperty
- OSStatus AUBase::RestoreState(CFPropertyListRef plist)
- {
- if (CFGetTypeID(plist) != CFDictionaryGetTypeID()) {
- return kAudioUnitErr_InvalidPropertyValue;
- }
-
- const AudioComponentDescription desc = GetComponentDescription();
-
- const auto* const dict = static_cast<CFDictionaryRef>(plist);
-
- // zeroeth step - make sure the Part key is NOT present, as this method is used
- // to restore the GLOBAL state of the dictionary
- if (CFDictionaryContainsKey(dict, kPartString)) {
- return kAudioUnitErr_InvalidPropertyValue;
- }
-
- // first step -> check the saved version in the data ref
- // at this point we're only dealing with version==0
- const auto* cfnum = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kVersionString));
- AUSDK_Require(cfnum != nullptr, kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require(CFGetTypeID(cfnum) == CFNumberGetTypeID(), kAudioUnitErr_InvalidPropertyValue);
- SInt32 value = 0;
- CFNumberGetValue(cfnum, kCFNumberSInt32Type, &value);
- if (value != kCurrentSavedStateVersion) {
- return kAudioUnitErr_InvalidPropertyValue;
- }
-
- // second step -> check that this data belongs to this kind of audio unit
- // by checking the component subtype and manuID
- // We're not checking the type, since there may be different versions (effect, format-converter,
- // offline) of essentially the same AU
- cfnum = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kSubtypeString));
- AUSDK_Require(cfnum != nullptr, kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require(CFGetTypeID(cfnum) == CFNumberGetTypeID(), kAudioUnitErr_InvalidPropertyValue);
- CFNumberGetValue(cfnum, kCFNumberSInt32Type, &value);
- if (static_cast<UInt32>(value) != desc.componentSubType) {
- return kAudioUnitErr_InvalidPropertyValue;
- }
-
- cfnum = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kManufacturerString));
- AUSDK_Require(cfnum != nullptr, kAudioUnitErr_InvalidPropertyValue);
- AUSDK_Require(CFGetTypeID(cfnum) == CFNumberGetTypeID(), kAudioUnitErr_InvalidPropertyValue);
- CFNumberGetValue(cfnum, kCFNumberSInt32Type, &value);
- if (static_cast<UInt32>(value) != desc.componentManufacturer) {
- return kAudioUnitErr_InvalidPropertyValue;
- }
-
- // fourth step -> restore the state of all of the parameters for each scope and element
- const auto* const data = static_cast<CFDataRef>(CFDictionaryGetValue(dict, kDataString));
- if ((data != nullptr) && (CFGetTypeID(data) == CFDataGetTypeID())) {
- const UInt8* p = CFDataGetBytePtr(data);
- const UInt8* const pend = p + CFDataGetLength(data); // NOLINT
-
- // we have a zero length data, which may just mean there were no parameters to save!
- // if (p >= pend) return noErr;
-
- while (p < pend) {
- const UInt32 scopeIdx =
- CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p)); // NOLINT
- p += sizeof(UInt32); // NOLINT
-
- const auto& scope = GetScope(scopeIdx);
- p = scope.RestoreState(p);
- }
- }
-
- // OK - now we're going to do some properties
- // restore the preset name...
- const auto* const name = static_cast<CFStringRef>(CFDictionaryGetValue(dict, kNameString));
- if (mCurrentPreset.presetName != nullptr) {
- CFRelease(mCurrentPreset.presetName);
- }
- if ((name != nullptr) && (CFGetTypeID(name) == CFStringGetTypeID())) {
- mCurrentPreset.presetName = name;
- mCurrentPreset.presetNumber = -1;
- } else { // no name entry make the default one
- mCurrentPreset.presetName = kUntitledString;
- mCurrentPreset.presetNumber = -1;
- }
-
- CFRetain(mCurrentPreset.presetName);
- PropertyChanged(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
-
- // Does the dict contain render quality information?
- cfnum = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kRenderQualityString));
- if (cfnum && (CFGetTypeID(cfnum) == CFNumberGetTypeID())) {
- CFNumberGetValue(cfnum, kCFNumberSInt32Type, &value);
- DispatchSetProperty(
- kAudioUnitProperty_RenderQuality, kAudioUnitScope_Global, 0, &value, sizeof(value));
- }
-
- // Do we have any element names for any of our scopes?
- const auto nameDict =
- static_cast<CFDictionaryRef>(CFDictionaryGetValue(dict, kElementNameString));
- if (nameDict && (CFGetTypeID(nameDict) == CFDictionaryGetTypeID())) {
- for (AudioUnitScope i = 0; i < kNumScopes; ++i) {
- const CFStringRef key = CFStringCreateWithFormat(
- nullptr, nullptr, CFSTR("%u"), static_cast<unsigned>(i)); // NOLINT
- const auto elementDict =
- static_cast<CFDictionaryRef>(CFDictionaryGetValue(nameDict, key));
- if (elementDict && (CFGetTypeID(elementDict) == CFDictionaryGetTypeID())) {
- const auto restoredElements = GetScope(i).RestoreElementNames(elementDict);
- for (const auto& element : restoredElements) {
- PropertyChanged(kAudioUnitProperty_ElementName, i, element);
- }
- }
- CFRelease(key);
- }
- }
-
- return noErr;
- }
-
- OSStatus AUBase::GetPresets(CFArrayRef* /*outData*/) const { return kAudioUnitErr_InvalidProperty; }
-
- OSStatus AUBase::NewFactoryPresetSet(const AUPreset& /*inNewFactoryPreset*/)
- {
- return kAudioUnitErr_InvalidProperty;
- }
-
- OSStatus AUBase::NewCustomPresetSet(const AUPreset& inNewCustomPreset)
- {
- CFRelease(mCurrentPreset.presetName);
- mCurrentPreset = inNewCustomPreset;
- CFRetain(mCurrentPreset.presetName);
- return noErr;
- }
-
- // set the default preset for the unit -> the number of the preset MUST be >= 0
- // and the name should be valid, or the preset WON'T take
- bool AUBase::SetAFactoryPresetAsCurrent(const AUPreset& inPreset)
- {
- if (inPreset.presetNumber < 0 || inPreset.presetName == nullptr) {
- return false;
- }
- CFRelease(mCurrentPreset.presetName);
- mCurrentPreset = inPreset;
- CFRetain(mCurrentPreset.presetName);
- return true;
- }
-
- bool AUBase::HasIcon()
- {
- const CFURLRef url = CopyIconLocation();
- if (url != nullptr) {
- CFRelease(url);
- return true;
- }
- return false;
- }
-
- CFURLRef AUBase::CopyIconLocation() { return nullptr; }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::GetParameterList(
- AudioUnitScope inScope, AudioUnitParameterID* outParameterList, UInt32& outNumParameters)
- {
- const auto& scope = GetScope(inScope);
- AUElement* elementWithMostParameters = nullptr;
- UInt32 maxNumParams = 0;
-
- const UInt32 nElems = scope.GetNumberOfElements();
- for (UInt32 ielem = 0; ielem < nElems; ++ielem) {
- AUElement* const element = scope.GetElement(ielem);
- const UInt32 nParams = element->GetNumberOfParameters();
- if (nParams > maxNumParams) {
- maxNumParams = nParams;
- elementWithMostParameters = element;
- }
- }
-
- if (outParameterList != nullptr && elementWithMostParameters != nullptr) {
- elementWithMostParameters->GetParameterList(outParameterList);
- }
-
- outNumParameters = maxNumParams;
- return noErr;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::GetParameterInfo(AudioUnitScope /*inScope*/,
- AudioUnitParameterID /*inParameterID*/, AudioUnitParameterInfo& /*outParameterInfo*/)
- {
- return kAudioUnitErr_InvalidParameter;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::GetParameterValueStrings(
- AudioUnitScope /*inScope*/, AudioUnitParameterID /*inParameterID*/, CFArrayRef* /*outStrings*/)
- {
- return kAudioUnitErr_InvalidProperty;
- }
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::GetParameterHistoryInfo(AudioUnitScope /*inScope*/,
- AudioUnitParameterID /*inParameterID*/, Float32& /*outUpdatesPerSecond*/,
- Float32& /*outHistoryDurationInSeconds*/)
- {
- return kAudioUnitErr_InvalidProperty;
- }
-
-
- //_____________________________________________________________________________
- //
- OSStatus AUBase::CopyClumpName(AudioUnitScope /*inScope*/, UInt32 /*inClumpID*/,
- UInt32 /*inDesiredNameLength*/, CFStringRef* /*outClumpName*/)
- {
- return kAudioUnitErr_InvalidProperty;
- }
-
- //_____________________________________________________________________________
- //
- void AUBase::SetNumberOfElements(AudioUnitScope inScope, UInt32 numElements)
- {
- if (inScope == kAudioUnitScope_Global && numElements != 1) {
- Throw(kAudioUnitErr_InvalidScope);
- }
-
- GetScope(inScope).SetNumberOfElements(numElements);
- }
-
- //_____________________________________________________________________________
- //
- std::unique_ptr<AUElement> AUBase::CreateElement(AudioUnitScope scope, AudioUnitElement /*element*/)
- {
- switch (scope) {
- case kAudioUnitScope_Global:
- return std::make_unique<AUElement>(*this);
- case kAudioUnitScope_Input:
- return std::make_unique<AUInputElement>(*this);
- case kAudioUnitScope_Output:
- return std::make_unique<AUOutputElement>(*this);
- case kAudioUnitScope_Group:
- case kAudioUnitScope_Part:
- return std::make_unique<AUElement>(*this);
- default:
- break;
- }
- Throw(kAudioUnitErr_InvalidScope);
- }
-
- const char* AUBase::GetLoggingString() const noexcept { return mLogString.c_str(); }
-
- std::string AUBase::CreateLoggingString() const
- {
- const auto desc = GetComponentDescription();
- std::array<char, 32> buf{};
- [[maybe_unused]] const int printCount =
- snprintf(buf.data(), buf.size(), "AU (%p): ", GetComponentInstance()); // NOLINT
- #if DEBUG
- assert(printCount < static_cast<int>(buf.size()));
- #endif
- return buf.data() + make_string_from_4cc(desc.componentType) + '/' +
- make_string_from_4cc(desc.componentSubType) + '/' +
- make_string_from_4cc(desc.componentManufacturer);
- }
-
- } // namespace ausdk
|