The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

436 lines
13KB

  1. /*!
  2. @file AudioUnitSDK/AUScopeElement.h
  3. @copyright © 2000-2021 Apple Inc. All rights reserved.
  4. */
  5. #ifndef AudioUnitSDK_AUScopeElement_h
  6. #define AudioUnitSDK_AUScopeElement_h
  7. // module
  8. #include <AudioUnitSDK/AUBuffer.h>
  9. #include <AudioUnitSDK/AUUtility.h>
  10. #include <AudioUnitSDK/ComponentBase.h>
  11. // OS
  12. #include <AudioToolbox/AudioUnit.h>
  13. // std
  14. #include <algorithm>
  15. #include <atomic>
  16. #include <memory>
  17. #include <vector>
  18. namespace ausdk {
  19. class AUBase;
  20. /// Wrap an atomic in a copy-constructible/assignable object. This allows storing atomic values in a
  21. /// vector (not directly possible since atomics are not copy-constructible/assignable).
  22. template <typename T>
  23. class AtomicValue {
  24. public:
  25. AtomicValue() = default;
  26. explicit AtomicValue(T val) : mValue{ val } {}
  27. ~AtomicValue() = default;
  28. AtomicValue(const AtomicValue& other) : mValue{ other.mValue.load() } {}
  29. AtomicValue(AtomicValue&& other) noexcept : mValue{ other.mValue.load() } {}
  30. AtomicValue& operator=(const AtomicValue& other)
  31. {
  32. if (&other != this) {
  33. mValue.store(other.mValue.load());
  34. }
  35. return *this;
  36. }
  37. AtomicValue& operator=(AtomicValue&& other) noexcept
  38. {
  39. mValue.store(other.mValue.load());
  40. return *this;
  41. }
  42. T load(std::memory_order m = std::memory_order_seq_cst) const { return mValue.load(m); }
  43. void store(T v, std::memory_order m = std::memory_order_seq_cst) { mValue.store(v, m); }
  44. operator T() const { return load(); } // NOLINT implicit conversions OK
  45. AtomicValue& operator=(T value)
  46. {
  47. store(value);
  48. return *this;
  49. }
  50. private:
  51. std::atomic<T> mValue{};
  52. };
  53. /// A bare-bones reinvention of boost::flat_map, just enough to hold parameters in sorted vectors.
  54. template <typename Key, typename Value>
  55. class flat_map {
  56. using KVPair = std::pair<Key, Value>;
  57. using Impl = std::vector<std::pair<Key, Value>>;
  58. static bool keyless(const KVPair& item, Key k) { return k > item.first; }
  59. Impl mImpl;
  60. public:
  61. using iterator = typename Impl::iterator;
  62. using const_iterator = typename Impl::const_iterator;
  63. [[nodiscard]] bool empty() const { return mImpl.empty(); }
  64. [[nodiscard]] size_t size() const { return mImpl.size(); }
  65. [[nodiscard]] const_iterator begin() const { return mImpl.begin(); }
  66. [[nodiscard]] const_iterator end() const { return mImpl.end(); }
  67. iterator begin() { return mImpl.begin(); }
  68. iterator end() { return mImpl.end(); }
  69. const_iterator cbegin() { return mImpl.cbegin(); }
  70. const_iterator cend() { return mImpl.cend(); }
  71. [[nodiscard]] const_iterator lower_bound(Key k) const
  72. {
  73. return std::lower_bound(mImpl.begin(), mImpl.end(), k, keyless);
  74. }
  75. iterator lower_bound(Key k) { return std::lower_bound(mImpl.begin(), mImpl.end(), k, keyless); }
  76. [[nodiscard]] const_iterator find(Key k) const
  77. {
  78. auto iter = lower_bound(k);
  79. if (iter != mImpl.end()) {
  80. if ((*iter).first != k) {
  81. iter = mImpl.end();
  82. }
  83. }
  84. return iter;
  85. }
  86. iterator find(Key k)
  87. {
  88. auto iter = lower_bound(k);
  89. if (iter != mImpl.end()) {
  90. if ((*iter).first != k) {
  91. iter = mImpl.end();
  92. }
  93. }
  94. return iter;
  95. }
  96. class ItemProxy {
  97. public:
  98. ItemProxy(flat_map& map, Key k) : mMap{ map }, mKey{ k } {}
  99. operator Value() const // NOLINT implicit conversion is OK
  100. {
  101. const auto iter = mMap.find(mKey);
  102. if (iter == mMap.end()) {
  103. throw std::runtime_error("Invalid map key");
  104. }
  105. return (*iter).second;
  106. }
  107. ItemProxy& operator=(const Value& v)
  108. {
  109. const auto iter = mMap.lower_bound(mKey);
  110. if (iter != mMap.end() && (*iter).first == mKey) {
  111. (*iter).second = v;
  112. } else {
  113. mMap.mImpl.insert(iter, { mKey, v });
  114. }
  115. return *this;
  116. }
  117. private:
  118. flat_map& mMap;
  119. const Key mKey;
  120. };
  121. ItemProxy operator[](Key k) { return ItemProxy{ *this, k }; }
  122. };
  123. // ____________________________________________________________________________
  124. //
  125. class AUIOElement;
  126. /// An organizational unit for parameters, with a name.
  127. class AUElement {
  128. using ParameterValue = AtomicValue<float>;
  129. using ParameterMap = flat_map<AudioUnitParameterID, ParameterValue>;
  130. public:
  131. explicit AUElement(AUBase& audioUnit) : mAudioUnit(audioUnit), mUseIndexedParameters(false) {}
  132. AUSDK_DEPRECATED("Construct with a reference")
  133. explicit AUElement(AUBase* audioUnit) : AUElement(*audioUnit) {}
  134. AUElement(const AUElement&) = delete;
  135. AUElement(AUElement&&) = delete;
  136. AUElement& operator=(const AUElement&) = delete;
  137. AUElement& operator=(AUElement&&) = delete;
  138. virtual ~AUElement() = default;
  139. virtual UInt32 GetNumberOfParameters()
  140. {
  141. return mUseIndexedParameters ? static_cast<UInt32>(mIndexedParameters.size())
  142. : static_cast<UInt32>(mParameters.size());
  143. }
  144. virtual void GetParameterList(AudioUnitParameterID* outList);
  145. [[nodiscard]] bool HasParameterID(AudioUnitParameterID paramID) const;
  146. [[nodiscard]] AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID) const;
  147. // Only set okWhenInitialized to true when you know the outside world cannot access this
  148. // element. Otherwise the parameter map could get corrupted.
  149. void SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value,
  150. bool okWhenInitialized = false);
  151. // Only set okWhenInitialized to true when you know the outside world cannot access this
  152. // element. Otherwise the parameter map could get corrupted. N.B. This only handles
  153. // immediate parameters. Override to implement ramping. Called from
  154. // AUBase::ProcessForScheduledParams.
  155. virtual void SetScheduledEvent(AudioUnitParameterID paramID,
  156. const AudioUnitParameterEvent& inEvent, UInt32 inSliceOffsetInBuffer,
  157. UInt32 inSliceDurationFrames, bool okWhenInitialized = false);
  158. [[nodiscard]] AUBase& GetAudioUnit() const noexcept { return mAudioUnit; }
  159. void SaveState(AudioUnitScope scope, CFMutableDataRef data);
  160. const UInt8* RestoreState(const UInt8* state);
  161. [[nodiscard]] Owned<CFStringRef> GetName() const { return mElementName; }
  162. void SetName(CFStringRef inName) { mElementName = inName; }
  163. [[nodiscard]] bool HasName() const { return *mElementName != nil; }
  164. virtual void UseIndexedParameters(UInt32 inNumberOfParameters);
  165. virtual AUIOElement* AsIOElement() { return nullptr; }
  166. private:
  167. // --
  168. AUBase& mAudioUnit;
  169. ParameterMap mParameters;
  170. bool mUseIndexedParameters;
  171. std::vector<ParameterValue> mIndexedParameters;
  172. Owned<CFStringRef> mElementName;
  173. };
  174. // ____________________________________________________________________________
  175. //
  176. /// A subclass of AUElement which represents an input or output bus, and has an associated
  177. /// audio format and buffers.
  178. class AUIOElement : public AUElement {
  179. public:
  180. explicit AUIOElement(AUBase& audioUnit);
  181. AUIOElement(AUBase& audioUnit, const AudioStreamBasicDescription& format)
  182. : AUIOElement{ audioUnit }
  183. {
  184. mStreamFormat = format;
  185. }
  186. AUSDK_DEPRECATED("Construct with a reference")
  187. explicit AUIOElement(AUBase* audioUnit) : AUIOElement(*audioUnit) {}
  188. [[nodiscard]] const AudioStreamBasicDescription& GetStreamFormat() const noexcept
  189. {
  190. return mStreamFormat;
  191. }
  192. virtual OSStatus SetStreamFormat(const AudioStreamBasicDescription& format);
  193. virtual void AllocateBuffer(UInt32 inFramesToAllocate = 0);
  194. void DeallocateBuffer();
  195. /// Determines (via subclass override) whether the element's buffer list needs to be allocated.
  196. [[nodiscard]] virtual bool NeedsBufferSpace() const = 0;
  197. void SetWillAllocateBuffer(bool inFlag) noexcept { mWillAllocate = inFlag; }
  198. [[nodiscard]] bool WillAllocateBuffer() const noexcept { return mWillAllocate; }
  199. AudioBufferList& PrepareBuffer(UInt32 nFrames)
  200. {
  201. if (mWillAllocate) {
  202. return mIOBuffer.PrepareBuffer(mStreamFormat, nFrames);
  203. }
  204. Throw(kAudioUnitErr_InvalidPropertyValue);
  205. }
  206. AudioBufferList& PrepareNullBuffer(UInt32 nFrames)
  207. {
  208. return mIOBuffer.PrepareNullBuffer(mStreamFormat, nFrames);
  209. }
  210. AudioBufferList& SetBufferList(AudioBufferList& abl) { return mIOBuffer.SetBufferList(abl); }
  211. void SetBuffer(UInt32 index, AudioBuffer& ab) { mIOBuffer.SetBuffer(index, ab); }
  212. void InvalidateBufferList() { mIOBuffer.InvalidateBufferList(); }
  213. [[nodiscard]] AudioBufferList& GetBufferList() const { return mIOBuffer.GetBufferList(); }
  214. [[nodiscard]] float* GetFloat32ChannelData(UInt32 ch)
  215. {
  216. if (IsInterleaved()) {
  217. return static_cast<float*>(mIOBuffer.GetBufferList().mBuffers[0].mData) + ch; // NOLINT
  218. }
  219. return static_cast<float*>(mIOBuffer.GetBufferList().mBuffers[ch].mData); // NOLINT
  220. }
  221. void CopyBufferListTo(AudioBufferList& abl) const { mIOBuffer.CopyBufferListTo(abl); }
  222. void CopyBufferContentsTo(AudioBufferList& abl) const { mIOBuffer.CopyBufferContentsTo(abl); }
  223. [[nodiscard]] bool IsInterleaved() const noexcept { return ASBD::IsInterleaved(mStreamFormat); }
  224. [[nodiscard]] UInt32 NumberChannels() const noexcept { return mStreamFormat.mChannelsPerFrame; }
  225. [[nodiscard]] UInt32 NumberInterleavedChannels() const noexcept
  226. {
  227. return ASBD::NumberInterleavedChannels(mStreamFormat);
  228. }
  229. virtual std::vector<AudioChannelLayoutTag> GetChannelLayoutTags();
  230. [[nodiscard]] const AUChannelLayout& ChannelLayout() const { return mChannelLayout; }
  231. // Old layout methods
  232. virtual OSStatus SetAudioChannelLayout(const AudioChannelLayout& inLayout);
  233. virtual UInt32 GetAudioChannelLayout(AudioChannelLayout* outLayoutPtr, bool& outWritable);
  234. virtual OSStatus RemoveAudioChannelLayout();
  235. /*! @fn AsIOElement*/
  236. AUIOElement* AsIOElement() override { return this; }
  237. protected:
  238. AUBufferList& IOBuffer() noexcept { return mIOBuffer; }
  239. void ForceSetAudioChannelLayout(const AudioChannelLayout& inLayout)
  240. {
  241. mChannelLayout = inLayout;
  242. }
  243. private:
  244. AudioStreamBasicDescription mStreamFormat{};
  245. AUChannelLayout mChannelLayout{};
  246. AUBufferList mIOBuffer; // for input: input proc buffer, only allocated when needed
  247. // for output: output cache, usually allocated early on
  248. bool mWillAllocate{ false };
  249. };
  250. // ____________________________________________________________________________
  251. //
  252. /*!
  253. @class AUScopeDelegate
  254. @brief Provides a way to customize a scope, thereby obtaining virtual scopes.
  255. Can be used to implement scopes with variable numbers of elements.
  256. */
  257. class AUScopeDelegate {
  258. public:
  259. AUScopeDelegate() = default;
  260. virtual ~AUScopeDelegate() = default;
  261. AUScopeDelegate(const AUScopeDelegate&) = delete;
  262. AUScopeDelegate(AUScopeDelegate&&) = delete;
  263. AUScopeDelegate& operator=(const AUScopeDelegate&) = delete;
  264. AUScopeDelegate& operator=(AUScopeDelegate&&) = delete;
  265. void Initialize(AUBase* creator, AudioUnitScope scope, UInt32 numElements)
  266. {
  267. mCreator = creator;
  268. mScope = scope;
  269. SetNumberOfElements(numElements);
  270. }
  271. virtual void SetNumberOfElements(UInt32 numElements) = 0;
  272. virtual UInt32 GetNumberOfElements() = 0;
  273. virtual AUElement* GetElement(UInt32 elementIndex) = 0;
  274. [[nodiscard]] AUBase* GetCreator() const noexcept { return mCreator; }
  275. [[nodiscard]] AudioUnitScope GetScope() const noexcept { return mScope; }
  276. private:
  277. AUBase* mCreator{ nullptr };
  278. AudioUnitScope mScope{ 0 };
  279. };
  280. // ____________________________________________________________________________
  281. //
  282. /*!
  283. @class AUScope
  284. @brief Organizes one or more elements into an addressable group (e.g. global, input, output).
  285. */
  286. class AUScope {
  287. public:
  288. AUScope() = default;
  289. ~AUScope() = default;
  290. AUScope(const AUScope&) = delete;
  291. AUScope(AUScope&&) = delete;
  292. AUScope& operator=(const AUScope&) = delete;
  293. AUScope& operator=(AUScope&&) = delete;
  294. void Initialize(AUBase* creator, AudioUnitScope scope, UInt32 numElements)
  295. {
  296. mCreator = creator;
  297. mScope = scope;
  298. if (mDelegate != nullptr) {
  299. return mDelegate->Initialize(creator, scope, numElements);
  300. }
  301. SetNumberOfElements(numElements);
  302. }
  303. void SetNumberOfElements(UInt32 numElements);
  304. [[nodiscard]] UInt32 GetNumberOfElements() const
  305. {
  306. if (mDelegate != nullptr) {
  307. return mDelegate->GetNumberOfElements();
  308. }
  309. return static_cast<UInt32>(mElements.size());
  310. }
  311. [[nodiscard]] AUElement* GetElement(UInt32 elementIndex) const
  312. {
  313. if (mDelegate != nullptr) {
  314. return mDelegate->GetElement(elementIndex);
  315. }
  316. return elementIndex < mElements.size() ? mElements[elementIndex].get() : nullptr;
  317. }
  318. [[nodiscard]] AUElement* SafeGetElement(UInt32 elementIndex) const
  319. {
  320. AUElement* const element = GetElement(elementIndex);
  321. ausdk::ThrowExceptionIf(element == nullptr, kAudioUnitErr_InvalidElement);
  322. return element;
  323. }
  324. [[nodiscard]] AUIOElement* GetIOElement(UInt32 elementIndex) const
  325. {
  326. AUElement* const element = GetElement(elementIndex);
  327. AUIOElement* const ioel = element != nullptr ? element->AsIOElement() : nullptr;
  328. ausdk::ThrowExceptionIf(ioel == nullptr, kAudioUnitErr_InvalidElement);
  329. return ioel;
  330. }
  331. [[nodiscard]] bool HasElementWithName() const;
  332. void AddElementNamesToDict(CFMutableDictionaryRef inNameDict) const;
  333. [[nodiscard]] std::vector<AudioUnitElement> RestoreElementNames(
  334. CFDictionaryRef inNameDict) const;
  335. [[nodiscard]] AudioUnitScope GetScope() const noexcept { return mScope; }
  336. void SetDelegate(AUScopeDelegate* inDelegate) noexcept { mDelegate = inDelegate; }
  337. void SaveState(CFMutableDataRef data) const;
  338. const UInt8* RestoreState(const UInt8* state) const;
  339. private:
  340. using ElementVector = std::vector<std::unique_ptr<AUElement>>;
  341. AUBase* mCreator{ nullptr };
  342. AudioUnitScope mScope{ 0 };
  343. ElementVector mElements;
  344. AUScopeDelegate* mDelegate{ nullptr };
  345. };
  346. } // namespace ausdk
  347. #endif // AudioUnitSDK_AUScopeElement_h