| 
							- /*!
 - 	@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
 
 
  |