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.

2062 lines
68KB

  1. /*!
  2. @file AudioUnitSDK/AUBase.cpp
  3. @copyright © 2000-2021 Apple Inc. All rights reserved.
  4. */
  5. #include <AudioUnitSDK/AUBase.h>
  6. #include <AudioUnitSDK/AUInputElement.h>
  7. #include <AudioUnitSDK/AUOutputElement.h>
  8. #include <AudioUnitSDK/AUUtility.h>
  9. #include <algorithm>
  10. #include <array>
  11. #include <cassert>
  12. #include <limits>
  13. namespace ausdk {
  14. #if TARGET_OS_MAC && (TARGET_CPU_X86 || TARGET_CPU_X86_64)
  15. class DenormalDisabler {
  16. public:
  17. DenormalDisabler() : mSavedMXCSR(GetCSR()) { SetCSR(mSavedMXCSR | 0x8040); }
  18. DenormalDisabler(const DenormalDisabler&) = delete;
  19. DenormalDisabler(DenormalDisabler&&) = delete;
  20. DenormalDisabler& operator=(const DenormalDisabler&) = delete;
  21. DenormalDisabler& operator=(DenormalDisabler&&) = delete;
  22. ~DenormalDisabler() { SetCSR(mSavedMXCSR); }
  23. private:
  24. #if 0 // not sure if this is right: // #if __has_include(<xmmintrin.h>)
  25. static unsigned GetCSR() { return _mm_getcsr(); }
  26. static void SetCSR(unsigned x) { _mm_setcsr(x); }
  27. #else
  28. // our compiler does ALL floating point with SSE
  29. static unsigned GetCSR()
  30. {
  31. unsigned result{};
  32. asm volatile("stmxcsr %0" : "=m"(*&result)); // NOLINT asm
  33. return result;
  34. }
  35. static void SetCSR(unsigned a)
  36. {
  37. unsigned temp = a;
  38. asm volatile("ldmxcsr %0" : : "m"(*&temp)); // NOLINT asm
  39. }
  40. #endif
  41. unsigned const mSavedMXCSR;
  42. };
  43. #else
  44. // while denormals can be flushed to zero on ARM processors, there is no performance benefit
  45. class DenormalDisabler {
  46. public:
  47. DenormalDisabler() = default;
  48. };
  49. #endif
  50. static std::once_flag sAUBaseCFStringsInitialized{}; // NOLINT non-const global
  51. // this is used for the presets
  52. static CFStringRef kUntitledString = nullptr; // NOLINT non-const global
  53. // these are the current keys for the class info document
  54. static CFStringRef kVersionString = nullptr; // NOLINT non-const global
  55. static CFStringRef kTypeString = nullptr; // NOLINT non-const global
  56. static CFStringRef kSubtypeString = nullptr; // NOLINT non-const global
  57. static CFStringRef kManufacturerString = nullptr; // NOLINT non-const global
  58. static CFStringRef kDataString = nullptr; // NOLINT non-const global
  59. static CFStringRef kNameString = nullptr; // NOLINT non-const global
  60. static CFStringRef kRenderQualityString = nullptr; // NOLINT non-const global
  61. static CFStringRef kElementNameString = nullptr; // NOLINT non-const global
  62. static CFStringRef kPartString = nullptr; // NOLINT non-const global
  63. static constexpr auto kNoLastRenderedSampleTime = std::numeric_limits<Float64>::lowest();
  64. //_____________________________________________________________________________
  65. //
  66. AUBase::AUBase(AudioComponentInstance inInstance, UInt32 numInputElements, UInt32 numOutputElements,
  67. UInt32 numGroupElements)
  68. : ComponentBase(inInstance), mInitNumInputEls(numInputElements),
  69. mInitNumOutputEls(numOutputElements), mInitNumGroupEls(numGroupElements),
  70. mLogString(CreateLoggingString())
  71. {
  72. ResetRenderTime();
  73. std::call_once(sAUBaseCFStringsInitialized, []() {
  74. kUntitledString = CFSTR("Untitled"); // NOLINT
  75. kVersionString = CFSTR(kAUPresetVersionKey); // NOLINT
  76. kTypeString = CFSTR(kAUPresetTypeKey); // NOLINT
  77. kSubtypeString = CFSTR(kAUPresetSubtypeKey); // NOLINT
  78. kManufacturerString = CFSTR(kAUPresetManufacturerKey); // NOLINT
  79. kDataString = CFSTR(kAUPresetDataKey); // NOLINT
  80. kNameString = CFSTR(kAUPresetNameKey); // NOLINT
  81. kRenderQualityString = CFSTR(kAUPresetRenderQualityKey); // NOLINT
  82. kElementNameString = CFSTR(kAUPresetElementNameKey); // NOLINT
  83. kPartString = CFSTR(kAUPresetPartKey); // NOLINT
  84. });
  85. GlobalScope().Initialize(this, kAudioUnitScope_Global, 1);
  86. mCurrentPreset.presetNumber = -1;
  87. CFRetain(mCurrentPreset.presetName = kUntitledString);
  88. }
  89. //_____________________________________________________________________________
  90. //
  91. AUBase::~AUBase()
  92. {
  93. if (mCurrentPreset.presetName != nullptr) {
  94. CFRelease(mCurrentPreset.presetName);
  95. }
  96. }
  97. //_____________________________________________________________________________
  98. //
  99. void AUBase::PostConstructorInternal()
  100. {
  101. // invoked after construction because they are virtual methods and/or call virtual methods
  102. if (mMaxFramesPerSlice == 0) {
  103. SetMaxFramesPerSlice(kAUDefaultMaxFramesPerSlice);
  104. }
  105. CreateElements();
  106. }
  107. //_____________________________________________________________________________
  108. //
  109. void AUBase::PreDestructorInternal()
  110. {
  111. // this is called from the ComponentBase dispatcher, which doesn't know anything about our
  112. // (optional) lock
  113. const AUEntryGuard guard(mAUMutex);
  114. DoCleanup();
  115. }
  116. //_____________________________________________________________________________
  117. //
  118. void AUBase::CreateElements()
  119. {
  120. if (!mElementsCreated) {
  121. Inputs().Initialize(this, kAudioUnitScope_Input, mInitNumInputEls);
  122. Outputs().Initialize(this, kAudioUnitScope_Output, mInitNumOutputEls);
  123. Groups().Initialize(this, kAudioUnitScope_Group, mInitNumGroupEls);
  124. CreateExtendedElements();
  125. mElementsCreated = true;
  126. }
  127. }
  128. //_____________________________________________________________________________
  129. //
  130. void AUBase::SetMaxFramesPerSlice(UInt32 nFrames)
  131. {
  132. if (nFrames == mMaxFramesPerSlice) {
  133. return;
  134. }
  135. mMaxFramesPerSlice = nFrames;
  136. if (mBuffersAllocated) {
  137. ReallocateBuffers();
  138. }
  139. PropertyChanged(kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0);
  140. }
  141. //_____________________________________________________________________________
  142. //
  143. OSStatus AUBase::CanSetMaxFrames() const
  144. {
  145. return IsInitialized() ? kAudioUnitErr_Initialized : OSStatus(noErr);
  146. }
  147. //_____________________________________________________________________________
  148. //
  149. void AUBase::ReallocateBuffers()
  150. {
  151. CreateElements();
  152. const UInt32 nOutputs = Outputs().GetNumberOfElements();
  153. for (UInt32 i = 0; i < nOutputs; ++i) {
  154. Output(i).AllocateBuffer(); // does no work if already allocated
  155. }
  156. const UInt32 nInputs = Inputs().GetNumberOfElements();
  157. for (UInt32 i = 0; i < nInputs; ++i) {
  158. Input(i).AllocateBuffer(); // does no work if already allocated
  159. }
  160. mBuffersAllocated = true;
  161. }
  162. //_____________________________________________________________________________
  163. //
  164. void AUBase::DeallocateIOBuffers()
  165. {
  166. if (!mBuffersAllocated) {
  167. return;
  168. }
  169. const UInt32 nOutputs = Outputs().GetNumberOfElements();
  170. for (UInt32 i = 0; i < nOutputs; ++i) {
  171. Output(i).DeallocateBuffer();
  172. }
  173. const UInt32 nInputs = Inputs().GetNumberOfElements();
  174. for (UInt32 i = 0; i < nInputs; ++i) {
  175. Input(i).DeallocateBuffer();
  176. }
  177. mBuffersAllocated = false;
  178. }
  179. //_____________________________________________________________________________
  180. //
  181. OSStatus AUBase::DoInitialize()
  182. {
  183. OSStatus result = noErr;
  184. if (!mInitialized) {
  185. result = Initialize();
  186. if (result == noErr) {
  187. if (CanScheduleParameters()) {
  188. mParamEventList.reserve(24); // NOLINT magic #
  189. }
  190. mHasBegunInitializing = true;
  191. ReallocateBuffers(); // calls CreateElements()
  192. mInitialized = true; // signal that it's okay to render
  193. std::atomic_thread_fence(std::memory_order_seq_cst);
  194. }
  195. }
  196. return result;
  197. }
  198. //_____________________________________________________________________________
  199. //
  200. OSStatus AUBase::Initialize() { return noErr; }
  201. //_____________________________________________________________________________
  202. //
  203. void AUBase::DoCleanup()
  204. {
  205. if (mInitialized) {
  206. Cleanup();
  207. }
  208. DeallocateIOBuffers();
  209. ResetRenderTime();
  210. mInitialized = false;
  211. mHasBegunInitializing = false;
  212. }
  213. //_____________________________________________________________________________
  214. //
  215. void AUBase::Cleanup() {}
  216. //_____________________________________________________________________________
  217. //
  218. OSStatus AUBase::Reset(AudioUnitScope /*inScope*/, AudioUnitElement /*inElement*/)
  219. {
  220. ResetRenderTime();
  221. return noErr;
  222. }
  223. //_____________________________________________________________________________
  224. //
  225. OSStatus AUBase::DispatchGetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
  226. AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable)
  227. {
  228. OSStatus result = noErr;
  229. bool validateElement = true;
  230. switch (inID) {
  231. case kAudioUnitProperty_MakeConnection:
  232. AUSDK_Require(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Global,
  233. kAudioUnitErr_InvalidScope);
  234. outDataSize = sizeof(AudioUnitConnection);
  235. outWritable = true;
  236. break;
  237. case kAudioUnitProperty_SetRenderCallback:
  238. AUSDK_Require(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Global,
  239. kAudioUnitErr_InvalidScope);
  240. outDataSize = sizeof(AURenderCallbackStruct);
  241. outWritable = true;
  242. break;
  243. case kAudioUnitProperty_StreamFormat:
  244. outDataSize = sizeof(AudioStreamBasicDescription);
  245. outWritable = IsStreamFormatWritable(inScope, inElement);
  246. break;
  247. case kAudioUnitProperty_SampleRate:
  248. outDataSize = sizeof(Float64);
  249. outWritable = IsStreamFormatWritable(inScope, inElement);
  250. break;
  251. case kAudioUnitProperty_ClassInfo:
  252. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  253. outDataSize = sizeof(CFPropertyListRef);
  254. outWritable = true;
  255. break;
  256. case kAudioUnitProperty_FactoryPresets:
  257. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  258. AUSDK_Require_noerr(GetPresets(nullptr));
  259. outDataSize = sizeof(CFArrayRef);
  260. outWritable = false;
  261. break;
  262. case kAudioUnitProperty_PresentPreset:
  263. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  264. outDataSize = sizeof(AUPreset);
  265. outWritable = true;
  266. break;
  267. case kAudioUnitProperty_ElementName:
  268. outDataSize = sizeof(CFStringRef);
  269. outWritable = true;
  270. break;
  271. case kAudioUnitProperty_ParameterList: {
  272. UInt32 nparams = 0;
  273. AUSDK_Require_noerr(GetParameterList(inScope, nullptr, nparams));
  274. outDataSize = sizeof(AudioUnitParameterID) * nparams;
  275. outWritable = false;
  276. validateElement = false;
  277. break;
  278. }
  279. case kAudioUnitProperty_ParameterInfo:
  280. outDataSize = sizeof(AudioUnitParameterInfo);
  281. outWritable = false;
  282. validateElement = false;
  283. break;
  284. case kAudioUnitProperty_ParameterHistoryInfo:
  285. outDataSize = sizeof(AudioUnitParameterHistoryInfo);
  286. outWritable = false;
  287. validateElement = false;
  288. break;
  289. case kAudioUnitProperty_ElementCount:
  290. outDataSize = sizeof(UInt32);
  291. outWritable = BusCountWritable(inScope);
  292. validateElement = false;
  293. break;
  294. case kAudioUnitProperty_Latency:
  295. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  296. outDataSize = sizeof(Float64);
  297. outWritable = false;
  298. break;
  299. case kAudioUnitProperty_TailTime:
  300. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  301. AUSDK_Require(SupportsTail(), kAudioUnitErr_InvalidProperty);
  302. outDataSize = sizeof(Float64);
  303. outWritable = false;
  304. break;
  305. case kAudioUnitProperty_MaximumFramesPerSlice:
  306. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  307. outDataSize = sizeof(UInt32);
  308. outWritable = true;
  309. break;
  310. case kAudioUnitProperty_LastRenderError:
  311. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  312. outDataSize = sizeof(OSStatus);
  313. outWritable = false;
  314. break;
  315. case kAudioUnitProperty_SupportedNumChannels: {
  316. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  317. const UInt32 num = SupportedNumChannels(nullptr);
  318. AUSDK_Require(num != 0u, kAudioUnitErr_InvalidProperty);
  319. outDataSize = sizeof(AUChannelInfo) * num;
  320. outWritable = false;
  321. break;
  322. }
  323. case kAudioUnitProperty_SupportedChannelLayoutTags: {
  324. const auto tags = GetChannelLayoutTags(inScope, inElement);
  325. AUSDK_Require(!tags.empty(), kAudioUnitErr_InvalidProperty);
  326. outDataSize = static_cast<UInt32>(tags.size() * sizeof(AudioChannelLayoutTag));
  327. outWritable = false;
  328. validateElement = false; // already done it
  329. break;
  330. }
  331. case kAudioUnitProperty_AudioChannelLayout: {
  332. outWritable = false;
  333. outDataSize = GetAudioChannelLayout(inScope, inElement, nullptr, outWritable);
  334. if (outDataSize != 0u) {
  335. result = noErr;
  336. } else {
  337. const auto tags = GetChannelLayoutTags(inScope, inElement);
  338. return tags.empty() ? kAudioUnitErr_InvalidProperty
  339. : kAudioUnitErr_InvalidPropertyValue;
  340. }
  341. validateElement = false; // already done it
  342. break;
  343. }
  344. case kAudioUnitProperty_ShouldAllocateBuffer:
  345. AUSDK_Require((inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output),
  346. kAudioUnitErr_InvalidScope);
  347. outWritable = true;
  348. outDataSize = sizeof(UInt32);
  349. break;
  350. case kAudioUnitProperty_ParameterValueStrings:
  351. AUSDK_Require_noerr(GetParameterValueStrings(inScope, inElement, nullptr));
  352. outDataSize = sizeof(CFArrayRef);
  353. outWritable = false;
  354. validateElement = false;
  355. break;
  356. case kAudioUnitProperty_HostCallbacks:
  357. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  358. outDataSize = sizeof(mHostCallbackInfo);
  359. outWritable = true;
  360. break;
  361. case kAudioUnitProperty_ContextName:
  362. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  363. outDataSize = sizeof(CFStringRef);
  364. outWritable = true;
  365. break;
  366. #if !TARGET_OS_IPHONE
  367. case kAudioUnitProperty_IconLocation:
  368. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  369. AUSDK_Require(HasIcon(), kAudioUnitErr_InvalidProperty);
  370. outWritable = false;
  371. outDataSize = sizeof(CFURLRef);
  372. break;
  373. #endif
  374. case kAudioUnitProperty_ParameterClumpName:
  375. outDataSize = sizeof(AudioUnitParameterNameInfo);
  376. outWritable = false;
  377. break;
  378. case 61: // kAudioUnitProperty_LastRenderSampleTime
  379. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  380. outDataSize = sizeof(Float64);
  381. outWritable = false;
  382. break;
  383. case kAudioUnitProperty_NickName:
  384. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  385. outDataSize = sizeof(CFStringRef);
  386. outWritable = true;
  387. break;
  388. default:
  389. result = GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable);
  390. validateElement = false;
  391. break;
  392. }
  393. if ((result == noErr) && validateElement) {
  394. AUSDK_Require(GetElement(inScope, inElement) != nullptr, kAudioUnitErr_InvalidElement);
  395. }
  396. return result;
  397. }
  398. //_____________________________________________________________________________
  399. //
  400. // NOLINTNEXTLINE(misc-no-recursion) with SaveState
  401. OSStatus AUBase::DispatchGetProperty(
  402. AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData)
  403. {
  404. // NOTE: We're currently only called from AUBase::ComponentEntryDispatch, which
  405. // calls DispatchGetPropertyInfo first, which performs validation of the scope/element,
  406. // and ensures that the outData buffer is non-null and large enough.
  407. OSStatus result = noErr;
  408. switch (inID) {
  409. case kAudioUnitProperty_StreamFormat:
  410. *static_cast<AudioStreamBasicDescription*>(outData) = GetStreamFormat(inScope, inElement);
  411. break;
  412. case kAudioUnitProperty_SampleRate:
  413. *static_cast<Float64*>(outData) = GetStreamFormat(inScope, inElement).mSampleRate;
  414. break;
  415. case kAudioUnitProperty_ParameterList: {
  416. UInt32 nparams = 0;
  417. result = GetParameterList(inScope, static_cast<AudioUnitParameterID*>(outData), nparams);
  418. break;
  419. }
  420. case kAudioUnitProperty_ParameterInfo:
  421. *static_cast<AudioUnitParameterInfo*>(outData) = {};
  422. result =
  423. GetParameterInfo(inScope, inElement, *static_cast<AudioUnitParameterInfo*>(outData));
  424. break;
  425. case kAudioUnitProperty_ParameterHistoryInfo: {
  426. auto* const info = static_cast<AudioUnitParameterHistoryInfo*>(outData);
  427. result = GetParameterHistoryInfo(
  428. inScope, inElement, info->updatesPerSecond, info->historyDurationInSeconds);
  429. break;
  430. }
  431. case kAudioUnitProperty_ClassInfo: {
  432. *static_cast<CFPropertyListRef*>(outData) = nullptr;
  433. result = SaveState(static_cast<CFPropertyListRef*>(outData));
  434. break;
  435. }
  436. case kAudioUnitProperty_FactoryPresets: {
  437. *static_cast<CFArrayRef*>(outData) = nullptr;
  438. result = GetPresets(static_cast<CFArrayRef*>(outData));
  439. break;
  440. }
  441. case kAudioUnitProperty_PresentPreset: {
  442. *static_cast<AUPreset*>(outData) = mCurrentPreset;
  443. // retain current string (as client owns a reference to it and will release it)
  444. if ((inID == kAudioUnitProperty_PresentPreset) && (mCurrentPreset.presetName != nullptr)) {
  445. CFRetain(mCurrentPreset.presetName);
  446. }
  447. result = noErr;
  448. break;
  449. }
  450. case kAudioUnitProperty_ElementName: {
  451. const AUElement* const element = GetElement(inScope, inElement);
  452. const CFStringRef name = *element->GetName();
  453. AUSDK_Require(name != nullptr, kAudioUnitErr_PropertyNotInUse);
  454. CFRetain(name); // must return a +1 reference
  455. *static_cast<CFStringRef*>(outData) = name;
  456. break;
  457. }
  458. case kAudioUnitProperty_ElementCount:
  459. *static_cast<UInt32*>(outData) = GetScope(inScope).GetNumberOfElements();
  460. break;
  461. case kAudioUnitProperty_Latency:
  462. *static_cast<Float64*>(outData) = GetLatency();
  463. break;
  464. case kAudioUnitProperty_TailTime:
  465. AUSDK_Require(SupportsTail(), kAudioUnitErr_InvalidProperty);
  466. *static_cast<Float64*>(outData) = GetTailTime();
  467. break;
  468. case kAudioUnitProperty_MaximumFramesPerSlice:
  469. *static_cast<UInt32*>(outData) = mMaxFramesPerSlice;
  470. break;
  471. case kAudioUnitProperty_LastRenderError:
  472. *static_cast<OSStatus*>(outData) = mLastRenderError;
  473. mLastRenderError = 0;
  474. break;
  475. case kAudioUnitProperty_SupportedNumChannels: {
  476. const AUChannelInfo* infoPtr = nullptr;
  477. const UInt32 num = SupportedNumChannels(&infoPtr);
  478. if (num != 0 && infoPtr != nullptr) {
  479. memcpy(outData, infoPtr, num * sizeof(AUChannelInfo));
  480. }
  481. break;
  482. }
  483. case kAudioUnitProperty_SupportedChannelLayoutTags: {
  484. const auto tags = GetChannelLayoutTags(inScope, inElement);
  485. AUSDK_Require(!tags.empty(), kAudioUnitErr_InvalidProperty);
  486. AudioChannelLayoutTag* const ptr =
  487. outData != nullptr ? static_cast<AudioChannelLayoutTag*>(outData) : nullptr;
  488. if (ptr != nullptr) {
  489. memcpy(ptr, tags.data(), tags.size() * sizeof(AudioChannelLayoutTag));
  490. }
  491. break;
  492. }
  493. case kAudioUnitProperty_AudioChannelLayout: {
  494. AudioChannelLayout* const ptr =
  495. outData != nullptr ? static_cast<AudioChannelLayout*>(outData) : nullptr;
  496. bool writable = false;
  497. const UInt32 dataSize = GetAudioChannelLayout(inScope, inElement, ptr, writable);
  498. AUSDK_Require(dataSize != 0, kAudioUnitErr_InvalidProperty);
  499. break;
  500. }
  501. case kAudioUnitProperty_ShouldAllocateBuffer: {
  502. const auto& element = IOElement(inScope, inElement);
  503. *static_cast<UInt32*>(outData) = static_cast<UInt32>(element.WillAllocateBuffer());
  504. break;
  505. }
  506. case kAudioUnitProperty_ParameterValueStrings:
  507. result = GetParameterValueStrings(inScope, inElement, static_cast<CFArrayRef*>(outData));
  508. break;
  509. case kAudioUnitProperty_HostCallbacks:
  510. memcpy(outData, &mHostCallbackInfo, sizeof(mHostCallbackInfo));
  511. break;
  512. case kAudioUnitProperty_ContextName:
  513. if (const CFStringRef name = *mContextName) {
  514. CFRetain(name); // must return a +1 reference
  515. *static_cast<CFStringRef*>(outData) = name;
  516. result = noErr;
  517. } else {
  518. *static_cast<CFStringRef*>(outData) = nullptr;
  519. result = kAudioUnitErr_PropertyNotInUse;
  520. }
  521. break;
  522. #if !TARGET_OS_IPHONE
  523. case kAudioUnitProperty_IconLocation: {
  524. const CFURLRef iconLocation = CopyIconLocation();
  525. AUSDK_Require(iconLocation != nullptr, kAudioUnitErr_InvalidProperty);
  526. *static_cast<CFURLRef*>(outData) = iconLocation;
  527. break;
  528. }
  529. #endif
  530. case kAudioUnitProperty_ParameterClumpName: {
  531. auto* const ioClumpInfo = static_cast<AudioUnitParameterNameInfo*>(outData);
  532. AUSDK_Require(ioClumpInfo->inID != kAudioUnitClumpID_System,
  533. kAudioUnitErr_InvalidPropertyValue); // this ID value is reserved
  534. result = CopyClumpName(inScope, ioClumpInfo->inID,
  535. static_cast<UInt32>(std::max(ioClumpInfo->inDesiredLength, SInt32(0))),
  536. &ioClumpInfo->outName);
  537. // this is provided for compatbility with existing implementations that don't know
  538. // about this new mechanism
  539. if (result == kAudioUnitErr_InvalidProperty) {
  540. result = GetProperty(inID, inScope, inElement, outData);
  541. }
  542. break;
  543. }
  544. case 61: // kAudioUnitProperty_LastRenderSampleTime
  545. *static_cast<Float64*>(outData) = mCurrentRenderTime.mSampleTime;
  546. break;
  547. case kAudioUnitProperty_NickName:
  548. // Ownership follows Core Foundation's 'Copy Rule'
  549. if (const auto* const name = *mNickName) {
  550. CFRetain(name);
  551. *static_cast<CFStringRef*>(outData) = name;
  552. } else {
  553. *static_cast<CFStringRef*>(outData) = nullptr;
  554. }
  555. break;
  556. default:
  557. result = GetProperty(inID, inScope, inElement, outData);
  558. break;
  559. }
  560. return result;
  561. }
  562. //_____________________________________________________________________________
  563. //
  564. // Note: We can be sure inData is non-null; otherwise RemoveProperty would have been called.
  565. // NOLINTNEXTLINE(misc-no-recursion) with RestoreState
  566. OSStatus AUBase::DispatchSetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
  567. AudioUnitElement inElement, const void* inData, UInt32 inDataSize)
  568. {
  569. OSStatus result = noErr;
  570. switch (inID) {
  571. case kAudioUnitProperty_MakeConnection: {
  572. AUSDK_Require(
  573. inDataSize >= sizeof(AudioUnitConnection), kAudioUnitErr_InvalidPropertyValue);
  574. const auto& connection = *static_cast<const AudioUnitConnection*>(inData);
  575. result = SetConnection(connection);
  576. break;
  577. }
  578. case kAudioUnitProperty_SetRenderCallback: {
  579. AUSDK_Require(
  580. inDataSize >= sizeof(AURenderCallbackStruct), kAudioUnitErr_InvalidPropertyValue);
  581. const auto& callback = *static_cast<const AURenderCallbackStruct*>(inData);
  582. result = SetInputCallback(kAudioUnitProperty_SetRenderCallback, inElement,
  583. callback.inputProc, callback.inputProcRefCon);
  584. break;
  585. }
  586. case kAudioUnitProperty_ElementCount:
  587. AUSDK_Require(inDataSize == sizeof(UInt32), kAudioUnitErr_InvalidPropertyValue);
  588. AUSDK_Require(BusCountWritable(inScope), kAudioUnitErr_PropertyNotWritable);
  589. result = SetBusCount(inScope, *static_cast<const UInt32*>(inData));
  590. if (result == noErr) {
  591. PropertyChanged(inID, inScope, inElement);
  592. }
  593. break;
  594. case kAudioUnitProperty_MaximumFramesPerSlice:
  595. AUSDK_Require(inDataSize == sizeof(UInt32), kAudioUnitErr_InvalidPropertyValue);
  596. AUSDK_Require_noerr(CanSetMaxFrames());
  597. SetMaxFramesPerSlice(*static_cast<const UInt32*>(inData));
  598. break;
  599. case kAudioUnitProperty_StreamFormat: {
  600. constexpr static UInt32 kMinimumValidASBDSize = 36;
  601. AUSDK_Require(inDataSize >= kMinimumValidASBDSize, kAudioUnitErr_InvalidPropertyValue);
  602. AUSDK_Require(GetElement(inScope, inElement) != nullptr, kAudioUnitErr_InvalidElement);
  603. AudioStreamBasicDescription newDesc = {};
  604. // now we're going to be ultra conservative! because of discrepancies between
  605. // sizes of this struct based on aligment padding inconsistencies
  606. memcpy(&newDesc, inData, kMinimumValidASBDSize);
  607. AUSDK_Require(ASBD::MinimalSafetyCheck(newDesc), kAudioUnitErr_FormatNotSupported);
  608. AUSDK_Require(ValidFormat(inScope, inElement, newDesc), kAudioUnitErr_FormatNotSupported);
  609. const AudioStreamBasicDescription curDesc = GetStreamFormat(inScope, inElement);
  610. if (!ASBD::IsEqual(curDesc, newDesc)) {
  611. AUSDK_Require(
  612. IsStreamFormatWritable(inScope, inElement), kAudioUnitErr_PropertyNotWritable);
  613. result = ChangeStreamFormat(inScope, inElement, curDesc, newDesc);
  614. }
  615. break;
  616. }
  617. case kAudioUnitProperty_SampleRate: {
  618. AUSDK_Require(inDataSize == sizeof(Float64), kAudioUnitErr_InvalidPropertyValue);
  619. AUSDK_Require(GetElement(inScope, inElement) != nullptr, kAudioUnitErr_InvalidElement);
  620. const AudioStreamBasicDescription curDesc = GetStreamFormat(inScope, inElement);
  621. AudioStreamBasicDescription newDesc = curDesc;
  622. newDesc.mSampleRate = *static_cast<const Float64*>(inData);
  623. AUSDK_Require(ValidFormat(inScope, inElement, newDesc), kAudioUnitErr_FormatNotSupported);
  624. if (!ASBD::IsEqual(curDesc, newDesc)) {
  625. AUSDK_Require(
  626. IsStreamFormatWritable(inScope, inElement), kAudioUnitErr_PropertyNotWritable);
  627. result = ChangeStreamFormat(inScope, inElement, curDesc, newDesc);
  628. }
  629. break;
  630. }
  631. case kAudioUnitProperty_AudioChannelLayout: {
  632. const auto& layout = *static_cast<const AudioChannelLayout*>(inData);
  633. constexpr size_t headerSize = sizeof(AudioChannelLayout) - sizeof(AudioChannelDescription);
  634. AUSDK_Require(inDataSize >= offsetof(AudioChannelLayout, mNumberChannelDescriptions) +
  635. sizeof(AudioChannelLayout::mNumberChannelDescriptions),
  636. kAudioUnitErr_InvalidPropertyValue);
  637. AUSDK_Require(inDataSize >= headerSize + layout.mNumberChannelDescriptions *
  638. sizeof(AudioChannelDescription),
  639. kAudioUnitErr_InvalidPropertyValue);
  640. result = SetAudioChannelLayout(inScope, inElement, &layout);
  641. if (result == noErr) {
  642. PropertyChanged(inID, inScope, inElement);
  643. }
  644. break;
  645. }
  646. case kAudioUnitProperty_ClassInfo:
  647. AUSDK_Require(inDataSize == sizeof(CFPropertyListRef*), kAudioUnitErr_InvalidPropertyValue);
  648. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  649. result = RestoreState(*static_cast<const CFPropertyListRef*>(inData));
  650. break;
  651. case kAudioUnitProperty_PresentPreset: {
  652. AUSDK_Require(inDataSize == sizeof(AUPreset), kAudioUnitErr_InvalidPropertyValue);
  653. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  654. const auto& newPreset = *static_cast<const AUPreset*>(inData);
  655. if (newPreset.presetNumber >= 0) {
  656. result = NewFactoryPresetSet(newPreset);
  657. // NewFactoryPresetSet SHOULD call SetAFactoryPreset if the preset is valid
  658. // from its own list of preset number->name
  659. if (result == noErr) {
  660. PropertyChanged(inID, inScope, inElement);
  661. }
  662. } else if (newPreset.presetName != nullptr) {
  663. result = NewCustomPresetSet(newPreset);
  664. if (result == noErr) {
  665. PropertyChanged(inID, inScope, inElement);
  666. }
  667. } else {
  668. result = kAudioUnitErr_InvalidPropertyValue;
  669. }
  670. break;
  671. }
  672. case kAudioUnitProperty_ElementName: {
  673. AUSDK_Require(GetElement(inScope, inElement) != nullptr, kAudioUnitErr_InvalidElement);
  674. AUSDK_Require(inDataSize == sizeof(CFStringRef), kAudioUnitErr_InvalidPropertyValue);
  675. const auto element = GetScope(inScope).GetElement(inElement);
  676. const CFStringRef inStr = *static_cast<const CFStringRef*>(inData);
  677. element->SetName(inStr);
  678. PropertyChanged(inID, inScope, inElement);
  679. break;
  680. }
  681. case kAudioUnitProperty_ShouldAllocateBuffer: {
  682. AUSDK_Require((inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output),
  683. kAudioUnitErr_InvalidScope);
  684. AUSDK_Require(GetElement(inScope, inElement) != nullptr, kAudioUnitErr_InvalidElement);
  685. AUSDK_Require(inDataSize == sizeof(UInt32), kAudioUnitErr_InvalidPropertyValue);
  686. AUSDK_Require(!IsInitialized(), kAudioUnitErr_Initialized);
  687. auto& element = IOElement(inScope, inElement);
  688. element.SetWillAllocateBuffer(*static_cast<const UInt32*>(inData) != 0);
  689. break;
  690. }
  691. case kAudioUnitProperty_HostCallbacks: {
  692. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  693. const UInt32 availSize =
  694. std::min(inDataSize, static_cast<UInt32>(sizeof(HostCallbackInfo)));
  695. const bool hasChanged = memcmp(&mHostCallbackInfo, inData, availSize) == 0;
  696. mHostCallbackInfo = {};
  697. memcpy(&mHostCallbackInfo, inData, availSize);
  698. if (hasChanged) {
  699. PropertyChanged(inID, inScope, inElement);
  700. }
  701. break;
  702. }
  703. case kAudioUnitProperty_ContextName: {
  704. AUSDK_Require(inDataSize == sizeof(CFStringRef), kAudioUnitErr_InvalidPropertyValue);
  705. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  706. const CFStringRef inStr = *static_cast<const CFStringRef*>(inData);
  707. mContextName = inStr;
  708. PropertyChanged(inID, inScope, inElement);
  709. break;
  710. }
  711. case kAudioUnitProperty_NickName: {
  712. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  713. AUSDK_Require(inDataSize == sizeof(CFStringRef), kAudioUnitErr_InvalidPropertyValue);
  714. const CFStringRef inStr = *static_cast<const CFStringRef*>(inData);
  715. mNickName = inStr;
  716. PropertyChanged(inID, inScope, inElement);
  717. break;
  718. }
  719. default:
  720. result = SetProperty(inID, inScope, inElement, inData, inDataSize);
  721. if (result == noErr) {
  722. PropertyChanged(inID, inScope, inElement);
  723. }
  724. break;
  725. }
  726. return result;
  727. }
  728. //_____________________________________________________________________________
  729. //
  730. OSStatus AUBase::DispatchRemovePropertyValue(
  731. AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement)
  732. {
  733. OSStatus result = noErr;
  734. switch (inID) {
  735. case kAudioUnitProperty_AudioChannelLayout: {
  736. result = RemoveAudioChannelLayout(inScope, inElement);
  737. if (result == noErr) {
  738. PropertyChanged(inID, inScope, inElement);
  739. }
  740. break;
  741. }
  742. case kAudioUnitProperty_HostCallbacks: {
  743. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  744. bool hasValue = false;
  745. const void* const ptr = &mHostCallbackInfo;
  746. for (size_t i = 0; i < sizeof(HostCallbackInfo); ++i) {
  747. if (static_cast<const char*>(ptr)[i] != 0) { // NOLINT
  748. hasValue = true;
  749. break;
  750. }
  751. }
  752. if (hasValue) {
  753. mHostCallbackInfo = {};
  754. PropertyChanged(inID, inScope, inElement);
  755. }
  756. break;
  757. }
  758. case kAudioUnitProperty_ContextName:
  759. mContextName = nullptr;
  760. result = noErr;
  761. break;
  762. case kAudioUnitProperty_NickName: {
  763. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  764. mNickName = nullptr;
  765. PropertyChanged(inID, inScope, inElement);
  766. break;
  767. }
  768. default:
  769. result = RemovePropertyValue(inID, inScope, inElement);
  770. break;
  771. }
  772. return result;
  773. }
  774. //_____________________________________________________________________________
  775. //
  776. OSStatus AUBase::GetPropertyInfo(AudioUnitPropertyID /*inID*/, AudioUnitScope /*inScope*/,
  777. AudioUnitElement /*inElement*/, UInt32& /*outDataSize*/, bool& /*outWritable*/)
  778. {
  779. return kAudioUnitErr_InvalidProperty;
  780. }
  781. //_____________________________________________________________________________
  782. //
  783. OSStatus AUBase::GetProperty(AudioUnitPropertyID /*inID*/, AudioUnitScope /*inScope*/,
  784. AudioUnitElement /*inElement*/, void* /*outData*/)
  785. {
  786. return kAudioUnitErr_InvalidProperty;
  787. }
  788. //_____________________________________________________________________________
  789. //
  790. OSStatus AUBase::SetProperty(AudioUnitPropertyID /*inID*/, AudioUnitScope /*inScope*/,
  791. AudioUnitElement /*inElement*/, const void* /*inData*/, UInt32 /*inDataSize*/)
  792. {
  793. return kAudioUnitErr_InvalidProperty;
  794. }
  795. //_____________________________________________________________________________
  796. //
  797. OSStatus AUBase::RemovePropertyValue(
  798. AudioUnitPropertyID /*inID*/, AudioUnitScope /*inScope*/, AudioUnitElement /*inElement*/)
  799. {
  800. return kAudioUnitErr_InvalidPropertyValue;
  801. }
  802. //_____________________________________________________________________________
  803. //
  804. OSStatus AUBase::AddPropertyListener(
  805. AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcRefCon)
  806. {
  807. const PropertyListener pl{
  808. .propertyID = inID, .listenerProc = inProc, .listenerRefCon = inProcRefCon
  809. };
  810. if (mPropertyListeners.empty()) {
  811. mPropertyListeners.reserve(32); // NOLINT magic#
  812. }
  813. mPropertyListeners.push_back(pl);
  814. return noErr;
  815. }
  816. //_____________________________________________________________________________
  817. //
  818. OSStatus AUBase::RemovePropertyListener(AudioUnitPropertyID inID,
  819. AudioUnitPropertyListenerProc inProc, void* inProcRefCon, bool refConSpecified)
  820. {
  821. const auto iter =
  822. std::remove_if(mPropertyListeners.begin(), mPropertyListeners.end(), [&](auto& item) {
  823. return item.propertyID == inID && item.listenerProc == inProc &&
  824. (!refConSpecified || item.listenerRefCon == inProcRefCon);
  825. });
  826. if (iter != mPropertyListeners.end()) {
  827. mPropertyListeners.erase(iter, mPropertyListeners.end());
  828. }
  829. return noErr;
  830. }
  831. //_____________________________________________________________________________
  832. //
  833. void AUBase::PropertyChanged(
  834. AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement)
  835. {
  836. for (const auto& pl : mPropertyListeners) {
  837. if (pl.propertyID == inID) {
  838. (pl.listenerProc)(pl.listenerRefCon, GetComponentInstance(), inID, inScope, inElement);
  839. }
  840. }
  841. }
  842. //_____________________________________________________________________________
  843. //
  844. OSStatus AUBase::SetRenderNotification(AURenderCallback inProc, void* inRefCon)
  845. {
  846. if (inProc == nullptr) {
  847. return kAudio_ParamError;
  848. }
  849. mRenderCallbacksTouched = true;
  850. mRenderCallbacks.add(RenderCallback(inProc, inRefCon));
  851. // this will do nothing if it's already in the list
  852. return noErr;
  853. }
  854. //_____________________________________________________________________________
  855. //
  856. OSStatus AUBase::RemoveRenderNotification(AURenderCallback inProc, void* inRefCon)
  857. {
  858. mRenderCallbacks.remove(RenderCallback(inProc, inRefCon));
  859. return noErr; // error?
  860. }
  861. //_____________________________________________________________________________
  862. //
  863. OSStatus AUBase::GetParameter(AudioUnitParameterID inID, AudioUnitScope inScope,
  864. AudioUnitElement inElement, AudioUnitParameterValue& outValue)
  865. {
  866. const auto& elem = Element(inScope, inElement);
  867. outValue = elem.GetParameter(inID);
  868. return noErr;
  869. }
  870. //_____________________________________________________________________________
  871. //
  872. OSStatus AUBase::SetParameter(AudioUnitParameterID inID, AudioUnitScope inScope,
  873. AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 /*inBufferOffsetInFrames*/)
  874. {
  875. auto& elem = Element(inScope, inElement);
  876. elem.SetParameter(inID, inValue);
  877. return noErr;
  878. }
  879. //_____________________________________________________________________________
  880. //
  881. OSStatus AUBase::ScheduleParameter(
  882. const AudioUnitParameterEvent* inParameterEvent, UInt32 inNumEvents)
  883. {
  884. const bool canScheduleParameters = CanScheduleParameters();
  885. for (UInt32 i = 0; i < inNumEvents; ++i) {
  886. const auto& pe = inParameterEvent[i]; // NOLINT subscript
  887. if (pe.eventType == kParameterEvent_Immediate) {
  888. SetParameter(pe.parameter, pe.scope, pe.element,
  889. pe.eventValues.immediate.value, // NOLINT union
  890. pe.eventValues.immediate.bufferOffset); // NOLINT union
  891. }
  892. if (canScheduleParameters) {
  893. mParamEventList.push_back(pe);
  894. }
  895. }
  896. return noErr;
  897. }
  898. // ____________________________________________________________________________
  899. //
  900. constexpr bool ParameterEventListSortPredicate(
  901. const AudioUnitParameterEvent& ev1, const AudioUnitParameterEvent& ev2) noexcept
  902. {
  903. // ramp.startBufferOffset is signed
  904. const SInt32 offset1 =
  905. ev1.eventType == kParameterEvent_Immediate
  906. ? static_cast<SInt32>(ev1.eventValues.immediate.bufferOffset) // NOLINT union
  907. : ev1.eventValues.ramp.startBufferOffset; // NOLINT union
  908. const SInt32 offset2 =
  909. ev2.eventType == kParameterEvent_Immediate
  910. ? static_cast<SInt32>(ev2.eventValues.immediate.bufferOffset) // NOLINT union
  911. : ev2.eventValues.ramp.startBufferOffset; // NOLINT union
  912. return offset1 < offset2;
  913. }
  914. // ____________________________________________________________________________
  915. //
  916. OSStatus AUBase::ProcessForScheduledParams(
  917. ParameterEventList& inParamList, UInt32 inFramesToProcess, void* inUserData)
  918. {
  919. OSStatus result = noErr;
  920. UInt32 framesRemaining = inFramesToProcess;
  921. UInt32 currentStartFrame = 0; // start of the whole buffer
  922. // sort the ParameterEventList by startBufferOffset
  923. std::sort(inParamList.begin(), inParamList.end(), ParameterEventListSortPredicate);
  924. while (framesRemaining > 0) {
  925. // first of all, go through the ramped automation events and find out where the next
  926. // division of our whole buffer will be
  927. UInt32 currentEndFrame = inFramesToProcess; // start out assuming we'll process all the way
  928. // to the end of the buffer
  929. // find the next break point
  930. for (const auto& event : inParamList) {
  931. SInt32 offset =
  932. event.eventType == kParameterEvent_Immediate
  933. ? static_cast<SInt32>(event.eventValues.immediate.bufferOffset) // NOLINT
  934. : event.eventValues.ramp.startBufferOffset;
  935. if (offset > static_cast<SInt32>(currentStartFrame) &&
  936. offset < static_cast<SInt32>(currentEndFrame)) {
  937. currentEndFrame = static_cast<UInt32>(offset);
  938. break;
  939. }
  940. // consider ramp end to be a possible choice (there may be gaps in the supplied ramp
  941. // events)
  942. if (event.eventType == kParameterEvent_Ramped) {
  943. offset = event.eventValues.ramp.startBufferOffset +
  944. static_cast<SInt32>(event.eventValues.ramp.durationInFrames); // NOLINT
  945. if (offset > static_cast<SInt32>(currentStartFrame) &&
  946. offset < static_cast<SInt32>(currentEndFrame)) {
  947. currentEndFrame = static_cast<UInt32>(offset);
  948. }
  949. }
  950. }
  951. const UInt32 framesThisTime = currentEndFrame - currentStartFrame;
  952. // next, setup the parameter maps to be current for the ramp parameters active during
  953. // this time segment...
  954. for (const auto& event : inParamList) {
  955. bool eventFallsInSlice = false;
  956. if (event.eventType == kParameterEvent_Ramped) {
  957. const auto& ramp = event.eventValues.ramp;
  958. eventFallsInSlice =
  959. ramp.startBufferOffset < static_cast<SInt32>(currentEndFrame) &&
  960. (ramp.startBufferOffset + static_cast<SInt32>(ramp.durationInFrames)) >
  961. static_cast<SInt32>(currentStartFrame);
  962. } else { /* kParameterEvent_Immediate */
  963. // actually, for the same parameter, there may be future immediate events which
  964. // override this one, but it's OK since the event list is sorted in time order,
  965. // we're guaranteed to end up with the current one
  966. eventFallsInSlice = event.eventValues.immediate.bufferOffset <= currentStartFrame;
  967. }
  968. if (eventFallsInSlice) {
  969. AUElement* const element = GetElement(event.scope, event.element);
  970. if (element != nullptr) {
  971. element->SetScheduledEvent(event.parameter, event, currentStartFrame,
  972. currentEndFrame - currentStartFrame);
  973. }
  974. }
  975. }
  976. // Finally, actually do the processing for this slice.....
  977. result =
  978. ProcessScheduledSlice(inUserData, currentStartFrame, framesThisTime, inFramesToProcess);
  979. if (result != noErr) {
  980. break;
  981. }
  982. framesRemaining -= std::min(framesThisTime, framesRemaining);
  983. currentStartFrame = currentEndFrame; // now start from where we left off last time
  984. }
  985. return result;
  986. }
  987. //_____________________________________________________________________________
  988. //
  989. void AUBase::ResetRenderTime()
  990. {
  991. mCurrentRenderTime = {};
  992. mCurrentRenderTime.mSampleTime = kNoLastRenderedSampleTime;
  993. }
  994. //_____________________________________________________________________________
  995. //
  996. void AUBase::SetWantsRenderThreadID(bool inFlag)
  997. {
  998. if (inFlag == mWantsRenderThreadID) {
  999. return;
  1000. }
  1001. mWantsRenderThreadID = inFlag;
  1002. if (!mWantsRenderThreadID) {
  1003. mRenderThreadID = {};
  1004. };
  1005. }
  1006. //_____________________________________________________________________________
  1007. //
  1008. OSStatus AUBase::DoRender(AudioUnitRenderActionFlags& ioActionFlags,
  1009. const AudioTimeStamp& inTimeStamp, UInt32 inBusNumber, UInt32 inFramesToProcess,
  1010. AudioBufferList& ioData)
  1011. {
  1012. const auto errorExit = [this](OSStatus error) {
  1013. AUSDK_LogError(" from %s, render err: %d", GetLoggingString(), static_cast<int>(error));
  1014. SetRenderError(error);
  1015. return error;
  1016. };
  1017. OSStatus theError = noErr;
  1018. [[maybe_unused]] const DenormalDisabler denormalDisabler;
  1019. try {
  1020. AUSDK_Require(IsInitialized(), errorExit(kAudioUnitErr_Uninitialized));
  1021. if (inFramesToProcess > mMaxFramesPerSlice) {
  1022. #ifndef AUSDK_NO_LOGGING
  1023. static UInt64 lastTimeMessagePrinted = 0;
  1024. const UInt64 now = HostTime::Current();
  1025. if (static_cast<double>(now - lastTimeMessagePrinted) >
  1026. mHostTimeFrequency) { // not more than once per second.
  1027. lastTimeMessagePrinted = now;
  1028. AUSDK_LogError("kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=%u, "
  1029. "mMaxFramesPerSlice=%u",
  1030. static_cast<unsigned>(inFramesToProcess),
  1031. static_cast<unsigned>(mMaxFramesPerSlice));
  1032. }
  1033. #endif
  1034. return errorExit(kAudioUnitErr_TooManyFramesToProcess);
  1035. }
  1036. AUSDK_Require(!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(),
  1037. errorExit(kAudio_ParamError));
  1038. auto& output = Output(inBusNumber); // will throw if non-existant
  1039. if (ASBD::NumberChannelStreams(output.GetStreamFormat()) != ioData.mNumberBuffers) {
  1040. AUSDK_LogError(
  1041. "ioData.mNumberBuffers=%u, "
  1042. "ASBD::NumberChannelStreams(output.GetStreamFormat())=%u; kAudio_ParamError",
  1043. static_cast<unsigned>(ioData.mNumberBuffers),
  1044. static_cast<unsigned>(ASBD::NumberChannelStreams(output.GetStreamFormat())));
  1045. return errorExit(kAudio_ParamError);
  1046. }
  1047. const unsigned expectedBufferByteSize =
  1048. inFramesToProcess * output.GetStreamFormat().mBytesPerFrame;
  1049. for (unsigned ibuf = 0; ibuf < ioData.mNumberBuffers; ++ibuf) {
  1050. AudioBuffer& buf = ioData.mBuffers[ibuf]; // NOLINT
  1051. if (buf.mData != nullptr) {
  1052. // only care about the size if the buffer is non-null
  1053. if (buf.mDataByteSize < expectedBufferByteSize) {
  1054. // if the buffer is too small, we cannot render safely. kAudio_ParamError.
  1055. AUSDK_LogError("%u frames, %u bytes/frame, expected %u-byte buffer; "
  1056. "ioData.mBuffers[%u].mDataByteSize=%u; kAudio_ParamError",
  1057. static_cast<unsigned>(inFramesToProcess),
  1058. static_cast<unsigned>(output.GetStreamFormat().mBytesPerFrame),
  1059. expectedBufferByteSize, ibuf, static_cast<unsigned>(buf.mDataByteSize));
  1060. return errorExit(kAudio_ParamError);
  1061. }
  1062. // Some clients incorrectly pass bigger buffers than expectedBufferByteSize.
  1063. // We will generally set the buffer size at the end of rendering, before we return.
  1064. // However we should ensure that no one, DURING rendering, READS a
  1065. // potentially incorrect size. This can lead to doing too much work, or
  1066. // reading past the end of an input buffer into unmapped memory.
  1067. buf.mDataByteSize = expectedBufferByteSize;
  1068. }
  1069. }
  1070. if (WantsRenderThreadID()) {
  1071. mRenderThreadID = std::this_thread::get_id();
  1072. }
  1073. if (mRenderCallbacksTouched) {
  1074. AudioUnitRenderActionFlags flags = ioActionFlags | kAudioUnitRenderAction_PreRender;
  1075. mRenderCallbacks.foreach ([&](const RenderCallback& rc) {
  1076. (*static_cast<AURenderCallback>(rc.mRenderNotify))(rc.mRenderNotifyRefCon, &flags,
  1077. &inTimeStamp, inBusNumber, inFramesToProcess, &ioData);
  1078. });
  1079. }
  1080. theError =
  1081. DoRenderBus(ioActionFlags, inTimeStamp, inBusNumber, output, inFramesToProcess, ioData);
  1082. SetRenderError(theError);
  1083. if (mRenderCallbacksTouched) {
  1084. AudioUnitRenderActionFlags flags = ioActionFlags | kAudioUnitRenderAction_PostRender;
  1085. if (theError != noErr) {
  1086. flags |= kAudioUnitRenderAction_PostRenderError;
  1087. }
  1088. mRenderCallbacks.foreach ([&](const RenderCallback& rc) {
  1089. (*static_cast<AURenderCallback>(rc.mRenderNotify))(rc.mRenderNotifyRefCon, &flags,
  1090. &inTimeStamp, inBusNumber, inFramesToProcess, &ioData);
  1091. });
  1092. }
  1093. // The vector's being emptied
  1094. // because these events should only apply to this Render cycle, so anything
  1095. // left over is from a preceding cycle and should be dumped. New scheduled
  1096. // parameters must be scheduled from the next pre-render callback.
  1097. if (!mParamEventList.empty()) {
  1098. mParamEventList.clear();
  1099. }
  1100. } catch (const OSStatus& err) {
  1101. return errorExit(err);
  1102. } catch (...) {
  1103. return errorExit(-1);
  1104. }
  1105. return theError;
  1106. }
  1107. inline bool CheckRenderArgs(AudioUnitRenderActionFlags flags)
  1108. {
  1109. return (flags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0u;
  1110. }
  1111. //_____________________________________________________________________________
  1112. //
  1113. OSStatus AUBase::DoProcess(AudioUnitRenderActionFlags& ioActionFlags,
  1114. const AudioTimeStamp& inTimeStamp, UInt32 inFramesToProcess, AudioBufferList& ioData)
  1115. {
  1116. const auto errorExit = [this](OSStatus error) {
  1117. AUSDK_LogError(" from %s, process err: %d", GetLoggingString(), static_cast<int>(error));
  1118. SetRenderError(error);
  1119. return error;
  1120. };
  1121. OSStatus theError = noErr;
  1122. [[maybe_unused]] const DenormalDisabler denormalDisabler;
  1123. try {
  1124. if (CheckRenderArgs(ioActionFlags)) {
  1125. AUSDK_Require(IsInitialized(), errorExit(kAudioUnitErr_Uninitialized));
  1126. AUSDK_Require(inFramesToProcess <= mMaxFramesPerSlice,
  1127. errorExit(kAudioUnitErr_TooManyFramesToProcess));
  1128. AUSDK_Require(!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(),
  1129. errorExit(kAudio_ParamError));
  1130. const auto& input = Input(0); // will throw if non-existant
  1131. if (ASBD::NumberChannelStreams(input.GetStreamFormat()) != ioData.mNumberBuffers) {
  1132. AUSDK_LogError(
  1133. "ioData.mNumberBuffers=%u, "
  1134. "ASBD::NumberChannelStreams(input->GetStreamFormat())=%u; kAudio_ParamError",
  1135. static_cast<unsigned>(ioData.mNumberBuffers),
  1136. static_cast<unsigned>(ASBD::NumberChannelStreams(input.GetStreamFormat())));
  1137. return errorExit(kAudio_ParamError);
  1138. }
  1139. const unsigned expectedBufferByteSize =
  1140. inFramesToProcess * input.GetStreamFormat().mBytesPerFrame;
  1141. for (unsigned ibuf = 0; ibuf < ioData.mNumberBuffers; ++ibuf) {
  1142. AudioBuffer& buf = ioData.mBuffers[ibuf]; // NOLINT
  1143. if (buf.mData != nullptr) {
  1144. // only care about the size if the buffer is non-null
  1145. if (buf.mDataByteSize < expectedBufferByteSize) {
  1146. // if the buffer is too small, we cannot render safely. kAudio_ParamError.
  1147. AUSDK_LogError("%u frames, %u bytes/frame, expected %u-byte buffer; "
  1148. "ioData.mBuffers[%u].mDataByteSize=%u; kAudio_ParamError",
  1149. static_cast<unsigned>(inFramesToProcess),
  1150. static_cast<unsigned>(input.GetStreamFormat().mBytesPerFrame),
  1151. expectedBufferByteSize, ibuf, static_cast<unsigned>(buf.mDataByteSize));
  1152. return errorExit(kAudio_ParamError);
  1153. }
  1154. // Some clients incorrectly pass bigger buffers than expectedBufferByteSize.
  1155. // We will generally set the buffer size at the end of rendering, before we
  1156. // return. However we should ensure that no one, DURING rendering, READS a
  1157. // potentially incorrect size. This can lead to doing too much work, or
  1158. // reading past the end of an input buffer into unmapped memory.
  1159. buf.mDataByteSize = expectedBufferByteSize;
  1160. }
  1161. }
  1162. }
  1163. if (WantsRenderThreadID()) {
  1164. mRenderThreadID = std::this_thread::get_id();
  1165. }
  1166. if (NeedsToRender(inTimeStamp)) {
  1167. theError = ProcessBufferLists(ioActionFlags, ioData, ioData, inFramesToProcess);
  1168. } else {
  1169. theError = noErr;
  1170. }
  1171. } catch (const OSStatus& err) {
  1172. return errorExit(err);
  1173. } catch (...) {
  1174. return errorExit(-1);
  1175. }
  1176. return theError;
  1177. }
  1178. OSStatus AUBase::DoProcessMultiple(AudioUnitRenderActionFlags& ioActionFlags,
  1179. const AudioTimeStamp& inTimeStamp, UInt32 inFramesToProcess, UInt32 inNumberInputBufferLists,
  1180. const AudioBufferList** inInputBufferLists, UInt32 inNumberOutputBufferLists,
  1181. AudioBufferList** ioOutputBufferLists)
  1182. {
  1183. const auto errorExit = [this](OSStatus error) {
  1184. AUSDK_LogError(
  1185. " from %s, processmultiple err: %d", GetLoggingString(), static_cast<int>(error));
  1186. SetRenderError(error);
  1187. return error;
  1188. };
  1189. OSStatus theError = noErr;
  1190. [[maybe_unused]] const DenormalDisabler denormalDisabler;
  1191. try {
  1192. if (CheckRenderArgs(ioActionFlags)) {
  1193. AUSDK_Require(IsInitialized(), errorExit(kAudioUnitErr_Uninitialized));
  1194. AUSDK_Require(inFramesToProcess <= mMaxFramesPerSlice,
  1195. errorExit(kAudioUnitErr_TooManyFramesToProcess));
  1196. AUSDK_Require(!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(),
  1197. errorExit(kAudio_ParamError));
  1198. for (unsigned ibl = 0; ibl < inNumberInputBufferLists; ++ibl) {
  1199. if (inInputBufferLists[ibl] != nullptr) { // NOLINT
  1200. const auto& input = Input(ibl); // will throw if non-existant
  1201. const unsigned expectedBufferByteSize =
  1202. inFramesToProcess * input.GetStreamFormat().mBytesPerFrame;
  1203. if (ASBD::NumberChannelStreams(input.GetStreamFormat()) !=
  1204. inInputBufferLists[ibl]->mNumberBuffers) { // NOLINT
  1205. AUSDK_LogError("inInputBufferLists[%u]->mNumberBuffers=%u, "
  1206. "ASBD::NumberChannelStreams(input.GetStreamFormat())=%u; "
  1207. "kAudio_ParamError",
  1208. ibl, static_cast<unsigned>(inInputBufferLists[ibl]->mNumberBuffers),
  1209. static_cast<unsigned>(
  1210. ASBD::NumberChannelStreams(input.GetStreamFormat())));
  1211. return errorExit(kAudio_ParamError);
  1212. }
  1213. for (unsigned ibuf = 0;
  1214. ibuf < inInputBufferLists[ibl]->mNumberBuffers; // NOLINT
  1215. ++ibuf) {
  1216. const AudioBuffer& buf = inInputBufferLists[ibl]->mBuffers[ibuf]; // NOLINT
  1217. if (buf.mData != nullptr) {
  1218. if (buf.mDataByteSize < expectedBufferByteSize) {
  1219. // the buffer is too small
  1220. AUSDK_LogError(
  1221. "%u frames, %u bytes/frame, expected %u-byte buffer; "
  1222. "inInputBufferLists[%u].mBuffers[%u].mDataByteSize=%u; "
  1223. "kAudio_ParamError",
  1224. static_cast<unsigned>(inFramesToProcess),
  1225. static_cast<unsigned>(input.GetStreamFormat().mBytesPerFrame),
  1226. expectedBufferByteSize, ibl, ibuf,
  1227. static_cast<unsigned>(buf.mDataByteSize));
  1228. return errorExit(kAudio_ParamError);
  1229. }
  1230. } else {
  1231. // the buffer must exist
  1232. return errorExit(kAudio_ParamError);
  1233. }
  1234. }
  1235. } else {
  1236. // skip NULL input audio buffer list
  1237. }
  1238. }
  1239. for (unsigned obl = 0; obl < inNumberOutputBufferLists; ++obl) {
  1240. if (ioOutputBufferLists[obl] != nullptr) { // NOLINT
  1241. const auto& output = Output(obl); // will throw if non-existant
  1242. const unsigned expectedBufferByteSize =
  1243. inFramesToProcess * output.GetStreamFormat().mBytesPerFrame;
  1244. if (ASBD::NumberChannelStreams(output.GetStreamFormat()) !=
  1245. ioOutputBufferLists[obl]->mNumberBuffers) { // NOLINT
  1246. AUSDK_LogError("ioOutputBufferLists[%u]->mNumberBuffers=%u, "
  1247. "ASBD::NumberChannelStreams(output.GetStreamFormat())=%u; "
  1248. "kAudio_ParamError",
  1249. obl, static_cast<unsigned>(ioOutputBufferLists[obl]->mNumberBuffers),
  1250. static_cast<unsigned>(
  1251. ASBD::NumberChannelStreams(output.GetStreamFormat())));
  1252. return errorExit(kAudio_ParamError);
  1253. }
  1254. for (unsigned obuf = 0;
  1255. obuf < ioOutputBufferLists[obl]->mNumberBuffers; // NOLINT
  1256. ++obuf) {
  1257. AudioBuffer& buf = ioOutputBufferLists[obl]->mBuffers[obuf]; // NOLINT
  1258. if (buf.mData != nullptr) {
  1259. // only care about the size if the buffer is non-null
  1260. if (buf.mDataByteSize < expectedBufferByteSize) {
  1261. // if the buffer is too small, we cannot render safely.
  1262. // kAudio_ParamError.
  1263. AUSDK_LogError(
  1264. "%u frames, %u bytes/frame, expected %u-byte buffer; "
  1265. "ioOutputBufferLists[%u]->mBuffers[%u].mDataByteSize=%u; "
  1266. "kAudio_ParamError",
  1267. static_cast<unsigned>(inFramesToProcess),
  1268. static_cast<unsigned>(output.GetStreamFormat().mBytesPerFrame),
  1269. expectedBufferByteSize, obl, obuf,
  1270. static_cast<unsigned>(buf.mDataByteSize));
  1271. return errorExit(kAudio_ParamError);
  1272. }
  1273. // Some clients incorrectly pass bigger buffers than
  1274. // expectedBufferByteSize. We will generally set the buffer size at the
  1275. // end of rendering, before we return. However we should ensure that no
  1276. // one, DURING rendering, READS a potentially incorrect size. This can
  1277. // lead to doing too much work, or reading past the end of an input
  1278. // buffer into unmapped memory.
  1279. buf.mDataByteSize = expectedBufferByteSize;
  1280. }
  1281. }
  1282. } else {
  1283. // skip NULL output audio buffer list
  1284. }
  1285. }
  1286. }
  1287. if (WantsRenderThreadID()) {
  1288. mRenderThreadID = std::this_thread::get_id();
  1289. }
  1290. if (NeedsToRender(inTimeStamp)) {
  1291. theError = ProcessMultipleBufferLists(ioActionFlags, inFramesToProcess,
  1292. inNumberInputBufferLists, inInputBufferLists, inNumberOutputBufferLists,
  1293. ioOutputBufferLists);
  1294. } else {
  1295. theError = noErr;
  1296. }
  1297. } catch (const OSStatus& err) {
  1298. return errorExit(err);
  1299. } catch (...) {
  1300. return errorExit(-1);
  1301. }
  1302. return theError;
  1303. }
  1304. //_____________________________________________________________________________
  1305. //
  1306. OSStatus AUBase::SetInputCallback(
  1307. UInt32 inPropertyID, AudioUnitElement inElement, AURenderCallback inProc, void* inRefCon)
  1308. {
  1309. auto& input = Input(inElement); // may throw
  1310. input.SetInputCallback(inProc, inRefCon);
  1311. PropertyChanged(inPropertyID, kAudioUnitScope_Input, inElement);
  1312. return noErr;
  1313. }
  1314. //_____________________________________________________________________________
  1315. //
  1316. // NOLINTNEXTLINE(misc-no-recursion) with DispatchSetProperty
  1317. OSStatus AUBase::SetConnection(const AudioUnitConnection& inConnection)
  1318. {
  1319. auto& input = Input(inConnection.destInputNumber); // may throw
  1320. if (inConnection.sourceAudioUnit != nullptr) {
  1321. // connecting, not disconnecting
  1322. AudioStreamBasicDescription sourceDesc;
  1323. UInt32 size = sizeof(AudioStreamBasicDescription);
  1324. AUSDK_Require_noerr(
  1325. AudioUnitGetProperty(inConnection.sourceAudioUnit, kAudioUnitProperty_StreamFormat,
  1326. kAudioUnitScope_Output, inConnection.sourceOutputNumber, &sourceDesc, &size));
  1327. AUSDK_Require_noerr(
  1328. DispatchSetProperty(kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
  1329. inConnection.destInputNumber, &sourceDesc, sizeof(AudioStreamBasicDescription)));
  1330. }
  1331. input.SetConnection(inConnection);
  1332. PropertyChanged(
  1333. kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, inConnection.destInputNumber);
  1334. return noErr;
  1335. }
  1336. //_____________________________________________________________________________
  1337. //
  1338. UInt32 AUBase::SupportedNumChannels(const AUChannelInfo** /*outInfo*/) { return 0; }
  1339. //_____________________________________________________________________________
  1340. //
  1341. bool AUBase::ValidFormat(AudioUnitScope /*inScope*/, AudioUnitElement /*inElement*/,
  1342. const AudioStreamBasicDescription& inNewFormat)
  1343. {
  1344. return ASBD::IsCommonFloat32(inNewFormat) &&
  1345. (!ASBD::IsInterleaved(inNewFormat) || inNewFormat.mChannelsPerFrame == 1);
  1346. }
  1347. //_____________________________________________________________________________
  1348. //
  1349. bool AUBase::IsStreamFormatWritable(AudioUnitScope scope, AudioUnitElement element)
  1350. {
  1351. switch (scope) {
  1352. case kAudioUnitScope_Input: {
  1353. const auto& input = Input(element);
  1354. if (input.HasConnection()) {
  1355. return false; // can't write format when input comes from connection
  1356. }
  1357. [[fallthrough]];
  1358. }
  1359. case kAudioUnitScope_Output:
  1360. return StreamFormatWritable(scope, element);
  1361. //#warning "aliasing of global scope format should be pushed to subclasses"
  1362. case kAudioUnitScope_Global:
  1363. return StreamFormatWritable(kAudioUnitScope_Output, 0);
  1364. default:
  1365. break;
  1366. }
  1367. return false;
  1368. }
  1369. //_____________________________________________________________________________
  1370. //
  1371. AudioStreamBasicDescription AUBase::GetStreamFormat(
  1372. AudioUnitScope inScope, AudioUnitElement inElement)
  1373. {
  1374. //#warning "aliasing of global scope format should be pushed to subclasses"
  1375. AUIOElement* element = nullptr;
  1376. switch (inScope) {
  1377. case kAudioUnitScope_Input:
  1378. element = Inputs().GetIOElement(inElement);
  1379. break;
  1380. case kAudioUnitScope_Output:
  1381. element = Outputs().GetIOElement(inElement);
  1382. break;
  1383. case kAudioUnitScope_Global: // global stream description is an alias for that of output 0
  1384. element = Outputs().GetIOElement(0);
  1385. break;
  1386. default:
  1387. Throw(kAudioUnitErr_InvalidScope);
  1388. }
  1389. return element->GetStreamFormat();
  1390. }
  1391. OSStatus AUBase::SetBusCount(AudioUnitScope inScope, UInt32 inCount)
  1392. {
  1393. if (IsInitialized()) {
  1394. return kAudioUnitErr_Initialized;
  1395. }
  1396. GetScope(inScope).SetNumberOfElements(inCount);
  1397. return noErr;
  1398. }
  1399. //_____________________________________________________________________________
  1400. //
  1401. OSStatus AUBase::ChangeStreamFormat(AudioUnitScope inScope, AudioUnitElement inElement,
  1402. const AudioStreamBasicDescription& inPrevFormat, const AudioStreamBasicDescription& inNewFormat)
  1403. {
  1404. if (ASBD::IsEqual(inNewFormat, inPrevFormat)) {
  1405. return noErr;
  1406. }
  1407. //#warning "aliasing of global scope format should be pushed to subclasses"
  1408. AUIOElement* element = nullptr;
  1409. switch (inScope) {
  1410. case kAudioUnitScope_Input:
  1411. element = Inputs().GetIOElement(inElement);
  1412. break;
  1413. case kAudioUnitScope_Output:
  1414. element = Outputs().GetIOElement(inElement);
  1415. break;
  1416. case kAudioUnitScope_Global:
  1417. element = Outputs().GetIOElement(0);
  1418. break;
  1419. default:
  1420. Throw(kAudioUnitErr_InvalidScope);
  1421. }
  1422. element->SetStreamFormat(inNewFormat);
  1423. PropertyChanged(kAudioUnitProperty_StreamFormat, inScope, inElement);
  1424. return noErr;
  1425. }
  1426. std::vector<AudioChannelLayoutTag> AUBase::GetChannelLayoutTags(
  1427. AudioUnitScope inScope, AudioUnitElement inElement)
  1428. {
  1429. return IOElement(inScope, inElement).GetChannelLayoutTags();
  1430. }
  1431. UInt32 AUBase::GetAudioChannelLayout(AudioUnitScope scope, AudioUnitElement element,
  1432. AudioChannelLayout* outLayoutPtr, bool& outWritable)
  1433. {
  1434. auto& el = IOElement(scope, element);
  1435. return el.GetAudioChannelLayout(outLayoutPtr, outWritable);
  1436. }
  1437. OSStatus AUBase::RemoveAudioChannelLayout(AudioUnitScope inScope, AudioUnitElement inElement)
  1438. {
  1439. OSStatus result = noErr;
  1440. auto& el = IOElement(inScope, inElement);
  1441. bool writable = false;
  1442. if (el.GetAudioChannelLayout(nullptr, writable) > 0) {
  1443. result = el.RemoveAudioChannelLayout();
  1444. }
  1445. return result;
  1446. }
  1447. OSStatus AUBase::SetAudioChannelLayout(
  1448. AudioUnitScope inScope, AudioUnitElement inElement, const AudioChannelLayout* inLayout)
  1449. {
  1450. auto& ioEl = IOElement(inScope, inElement);
  1451. // the num channels of the layout HAS TO MATCH the current channels of the Element's stream
  1452. // format
  1453. const UInt32 currentChannels = ioEl.GetStreamFormat().mChannelsPerFrame;
  1454. const UInt32 numChannelsInLayout = AUChannelLayout::NumberChannels(*inLayout);
  1455. if (currentChannels != numChannelsInLayout) {
  1456. return kAudioUnitErr_InvalidPropertyValue;
  1457. }
  1458. const auto tags = GetChannelLayoutTags(inScope, inElement);
  1459. if (tags.empty()) {
  1460. return kAudioUnitErr_InvalidProperty;
  1461. }
  1462. const auto inTag = inLayout->mChannelLayoutTag;
  1463. const auto iter = std::find_if(tags.begin(), tags.end(), [&inTag](auto& tag) {
  1464. return tag == inTag || tag == kAudioChannelLayoutTag_UseChannelDescriptions;
  1465. });
  1466. if (iter == tags.end()) {
  1467. return kAudioUnitErr_InvalidPropertyValue;
  1468. }
  1469. return ioEl.SetAudioChannelLayout(*inLayout);
  1470. }
  1471. constexpr int kCurrentSavedStateVersion = 0;
  1472. static void AddNumToDictionary(CFMutableDictionaryRef dict, CFStringRef key, SInt32 value)
  1473. {
  1474. const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value);
  1475. CFDictionarySetValue(dict, key, num);
  1476. CFRelease(num);
  1477. }
  1478. // NOLINTNEXTLINE(misc-no-recursion) with DispatchGetProperty
  1479. OSStatus AUBase::SaveState(CFPropertyListRef* outData)
  1480. {
  1481. const AudioComponentDescription desc = GetComponentDescription();
  1482. auto dict = Owned<CFMutableDictionaryRef>::from_create(CFDictionaryCreateMutable(
  1483. nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
  1484. // first step -> save the version to the data ref
  1485. SInt32 value = kCurrentSavedStateVersion;
  1486. AddNumToDictionary(*dict, kVersionString, value);
  1487. // second step -> save the component type, subtype, manu to the data ref
  1488. value = static_cast<SInt32>(desc.componentType);
  1489. AddNumToDictionary(*dict, kTypeString, value);
  1490. value = static_cast<SInt32>(desc.componentSubType);
  1491. AddNumToDictionary(*dict, kSubtypeString, value);
  1492. value = static_cast<SInt32>(desc.componentManufacturer);
  1493. AddNumToDictionary(*dict, kManufacturerString, value);
  1494. // fourth step -> save the state of all parameters on all scopes and elements
  1495. auto data = Owned<CFMutableDataRef>::from_create(CFDataCreateMutable(nullptr, 0));
  1496. for (AudioUnitScope iscope = 0; iscope < 3; ++iscope) {
  1497. const auto& scope = GetScope(iscope);
  1498. scope.SaveState(*data);
  1499. }
  1500. SaveExtendedScopes(*data);
  1501. // save all this in the data section of the dictionary
  1502. CFDictionarySetValue(*dict, kDataString, *data);
  1503. data = nullptr; // data can be large-ish, so destroy it now.
  1504. // OK - now we're going to do some properties
  1505. // save the preset name...
  1506. CFDictionarySetValue(*dict, kNameString, mCurrentPreset.presetName);
  1507. // Does the unit support the RenderQuality property - if so, save it...
  1508. OSStatus result =
  1509. DispatchGetProperty(kAudioUnitProperty_RenderQuality, kAudioUnitScope_Global, 0, &value);
  1510. if (result == noErr) {
  1511. AddNumToDictionary(*dict, kRenderQualityString, value);
  1512. }
  1513. // Do we have any element names for any of our scopes?
  1514. // first check to see if we have any names...
  1515. bool foundName = false;
  1516. for (AudioUnitScope i = 0; i < kNumScopes; ++i) {
  1517. foundName = GetScope(i).HasElementWithName();
  1518. if (foundName) {
  1519. break;
  1520. }
  1521. }
  1522. // OK - we found a name away we go...
  1523. if (foundName) {
  1524. auto nameDict = Owned<CFMutableDictionaryRef>::from_create(CFDictionaryCreateMutable(
  1525. nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
  1526. for (AudioUnitScope i = 0; i < kNumScopes; ++i) {
  1527. GetScope(i).AddElementNamesToDict(*nameDict);
  1528. }
  1529. CFDictionarySetValue(*dict, kElementNameString, *nameDict);
  1530. }
  1531. // we're done!!!
  1532. *outData = static_cast<CFPropertyListRef>(dict.release()); // transfer ownership
  1533. return noErr;
  1534. }
  1535. //_____________________________________________________________________________
  1536. //
  1537. // NOLINTNEXTLINE(misc-no-recursion) with DispatchSetProperty
  1538. OSStatus AUBase::RestoreState(CFPropertyListRef plist)
  1539. {
  1540. if (CFGetTypeID(plist) != CFDictionaryGetTypeID()) {
  1541. return kAudioUnitErr_InvalidPropertyValue;
  1542. }
  1543. const AudioComponentDescription desc = GetComponentDescription();
  1544. const auto* const dict = static_cast<CFDictionaryRef>(plist);
  1545. // zeroeth step - make sure the Part key is NOT present, as this method is used
  1546. // to restore the GLOBAL state of the dictionary
  1547. if (CFDictionaryContainsKey(dict, kPartString)) {
  1548. return kAudioUnitErr_InvalidPropertyValue;
  1549. }
  1550. // first step -> check the saved version in the data ref
  1551. // at this point we're only dealing with version==0
  1552. const auto* cfnum = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kVersionString));
  1553. AUSDK_Require(cfnum != nullptr, kAudioUnitErr_InvalidPropertyValue);
  1554. AUSDK_Require(CFGetTypeID(cfnum) == CFNumberGetTypeID(), kAudioUnitErr_InvalidPropertyValue);
  1555. SInt32 value = 0;
  1556. CFNumberGetValue(cfnum, kCFNumberSInt32Type, &value);
  1557. if (value != kCurrentSavedStateVersion) {
  1558. return kAudioUnitErr_InvalidPropertyValue;
  1559. }
  1560. // second step -> check that this data belongs to this kind of audio unit
  1561. // by checking the component subtype and manuID
  1562. // We're not checking the type, since there may be different versions (effect, format-converter,
  1563. // offline) of essentially the same AU
  1564. cfnum = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kSubtypeString));
  1565. AUSDK_Require(cfnum != nullptr, kAudioUnitErr_InvalidPropertyValue);
  1566. AUSDK_Require(CFGetTypeID(cfnum) == CFNumberGetTypeID(), kAudioUnitErr_InvalidPropertyValue);
  1567. CFNumberGetValue(cfnum, kCFNumberSInt32Type, &value);
  1568. if (static_cast<UInt32>(value) != desc.componentSubType) {
  1569. return kAudioUnitErr_InvalidPropertyValue;
  1570. }
  1571. cfnum = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kManufacturerString));
  1572. AUSDK_Require(cfnum != nullptr, kAudioUnitErr_InvalidPropertyValue);
  1573. AUSDK_Require(CFGetTypeID(cfnum) == CFNumberGetTypeID(), kAudioUnitErr_InvalidPropertyValue);
  1574. CFNumberGetValue(cfnum, kCFNumberSInt32Type, &value);
  1575. if (static_cast<UInt32>(value) != desc.componentManufacturer) {
  1576. return kAudioUnitErr_InvalidPropertyValue;
  1577. }
  1578. // fourth step -> restore the state of all of the parameters for each scope and element
  1579. const auto* const data = static_cast<CFDataRef>(CFDictionaryGetValue(dict, kDataString));
  1580. if ((data != nullptr) && (CFGetTypeID(data) == CFDataGetTypeID())) {
  1581. const UInt8* p = CFDataGetBytePtr(data);
  1582. const UInt8* const pend = p + CFDataGetLength(data); // NOLINT
  1583. // we have a zero length data, which may just mean there were no parameters to save!
  1584. // if (p >= pend) return noErr;
  1585. while (p < pend) {
  1586. const UInt32 scopeIdx =
  1587. CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p)); // NOLINT
  1588. p += sizeof(UInt32); // NOLINT
  1589. const auto& scope = GetScope(scopeIdx);
  1590. p = scope.RestoreState(p);
  1591. }
  1592. }
  1593. // OK - now we're going to do some properties
  1594. // restore the preset name...
  1595. const auto* const name = static_cast<CFStringRef>(CFDictionaryGetValue(dict, kNameString));
  1596. if (mCurrentPreset.presetName != nullptr) {
  1597. CFRelease(mCurrentPreset.presetName);
  1598. }
  1599. if ((name != nullptr) && (CFGetTypeID(name) == CFStringGetTypeID())) {
  1600. mCurrentPreset.presetName = name;
  1601. mCurrentPreset.presetNumber = -1;
  1602. } else { // no name entry make the default one
  1603. mCurrentPreset.presetName = kUntitledString;
  1604. mCurrentPreset.presetNumber = -1;
  1605. }
  1606. CFRetain(mCurrentPreset.presetName);
  1607. PropertyChanged(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
  1608. // Does the dict contain render quality information?
  1609. cfnum = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kRenderQualityString));
  1610. if (cfnum && (CFGetTypeID(cfnum) == CFNumberGetTypeID())) {
  1611. CFNumberGetValue(cfnum, kCFNumberSInt32Type, &value);
  1612. DispatchSetProperty(
  1613. kAudioUnitProperty_RenderQuality, kAudioUnitScope_Global, 0, &value, sizeof(value));
  1614. }
  1615. // Do we have any element names for any of our scopes?
  1616. const auto nameDict =
  1617. static_cast<CFDictionaryRef>(CFDictionaryGetValue(dict, kElementNameString));
  1618. if (nameDict && (CFGetTypeID(nameDict) == CFDictionaryGetTypeID())) {
  1619. for (AudioUnitScope i = 0; i < kNumScopes; ++i) {
  1620. const CFStringRef key = CFStringCreateWithFormat(
  1621. nullptr, nullptr, CFSTR("%u"), static_cast<unsigned>(i)); // NOLINT
  1622. const auto elementDict =
  1623. static_cast<CFDictionaryRef>(CFDictionaryGetValue(nameDict, key));
  1624. if (elementDict && (CFGetTypeID(elementDict) == CFDictionaryGetTypeID())) {
  1625. const auto restoredElements = GetScope(i).RestoreElementNames(elementDict);
  1626. for (const auto& element : restoredElements) {
  1627. PropertyChanged(kAudioUnitProperty_ElementName, i, element);
  1628. }
  1629. }
  1630. CFRelease(key);
  1631. }
  1632. }
  1633. return noErr;
  1634. }
  1635. OSStatus AUBase::GetPresets(CFArrayRef* /*outData*/) const { return kAudioUnitErr_InvalidProperty; }
  1636. OSStatus AUBase::NewFactoryPresetSet(const AUPreset& /*inNewFactoryPreset*/)
  1637. {
  1638. return kAudioUnitErr_InvalidProperty;
  1639. }
  1640. OSStatus AUBase::NewCustomPresetSet(const AUPreset& inNewCustomPreset)
  1641. {
  1642. CFRelease(mCurrentPreset.presetName);
  1643. mCurrentPreset = inNewCustomPreset;
  1644. CFRetain(mCurrentPreset.presetName);
  1645. return noErr;
  1646. }
  1647. // set the default preset for the unit -> the number of the preset MUST be >= 0
  1648. // and the name should be valid, or the preset WON'T take
  1649. bool AUBase::SetAFactoryPresetAsCurrent(const AUPreset& inPreset)
  1650. {
  1651. if (inPreset.presetNumber < 0 || inPreset.presetName == nullptr) {
  1652. return false;
  1653. }
  1654. CFRelease(mCurrentPreset.presetName);
  1655. mCurrentPreset = inPreset;
  1656. CFRetain(mCurrentPreset.presetName);
  1657. return true;
  1658. }
  1659. bool AUBase::HasIcon()
  1660. {
  1661. const CFURLRef url = CopyIconLocation();
  1662. if (url != nullptr) {
  1663. CFRelease(url);
  1664. return true;
  1665. }
  1666. return false;
  1667. }
  1668. CFURLRef AUBase::CopyIconLocation() { return nullptr; }
  1669. //_____________________________________________________________________________
  1670. //
  1671. OSStatus AUBase::GetParameterList(
  1672. AudioUnitScope inScope, AudioUnitParameterID* outParameterList, UInt32& outNumParameters)
  1673. {
  1674. const auto& scope = GetScope(inScope);
  1675. AUElement* elementWithMostParameters = nullptr;
  1676. UInt32 maxNumParams = 0;
  1677. const UInt32 nElems = scope.GetNumberOfElements();
  1678. for (UInt32 ielem = 0; ielem < nElems; ++ielem) {
  1679. AUElement* const element = scope.GetElement(ielem);
  1680. const UInt32 nParams = element->GetNumberOfParameters();
  1681. if (nParams > maxNumParams) {
  1682. maxNumParams = nParams;
  1683. elementWithMostParameters = element;
  1684. }
  1685. }
  1686. if (outParameterList != nullptr && elementWithMostParameters != nullptr) {
  1687. elementWithMostParameters->GetParameterList(outParameterList);
  1688. }
  1689. outNumParameters = maxNumParams;
  1690. return noErr;
  1691. }
  1692. //_____________________________________________________________________________
  1693. //
  1694. OSStatus AUBase::GetParameterInfo(AudioUnitScope /*inScope*/,
  1695. AudioUnitParameterID /*inParameterID*/, AudioUnitParameterInfo& /*outParameterInfo*/)
  1696. {
  1697. return kAudioUnitErr_InvalidParameter;
  1698. }
  1699. //_____________________________________________________________________________
  1700. //
  1701. OSStatus AUBase::GetParameterValueStrings(
  1702. AudioUnitScope /*inScope*/, AudioUnitParameterID /*inParameterID*/, CFArrayRef* /*outStrings*/)
  1703. {
  1704. return kAudioUnitErr_InvalidProperty;
  1705. }
  1706. //_____________________________________________________________________________
  1707. //
  1708. OSStatus AUBase::GetParameterHistoryInfo(AudioUnitScope /*inScope*/,
  1709. AudioUnitParameterID /*inParameterID*/, Float32& /*outUpdatesPerSecond*/,
  1710. Float32& /*outHistoryDurationInSeconds*/)
  1711. {
  1712. return kAudioUnitErr_InvalidProperty;
  1713. }
  1714. //_____________________________________________________________________________
  1715. //
  1716. OSStatus AUBase::CopyClumpName(AudioUnitScope /*inScope*/, UInt32 /*inClumpID*/,
  1717. UInt32 /*inDesiredNameLength*/, CFStringRef* /*outClumpName*/)
  1718. {
  1719. return kAudioUnitErr_InvalidProperty;
  1720. }
  1721. //_____________________________________________________________________________
  1722. //
  1723. void AUBase::SetNumberOfElements(AudioUnitScope inScope, UInt32 numElements)
  1724. {
  1725. if (inScope == kAudioUnitScope_Global && numElements != 1) {
  1726. Throw(kAudioUnitErr_InvalidScope);
  1727. }
  1728. GetScope(inScope).SetNumberOfElements(numElements);
  1729. }
  1730. //_____________________________________________________________________________
  1731. //
  1732. std::unique_ptr<AUElement> AUBase::CreateElement(AudioUnitScope scope, AudioUnitElement /*element*/)
  1733. {
  1734. switch (scope) {
  1735. case kAudioUnitScope_Global:
  1736. return std::make_unique<AUElement>(*this);
  1737. case kAudioUnitScope_Input:
  1738. return std::make_unique<AUInputElement>(*this);
  1739. case kAudioUnitScope_Output:
  1740. return std::make_unique<AUOutputElement>(*this);
  1741. case kAudioUnitScope_Group:
  1742. case kAudioUnitScope_Part:
  1743. return std::make_unique<AUElement>(*this);
  1744. default:
  1745. break;
  1746. }
  1747. Throw(kAudioUnitErr_InvalidScope);
  1748. }
  1749. const char* AUBase::GetLoggingString() const noexcept { return mLogString.c_str(); }
  1750. std::string AUBase::CreateLoggingString() const
  1751. {
  1752. const auto desc = GetComponentDescription();
  1753. std::array<char, 32> buf{};
  1754. [[maybe_unused]] const int printCount =
  1755. snprintf(buf.data(), buf.size(), "AU (%p): ", GetComponentInstance()); // NOLINT
  1756. #if DEBUG
  1757. assert(printCount < static_cast<int>(buf.size()));
  1758. #endif
  1759. return buf.data() + make_string_from_4cc(desc.componentType) + '/' +
  1760. make_string_from_4cc(desc.componentSubType) + '/' +
  1761. make_string_from_4cc(desc.componentManufacturer);
  1762. }
  1763. } // namespace ausdk