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.

525 lines
17KB

  1. /*!
  2. @file AudioUnitSDK/AUUtility.h
  3. @copyright © 2000-2021 Apple Inc. All rights reserved.
  4. */
  5. #ifndef AudioUnitSDK_AUUtility_h
  6. #define AudioUnitSDK_AUUtility_h
  7. // OS
  8. #if defined __has_include && __has_include(<CoreAudioTypes/CoreAudioTypes.h>)
  9. #include <CoreAudioTypes/CoreAudioTypes.h>
  10. #else
  11. #include <CoreAudio/CoreAudioTypes.h>
  12. #endif
  13. #include <libkern/OSByteOrder.h>
  14. #include <mach/mach_time.h>
  15. #include <os/log.h>
  16. #include <syslog.h>
  17. // std
  18. #include <bitset>
  19. #include <cstddef>
  20. #include <exception>
  21. #include <mutex>
  22. #include <string>
  23. #include <system_error>
  24. #include <vector>
  25. // -------------------------------------------------------------------------------------------------
  26. #pragma mark -
  27. #pragma mark General
  28. #ifdef AUSDK_NO_DEPRECATIONS
  29. #define AUSDK_DEPRECATED(msg)
  30. #else
  31. #define AUSDK_DEPRECATED(msg) [[deprecated(msg)]] // NOLINT macro
  32. #endif
  33. #ifndef AUSDK_LOG_OBJECT
  34. #define AUSDK_LOG_OBJECT OS_LOG_DEFAULT // NOLINT macro
  35. #endif
  36. // -------------------------------------------------------------------------------------------------
  37. #pragma mark -
  38. #pragma mark Version
  39. #define AUSDK_VERSION_MAJOR 1
  40. #define AUSDK_VERSION_MINOR 1
  41. #define AUSDK_VERSION_PATCH 0
  42. // -------------------------------------------------------------------------------------------------
  43. #pragma mark -
  44. #pragma mark Error-handling macros
  45. #ifdef AUSDK_NO_LOGGING
  46. #define AUSDK_LogError(...) /* NOLINT macro */
  47. #else
  48. #define AUSDK_LogError(...) /* NOLINT macro */ \
  49. if (__builtin_available(macOS 10.11, *)) { \
  50. os_log_error(AUSDK_LOG_OBJECT, __VA_ARGS__); \
  51. } else { \
  52. syslog(LOG_ERR, __VA_ARGS__); \
  53. }
  54. #endif
  55. #define AUSDK_Catch(result) /* NOLINT(cppcoreguidelines-macro-usage) */ \
  56. catch (const ausdk::AUException& exc) { (result) = exc.mError; } \
  57. catch (const std::bad_alloc&) { (result) = kAudio_MemFullError; } \
  58. catch (const OSStatus& catch_err) { (result) = catch_err; } \
  59. catch (const std::system_error& exc) { (result) = exc.code().value(); } \
  60. catch (...) { (result) = -1; }
  61. #define AUSDK_Require(expr, error) /* NOLINT(cppcoreguidelines-macro-usage) */ \
  62. do { \
  63. if (!(expr)) { \
  64. return error; \
  65. } \
  66. } while (0) /* NOLINT */
  67. #define AUSDK_Require_noerr(expr) /* NOLINT(cppcoreguidelines-macro-usage) */ \
  68. do { \
  69. if (const auto status_tmp_macro_detail_ = (expr); status_tmp_macro_detail_ != noErr) { \
  70. return status_tmp_macro_detail_; \
  71. } \
  72. } while (0)
  73. #pragma mark -
  74. // -------------------------------------------------------------------------------------------------
  75. namespace ausdk {
  76. // -------------------------------------------------------------------------------------------------
  77. /// A subclass of std::runtime_error that holds an OSStatus error.
  78. class AUException : public std::runtime_error {
  79. public:
  80. explicit AUException(OSStatus err)
  81. : std::runtime_error{ std::string("OSStatus ") + std::to_string(err) }, mError{ err }
  82. {
  83. }
  84. const OSStatus mError;
  85. };
  86. inline void ThrowExceptionIf(bool condition, OSStatus err)
  87. {
  88. if (condition) {
  89. AUSDK_LogError("throwing %d", static_cast<int>(err));
  90. throw AUException{ err };
  91. }
  92. }
  93. [[noreturn]] inline void Throw(OSStatus err)
  94. {
  95. AUSDK_LogError("throwing %d", static_cast<int>(err));
  96. throw AUException{ err };
  97. }
  98. inline void ThrowQuietIf(bool condition, OSStatus err)
  99. {
  100. if (condition) {
  101. throw AUException{ err };
  102. }
  103. }
  104. [[noreturn]] inline void ThrowQuiet(OSStatus err) { throw AUException{ err }; }
  105. // -------------------------------------------------------------------------------------------------
  106. /// Wrap a std::recursive_mutex in a C++ Mutex (named requirement). Methods are virtual to support
  107. /// customization.
  108. class AUMutex {
  109. public:
  110. AUMutex() = default;
  111. virtual ~AUMutex() = default;
  112. AUMutex(const AUMutex&) = delete;
  113. AUMutex(AUMutex&&) = delete;
  114. AUMutex& operator=(const AUMutex&) = delete;
  115. AUMutex& operator=(AUMutex&&) = delete;
  116. virtual void lock() { mImpl.lock(); }
  117. virtual void unlock() { mImpl.unlock(); }
  118. virtual bool try_lock() { return mImpl.try_lock(); }
  119. private:
  120. std::recursive_mutex mImpl;
  121. };
  122. // -------------------------------------------------------------------------------------------------
  123. /// Implement optional locking at AudioUnit non-realtime entry points (required only for a small
  124. /// number of plug-ins which must synchronize against external entry points).
  125. class AUEntryGuard {
  126. public:
  127. explicit AUEntryGuard(AUMutex* maybeMutex) : mMutex{ maybeMutex }
  128. {
  129. if (mMutex != nullptr) {
  130. mMutex->lock();
  131. }
  132. }
  133. ~AUEntryGuard()
  134. {
  135. if (mMutex != nullptr) {
  136. mMutex->unlock();
  137. }
  138. }
  139. AUEntryGuard(const AUEntryGuard&) = delete;
  140. AUEntryGuard(AUEntryGuard&&) = delete;
  141. AUEntryGuard& operator=(const AUEntryGuard&) = delete;
  142. AUEntryGuard& operator=(AUEntryGuard&&) = delete;
  143. private:
  144. AUMutex* mMutex;
  145. };
  146. // -------------------------------------------------------------------------------------------------
  147. #pragma mark -
  148. #pragma mark ASBD
  149. /// Utility functions relating to AudioStreamBasicDescription.
  150. namespace ASBD {
  151. constexpr bool IsInterleaved(const AudioStreamBasicDescription& format) noexcept
  152. {
  153. return (format.mFormatFlags & kLinearPCMFormatFlagIsNonInterleaved) == 0u;
  154. }
  155. constexpr UInt32 NumberInterleavedChannels(const AudioStreamBasicDescription& format) noexcept
  156. {
  157. return IsInterleaved(format) ? format.mChannelsPerFrame : 1;
  158. }
  159. constexpr UInt32 NumberChannelStreams(const AudioStreamBasicDescription& format) noexcept
  160. {
  161. return IsInterleaved(format) ? 1 : format.mChannelsPerFrame;
  162. }
  163. constexpr bool IsCommonFloat32(const AudioStreamBasicDescription& format) noexcept
  164. {
  165. return (
  166. format.mFormatID == kAudioFormatLinearPCM && format.mFramesPerPacket == 1 &&
  167. format.mBytesPerPacket == format.mBytesPerFrame
  168. // so far, it's a valid PCM format
  169. && (format.mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0 &&
  170. (format.mChannelsPerFrame == 1 ||
  171. (format.mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) &&
  172. ((format.mFormatFlags & kAudioFormatFlagIsBigEndian) == kAudioFormatFlagsNativeEndian) &&
  173. format.mBitsPerChannel == 32 // NOLINT
  174. && format.mBytesPerFrame == NumberInterleavedChannels(format) * sizeof(float));
  175. }
  176. constexpr AudioStreamBasicDescription CreateCommonFloat32(
  177. Float64 sampleRate, UInt32 numChannels, bool interleaved = false) noexcept
  178. {
  179. constexpr auto sampleSize = sizeof(Float32);
  180. AudioStreamBasicDescription asbd{};
  181. asbd.mFormatID = kAudioFormatLinearPCM;
  182. asbd.mFormatFlags = kAudioFormatFlagIsFloat |
  183. static_cast<AudioFormatFlags>(kAudioFormatFlagsNativeEndian) |
  184. kAudioFormatFlagIsPacked;
  185. asbd.mBitsPerChannel = 8 * sampleSize; // NOLINT magic number
  186. asbd.mChannelsPerFrame = numChannels;
  187. asbd.mFramesPerPacket = 1;
  188. asbd.mSampleRate = sampleRate;
  189. if (interleaved) {
  190. asbd.mBytesPerPacket = asbd.mBytesPerFrame = numChannels * sampleSize;
  191. } else {
  192. asbd.mBytesPerPacket = asbd.mBytesPerFrame = sampleSize;
  193. asbd.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
  194. }
  195. return asbd;
  196. }
  197. constexpr bool MinimalSafetyCheck(const AudioStreamBasicDescription& x) noexcept
  198. {
  199. // This function returns false if there are sufficiently unreasonable values in any field.
  200. // It is very conservative so even some very unlikely values will pass.
  201. // This is just meant to catch the case where the data from a file is corrupted.
  202. return (x.mSampleRate >= 0.) && (x.mSampleRate < 3e6) // NOLINT SACD sample rate is 2.8224 MHz
  203. && (x.mBytesPerPacket < 1000000) // NOLINT
  204. && (x.mFramesPerPacket < 1000000) // NOLINT
  205. && (x.mBytesPerFrame < 1000000) // NOLINT
  206. && (x.mChannelsPerFrame > 0) && (x.mChannelsPerFrame <= 1024) // NOLINT
  207. && (x.mBitsPerChannel <= 1024) // NOLINT
  208. && (x.mFormatID != 0) &&
  209. !(x.mFormatID == kAudioFormatLinearPCM &&
  210. (x.mFramesPerPacket != 1 || x.mBytesPerPacket != x.mBytesPerFrame));
  211. }
  212. inline bool IsEqual(
  213. const AudioStreamBasicDescription& lhs, const AudioStreamBasicDescription& rhs) noexcept
  214. {
  215. return memcmp(&lhs, &rhs, sizeof(AudioStreamBasicDescription)) == 0;
  216. }
  217. } // namespace ASBD
  218. // -------------------------------------------------------------------------------------------------
  219. #pragma mark -
  220. #pragma mark ACL
  221. /// Utility functions relating to AudioChannelLayout.
  222. namespace ACL {
  223. constexpr bool operator==(const AudioChannelLayout& lhs, const AudioChannelLayout& rhs) noexcept
  224. {
  225. if (lhs.mChannelLayoutTag != rhs.mChannelLayoutTag) {
  226. return false;
  227. }
  228. if (lhs.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
  229. return lhs.mChannelBitmap == rhs.mChannelBitmap;
  230. }
  231. if (lhs.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
  232. if (lhs.mNumberChannelDescriptions != rhs.mNumberChannelDescriptions) {
  233. return false;
  234. }
  235. for (auto i = 0u; i < lhs.mNumberChannelDescriptions; ++i) {
  236. const auto& lhdesc = lhs.mChannelDescriptions[i]; // NOLINT array subscript
  237. const auto& rhdesc = rhs.mChannelDescriptions[i]; // NOLINT array subscript
  238. if (lhdesc.mChannelLabel != rhdesc.mChannelLabel) {
  239. return false;
  240. }
  241. if (lhdesc.mChannelLabel == kAudioChannelLabel_UseCoordinates) {
  242. if (memcmp(&lhdesc, &rhdesc, sizeof(AudioChannelDescription)) != 0) {
  243. return false;
  244. }
  245. }
  246. }
  247. }
  248. return true;
  249. }
  250. } // namespace ACL
  251. // -------------------------------------------------------------------------------------------------
  252. /// Utility wrapper for the variably-sized AudioChannelLayout struct.
  253. class AUChannelLayout {
  254. public:
  255. AUChannelLayout() : AUChannelLayout(0, kAudioChannelLayoutTag_UseChannelDescriptions, 0) {}
  256. /// Can construct from a layout tag.
  257. explicit AUChannelLayout(AudioChannelLayoutTag inTag) : AUChannelLayout(0, inTag, 0) {}
  258. AUChannelLayout(uint32_t inNumberChannelDescriptions, AudioChannelLayoutTag inChannelLayoutTag,
  259. AudioChannelBitmap inChannelBitMap)
  260. : mStorage(
  261. kHeaderSize + (inNumberChannelDescriptions * sizeof(AudioChannelDescription)), {})
  262. {
  263. auto* const acl = reinterpret_cast<AudioChannelLayout*>(mStorage.data()); // NOLINT
  264. acl->mChannelLayoutTag = inChannelLayoutTag;
  265. acl->mChannelBitmap = inChannelBitMap;
  266. acl->mNumberChannelDescriptions = inNumberChannelDescriptions;
  267. }
  268. /// Implicit conversion from AudioChannelLayout& is allowed.
  269. AUChannelLayout(const AudioChannelLayout& acl) // NOLINT
  270. : mStorage(kHeaderSize + (acl.mNumberChannelDescriptions * sizeof(AudioChannelDescription)))
  271. {
  272. memcpy(mStorage.data(), &acl, mStorage.size());
  273. }
  274. bool operator==(const AUChannelLayout& other) const noexcept
  275. {
  276. return ACL::operator==(Layout(), other.Layout());
  277. }
  278. bool operator!=(const AUChannelLayout& y) const noexcept { return !(*this == y); }
  279. [[nodiscard]] bool IsValid() const noexcept { return NumberChannels() > 0; }
  280. [[nodiscard]] const AudioChannelLayout& Layout() const noexcept { return *LayoutPtr(); }
  281. [[nodiscard]] const AudioChannelLayout* LayoutPtr() const noexcept
  282. {
  283. return reinterpret_cast<const AudioChannelLayout*>(mStorage.data()); // NOLINT
  284. }
  285. /// After default construction, this method will return
  286. /// kAudioChannelLayoutTag_UseChannelDescriptions with 0 channel descriptions.
  287. [[nodiscard]] AudioChannelLayoutTag Tag() const noexcept { return Layout().mChannelLayoutTag; }
  288. [[nodiscard]] uint32_t NumberChannels() const noexcept { return NumberChannels(*LayoutPtr()); }
  289. [[nodiscard]] uint32_t Size() const noexcept { return static_cast<uint32_t>(mStorage.size()); }
  290. static uint32_t NumberChannels(const AudioChannelLayout& inLayout) noexcept
  291. {
  292. if (inLayout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
  293. return inLayout.mNumberChannelDescriptions;
  294. }
  295. if (inLayout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
  296. return static_cast<uint32_t>(
  297. std::bitset<32>(inLayout.mChannelBitmap).count()); // NOLINT magic #
  298. }
  299. return AudioChannelLayoutTag_GetNumberOfChannels(inLayout.mChannelLayoutTag);
  300. }
  301. private:
  302. constexpr static size_t kHeaderSize = offsetof(AudioChannelLayout, mChannelDescriptions[0]);
  303. std::vector<std::byte> mStorage;
  304. };
  305. // -------------------------------------------------------------------------------------------------
  306. #pragma mark -
  307. #pragma mark AudioBufferList
  308. /// Utility functions relating to AudioBufferList.
  309. namespace ABL {
  310. // if the return result is odd, there was a null buffer.
  311. inline uint32_t IsBogusAudioBufferList(const AudioBufferList& abl)
  312. {
  313. const AudioBuffer *buf = abl.mBuffers, *const bufEnd = buf + abl.mNumberBuffers;
  314. uint32_t sum =
  315. 0; // defeat attempts by the compiler to optimize away the code that touches the buffers
  316. uint32_t anyNull = 0;
  317. for (; buf < bufEnd; ++buf) {
  318. const uint32_t* const p = static_cast<const uint32_t*>(buf->mData);
  319. if (p == nullptr) {
  320. anyNull = 1;
  321. continue;
  322. }
  323. const auto dataSize = buf->mDataByteSize;
  324. if (dataSize >= sizeof(*p)) {
  325. const size_t frameCount = dataSize / sizeof(*p);
  326. sum += p[0];
  327. sum += p[frameCount - 1];
  328. }
  329. }
  330. return anyNull | (sum & ~1u);
  331. }
  332. } // namespace ABL
  333. // -------------------------------------------------------------------------------------------------
  334. #pragma mark -
  335. #pragma mark HostTime
  336. /// Utility functions relating to Mach absolute time.
  337. namespace HostTime {
  338. /// Returns the current host time
  339. inline uint64_t Current() { return mach_absolute_time(); }
  340. /// Returns the frequency of the host timebase, in ticks per second.
  341. inline double Frequency()
  342. {
  343. struct mach_timebase_info timeBaseInfo {
  344. }; // NOLINT
  345. mach_timebase_info(&timeBaseInfo);
  346. // the frequency of that clock is: (sToNanosDenominator / sToNanosNumerator) * 10^9
  347. return static_cast<double>(timeBaseInfo.denom) / static_cast<double>(timeBaseInfo.numer) *
  348. 1.0e9; // NOLINT
  349. }
  350. } // namespace HostTime
  351. // -------------------------------------------------------------------------------------------------
  352. /// Basic RAII wrapper for CoreFoundation types
  353. template <typename T>
  354. class Owned {
  355. explicit Owned(T obj, bool fromget) noexcept : mImpl{ obj }
  356. {
  357. if (fromget) {
  358. retainRef();
  359. }
  360. }
  361. public:
  362. static Owned from_get(T obj) noexcept { return Owned{ obj, true }; }
  363. static Owned from_create(T obj) noexcept { return Owned{ obj, false }; }
  364. static Owned from_copy(T obj) noexcept { return Owned{ obj, false }; }
  365. Owned() noexcept = default;
  366. ~Owned() noexcept { releaseRef(); }
  367. Owned(const Owned& other) noexcept : mImpl{ other.mImpl } { retainRef(); }
  368. Owned(Owned&& other) noexcept : mImpl{ std::exchange(other.mImpl, nullptr) } {}
  369. Owned& operator=(const Owned& other) noexcept
  370. {
  371. if (this != &other) {
  372. releaseRef();
  373. mImpl = other.mImpl;
  374. retainRef();
  375. }
  376. return *this;
  377. }
  378. Owned& operator=(Owned&& other) noexcept
  379. {
  380. std::swap(mImpl, other.mImpl);
  381. return *this;
  382. }
  383. T operator*() const noexcept { return get(); }
  384. T get() const noexcept { return mImpl; }
  385. /// As with `unique_ptr<T>::release()`, releases ownership of the reference to the caller (not
  386. /// to be confused with decrementing the reference count as with `CFRelease()`).
  387. T release() noexcept { return std::exchange(mImpl, nullptr); }
  388. /// This is a from_get operation.
  389. Owned& operator=(T cfobj) noexcept
  390. {
  391. if (mImpl != cfobj) {
  392. releaseRef();
  393. mImpl = cfobj;
  394. retainRef();
  395. }
  396. return *this;
  397. }
  398. private:
  399. void retainRef() noexcept
  400. {
  401. if (mImpl != nullptr) {
  402. CFRetain(mImpl);
  403. }
  404. }
  405. void releaseRef() noexcept
  406. {
  407. if (mImpl != nullptr) {
  408. CFRelease(mImpl);
  409. }
  410. }
  411. T mImpl{ nullptr };
  412. };
  413. // -------------------------------------------------------------------------------------------------
  414. constexpr bool safe_isprint(char in_char) noexcept { return (in_char >= ' ') && (in_char <= '~'); }
  415. inline std::string make_string_from_4cc(uint32_t in_4cc) noexcept
  416. {
  417. #if !TARGET_RT_BIG_ENDIAN
  418. in_4cc = OSSwapInt32(in_4cc); // NOLINT
  419. #endif
  420. char* const string = reinterpret_cast<char*>(&in_4cc); // NOLINT
  421. for (size_t i = 0; i < sizeof(in_4cc); ++i) {
  422. if (!safe_isprint(string[i])) { // NOLINT
  423. string[i] = '.'; // NOLINT
  424. }
  425. }
  426. return std::string{ string, sizeof(in_4cc) };
  427. }
  428. } // namespace ausdk
  429. #endif // AudioUnitSDK_AUUtility_h