| 
							- /*!
 - 	@file		AudioUnitSDK/AUScopeElement.cpp
 - 	@copyright	© 2000-2021 Apple Inc. All rights reserved.
 - */
 - #include <AudioUnitSDK/AUBase.h>
 - #include <AudioUnitSDK/AUScopeElement.h>
 - 
 - #include <AudioToolbox/AudioUnitProperties.h>
 - 
 - #include <array>
 - 
 - namespace ausdk {
 - 
 - //_____________________________________________________________________________
 - //
 - //	By default, parameterIDs may be arbitrarily spaced, and a flat map
 - //  will be used for access.  Calling UseIndexedParameters() will
 - //	instead use an STL vector for faster indexed access.
 - //	This assumes the paramIDs are numbered 0.....inNumberOfParameters-1
 - //	Call this before defining/adding any parameters with SetParameter()
 - //
 - void AUElement::UseIndexedParameters(UInt32 inNumberOfParameters)
 - {
 - 	mIndexedParameters.resize(inNumberOfParameters);
 - 	mUseIndexedParameters = true;
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - //	Helper method.
 - //	returns whether the specified paramID is known to the element
 - //
 - bool AUElement::HasParameterID(AudioUnitParameterID paramID) const
 - {
 - 	if (mUseIndexedParameters) {
 - 		return paramID < mIndexedParameters.size();
 - 	}
 - 
 - 	return mParameters.find(paramID) != mParameters.end();
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - //	caller assumes that this is actually an immediate parameter
 - //
 - AudioUnitParameterValue AUElement::GetParameter(AudioUnitParameterID paramID) const
 - {
 - 	if (mUseIndexedParameters) {
 - 		ausdk::ThrowExceptionIf(
 - 			paramID >= mIndexedParameters.size(), kAudioUnitErr_InvalidParameter);
 - 		return mIndexedParameters[paramID].load(std::memory_order_acquire);
 - 	}
 - 	const auto i = mParameters.find(paramID);
 - 	ausdk::ThrowExceptionIf(i == mParameters.end(), kAudioUnitErr_InvalidParameter);
 - 	return (*i).second.load(std::memory_order_acquire);
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - void AUElement::SetParameter(
 - 	AudioUnitParameterID paramID, AudioUnitParameterValue inValue, bool okWhenInitialized)
 - {
 - 	if (mUseIndexedParameters) {
 - 		ausdk::ThrowExceptionIf(
 - 			paramID >= mIndexedParameters.size(), kAudioUnitErr_InvalidParameter);
 - 		mIndexedParameters[paramID].store(inValue, std::memory_order_release);
 - 	} else {
 - 		const auto i = mParameters.find(paramID);
 - 
 - 		if (i == mParameters.end()) {
 - 			if (mAudioUnit.IsInitialized() && !okWhenInitialized) {
 - 				// The AU should not be creating new parameters once initialized.
 - 				// If a client tries to set an undefined parameter, we could throw as follows,
 - 				// but this might cause a regression. So it is better to just fail silently.
 - 				// Throw(kAudioUnitErr_InvalidParameter);
 - 				AUSDK_LogError(
 - 					"Warning: %s SetParameter for undefined param ID %u while initialized. "
 - 					"Ignoring.",
 - 					mAudioUnit.GetLoggingString(), static_cast<unsigned>(paramID));
 - 			} else {
 - 				// create new entry in map for the paramID (only happens first time)
 - 				mParameters[paramID] = ParameterValue{ inValue };
 - 			}
 - 		} else {
 - 			// paramID already exists in map so simply change its value
 - 			(*i).second.store(inValue, std::memory_order_release);
 - 		}
 - 	}
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - void AUElement::SetScheduledEvent(AudioUnitParameterID paramID,
 - 	const AudioUnitParameterEvent& inEvent, UInt32 /*inSliceOffsetInBuffer*/,
 - 	UInt32 /*inSliceDurationFrames*/, bool okWhenInitialized)
 - {
 - 	if (inEvent.eventType != kParameterEvent_Immediate) {
 - 		AUSDK_LogError("Warning: %s was passed a ramped parameter event but does not implement "
 - 					   "them. Ignoring.",
 - 			mAudioUnit.GetLoggingString());
 - 		return;
 - 	}
 - 	SetParameter(paramID, inEvent.eventValues.immediate.value, okWhenInitialized); // NOLINT
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - void AUElement::GetParameterList(AudioUnitParameterID* outList)
 - {
 - 	if (mUseIndexedParameters) {
 - 		const auto nparams = static_cast<UInt32>(mIndexedParameters.size());
 - 		for (UInt32 i = 0; i < nparams; i++) {
 - 			*outList++ = (AudioUnitParameterID)i; // NOLINT
 - 		}
 - 	} else {
 - 		for (const auto& param : mParameters) {
 - 			*outList++ = param.first; // NOLINT
 - 		}
 - 	}
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - void AUElement::SaveState(AudioUnitScope scope, CFMutableDataRef data)
 - {
 - 	AudioUnitParameterInfo paramInfo{};
 - 	const CFIndex countOffset = CFDataGetLength(data);
 - 	uint32_t paramsWritten = 0;
 - 
 - 	const auto appendBytes = [data](const void* bytes, CFIndex length) {
 - 		CFDataAppendBytes(data, static_cast<const UInt8*>(bytes), length);
 - 	};
 - 
 - 	const auto appendParameter = [&](AudioUnitParameterID paramID, AudioUnitParameterValue value) {
 - 		struct {
 - 			UInt32 paramID;
 - 			UInt32 value; // really a big-endian float
 - 		} entry{};
 - 		static_assert(sizeof(entry) == (sizeof(entry.paramID) + sizeof(entry.value)));
 - 
 - 		if (mAudioUnit.GetParameterInfo(scope, paramID, paramInfo) == noErr) {
 - 			if ((paramInfo.flags & kAudioUnitParameterFlag_CFNameRelease) != 0u) {
 - 				if (paramInfo.cfNameString != nullptr) {
 - 					CFRelease(paramInfo.cfNameString);
 - 				}
 - 				if (paramInfo.unit == kAudioUnitParameterUnit_CustomUnit &&
 - 					paramInfo.unitName != nullptr) {
 - 					CFRelease(paramInfo.unitName);
 - 				}
 - 			}
 - 			if (((paramInfo.flags & kAudioUnitParameterFlag_OmitFromPresets) != 0u) ||
 - 				((paramInfo.flags & kAudioUnitParameterFlag_MeterReadOnly) != 0u)) {
 - 				return;
 - 			}
 - 		}
 - 
 - 		entry.paramID = CFSwapInt32HostToBig(paramID);
 - 		entry.value = CFSwapInt32HostToBig(*reinterpret_cast<UInt32*>(&value)); // NOLINT
 - 
 - 		appendBytes(&entry, sizeof(entry));
 - 		++paramsWritten;
 - 	};
 - 
 - 	constexpr UInt32 placeholderCount = 0;
 - 	appendBytes(&placeholderCount, sizeof(placeholderCount));
 - 
 - 	if (mUseIndexedParameters) {
 - 		const auto nparams = static_cast<UInt32>(mIndexedParameters.size());
 - 		for (UInt32 i = 0; i < nparams; i++) {
 - 			appendParameter(i, mIndexedParameters[i]);
 - 		}
 - 	} else {
 - 		for (const auto& item : mParameters) {
 - 			appendParameter(item.first, item.second);
 - 		}
 - 	}
 - 
 - 	const auto count_BE = CFSwapInt32HostToBig(paramsWritten);
 - 	memcpy(CFDataGetMutableBytePtr(data) + countOffset, // NOLINT ptr math
 - 		&count_BE, sizeof(count_BE));
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - const UInt8* AUElement::RestoreState(const UInt8* state)
 - {
 - 	union FloatInt32 {
 - 		UInt32 i;
 - 		AudioUnitParameterValue f;
 - 	};
 - 	const UInt8* p = state;
 - 	const UInt32 nparams = CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p)); // NOLINT
 - 	p += sizeof(UInt32);                                                              // NOLINT
 - 
 - 	for (UInt32 i = 0; i < nparams; ++i) {
 - 		struct {
 - 			AudioUnitParameterID paramID;
 - 			AudioUnitParameterValue value;
 - 		} entry{};
 - 		static_assert(sizeof(entry) == (sizeof(entry.paramID) + sizeof(entry.value)));
 - 
 - 		entry.paramID = CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p)); // NOLINT
 - 		p += sizeof(UInt32);                                                       // NOLINT
 - 		FloatInt32 temp{};                                                         // NOLINT
 - 		temp.i = CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p));        // NOLINT
 - 		entry.value = temp.f;                                                      // NOLINT
 - 		p += sizeof(AudioUnitParameterValue);                                      // NOLINT
 - 
 - 		SetParameter(entry.paramID, entry.value);
 - 	}
 - 	return p;
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - AUIOElement::AUIOElement(AUBase& audioUnit) : AUElement(audioUnit), mWillAllocate(true)
 - {
 - 	mStreamFormat = AudioStreamBasicDescription{ .mSampleRate = AUBase::kAUDefaultSampleRate,
 - 		.mFormatID = kAudioFormatLinearPCM,
 - 		.mFormatFlags = AudioFormatFlags(kAudioFormatFlagsNativeFloatPacked) |
 - 						AudioFormatFlags(kAudioFormatFlagIsNonInterleaved), // NOLINT
 - 		.mBytesPerPacket = sizeof(float),
 - 		.mFramesPerPacket = 1,
 - 		.mBytesPerFrame = sizeof(float),
 - 		.mChannelsPerFrame = 2,
 - 		.mBitsPerChannel = 32, // NOLINT
 - 		.mReserved = 0 };
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - OSStatus AUIOElement::SetStreamFormat(const AudioStreamBasicDescription& format)
 - {
 - 	mStreamFormat = format;
 - 
 - 	// Clear the previous channel layout if it is inconsistent with the newly set format;
 - 	// preserve it if it is acceptable, in case the new format has no layout.
 - 	if (ChannelLayout().IsValid() && NumberChannels() != ChannelLayout().NumberChannels()) {
 - 		RemoveAudioChannelLayout();
 - 	}
 - 
 - 	return noErr;
 - }
 - 
 - //_____________________________________________________________________________
 - // inFramesToAllocate == 0 implies the AudioUnit's max-frames-per-slice will be used
 - void AUIOElement::AllocateBuffer(UInt32 inFramesToAllocate)
 - {
 - 	if (GetAudioUnit().HasBegunInitializing()) {
 - 		UInt32 framesToAllocate =
 - 			inFramesToAllocate > 0 ? inFramesToAllocate : GetAudioUnit().GetMaxFramesPerSlice();
 - 
 - 		mIOBuffer.Allocate(
 - 			mStreamFormat, (mWillAllocate && NeedsBufferSpace()) ? framesToAllocate : 0);
 - 	}
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - void AUIOElement::DeallocateBuffer() { mIOBuffer.Deallocate(); }
 - 
 - //_____________________________________________________________________________
 - //
 - //		AudioChannelLayout support
 - 
 - // return an empty vector (ie. NO channel layouts) if the AU doesn't require channel layout
 - // knowledge
 - std::vector<AudioChannelLayoutTag> AUIOElement::GetChannelLayoutTags() { return {}; }
 - 
 - // outLayoutPtr WILL be NULL if called to determine layout size
 - UInt32 AUIOElement::GetAudioChannelLayout(AudioChannelLayout* outLayoutPtr, bool& outWritable)
 - {
 - 	outWritable = true;
 - 
 - 	UInt32 size = mChannelLayout.IsValid() ? mChannelLayout.Size() : 0;
 - 	if (size > 0 && outLayoutPtr != nullptr) {
 - 		memcpy(outLayoutPtr, &mChannelLayout.Layout(), size);
 - 	}
 - 
 - 	return size;
 - }
 - 
 - // the incoming channel map will be at least as big as a basic AudioChannelLayout
 - // but its contents will determine its actual size
 - // Subclass should overide if channel map is writable
 - OSStatus AUIOElement::SetAudioChannelLayout(const AudioChannelLayout& inLayout)
 - {
 - 	if (NumberChannels() != AUChannelLayout::NumberChannels(inLayout)) {
 - 		return kAudioUnitErr_InvalidPropertyValue;
 - 	}
 - 	mChannelLayout = inLayout;
 - 	return noErr;
 - }
 - 
 - // Some units support optional usage of channel maps - typically converter units
 - // that can do channel remapping between different maps. In that optional case
 - // the user should be able to remove a channel map if that is possible.
 - // Typically this is NOT the case (e.g., the 3DMixer even in the stereo case
 - // needs to know if it is rendering to speakers or headphones)
 - OSStatus AUIOElement::RemoveAudioChannelLayout()
 - {
 - 	mChannelLayout = {};
 - 	return noErr;
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - void AUScope::SetNumberOfElements(UInt32 numElements)
 - {
 - 	if (mDelegate != nullptr) {
 - 		return mDelegate->SetNumberOfElements(numElements);
 - 	}
 - 
 - 	if (numElements > mElements.size()) {
 - 		mElements.reserve(numElements);
 - 		while (numElements > mElements.size()) {
 - 			auto elem = mCreator->CreateElement(GetScope(), static_cast<UInt32>(mElements.size()));
 - 			mElements.push_back(std::move(elem));
 - 		}
 - 	} else {
 - 		while (numElements < mElements.size()) {
 - 			mElements.pop_back();
 - 		}
 - 	}
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - bool AUScope::HasElementWithName() const
 - {
 - 	for (UInt32 i = 0; i < GetNumberOfElements(); ++i) {
 - 		AUElement* const el = GetElement(i);
 - 		if ((el != nullptr) && el->HasName()) {
 - 			return true;
 - 		}
 - 	}
 - 	return false;
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - 
 - void AUScope::AddElementNamesToDict(CFMutableDictionaryRef inNameDict) const
 - {
 - 	if (HasElementWithName()) {
 - 		const auto elementDict =
 - 			Owned<CFMutableDictionaryRef>::from_create(CFDictionaryCreateMutable(
 - 				nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
 - 		for (UInt32 i = 0; i < GetNumberOfElements(); ++i) {
 - 			AUElement* const el = GetElement(i);
 - 			if (el != nullptr && el->HasName()) {
 - 				const auto key = Owned<CFStringRef>::from_create(CFStringCreateWithFormat(
 - 					nullptr, nullptr, CFSTR("%u"), static_cast<unsigned>(i)));
 - 				CFDictionarySetValue(*elementDict, *key, *el->GetName());
 - 			}
 - 		}
 - 
 - 		const auto key = Owned<CFStringRef>::from_create(
 - 			CFStringCreateWithFormat(nullptr, nullptr, CFSTR("%u"), static_cast<unsigned>(mScope)));
 - 		CFDictionarySetValue(inNameDict, *key, *elementDict);
 - 	}
 - }
 - 
 - //_____________________________________________________________________________
 - //
 - std::vector<AudioUnitElement> AUScope::RestoreElementNames(CFDictionaryRef inNameDict) const
 - {
 - 	// first we have to see if we have enough elements
 - 	std::vector<AudioUnitElement> restoredElements;
 - 	const auto maxElNum = GetNumberOfElements();
 - 
 - 	const auto dictSize =
 - 		static_cast<size_t>(std::max(CFDictionaryGetCount(inNameDict), CFIndex(0)));
 - 	std::vector<CFStringRef> keys(dictSize);
 - 	CFDictionaryGetKeysAndValues(
 - 		inNameDict, reinterpret_cast<const void**>(keys.data()), nullptr); // NOLINT
 - 	for (size_t i = 0; i < dictSize; i++) {
 - 		unsigned int intKey = 0;
 - 		std::array<char, 32> string{};
 - 		CFStringGetCString(keys[i], string.data(), string.size(), kCFStringEncodingASCII);
 - 		const int result = sscanf(string.data(), "%u", &intKey); // NOLINT
 - 		// check if sscanf succeeded and element index is less than max elements.
 - 		if ((result != 0) && (static_cast<UInt32>(intKey) < maxElNum)) {
 - 			auto* const elName =
 - 				static_cast<CFStringRef>(CFDictionaryGetValue(inNameDict, keys[i]));
 - 			if ((elName != nullptr) && (CFGetTypeID(elName) == CFStringGetTypeID())) {
 - 				AUElement* const element = GetElement(intKey);
 - 				if (element != nullptr) {
 - 					auto* const currentName = element->GetName().get();
 - 
 - 					if (currentName == nullptr || CFStringCompare(elName, currentName, 0) != kCFCompareEqualTo) {
 - 						element->SetName(elName);
 - 						restoredElements.push_back(intKey);
 - 					}
 - 				}
 - 			}
 - 		}
 - 	}
 - 
 - 	return restoredElements;
 - }
 - 
 - void AUScope::SaveState(CFMutableDataRef data) const
 - {
 - 	const AudioUnitElement nElems = GetNumberOfElements();
 - 	for (AudioUnitElement ielem = 0; ielem < nElems; ++ielem) {
 - 		AUElement* const element = GetElement(ielem);
 - 		const UInt32 nparams = element->GetNumberOfParameters();
 - 		if (nparams > 0) {
 - 			struct {
 - 				const UInt32 scope;
 - 				const UInt32 element;
 - 			} hdr{ .scope = CFSwapInt32HostToBig(GetScope()),
 - 				.element = CFSwapInt32HostToBig(ielem) };
 - 			static_assert(sizeof(hdr) == (sizeof(hdr.scope) + sizeof(hdr.element)));
 - 			CFDataAppendBytes(data, reinterpret_cast<const UInt8*>(&hdr), sizeof(hdr)); // NOLINT
 - 
 - 			element->SaveState(mScope, data);
 - 		}
 - 	}
 - }
 - 
 - const UInt8* AUScope::RestoreState(const UInt8* state) const
 - {
 - 	const UInt8* p = state;
 - 	const UInt32 elementIdx = CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p)); // NOLINT
 - 	p += sizeof(UInt32);                                                                 // NOLINT
 - 	AUElement* const element = GetElement(elementIdx);
 - 	if (element == nullptr) {
 - 		struct {
 - 			AudioUnitParameterID paramID;
 - 			AudioUnitParameterValue value;
 - 		} entry{};
 - 		static_assert(sizeof(entry) == (sizeof(entry.paramID) + sizeof(entry.value)));
 - 		const UInt32 nparams = CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p)); // NOLINT
 - 		p += sizeof(UInt32);                                                              // NOLINT
 - 
 - 		p += nparams * sizeof(entry); // NOLINT
 - 	} else {
 - 		p = element->RestoreState(p);
 - 	}
 - 
 - 	return p;
 - }
 - 
 - } // namespace ausdk
 
 
  |