mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
435 lines
13 KiB
Objective-C
435 lines
13 KiB
Objective-C
/*!
|
|
@file AudioUnitSDK/AUScopeElement.h
|
|
@copyright © 2000-2021 Apple Inc. All rights reserved.
|
|
*/
|
|
#ifndef AudioUnitSDK_AUScopeElement_h
|
|
#define AudioUnitSDK_AUScopeElement_h
|
|
|
|
// module
|
|
#include <AudioUnitSDK/AUBuffer.h>
|
|
#include <AudioUnitSDK/AUUtility.h>
|
|
#include <AudioUnitSDK/ComponentBase.h>
|
|
|
|
// OS
|
|
#include <AudioToolbox/AudioUnit.h>
|
|
|
|
// std
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
namespace ausdk {
|
|
|
|
class AUBase;
|
|
|
|
/// Wrap an atomic in a copy-constructible/assignable object. This allows storing atomic values in a
|
|
/// vector (not directly possible since atomics are not copy-constructible/assignable).
|
|
template <typename T>
|
|
class AtomicValue {
|
|
public:
|
|
AtomicValue() = default;
|
|
explicit AtomicValue(T val) : mValue{ val } {}
|
|
~AtomicValue() = default;
|
|
|
|
AtomicValue(const AtomicValue& other) : mValue{ other.mValue.load() } {}
|
|
AtomicValue(AtomicValue&& other) noexcept : mValue{ other.mValue.load() } {}
|
|
|
|
AtomicValue& operator=(const AtomicValue& other)
|
|
{
|
|
if (&other != this) {
|
|
mValue.store(other.mValue.load());
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
AtomicValue& operator=(AtomicValue&& other) noexcept
|
|
{
|
|
mValue.store(other.mValue.load());
|
|
return *this;
|
|
}
|
|
|
|
T load(std::memory_order m = std::memory_order_seq_cst) const { return mValue.load(m); }
|
|
void store(T v, std::memory_order m = std::memory_order_seq_cst) { mValue.store(v, m); }
|
|
|
|
operator T() const { return load(); } // NOLINT implicit conversions OK
|
|
|
|
AtomicValue& operator=(T value)
|
|
{
|
|
store(value);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
std::atomic<T> mValue{};
|
|
};
|
|
|
|
/// A bare-bones reinvention of boost::flat_map, just enough to hold parameters in sorted vectors.
|
|
template <typename Key, typename Value>
|
|
class flat_map {
|
|
using KVPair = std::pair<Key, Value>;
|
|
using Impl = std::vector<std::pair<Key, Value>>;
|
|
|
|
static bool keyless(const KVPair& item, Key k) { return k > item.first; }
|
|
|
|
Impl mImpl;
|
|
|
|
public:
|
|
using iterator = typename Impl::iterator;
|
|
using const_iterator = typename Impl::const_iterator;
|
|
|
|
[[nodiscard]] bool empty() const { return mImpl.empty(); }
|
|
[[nodiscard]] size_t size() const { return mImpl.size(); }
|
|
[[nodiscard]] const_iterator begin() const { return mImpl.begin(); }
|
|
[[nodiscard]] const_iterator end() const { return mImpl.end(); }
|
|
iterator begin() { return mImpl.begin(); }
|
|
iterator end() { return mImpl.end(); }
|
|
const_iterator cbegin() { return mImpl.cbegin(); }
|
|
const_iterator cend() { return mImpl.cend(); }
|
|
|
|
[[nodiscard]] const_iterator lower_bound(Key k) const
|
|
{
|
|
return std::lower_bound(mImpl.begin(), mImpl.end(), k, keyless);
|
|
}
|
|
|
|
iterator lower_bound(Key k) { return std::lower_bound(mImpl.begin(), mImpl.end(), k, keyless); }
|
|
|
|
[[nodiscard]] const_iterator find(Key k) const
|
|
{
|
|
auto iter = lower_bound(k);
|
|
if (iter != mImpl.end()) {
|
|
if ((*iter).first != k) {
|
|
iter = mImpl.end();
|
|
}
|
|
}
|
|
return iter;
|
|
}
|
|
|
|
iterator find(Key k)
|
|
{
|
|
auto iter = lower_bound(k);
|
|
if (iter != mImpl.end()) {
|
|
if ((*iter).first != k) {
|
|
iter = mImpl.end();
|
|
}
|
|
}
|
|
return iter;
|
|
}
|
|
|
|
class ItemProxy {
|
|
public:
|
|
ItemProxy(flat_map& map, Key k) : mMap{ map }, mKey{ k } {}
|
|
|
|
operator Value() const // NOLINT implicit conversion is OK
|
|
{
|
|
const auto iter = mMap.find(mKey);
|
|
if (iter == mMap.end()) {
|
|
throw std::runtime_error("Invalid map key");
|
|
}
|
|
return (*iter).second;
|
|
}
|
|
|
|
ItemProxy& operator=(const Value& v)
|
|
{
|
|
const auto iter = mMap.lower_bound(mKey);
|
|
if (iter != mMap.end() && (*iter).first == mKey) {
|
|
(*iter).second = v;
|
|
} else {
|
|
mMap.mImpl.insert(iter, { mKey, v });
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
flat_map& mMap;
|
|
const Key mKey;
|
|
};
|
|
|
|
ItemProxy operator[](Key k) { return ItemProxy{ *this, k }; }
|
|
};
|
|
|
|
// ____________________________________________________________________________
|
|
//
|
|
class AUIOElement;
|
|
|
|
/// An organizational unit for parameters, with a name.
|
|
class AUElement {
|
|
using ParameterValue = AtomicValue<float>;
|
|
using ParameterMap = flat_map<AudioUnitParameterID, ParameterValue>;
|
|
|
|
public:
|
|
explicit AUElement(AUBase& audioUnit) : mAudioUnit(audioUnit), mUseIndexedParameters(false) {}
|
|
|
|
AUSDK_DEPRECATED("Construct with a reference")
|
|
explicit AUElement(AUBase* audioUnit) : AUElement(*audioUnit) {}
|
|
|
|
AUElement(const AUElement&) = delete;
|
|
AUElement(AUElement&&) = delete;
|
|
AUElement& operator=(const AUElement&) = delete;
|
|
AUElement& operator=(AUElement&&) = delete;
|
|
|
|
virtual ~AUElement() = default;
|
|
|
|
virtual UInt32 GetNumberOfParameters()
|
|
{
|
|
return mUseIndexedParameters ? static_cast<UInt32>(mIndexedParameters.size())
|
|
: static_cast<UInt32>(mParameters.size());
|
|
}
|
|
virtual void GetParameterList(AudioUnitParameterID* outList);
|
|
[[nodiscard]] bool HasParameterID(AudioUnitParameterID paramID) const;
|
|
[[nodiscard]] AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID) const;
|
|
|
|
// Only set okWhenInitialized to true when you know the outside world cannot access this
|
|
// element. Otherwise the parameter map could get corrupted.
|
|
void SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value,
|
|
bool okWhenInitialized = false);
|
|
|
|
// Only set okWhenInitialized to true when you know the outside world cannot access this
|
|
// element. Otherwise the parameter map could get corrupted. N.B. This only handles
|
|
// immediate parameters. Override to implement ramping. Called from
|
|
// AUBase::ProcessForScheduledParams.
|
|
virtual void SetScheduledEvent(AudioUnitParameterID paramID,
|
|
const AudioUnitParameterEvent& inEvent, UInt32 inSliceOffsetInBuffer,
|
|
UInt32 inSliceDurationFrames, bool okWhenInitialized = false);
|
|
|
|
[[nodiscard]] AUBase& GetAudioUnit() const noexcept { return mAudioUnit; }
|
|
|
|
void SaveState(AudioUnitScope scope, CFMutableDataRef data);
|
|
const UInt8* RestoreState(const UInt8* state);
|
|
|
|
[[nodiscard]] Owned<CFStringRef> GetName() const { return mElementName; }
|
|
void SetName(CFStringRef inName) { mElementName = inName; }
|
|
|
|
[[nodiscard]] bool HasName() const { return *mElementName != nil; }
|
|
|
|
virtual void UseIndexedParameters(UInt32 inNumberOfParameters);
|
|
|
|
virtual AUIOElement* AsIOElement() { return nullptr; }
|
|
|
|
private:
|
|
// --
|
|
AUBase& mAudioUnit;
|
|
ParameterMap mParameters;
|
|
bool mUseIndexedParameters;
|
|
std::vector<ParameterValue> mIndexedParameters;
|
|
Owned<CFStringRef> mElementName;
|
|
};
|
|
|
|
|
|
// ____________________________________________________________________________
|
|
//
|
|
|
|
/// A subclass of AUElement which represents an input or output bus, and has an associated
|
|
/// audio format and buffers.
|
|
class AUIOElement : public AUElement {
|
|
public:
|
|
explicit AUIOElement(AUBase& audioUnit);
|
|
|
|
AUIOElement(AUBase& audioUnit, const AudioStreamBasicDescription& format)
|
|
: AUIOElement{ audioUnit }
|
|
{
|
|
mStreamFormat = format;
|
|
}
|
|
|
|
AUSDK_DEPRECATED("Construct with a reference")
|
|
explicit AUIOElement(AUBase* audioUnit) : AUIOElement(*audioUnit) {}
|
|
|
|
[[nodiscard]] const AudioStreamBasicDescription& GetStreamFormat() const noexcept
|
|
{
|
|
return mStreamFormat;
|
|
}
|
|
|
|
virtual OSStatus SetStreamFormat(const AudioStreamBasicDescription& format);
|
|
|
|
virtual void AllocateBuffer(UInt32 inFramesToAllocate = 0);
|
|
|
|
void DeallocateBuffer();
|
|
|
|
/// Determines (via subclass override) whether the element's buffer list needs to be allocated.
|
|
[[nodiscard]] virtual bool NeedsBufferSpace() const = 0;
|
|
|
|
void SetWillAllocateBuffer(bool inFlag) noexcept { mWillAllocate = inFlag; }
|
|
|
|
[[nodiscard]] bool WillAllocateBuffer() const noexcept { return mWillAllocate; }
|
|
|
|
AudioBufferList& PrepareBuffer(UInt32 nFrames)
|
|
{
|
|
if (mWillAllocate) {
|
|
return mIOBuffer.PrepareBuffer(mStreamFormat, nFrames);
|
|
}
|
|
Throw(kAudioUnitErr_InvalidPropertyValue);
|
|
}
|
|
|
|
AudioBufferList& PrepareNullBuffer(UInt32 nFrames)
|
|
{
|
|
return mIOBuffer.PrepareNullBuffer(mStreamFormat, nFrames);
|
|
}
|
|
AudioBufferList& SetBufferList(AudioBufferList& abl) { return mIOBuffer.SetBufferList(abl); }
|
|
void SetBuffer(UInt32 index, AudioBuffer& ab) { mIOBuffer.SetBuffer(index, ab); }
|
|
void InvalidateBufferList() { mIOBuffer.InvalidateBufferList(); }
|
|
[[nodiscard]] AudioBufferList& GetBufferList() const { return mIOBuffer.GetBufferList(); }
|
|
|
|
[[nodiscard]] float* GetFloat32ChannelData(UInt32 ch)
|
|
{
|
|
if (IsInterleaved()) {
|
|
return static_cast<float*>(mIOBuffer.GetBufferList().mBuffers[0].mData) + ch; // NOLINT
|
|
}
|
|
return static_cast<float*>(mIOBuffer.GetBufferList().mBuffers[ch].mData); // NOLINT
|
|
}
|
|
|
|
void CopyBufferListTo(AudioBufferList& abl) const { mIOBuffer.CopyBufferListTo(abl); }
|
|
void CopyBufferContentsTo(AudioBufferList& abl) const { mIOBuffer.CopyBufferContentsTo(abl); }
|
|
[[nodiscard]] bool IsInterleaved() const noexcept { return ASBD::IsInterleaved(mStreamFormat); }
|
|
[[nodiscard]] UInt32 NumberChannels() const noexcept { return mStreamFormat.mChannelsPerFrame; }
|
|
[[nodiscard]] UInt32 NumberInterleavedChannels() const noexcept
|
|
{
|
|
return ASBD::NumberInterleavedChannels(mStreamFormat);
|
|
}
|
|
virtual std::vector<AudioChannelLayoutTag> GetChannelLayoutTags();
|
|
|
|
[[nodiscard]] const AUChannelLayout& ChannelLayout() const { return mChannelLayout; }
|
|
|
|
// Old layout methods
|
|
virtual OSStatus SetAudioChannelLayout(const AudioChannelLayout& inLayout);
|
|
virtual UInt32 GetAudioChannelLayout(AudioChannelLayout* outLayoutPtr, bool& outWritable);
|
|
|
|
virtual OSStatus RemoveAudioChannelLayout();
|
|
|
|
/*! @fn AsIOElement*/
|
|
AUIOElement* AsIOElement() override { return this; }
|
|
|
|
protected:
|
|
AUBufferList& IOBuffer() noexcept { return mIOBuffer; }
|
|
void ForceSetAudioChannelLayout(const AudioChannelLayout& inLayout)
|
|
{
|
|
mChannelLayout = inLayout;
|
|
}
|
|
|
|
private:
|
|
AudioStreamBasicDescription mStreamFormat{};
|
|
AUChannelLayout mChannelLayout{};
|
|
AUBufferList mIOBuffer; // for input: input proc buffer, only allocated when needed
|
|
// for output: output cache, usually allocated early on
|
|
bool mWillAllocate{ false };
|
|
};
|
|
|
|
// ____________________________________________________________________________
|
|
//
|
|
/*!
|
|
@class AUScopeDelegate
|
|
@brief Provides a way to customize a scope, thereby obtaining virtual scopes.
|
|
|
|
Can be used to implement scopes with variable numbers of elements.
|
|
*/
|
|
class AUScopeDelegate {
|
|
public:
|
|
AUScopeDelegate() = default;
|
|
|
|
virtual ~AUScopeDelegate() = default;
|
|
|
|
AUScopeDelegate(const AUScopeDelegate&) = delete;
|
|
AUScopeDelegate(AUScopeDelegate&&) = delete;
|
|
AUScopeDelegate& operator=(const AUScopeDelegate&) = delete;
|
|
AUScopeDelegate& operator=(AUScopeDelegate&&) = delete;
|
|
|
|
void Initialize(AUBase* creator, AudioUnitScope scope, UInt32 numElements)
|
|
{
|
|
mCreator = creator;
|
|
mScope = scope;
|
|
SetNumberOfElements(numElements);
|
|
}
|
|
virtual void SetNumberOfElements(UInt32 numElements) = 0;
|
|
virtual UInt32 GetNumberOfElements() = 0;
|
|
virtual AUElement* GetElement(UInt32 elementIndex) = 0;
|
|
|
|
[[nodiscard]] AUBase* GetCreator() const noexcept { return mCreator; }
|
|
[[nodiscard]] AudioUnitScope GetScope() const noexcept { return mScope; }
|
|
|
|
|
|
private:
|
|
AUBase* mCreator{ nullptr };
|
|
AudioUnitScope mScope{ 0 };
|
|
};
|
|
|
|
// ____________________________________________________________________________
|
|
//
|
|
/*!
|
|
@class AUScope
|
|
@brief Organizes one or more elements into an addressable group (e.g. global, input, output).
|
|
*/
|
|
class AUScope {
|
|
public:
|
|
AUScope() = default;
|
|
|
|
~AUScope() = default;
|
|
|
|
AUScope(const AUScope&) = delete;
|
|
AUScope(AUScope&&) = delete;
|
|
AUScope& operator=(const AUScope&) = delete;
|
|
AUScope& operator=(AUScope&&) = delete;
|
|
|
|
void Initialize(AUBase* creator, AudioUnitScope scope, UInt32 numElements)
|
|
{
|
|
mCreator = creator;
|
|
mScope = scope;
|
|
|
|
if (mDelegate != nullptr) {
|
|
return mDelegate->Initialize(creator, scope, numElements);
|
|
}
|
|
|
|
SetNumberOfElements(numElements);
|
|
}
|
|
void SetNumberOfElements(UInt32 numElements);
|
|
[[nodiscard]] UInt32 GetNumberOfElements() const
|
|
{
|
|
if (mDelegate != nullptr) {
|
|
return mDelegate->GetNumberOfElements();
|
|
}
|
|
|
|
return static_cast<UInt32>(mElements.size());
|
|
}
|
|
[[nodiscard]] AUElement* GetElement(UInt32 elementIndex) const
|
|
{
|
|
if (mDelegate != nullptr) {
|
|
return mDelegate->GetElement(elementIndex);
|
|
}
|
|
return elementIndex < mElements.size() ? mElements[elementIndex].get() : nullptr;
|
|
}
|
|
[[nodiscard]] AUElement* SafeGetElement(UInt32 elementIndex) const
|
|
{
|
|
AUElement* const element = GetElement(elementIndex);
|
|
ausdk::ThrowExceptionIf(element == nullptr, kAudioUnitErr_InvalidElement);
|
|
return element;
|
|
}
|
|
[[nodiscard]] AUIOElement* GetIOElement(UInt32 elementIndex) const
|
|
{
|
|
AUElement* const element = GetElement(elementIndex);
|
|
AUIOElement* const ioel = element != nullptr ? element->AsIOElement() : nullptr;
|
|
ausdk::ThrowExceptionIf(ioel == nullptr, kAudioUnitErr_InvalidElement);
|
|
return ioel;
|
|
}
|
|
|
|
[[nodiscard]] bool HasElementWithName() const;
|
|
void AddElementNamesToDict(CFMutableDictionaryRef inNameDict) const;
|
|
|
|
[[nodiscard]] std::vector<AudioUnitElement> RestoreElementNames(
|
|
CFDictionaryRef inNameDict) const;
|
|
|
|
[[nodiscard]] AudioUnitScope GetScope() const noexcept { return mScope; }
|
|
|
|
void SetDelegate(AUScopeDelegate* inDelegate) noexcept { mDelegate = inDelegate; }
|
|
void SaveState(CFMutableDataRef data) const;
|
|
const UInt8* RestoreState(const UInt8* state) const;
|
|
|
|
private:
|
|
using ElementVector = std::vector<std::unique_ptr<AUElement>>;
|
|
|
|
AUBase* mCreator{ nullptr };
|
|
AudioUnitScope mScope{ 0 };
|
|
ElementVector mElements;
|
|
AUScopeDelegate* mDelegate{ nullptr };
|
|
};
|
|
|
|
} // namespace ausdk
|
|
|
|
#endif // AudioUnitSDK_AUScopeElement_h
|