DISTRHO Plugin Framework
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.

2505 lines
101KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. // TODO
  17. // - g_nextBundlePath vs d_nextBundlePath cleanup
  18. // - scale points to kAudioUnitParameterFlag_ValuesHaveStrings
  19. #include "DistrhoPluginInternal.hpp"
  20. #include "../DistrhoPluginUtils.hpp"
  21. #if DISTRHO_PLUGIN_HAS_UI
  22. # include "../extra/RingBuffer.hpp"
  23. #endif
  24. #include <AudioUnit/AudioUnit.h>
  25. #include <AudioToolbox/AudioUnitUtilities.h>
  26. #include <map>
  27. #include <vector>
  28. #ifndef DISTRHO_PLUGIN_BRAND_ID
  29. # error DISTRHO_PLUGIN_BRAND_ID undefined!
  30. #endif
  31. #ifndef DISTRHO_PLUGIN_UNIQUE_ID
  32. # error DISTRHO_PLUGIN_UNIQUE_ID undefined!
  33. #endif
  34. START_NAMESPACE_DISTRHO
  35. // --------------------------------------------------------------------------------------------------------------------
  36. static const char* AudioUnitPropertyID2Str(const AudioUnitPropertyID prop) noexcept
  37. {
  38. switch (prop)
  39. {
  40. #define PROP(s) case s: return #s;
  41. PROP(kAudioUnitProperty_ClassInfo)
  42. PROP(kAudioUnitProperty_MakeConnection)
  43. PROP(kAudioUnitProperty_SampleRate)
  44. PROP(kAudioUnitProperty_ParameterList)
  45. PROP(kAudioUnitProperty_ParameterInfo)
  46. #if !TARGET_OS_IPHONE
  47. PROP(kAudioUnitProperty_FastDispatch)
  48. #endif
  49. PROP(kAudioUnitProperty_CPULoad)
  50. PROP(kAudioUnitProperty_StreamFormat)
  51. PROP(kAudioUnitProperty_ElementCount)
  52. PROP(kAudioUnitProperty_Latency)
  53. PROP(kAudioUnitProperty_SupportedNumChannels)
  54. PROP(kAudioUnitProperty_MaximumFramesPerSlice)
  55. PROP(kAudioUnitProperty_ParameterValueStrings)
  56. PROP(kAudioUnitProperty_AudioChannelLayout)
  57. PROP(kAudioUnitProperty_TailTime)
  58. PROP(kAudioUnitProperty_BypassEffect)
  59. PROP(kAudioUnitProperty_LastRenderError)
  60. PROP(kAudioUnitProperty_SetRenderCallback)
  61. PROP(kAudioUnitProperty_FactoryPresets)
  62. PROP(kAudioUnitProperty_RenderQuality)
  63. PROP(kAudioUnitProperty_HostCallbacks)
  64. PROP(kAudioUnitProperty_InPlaceProcessing)
  65. PROP(kAudioUnitProperty_ElementName)
  66. PROP(kAudioUnitProperty_SupportedChannelLayoutTags)
  67. PROP(kAudioUnitProperty_PresentPreset)
  68. PROP(kAudioUnitProperty_DependentParameters)
  69. PROP(kAudioUnitProperty_InputSamplesInOutput)
  70. PROP(kAudioUnitProperty_ShouldAllocateBuffer)
  71. PROP(kAudioUnitProperty_FrequencyResponse)
  72. PROP(kAudioUnitProperty_ParameterHistoryInfo)
  73. PROP(kAudioUnitProperty_NickName)
  74. PROP(kAudioUnitProperty_OfflineRender)
  75. PROP(kAudioUnitProperty_ParameterIDName)
  76. PROP(kAudioUnitProperty_ParameterStringFromValue)
  77. PROP(kAudioUnitProperty_ParameterClumpName)
  78. PROP(kAudioUnitProperty_ParameterValueFromString)
  79. PROP(kAudioUnitProperty_PresentationLatency)
  80. PROP(kAudioUnitProperty_ClassInfoFromDocument)
  81. PROP(kAudioUnitProperty_RequestViewController)
  82. PROP(kAudioUnitProperty_ParametersForOverview)
  83. PROP(kAudioUnitProperty_SupportsMPE)
  84. PROP(kAudioUnitProperty_RenderContextObserver)
  85. PROP(kAudioUnitProperty_LastRenderSampleTime)
  86. PROP(kAudioUnitProperty_LoadedOutOfProcess)
  87. #if !TARGET_OS_IPHONE
  88. PROP(kAudioUnitProperty_SetExternalBuffer)
  89. PROP(kAudioUnitProperty_GetUIComponentList)
  90. PROP(kAudioUnitProperty_CocoaUI)
  91. PROP(kAudioUnitProperty_IconLocation)
  92. PROP(kAudioUnitProperty_AUHostIdentifier)
  93. #endif
  94. PROP(kAudioUnitProperty_MIDIOutputCallbackInfo)
  95. PROP(kAudioUnitProperty_MIDIOutputCallback)
  96. PROP(kAudioUnitProperty_MIDIOutputEventListCallback)
  97. PROP(kAudioUnitProperty_AudioUnitMIDIProtocol)
  98. PROP(kAudioUnitProperty_HostMIDIProtocol)
  99. PROP(kAudioUnitProperty_MIDIOutputBufferSizeHint)
  100. #undef PROP
  101. }
  102. return "[unknown]";
  103. }
  104. static const char* AudioUnitScope2Str(const AudioUnitScope scope) noexcept
  105. {
  106. switch (scope)
  107. {
  108. #define SCOPE(s) case s: return #s;
  109. SCOPE(kAudioUnitScope_Global)
  110. SCOPE(kAudioUnitScope_Input)
  111. SCOPE(kAudioUnitScope_Output)
  112. SCOPE(kAudioUnitScope_Group)
  113. SCOPE(kAudioUnitScope_Part)
  114. SCOPE(kAudioUnitScope_Note)
  115. SCOPE(kAudioUnitScope_Layer)
  116. SCOPE(kAudioUnitScope_LayerItem)
  117. #undef SCOPE
  118. }
  119. return "[unknown]";
  120. }
  121. static const char* AudioUnitSelector2Str(const SInt16 selector) noexcept
  122. {
  123. switch (selector)
  124. {
  125. #define SEL(s) case s: return #s;
  126. SEL(kAudioUnitInitializeSelect)
  127. SEL(kAudioUnitUninitializeSelect)
  128. SEL(kAudioUnitGetPropertyInfoSelect)
  129. SEL(kAudioUnitGetPropertySelect)
  130. SEL(kAudioUnitSetPropertySelect)
  131. SEL(kAudioUnitAddPropertyListenerSelect)
  132. SEL(kAudioUnitRemovePropertyListenerSelect)
  133. SEL(kAudioUnitRemovePropertyListenerWithUserDataSelect)
  134. SEL(kAudioUnitAddRenderNotifySelect)
  135. SEL(kAudioUnitRemoveRenderNotifySelect)
  136. SEL(kAudioUnitGetParameterSelect)
  137. SEL(kAudioUnitSetParameterSelect)
  138. SEL(kAudioUnitScheduleParametersSelect)
  139. SEL(kAudioUnitRenderSelect)
  140. SEL(kAudioUnitResetSelect)
  141. SEL(kAudioUnitComplexRenderSelect)
  142. SEL(kAudioUnitProcessSelect)
  143. SEL(kAudioUnitProcessMultipleSelect)
  144. SEL(kMusicDeviceMIDIEventSelect)
  145. SEL(kMusicDeviceSysExSelect)
  146. SEL(kMusicDevicePrepareInstrumentSelect)
  147. SEL(kMusicDeviceReleaseInstrumentSelect)
  148. SEL(kMusicDeviceStartNoteSelect)
  149. SEL(kMusicDeviceStopNoteSelect)
  150. SEL(kMusicDeviceMIDIEventListSelect)
  151. SEL(kAudioOutputUnitStartSelect)
  152. SEL(kAudioOutputUnitStopSelect)
  153. #undef SEL
  154. }
  155. return "[unknown]";
  156. }
  157. // --------------------------------------------------------------------------------------------------------------------
  158. static constexpr const uint32_t kType = d_cconst(STRINGIFY(DISTRHO_PLUGIN_AU_TYPE));
  159. static constexpr const uint32_t kSubType = d_cconst(STRINGIFY(DISTRHO_PLUGIN_UNIQUE_ID));
  160. static constexpr const uint32_t kManufacturer = d_cconst(STRINGIFY(DISTRHO_PLUGIN_BRAND_ID));
  161. // --------------------------------------------------------------------------------------------------------------------
  162. struct PropertyListener {
  163. AudioUnitPropertyID prop;
  164. AudioUnitPropertyListenerProc proc;
  165. void* userData;
  166. };
  167. typedef std::vector<PropertyListener> PropertyListeners;
  168. // --------------------------------------------------------------------------------------------------------------------
  169. typedef struct {
  170. UInt32 numPackets;
  171. MIDIPacket packets[kMaxMidiEvents];
  172. } MIDIPacketList;
  173. // --------------------------------------------------------------------------------------------------------------------
  174. #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  175. static constexpr const writeMidiFunc writeMidiCallback = nullptr;
  176. #endif
  177. #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
  178. static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
  179. #endif
  180. #if ! DISTRHO_PLUGIN_WANT_STATE
  181. static constexpr const updateStateValueFunc updateStateValueCallback = nullptr;
  182. #endif
  183. typedef std::map<const String, String> StringMap;
  184. // --------------------------------------------------------------------------------------------------------------------
  185. class PluginAU
  186. {
  187. public:
  188. PluginAU(const AudioUnit component)
  189. : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, updateStateValueCallback),
  190. fComponent(component),
  191. fLastRenderError(noErr),
  192. fPropertyListeners(),
  193. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  194. fSampleRateForInput(d_nextSampleRate),
  195. #endif
  196. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  197. fSampleRateForOutput(d_nextSampleRate),
  198. #endif
  199. fParameterCount(fPlugin.getParameterCount()),
  200. fLastParameterValues(nullptr),
  201. fBypassParameterIndex(UINT32_MAX)
  202. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  203. , fMidiEventCount(0)
  204. #endif
  205. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  206. , fCurrentProgram(0)
  207. #endif
  208. #if DISTRHO_PLUGIN_WANT_STATE
  209. , fStateCount(fPlugin.getStateCount())
  210. #endif
  211. {
  212. fCurrentPreset.presetName = CFSTR("Default");
  213. fCurrentPreset.presetNumber = 0;
  214. if (fParameterCount != 0)
  215. {
  216. fLastParameterValues = new float[fParameterCount];
  217. std::memset(fLastParameterValues, 0, sizeof(float) * fParameterCount);
  218. for (uint32_t i=0; i<fParameterCount; ++i)
  219. {
  220. fLastParameterValues[i] = fPlugin.getParameterValue(i);
  221. if (fPlugin.getParameterDesignation(i) == kParameterDesignationBypass)
  222. fBypassParameterIndex = i;
  223. }
  224. }
  225. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  226. std::memset(&fMidiEvents, 0, sizeof(fMidiEvents));
  227. #endif
  228. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  229. std::memset(&fMidiOutput, 0, sizeof(fMidiOutput));
  230. std::memset(&fMidiOutputPackets, 0, sizeof(fMidiOutputPackets));
  231. #endif
  232. #if DISTRHO_PLUGIN_WANT_STATE
  233. for (uint32_t i=0; i<fStateCount; ++i)
  234. {
  235. const String& dkey(fPlugin.getStateKey(i));
  236. fStateMap[dkey] = fPlugin.getStateDefaultValue(i);
  237. }
  238. #endif
  239. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  240. std::memset(&fHostCallbackInfo, 0, sizeof(fHostCallbackInfo));
  241. // ticksPerBeat is not possible with AU
  242. fTimePosition.bbt.ticksPerBeat = 1920.0;
  243. #endif
  244. }
  245. ~PluginAU()
  246. {
  247. CFRelease(fCurrentPreset.presetName);
  248. delete[] fLastParameterValues;
  249. }
  250. OSStatus auInitialize()
  251. {
  252. fPlugin.activate();
  253. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  254. fMidiEventCount = 0;
  255. #endif
  256. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  257. fMidiOutputPackets.numPackets = 0;
  258. #endif
  259. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  260. fTimePosition.clear();
  261. #endif
  262. return noErr;
  263. }
  264. OSStatus auUninitialize()
  265. {
  266. fPlugin.deactivateIfNeeded();
  267. return noErr;
  268. }
  269. OSStatus auGetPropertyInfo(const AudioUnitPropertyID inProp,
  270. const AudioUnitScope inScope,
  271. const AudioUnitElement inElement,
  272. UInt32& outDataSize,
  273. Boolean& outWritable)
  274. {
  275. switch (inProp)
  276. {
  277. case kAudioUnitProperty_ClassInfo:
  278. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  279. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  280. outDataSize = sizeof(CFPropertyListRef);
  281. outWritable = true;
  282. return noErr;
  283. case kAudioUnitProperty_MakeConnection:
  284. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global || inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
  285. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  286. outDataSize = sizeof(AudioUnitConnection);
  287. outWritable = true;
  288. return noErr;
  289. #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  290. case kAudioUnitProperty_SampleRate:
  291. #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  292. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
  293. #elif DISTRHO_PLUGIN_NUM_INPUTS != 0
  294. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
  295. #else
  296. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
  297. #endif
  298. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  299. outDataSize = sizeof(Float64);
  300. outWritable = true;
  301. return noErr;
  302. #endif
  303. case kAudioUnitProperty_ParameterList:
  304. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  305. outDataSize = inScope == kAudioUnitScope_Global ? sizeof(AudioUnitParameterID) * fParameterCount : 0;
  306. outWritable = false;
  307. return noErr;
  308. case kAudioUnitProperty_ParameterInfo:
  309. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  310. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement);
  311. outDataSize = sizeof(AudioUnitParameterInfo);
  312. outWritable = false;
  313. return noErr;
  314. #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  315. case kAudioUnitProperty_StreamFormat:
  316. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  317. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  318. if (inScope == kAudioUnitScope_Input)
  319. {
  320. outDataSize = sizeof(AudioStreamBasicDescription);
  321. outWritable = true;
  322. return noErr;
  323. }
  324. #endif
  325. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  326. if (inScope == kAudioUnitScope_Output)
  327. {
  328. outDataSize = sizeof(AudioStreamBasicDescription);
  329. outWritable = true;
  330. return noErr;
  331. }
  332. #endif
  333. return kAudioUnitErr_InvalidScope;
  334. #endif
  335. case kAudioUnitProperty_ElementCount:
  336. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  337. outDataSize = sizeof(UInt32);
  338. outWritable = false;
  339. return noErr;
  340. case kAudioUnitProperty_Latency:
  341. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  342. #if DISTRHO_PLUGIN_WANT_LATENCY
  343. if (inScope == kAudioUnitScope_Global)
  344. {
  345. outDataSize = sizeof(Float64);
  346. outWritable = false;
  347. return noErr;
  348. }
  349. #endif
  350. return kAudioUnitErr_InvalidProperty;
  351. case kAudioUnitProperty_SupportedNumChannels:
  352. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  353. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  354. outDataSize = sizeof(AUChannelInfo);
  355. outWritable = false;
  356. return noErr;
  357. case kAudioUnitProperty_MaximumFramesPerSlice:
  358. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  359. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  360. outDataSize = sizeof(UInt32);
  361. outWritable = true;
  362. return noErr;
  363. case kAudioUnitProperty_BypassEffect:
  364. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  365. if (inScope == kAudioUnitScope_Global && fBypassParameterIndex != UINT32_MAX)
  366. {
  367. outDataSize = sizeof(UInt32);
  368. outWritable = true;
  369. return noErr;
  370. }
  371. return kAudioUnitErr_InvalidProperty;
  372. case kAudioUnitProperty_LastRenderError:
  373. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  374. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  375. outDataSize = sizeof(OSStatus);
  376. outWritable = false;
  377. return noErr;
  378. case kAudioUnitProperty_SetRenderCallback:
  379. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
  380. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  381. outDataSize = sizeof(AURenderCallbackStruct);
  382. outWritable = true;
  383. return noErr;
  384. case kAudioUnitProperty_HostCallbacks:
  385. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  386. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  387. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  388. outDataSize = sizeof(HostCallbackInfo);
  389. outWritable = false;
  390. return noErr;
  391. #else
  392. return kAudioUnitErr_InvalidProperty;
  393. #endif
  394. case kAudioUnitProperty_InPlaceProcessing:
  395. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  396. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  397. outDataSize = sizeof(UInt32);
  398. outWritable = false;
  399. return noErr;
  400. case kAudioUnitProperty_PresentPreset:
  401. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  402. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  403. outDataSize = sizeof(AUPreset);
  404. outWritable = true;
  405. return noErr;
  406. case kAudioUnitProperty_CocoaUI:
  407. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  408. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  409. #if DISTRHO_PLUGIN_HAS_UI
  410. outDataSize = sizeof(AudioUnitCocoaViewInfo);
  411. outWritable = false;
  412. return noErr;
  413. #else
  414. return kAudioUnitErr_InvalidProperty;
  415. #endif
  416. case kAudioUnitProperty_MIDIOutputCallbackInfo:
  417. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  418. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  419. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  420. outDataSize = sizeof(CFArrayRef);
  421. outWritable = false;
  422. return noErr;
  423. #else
  424. return kAudioUnitErr_InvalidProperty;
  425. #endif
  426. case kAudioUnitProperty_MIDIOutputCallback:
  427. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  428. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  429. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  430. outDataSize = sizeof(AUMIDIOutputCallbackStruct);
  431. outWritable = true;
  432. return noErr;
  433. #else
  434. return kAudioUnitErr_InvalidProperty;
  435. #endif
  436. case kAudioUnitProperty_AudioUnitMIDIProtocol:
  437. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  438. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  439. // FIXME implement the event list stuff
  440. #if 0 && (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT)
  441. outDataSize = sizeof(SInt32);
  442. outWritable = false;
  443. return noErr;
  444. #else
  445. return kAudioUnitErr_InvalidProperty;
  446. #endif
  447. case 'DPFi':
  448. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  449. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  450. outDataSize = sizeof(uint16_t);
  451. outWritable = false;
  452. return noErr;
  453. case 'DPFe':
  454. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  455. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement);
  456. outDataSize = sizeof(uint8_t);
  457. outWritable = true;
  458. return noErr;
  459. case 'DPFp':
  460. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  461. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement);
  462. outDataSize = sizeof(float);
  463. outWritable = true;
  464. return noErr;
  465. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  466. case 'DPFn':
  467. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  468. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  469. outDataSize = sizeof(uint8_t) * 3;
  470. outWritable = true;
  471. return noErr;
  472. #endif
  473. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  474. case 'DPFo':
  475. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  476. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  477. outDataSize = sizeof(uint32_t);
  478. outWritable = false;
  479. return noErr;
  480. #endif
  481. #if DISTRHO_PLUGIN_WANT_STATE
  482. case 'DPFl':
  483. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  484. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  485. outDataSize = sizeof(CFArrayRef);
  486. outWritable = false;
  487. return noErr;
  488. case 'DPFs':
  489. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  490. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fStateCount, inElement, kAudioUnitErr_InvalidElement);
  491. outDataSize = sizeof(CFStringRef);
  492. outWritable = true;
  493. return noErr;
  494. #endif
  495. #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  496. case 'DPFa':
  497. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  498. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  499. outDataSize = sizeof(void*);
  500. outWritable = false;
  501. return noErr;
  502. #endif
  503. // unwanted properties
  504. case kAudioUnitProperty_CPULoad:
  505. case kAudioUnitProperty_RenderContextObserver:
  506. case kAudioUnitProperty_TailTime:
  507. #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  508. case kAudioUnitProperty_SampleRate:
  509. case kAudioUnitProperty_StreamFormat:
  510. #endif
  511. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  512. return kAudioUnitErr_InvalidProperty;
  513. }
  514. d_stdout("TODO GetPropertyInfo(%2d:%s, %d:%s, %d, ...)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);
  515. return kAudioUnitErr_InvalidProperty;
  516. }
  517. OSStatus auGetProperty(const AudioUnitPropertyID inProp,
  518. const AudioUnitScope inScope,
  519. const AudioUnitElement inElement,
  520. void* const outData)
  521. {
  522. switch (inProp)
  523. {
  524. case kAudioUnitProperty_ClassInfo:
  525. *static_cast<CFPropertyListRef*>(outData) = retrieveClassInfo();
  526. return noErr;
  527. #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  528. case kAudioUnitProperty_SampleRate:
  529. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  530. if (inScope == kAudioUnitScope_Input)
  531. {
  532. *static_cast<Float64*>(outData) = fSampleRateForInput;
  533. }
  534. else
  535. #endif
  536. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  537. if (inScope == kAudioUnitScope_Output)
  538. {
  539. *static_cast<Float64*>(outData) = fSampleRateForOutput;
  540. }
  541. else
  542. #endif
  543. {
  544. return kAudioUnitErr_InvalidScope;
  545. }
  546. return noErr;
  547. #endif
  548. case kAudioUnitProperty_ParameterList:
  549. {
  550. AudioUnitParameterID* const paramList = static_cast<AudioUnitParameterID*>(outData);
  551. for (uint32_t i=0; i<fParameterCount; ++i)
  552. paramList[i] = i;
  553. }
  554. return noErr;
  555. case kAudioUnitProperty_ParameterInfo:
  556. {
  557. AudioUnitParameterInfo* const info = static_cast<AudioUnitParameterInfo*>(outData);
  558. std::memset(info, 0, sizeof(*info));
  559. const ParameterRanges& ranges(fPlugin.getParameterRanges(inElement));
  560. info->flags = kAudioUnitParameterFlag_IsHighResolution
  561. | kAudioUnitParameterFlag_IsReadable
  562. | kAudioUnitParameterFlag_HasCFNameString;
  563. if (fPlugin.getParameterDesignation(inElement) == kParameterDesignationBypass)
  564. {
  565. info->flags |= kAudioUnitParameterFlag_IsWritable;
  566. info->unit = kAudioUnitParameterUnit_Generic;
  567. d_strncpy(info->name, "Bypass", sizeof(info->name));
  568. info->cfNameString = CFSTR("Bypass");
  569. }
  570. else
  571. {
  572. const uint32_t hints = fPlugin.getParameterHints(inElement);
  573. info->flags |= kAudioUnitParameterFlag_CFNameRelease;
  574. if (hints & kParameterIsOutput)
  575. {
  576. info->flags |= kAudioUnitParameterFlag_MeterReadOnly;
  577. }
  578. else
  579. {
  580. info->flags |= kAudioUnitParameterFlag_IsWritable;
  581. if ((hints & kParameterIsAutomatable) == 0x0)
  582. info->flags |= kAudioUnitParameterFlag_NonRealTime;
  583. }
  584. if (hints & kParameterIsBoolean)
  585. info->unit = kAudioUnitParameterUnit_Boolean;
  586. else if (hints & kParameterIsInteger)
  587. info->unit = kAudioUnitParameterUnit_Indexed;
  588. else
  589. info->unit = kAudioUnitParameterUnit_Generic;
  590. // | kAudioUnitParameterFlag_ValuesHaveStrings;
  591. const String& name(fPlugin.getParameterName(inElement));
  592. d_strncpy(info->name, name, sizeof(info->name));
  593. info->cfNameString = CFStringCreateWithCString(nullptr, name, kCFStringEncodingUTF8);
  594. }
  595. info->minValue = ranges.min;
  596. info->maxValue = ranges.max;
  597. info->defaultValue = ranges.def;
  598. }
  599. return noErr;
  600. #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  601. case kAudioUnitProperty_StreamFormat:
  602. {
  603. AudioStreamBasicDescription* const desc = static_cast<AudioStreamBasicDescription*>(outData);
  604. std::memset(desc, 0, sizeof(*desc));
  605. if (inElement != 0)
  606. return kAudioUnitErr_InvalidElement;
  607. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  608. if (inScope == kAudioUnitScope_Input)
  609. {
  610. desc->mChannelsPerFrame = DISTRHO_PLUGIN_NUM_INPUTS;
  611. }
  612. else
  613. #endif
  614. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  615. if (inScope == kAudioUnitScope_Output)
  616. {
  617. desc->mChannelsPerFrame = DISTRHO_PLUGIN_NUM_OUTPUTS;
  618. }
  619. else
  620. #endif
  621. {
  622. return kAudioUnitErr_InvalidScope;
  623. }
  624. desc->mFormatID = kAudioFormatLinearPCM;
  625. desc->mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
  626. desc->mSampleRate = fPlugin.getSampleRate();
  627. desc->mBitsPerChannel = 32;
  628. desc->mBytesPerFrame = sizeof(float);
  629. desc->mBytesPerPacket = sizeof(float);
  630. desc->mFramesPerPacket = 1;
  631. }
  632. return noErr;
  633. #endif
  634. case kAudioUnitProperty_ElementCount:
  635. switch (inScope)
  636. {
  637. case kAudioUnitScope_Global:
  638. *static_cast<UInt32*>(outData) = 1;
  639. break;
  640. case kAudioUnitScope_Input:
  641. *static_cast<UInt32*>(outData) = DISTRHO_PLUGIN_NUM_INPUTS != 0 ? 1 : 0;
  642. break;
  643. case kAudioUnitScope_Output:
  644. *static_cast<UInt32*>(outData) = DISTRHO_PLUGIN_NUM_OUTPUTS != 0 ? 1 : 0;
  645. break;
  646. default:
  647. *static_cast<UInt32*>(outData) = 0;
  648. break;
  649. }
  650. return noErr;
  651. #if DISTRHO_PLUGIN_WANT_LATENCY
  652. case kAudioUnitProperty_Latency:
  653. *static_cast<Float64*>(outData) = static_cast<double>(fPlugin.getLatency()) / fPlugin.getSampleRate();
  654. return noErr;
  655. #endif
  656. case kAudioUnitProperty_SupportedNumChannels:
  657. *static_cast<AUChannelInfo*>(outData) = { DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS };
  658. return noErr;
  659. case kAudioUnitProperty_MaximumFramesPerSlice:
  660. *static_cast<UInt32*>(outData) = fPlugin.getBufferSize();
  661. return noErr;
  662. case kAudioUnitProperty_LastRenderError:
  663. *static_cast<OSStatus*>(outData) = fLastRenderError;
  664. fLastRenderError = noErr;
  665. return noErr;
  666. case kAudioUnitProperty_BypassEffect:
  667. *static_cast<OSStatus*>(outData) = fPlugin.getParameterValue(fBypassParameterIndex) > 0.5f ? 1 : 0;
  668. return noErr;
  669. case kAudioUnitProperty_SetRenderCallback:
  670. d_stdout("WIP GetProperty(%d:%s, %d:%s, %d, ...)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);
  671. // TODO
  672. break;
  673. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  674. case kAudioUnitProperty_HostCallbacks:
  675. std::memcpy(outData, &fHostCallbackInfo, sizeof(HostCallbackInfo));
  676. return noErr;
  677. #endif
  678. case kAudioUnitProperty_InPlaceProcessing:
  679. *static_cast<UInt32*>(outData) = 1;
  680. return noErr;
  681. case kAudioUnitProperty_PresentPreset:
  682. std::memcpy(outData, &fCurrentPreset, sizeof(AUPreset));
  683. return noErr;
  684. #if DISTRHO_PLUGIN_HAS_UI
  685. case kAudioUnitProperty_CocoaUI:
  686. {
  687. AudioUnitCocoaViewInfo* const info = static_cast<AudioUnitCocoaViewInfo*>(outData);
  688. std::memset(info, 0, sizeof(*info));
  689. NSString* const bundlePathString = [[NSString alloc]
  690. initWithBytes:d_nextBundlePath
  691. length:strlen(d_nextBundlePath)
  692. encoding:NSUTF8StringEncoding];
  693. info->mCocoaAUViewBundleLocation = static_cast<CFURLRef>([[NSURL fileURLWithPath: bundlePathString] retain]);
  694. #define MACRO_STR3(a, b, c) a "_" b "_" c
  695. #define MACRO_STR2(a, b, c) MACRO_STR3(#a, #b, #c)
  696. #define MACRO_STR(a, b, c) MACRO_STR2(a, b, c)
  697. info->mCocoaAUViewClass[0] = CFSTR("CocoaAUView_" MACRO_STR(DISTRHO_PLUGIN_AU_TYPE,
  698. DISTRHO_PLUGIN_UNIQUE_ID,
  699. DISTRHO_PLUGIN_BRAND_ID));
  700. #undef MACRO_STR
  701. #undef MACRO_STR2
  702. #undef MACRO_STR3
  703. [bundlePathString release];
  704. }
  705. return noErr;
  706. #endif
  707. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  708. case kAudioUnitProperty_MIDIOutputCallbackInfo:
  709. {
  710. CFStringRef refs[1] = { CFSTR("MIDI Output") };
  711. *static_cast<CFArrayRef*>(outData) = CFArrayCreate(nullptr,
  712. reinterpret_cast<const void**>(refs),
  713. 1,
  714. &kCFTypeArrayCallBacks);
  715. }
  716. return noErr;
  717. #endif
  718. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  719. /* FIXME implement the event list stuff
  720. case kAudioUnitProperty_AudioUnitMIDIProtocol:
  721. *static_cast<SInt32*>(outData) = kMIDIProtocol_1_0;
  722. return noErr;
  723. */
  724. #endif
  725. case 'DPFp':
  726. *static_cast<float*>(outData) = fPlugin.getParameterValue(inElement);
  727. return noErr;
  728. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  729. case 'DPFo':
  730. *static_cast<uint32_t*>(outData) = fCurrentProgram;
  731. return noErr;
  732. #endif
  733. #if DISTRHO_PLUGIN_WANT_STATE
  734. case 'DPFl':
  735. if (const CFMutableArrayRef keysRef = CFArrayCreateMutable(nullptr,
  736. fStateCount,
  737. &kCFTypeArrayCallBacks))
  738. {
  739. for (uint32_t i=0; i<fStateCount; ++i)
  740. {
  741. const CFStringRef keyRef = CFStringCreateWithCString(nullptr,
  742. fPlugin.getStateKey(i),
  743. kCFStringEncodingASCII);
  744. CFArrayAppendValue(keysRef, keyRef);
  745. CFRelease(keyRef);
  746. }
  747. *static_cast<CFArrayRef*>(outData) = keysRef;
  748. return noErr;
  749. }
  750. return kAudio_ParamError;
  751. case 'DPFs':
  752. {
  753. const String& key(fPlugin.getStateKey(inElement));
  754. #if DISTRHO_PLUGIN_WANT_FULL_STATE
  755. fStateMap[key] = fPlugin.getStateValue(key);
  756. #endif
  757. *static_cast<CFStringRef*>(outData) = CFStringCreateWithCString(nullptr,
  758. fStateMap[key],
  759. kCFStringEncodingUTF8);
  760. }
  761. return noErr;
  762. #endif
  763. #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  764. case 'DPFa':
  765. *static_cast<void**>(outData) = fPlugin.getInstancePointer();
  766. return noErr;
  767. #endif
  768. }
  769. d_stdout("TODO GetProperty(%d:%s, %d:%s, %d, ...)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);
  770. return kAudioUnitErr_InvalidProperty;
  771. }
  772. OSStatus auSetProperty(const AudioUnitPropertyID inProp,
  773. const AudioUnitScope inScope,
  774. const AudioUnitElement inElement,
  775. const void* const inData,
  776. const UInt32 inDataSize)
  777. {
  778. switch (inProp)
  779. {
  780. case kAudioUnitProperty_ClassInfo:
  781. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  782. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  783. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFPropertyListRef), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  784. {
  785. const CFPropertyListRef propList = *static_cast<const CFPropertyListRef*>(inData);
  786. DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(propList) == CFDictionaryGetTypeID(), kAudioUnitErr_InvalidPropertyValue);
  787. restoreClassInfo(static_cast<CFDictionaryRef>(propList));
  788. }
  789. return noErr;
  790. case kAudioUnitProperty_MakeConnection:
  791. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global || inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
  792. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  793. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AudioUnitConnection), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  794. d_stdout("WIP SetProperty(%d:%s, %d:%s, %d, %p, %u)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize);
  795. // TODO
  796. return noErr;
  797. #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  798. case kAudioUnitProperty_SampleRate:
  799. #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  800. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
  801. #elif DISTRHO_PLUGIN_NUM_INPUTS != 0
  802. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
  803. #else
  804. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
  805. #endif
  806. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  807. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(Float64), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  808. {
  809. const Float64 sampleRate = *static_cast<const Float64*>(inData);
  810. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  811. if (inScope == kAudioUnitScope_Input)
  812. {
  813. if (d_isNotEqual(fSampleRateForInput, sampleRate))
  814. {
  815. fSampleRateForInput = sampleRate;
  816. d_nextSampleRate = sampleRate;
  817. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  818. if (d_isEqual(fSampleRateForOutput, sampleRate))
  819. #endif
  820. {
  821. fPlugin.setSampleRate(sampleRate, true);
  822. }
  823. notifyListeners(inProp, inScope, inElement);
  824. }
  825. return noErr;
  826. }
  827. #endif
  828. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  829. if (inScope == kAudioUnitScope_Output)
  830. {
  831. if (d_isNotEqual(fSampleRateForOutput, sampleRate))
  832. {
  833. fSampleRateForOutput = sampleRate;
  834. d_nextSampleRate = sampleRate;
  835. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  836. if (d_isEqual(fSampleRateForInput, sampleRate))
  837. #endif
  838. {
  839. fPlugin.setSampleRate(sampleRate, true);
  840. }
  841. notifyListeners(inProp, inScope, inElement);
  842. }
  843. return noErr;
  844. }
  845. #endif
  846. }
  847. return kAudioUnitErr_InvalidScope;
  848. #endif
  849. #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  850. case kAudioUnitProperty_StreamFormat:
  851. #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  852. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
  853. #elif DISTRHO_PLUGIN_NUM_INPUTS != 0
  854. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
  855. #else
  856. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
  857. #endif
  858. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  859. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AudioStreamBasicDescription), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  860. {
  861. const AudioStreamBasicDescription* const desc = static_cast<const AudioStreamBasicDescription*>(inData);
  862. if (desc->mFormatID != kAudioFormatLinearPCM)
  863. return kAudioUnitErr_FormatNotSupported;
  864. if (desc->mBitsPerChannel != 32)
  865. return kAudioUnitErr_FormatNotSupported;
  866. if (desc->mBytesPerFrame != sizeof(float))
  867. return kAudioUnitErr_FormatNotSupported;
  868. if (desc->mBytesPerPacket != sizeof(float))
  869. return kAudioUnitErr_FormatNotSupported;
  870. if (desc->mFramesPerPacket != 1)
  871. return kAudioUnitErr_FormatNotSupported;
  872. #if 1
  873. // dont allow interleaved data
  874. if (desc->mFormatFlags != (kAudioFormatFlagsNativeFloatPacked|kAudioFormatFlagIsNonInterleaved))
  875. #else
  876. // allow interleaved data
  877. if ((desc->mFormatFlags & ~kAudioFormatFlagIsNonInterleaved) != kAudioFormatFlagsNativeFloatPacked)
  878. #endif
  879. return kAudioUnitErr_FormatNotSupported;
  880. if (desc->mChannelsPerFrame != (inScope == kAudioUnitScope_Input ? DISTRHO_PLUGIN_NUM_INPUTS
  881. : DISTRHO_PLUGIN_NUM_OUTPUTS))
  882. return kAudioUnitErr_FormatNotSupported;
  883. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  884. if (inScope == kAudioUnitScope_Input)
  885. {
  886. if (d_isNotEqual(fSampleRateForInput, desc->mSampleRate))
  887. {
  888. fSampleRateForInput = desc->mSampleRate;
  889. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  890. if (d_isEqual(fSampleRateForOutput, desc->mSampleRate))
  891. #endif
  892. {
  893. fPlugin.setSampleRate(desc->mSampleRate, true);
  894. }
  895. notifyListeners(inProp, inScope, inElement);
  896. notifyListeners(kAudioUnitProperty_SampleRate, inScope, inElement);
  897. }
  898. return noErr;
  899. }
  900. #endif
  901. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  902. if (inScope == kAudioUnitScope_Output)
  903. {
  904. if (d_isNotEqual(fSampleRateForOutput, desc->mSampleRate))
  905. {
  906. fSampleRateForOutput = desc->mSampleRate;
  907. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  908. if (d_isEqual(fSampleRateForInput, desc->mSampleRate))
  909. #endif
  910. {
  911. fPlugin.setSampleRate(desc->mSampleRate, true);
  912. }
  913. notifyListeners(inProp, inScope, inElement);
  914. notifyListeners(kAudioUnitProperty_SampleRate, inScope, inElement);
  915. }
  916. return noErr;
  917. }
  918. #endif
  919. }
  920. return kAudioUnitErr_InvalidScope;
  921. #endif
  922. case kAudioUnitProperty_MaximumFramesPerSlice:
  923. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  924. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  925. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(UInt32), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  926. if (fPlugin.setBufferSize(*static_cast<const UInt32*>(inData), true))
  927. notifyListeners(inProp, inScope, inElement);
  928. return noErr;
  929. case kAudioUnitProperty_BypassEffect:
  930. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  931. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  932. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(UInt32), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  933. DISTRHO_SAFE_ASSERT_RETURN(fBypassParameterIndex != UINT32_MAX, kAudioUnitErr_InvalidPropertyValue);
  934. {
  935. const bool bypass = *static_cast<const UInt32*>(inData) != 0;
  936. if ((fLastParameterValues[fBypassParameterIndex] > 0.5f) != bypass)
  937. {
  938. const float value = bypass ? 1.f : 0.f;
  939. fLastParameterValues[fBypassParameterIndex] = value;
  940. fPlugin.setParameterValue(fBypassParameterIndex, value);
  941. notifyListeners(inProp, inScope, inElement);
  942. }
  943. }
  944. return noErr;
  945. case kAudioUnitProperty_SetRenderCallback:
  946. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
  947. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  948. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AURenderCallbackStruct), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  949. d_stdout("WIP SetProperty(%d:%s, %d:%s, %d, %p, %u)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize);
  950. // TODO
  951. return noErr;
  952. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  953. case kAudioUnitProperty_HostCallbacks:
  954. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  955. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  956. {
  957. const UInt32 usableDataSize = std::min(inDataSize, static_cast<UInt32>(sizeof(HostCallbackInfo)));
  958. const bool changed = std::memcmp(&fHostCallbackInfo, inData, usableDataSize) != 0;
  959. std::memcpy(&fHostCallbackInfo, inData, usableDataSize);
  960. if (sizeof(HostCallbackInfo) > usableDataSize)
  961. std::memset(&fHostCallbackInfo + usableDataSize, 0, sizeof(HostCallbackInfo) - usableDataSize);
  962. if (changed)
  963. notifyListeners(inProp, inScope, inElement);
  964. }
  965. return noErr;
  966. #endif
  967. case kAudioUnitProperty_PresentPreset:
  968. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  969. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  970. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AUPreset), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  971. {
  972. CFRelease(fCurrentPreset.presetName);
  973. std::memcpy(&fCurrentPreset, inData, sizeof(AUPreset));
  974. }
  975. return noErr;
  976. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  977. case kAudioUnitProperty_MIDIOutputCallback:
  978. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  979. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  980. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AUMIDIOutputCallbackStruct), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  981. std::memcpy(&fMidiOutput, inData, sizeof(AUMIDIOutputCallbackStruct));
  982. return noErr;
  983. #endif
  984. case 'DPFi':
  985. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  986. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  987. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint16_t), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  988. {
  989. const uint16_t magic = *static_cast<const uint16_t*>(inData);
  990. if (magic != 1337)
  991. return noErr;
  992. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  993. notifyListeners('DPFo', kAudioUnitScope_Global, 0);
  994. #endif
  995. #if DISTRHO_PLUGIN_WANT_STATE
  996. for (uint32_t i=0; i<fStateCount; ++i)
  997. notifyListeners('DPFs', kAudioUnitScope_Global, i);
  998. #endif
  999. for (uint32_t i=0; i<fParameterCount; ++i)
  1000. notifyListeners('DPFp', kAudioUnitScope_Global, i);
  1001. }
  1002. return noErr;
  1003. case 'DPFe':
  1004. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  1005. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement);
  1006. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint8_t), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  1007. {
  1008. const uint8_t flag = *static_cast<const uint8_t*>(inData);
  1009. if (flag != 1 && flag != 2)
  1010. return noErr;
  1011. AudioUnitEvent event;
  1012. std::memset(&event, 0, sizeof(event));
  1013. event.mEventType = flag == 1 ? kAudioUnitEvent_BeginParameterChangeGesture
  1014. : kAudioUnitEvent_EndParameterChangeGesture;
  1015. event.mArgument.mParameter.mAudioUnit = fComponent;
  1016. event.mArgument.mParameter.mParameterID = inElement;
  1017. event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
  1018. AUEventListenerNotify(NULL, NULL, &event);
  1019. }
  1020. return noErr;
  1021. case 'DPFp':
  1022. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  1023. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement);
  1024. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(float), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  1025. {
  1026. const float value = *static_cast<const float*>(inData);
  1027. DISTRHO_SAFE_ASSERT_RETURN(std::isfinite(value), kAudioUnitErr_InvalidParameterValue);
  1028. if (d_isEqual(fLastParameterValues[inElement], value))
  1029. return noErr;
  1030. fLastParameterValues[inElement] = value;
  1031. fPlugin.setParameterValue(inElement, value);
  1032. AudioUnitEvent event;
  1033. std::memset(&event, 0, sizeof(event));
  1034. event.mEventType = kAudioUnitEvent_ParameterValueChange;
  1035. event.mArgument.mParameter.mAudioUnit = fComponent;
  1036. event.mArgument.mParameter.mParameterID = inElement;
  1037. event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
  1038. AUEventListenerNotify(NULL, NULL, &event);
  1039. if (fBypassParameterIndex == inElement)
  1040. notifyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
  1041. }
  1042. return noErr;
  1043. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1044. case 'DPFn':
  1045. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  1046. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
  1047. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint8_t) * 3, inDataSize, kAudioUnitErr_InvalidPropertyValue);
  1048. #if DISTRHO_PLUGIN_HAS_UI
  1049. {
  1050. const uint8_t* const midiData = static_cast<const uint8_t*>(inData);
  1051. if (midiData[0] == 0)
  1052. return noErr;
  1053. fNotesRingBuffer.writeCustomData(midiData, 3);
  1054. fNotesRingBuffer.commitWrite();
  1055. }
  1056. #endif
  1057. return noErr;
  1058. #endif
  1059. #if DISTRHO_PLUGIN_WANT_STATE
  1060. case 'DPFs':
  1061. DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
  1062. DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fStateCount, inElement, kAudioUnitErr_InvalidElement);
  1063. DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFStringRef), inDataSize, kAudioUnitErr_InvalidPropertyValue);
  1064. {
  1065. const CFStringRef valueRef = *static_cast<const CFStringRef*>(inData);
  1066. DISTRHO_SAFE_ASSERT_RETURN(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID(),
  1067. kAudioUnitErr_InvalidPropertyValue);
  1068. const CFIndex valueLen = CFStringGetLength(valueRef);
  1069. char* const value = static_cast<char*>(std::malloc(valueLen + 1));
  1070. DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, kAudio_ParamError);
  1071. DISTRHO_SAFE_ASSERT_RETURN(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8),
  1072. kAudioUnitErr_InvalidPropertyValue);
  1073. const String& key(fPlugin.getStateKey(inElement));
  1074. // save this key as needed
  1075. if (fPlugin.wantStateKey(key))
  1076. fStateMap[key] = value;
  1077. fPlugin.setState(key, value);
  1078. CFRelease(valueRef);
  1079. std::free(value);
  1080. }
  1081. return noErr;
  1082. #endif
  1083. }
  1084. d_stdout("TODO SetProperty(%d:%s, %d:%s, %d, %p, %u)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize);
  1085. return kAudioUnitErr_InvalidProperty;
  1086. }
  1087. OSStatus auAddPropertyListener(const AudioUnitPropertyID prop,
  1088. const AudioUnitPropertyListenerProc proc,
  1089. void* const userData)
  1090. {
  1091. const PropertyListener pl = {
  1092. prop, proc, userData
  1093. };
  1094. if (fPropertyListeners.empty())
  1095. fPropertyListeners.reserve(32);
  1096. fPropertyListeners.push_back(pl);
  1097. return noErr;
  1098. }
  1099. OSStatus auRemovePropertyListener(const AudioUnitPropertyID prop, const AudioUnitPropertyListenerProc proc)
  1100. {
  1101. for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it)
  1102. {
  1103. const PropertyListener& pl(*it);
  1104. if (pl.prop == prop && pl.proc == proc)
  1105. {
  1106. fPropertyListeners.erase(it);
  1107. return auRemovePropertyListener(prop, proc);
  1108. }
  1109. }
  1110. return noErr;
  1111. }
  1112. OSStatus auRemovePropertyListenerWithUserData(const AudioUnitPropertyID prop,
  1113. const AudioUnitPropertyListenerProc proc,
  1114. void* const userData)
  1115. {
  1116. for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it)
  1117. {
  1118. const PropertyListener& pl(*it);
  1119. if (pl.prop == prop && pl.proc == proc && pl.userData == userData)
  1120. {
  1121. fPropertyListeners.erase(it);
  1122. return auRemovePropertyListenerWithUserData(prop, proc, userData);
  1123. }
  1124. }
  1125. return noErr;
  1126. }
  1127. OSStatus auAddRenderNotify(const AURenderCallback proc, void* const userData)
  1128. {
  1129. d_stdout("WIP AddRenderNotify(%p, %p)", proc, userData);
  1130. // TODO
  1131. return noErr;
  1132. }
  1133. OSStatus auRemoveRenderNotify(const AURenderCallback proc, void* const userData)
  1134. {
  1135. d_stdout("WIP RemoveRenderNotify(%p, %p)", proc, userData);
  1136. // TODO
  1137. return noErr;
  1138. }
  1139. OSStatus auGetParameter(const AudioUnitParameterID param,
  1140. const AudioUnitScope scope,
  1141. const AudioUnitElement elem,
  1142. AudioUnitParameterValue* const value)
  1143. {
  1144. DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope, kAudioUnitErr_InvalidScope);
  1145. DISTRHO_SAFE_ASSERT_UINT_RETURN(param < fParameterCount, param, kAudioUnitErr_InvalidElement);
  1146. DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement);
  1147. DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, kAudio_ParamError);
  1148. *value = fPlugin.getParameterValue(param);
  1149. return noErr;
  1150. }
  1151. OSStatus auSetParameter(const AudioUnitParameterID param,
  1152. const AudioUnitScope scope,
  1153. const AudioUnitElement elem,
  1154. const AudioUnitParameterValue value,
  1155. UInt32 /* bufferOffset */)
  1156. {
  1157. DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope, kAudioUnitErr_InvalidScope);
  1158. DISTRHO_SAFE_ASSERT_UINT_RETURN(param < fParameterCount, param, kAudioUnitErr_InvalidElement);
  1159. DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement);
  1160. DISTRHO_SAFE_ASSERT_RETURN(std::isfinite(value), kAudioUnitErr_InvalidParameterValue);
  1161. if (d_isNotEqual(fLastParameterValues[param], value))
  1162. {
  1163. fLastParameterValues[param] = value;
  1164. fPlugin.setParameterValue(param, value);
  1165. // TODO flag param only, notify listeners later on bg thread (sem_post etc)
  1166. notifyListeners('DPFp', kAudioUnitScope_Global, param);
  1167. if (fBypassParameterIndex == elem)
  1168. notifyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
  1169. }
  1170. return noErr;
  1171. }
  1172. OSStatus auScheduleParameters(const AudioUnitParameterEvent* const events, const UInt32 numEvents)
  1173. {
  1174. for (UInt32 i=0; i<numEvents; ++i)
  1175. {
  1176. const AudioUnitParameterEvent& event(events[i]);
  1177. if (event.eventType == kParameterEvent_Immediate)
  1178. {
  1179. auSetParameter(event.parameter,
  1180. event.scope,
  1181. event.element,
  1182. event.eventValues.immediate.value,
  1183. event.eventValues.immediate.bufferOffset);
  1184. }
  1185. else
  1186. {
  1187. // TODO?
  1188. d_stdout("WIP ScheduleParameters(%p, %u)", events, numEvents);
  1189. }
  1190. }
  1191. return noErr;
  1192. }
  1193. OSStatus auReset(const AudioUnitScope scope, const AudioUnitElement elem)
  1194. {
  1195. DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global || scope == kAudioUnitScope_Input || scope == kAudioUnitScope_Output, scope, kAudioUnitErr_InvalidScope);
  1196. DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement);
  1197. if (fPlugin.isActive())
  1198. {
  1199. fPlugin.deactivate();
  1200. fPlugin.activate();
  1201. }
  1202. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1203. fMidiEventCount = 0;
  1204. #endif
  1205. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  1206. fMidiOutputPackets.numPackets = 0;
  1207. #endif
  1208. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  1209. fTimePosition.clear();
  1210. #endif
  1211. return noErr;
  1212. }
  1213. OSStatus auRender(AudioUnitRenderActionFlags* const ioActionFlags,
  1214. const AudioTimeStamp* const inTimeStamp,
  1215. const UInt32 inBusNumber,
  1216. const UInt32 inFramesToProcess,
  1217. AudioBufferList& ioData)
  1218. {
  1219. DISTRHO_SAFE_ASSERT_UINT_RETURN(inBusNumber == 0, inBusNumber, kAudioUnitErr_InvalidElement);
  1220. DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData.mNumberBuffers == std::max<uint>(DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS), ioData.mNumberBuffers, kAudio_ParamError);
  1221. if (inFramesToProcess > fPlugin.getBufferSize())
  1222. {
  1223. setLastRenderError(kAudioUnitErr_TooManyFramesToProcess);
  1224. return kAudioUnitErr_TooManyFramesToProcess;
  1225. }
  1226. for (uint i=0; i<ioData.mNumberBuffers; ++i)
  1227. {
  1228. AudioBuffer& buffer(ioData.mBuffers[i]);
  1229. // TODO there must be something more to this...
  1230. if (buffer.mData == nullptr)
  1231. {
  1232. return noErr;
  1233. }
  1234. DISTRHO_SAFE_ASSERT_UINT_RETURN(buffer.mDataByteSize == inFramesToProcess * sizeof(float), buffer.mDataByteSize, kAudio_ParamError);
  1235. }
  1236. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  1237. const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS];
  1238. for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  1239. {
  1240. inputs[i] = static_cast<const float*>(ioData.mBuffers[i].mData);
  1241. }
  1242. #else
  1243. constexpr const float** inputs = nullptr;
  1244. #endif
  1245. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  1246. float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS];
  1247. for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  1248. {
  1249. outputs[i] = static_cast<float*>(ioData.mBuffers[i].mData);
  1250. }
  1251. #else
  1252. constexpr float** outputs = nullptr;
  1253. #endif
  1254. run(inputs, outputs, inFramesToProcess, inTimeStamp);
  1255. if (ioActionFlags != nullptr)
  1256. {
  1257. // TODO what now?
  1258. }
  1259. return noErr;
  1260. }
  1261. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1262. OSStatus auMIDIEvent(const UInt32 inStatus,
  1263. const UInt32 inData1,
  1264. const UInt32 inData2,
  1265. const UInt32 inOffsetSampleFrame)
  1266. {
  1267. if (fMidiEventCount >= kMaxMidiEvents)
  1268. return noErr;
  1269. MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
  1270. midiEvent.frame = inOffsetSampleFrame;
  1271. midiEvent.data[0] = inStatus;
  1272. midiEvent.data[1] = inData1;
  1273. midiEvent.data[2] = inData2;
  1274. // TODO
  1275. switch (inStatus)
  1276. {
  1277. case 0x80:
  1278. case 0x90:
  1279. case 0xA0:
  1280. case 0xB0:
  1281. case 0xD0:
  1282. case 0xE0:
  1283. midiEvent.size = 3;
  1284. break;
  1285. case 0xC0:
  1286. midiEvent.size = 2;
  1287. break;
  1288. default:
  1289. midiEvent.size = 1;
  1290. break;
  1291. }
  1292. #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  1293. // if plugin has no audio, assume render function is not going to be called
  1294. run(nullptr, nullptr, std::max(1u, inOffsetSampleFrame), nullptr);
  1295. #endif
  1296. return noErr;
  1297. }
  1298. OSStatus auSysEx(const UInt8* const inData, const UInt32 inLength)
  1299. {
  1300. if (fMidiEventCount >= kMaxMidiEvents)
  1301. return noErr;
  1302. MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
  1303. midiEvent.frame = fMidiEventCount != 1 ? fMidiEvents[fMidiEventCount - 1].frame : 0;
  1304. midiEvent.size = inLength;
  1305. // FIXME who owns inData ??
  1306. if (inLength > MidiEvent::kDataSize)
  1307. {
  1308. std::memset(midiEvent.data, 0, MidiEvent::kDataSize);
  1309. midiEvent.dataExt = inData;
  1310. }
  1311. else
  1312. {
  1313. std::memcpy(midiEvent.data, inData, inLength);
  1314. }
  1315. #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  1316. // if plugin has no audio, assume render function is not going to be called
  1317. run(nullptr, nullptr, 1, nullptr);
  1318. #endif
  1319. return noErr;
  1320. }
  1321. #endif
  1322. // ----------------------------------------------------------------------------------------------------------------
  1323. private:
  1324. PluginExporter fPlugin;
  1325. // AU component
  1326. const AudioUnit fComponent;
  1327. // AUv2 related fields
  1328. AUPreset fCurrentPreset;
  1329. OSStatus fLastRenderError;
  1330. PropertyListeners fPropertyListeners;
  1331. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  1332. Float64 fSampleRateForInput;
  1333. #endif
  1334. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  1335. Float64 fSampleRateForOutput;
  1336. #endif
  1337. // Caching
  1338. const uint32_t fParameterCount;
  1339. float* fLastParameterValues;
  1340. uint32_t fBypassParameterIndex;
  1341. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1342. uint32_t fMidiEventCount;
  1343. MidiEvent fMidiEvents[kMaxMidiEvents];
  1344. #if DISTRHO_PLUGIN_HAS_UI
  1345. SmallStackRingBuffer fNotesRingBuffer;
  1346. #endif
  1347. #endif
  1348. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  1349. AUMIDIOutputCallbackStruct fMidiOutput;
  1350. MIDIPacketList fMidiOutputPackets;
  1351. #endif
  1352. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1353. uint32_t fCurrentProgram;
  1354. #endif
  1355. #if DISTRHO_PLUGIN_WANT_STATE
  1356. const uint32_t fStateCount;
  1357. StringMap fStateMap;
  1358. #endif
  1359. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  1360. HostCallbackInfo fHostCallbackInfo;
  1361. TimePosition fTimePosition;
  1362. #endif
  1363. // ----------------------------------------------------------------------------------------------------------------
  1364. void notifyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem)
  1365. {
  1366. for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it)
  1367. {
  1368. const PropertyListener& pl(*it);
  1369. if (pl.prop == prop)
  1370. pl.proc(pl.userData, fComponent, prop, scope, elem);
  1371. }
  1372. }
  1373. void run(const float** inputs, float** outputs, const uint32_t frames, const AudioTimeStamp* const inTimeStamp)
  1374. {
  1375. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT && DISTRHO_PLUGIN_HAS_UI
  1376. if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading())
  1377. {
  1378. uint8_t midiData[3];
  1379. const uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount - 1].frame : 0;
  1380. while (fNotesRingBuffer.isDataAvailableForReading())
  1381. {
  1382. if (! fNotesRingBuffer.readCustomData(midiData, 3))
  1383. break;
  1384. MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
  1385. midiEvent.frame = frame;
  1386. midiEvent.size = 3;
  1387. std::memcpy(midiEvent.data, midiData, 3);
  1388. if (fMidiEventCount == kMaxMidiEvents)
  1389. break;
  1390. }
  1391. }
  1392. #endif
  1393. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  1394. fMidiOutputPackets.numPackets = 0;
  1395. #endif
  1396. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  1397. if (fHostCallbackInfo.beatAndTempoProc != nullptr ||
  1398. fHostCallbackInfo.musicalTimeLocationProc != nullptr ||
  1399. fHostCallbackInfo.transportStateProc != nullptr)
  1400. {
  1401. // reused values so we can check null and return value together
  1402. Boolean b1 = false;
  1403. Float32 f1 = 4.f; // initial value for beats per bar
  1404. Float64 g1 = 0.0;
  1405. Float64 g2 = 0.0;
  1406. UInt32 u1 = 0.0;
  1407. if (fHostCallbackInfo.musicalTimeLocationProc != nullptr
  1408. && fHostCallbackInfo.musicalTimeLocationProc(fHostCallbackInfo.hostUserData,
  1409. nullptr, &f1, &u1, nullptr) == noErr)
  1410. {
  1411. fTimePosition.bbt.beatsPerBar = f1;
  1412. fTimePosition.bbt.beatType = u1;
  1413. }
  1414. else
  1415. {
  1416. fTimePosition.bbt.beatsPerBar = 4.f;
  1417. fTimePosition.bbt.beatType = 4.f;
  1418. }
  1419. if (fHostCallbackInfo.beatAndTempoProc != nullptr
  1420. && fHostCallbackInfo.beatAndTempoProc(fHostCallbackInfo.hostUserData, &g1, &g2) == noErr)
  1421. {
  1422. const double beat = static_cast<int32_t>(g1);
  1423. fTimePosition.bbt.valid = true;
  1424. fTimePosition.bbt.bar = static_cast<int32_t>(beat / f1) + 1;
  1425. fTimePosition.bbt.beat = static_cast<int32_t>(std::fmod(beat, f1)) + 1;
  1426. fTimePosition.bbt.tick = std::fmod(g1, 1.0) * 1920.0;
  1427. fTimePosition.bbt.beatsPerMinute = g2;
  1428. }
  1429. else
  1430. {
  1431. fTimePosition.bbt.valid = false;
  1432. fTimePosition.bbt.bar = 1;
  1433. fTimePosition.bbt.beat = 1;
  1434. fTimePosition.bbt.tick = 0.0;
  1435. fTimePosition.bbt.beatsPerMinute = 120.0;
  1436. }
  1437. if (fHostCallbackInfo.transportStateProc != nullptr
  1438. && fHostCallbackInfo.transportStateProc(fHostCallbackInfo.hostUserData,
  1439. &b1, nullptr, &g1, nullptr, nullptr, nullptr) == noErr)
  1440. {
  1441. fTimePosition.playing = b1;
  1442. fTimePosition.frame = static_cast<int64_t>(g1);
  1443. }
  1444. else
  1445. {
  1446. fTimePosition.playing = false;
  1447. fTimePosition.frame = 0;
  1448. }
  1449. fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat *
  1450. fTimePosition.bbt.beatsPerBar *
  1451. (fTimePosition.bbt.bar - 1);
  1452. fPlugin.setTimePosition(fTimePosition);
  1453. }
  1454. else
  1455. {
  1456. d_stdout("no time");
  1457. }
  1458. #endif
  1459. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1460. fPlugin.run(inputs, outputs, frames, fMidiEvents, fMidiEventCount);
  1461. fMidiEventCount = 0;
  1462. #else
  1463. fPlugin.run(inputs, outputs, frames);
  1464. #endif
  1465. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  1466. if (fMidiOutputPackets.numPackets != 0 && fMidiOutput.midiOutputCallback != nullptr)
  1467. {
  1468. fMidiOutput.midiOutputCallback(fMidiOutput.userData,
  1469. inTimeStamp,
  1470. 0,
  1471. reinterpret_cast<const ::MIDIPacketList*>(&fMidiOutputPackets));
  1472. }
  1473. #else
  1474. // unused
  1475. (void)inTimeStamp;
  1476. #endif
  1477. float value;
  1478. AudioUnitEvent event;
  1479. std::memset(&event, 0, sizeof(event));
  1480. event.mEventType = kAudioUnitEvent_ParameterValueChange;
  1481. event.mArgument.mParameter.mAudioUnit = fComponent;
  1482. event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
  1483. for (uint32_t i=0; i<fParameterCount; ++i)
  1484. {
  1485. if (fPlugin.isParameterOutputOrTrigger(i))
  1486. {
  1487. value = fPlugin.getParameterValue(i);
  1488. if (d_isEqual(fLastParameterValues[i], value))
  1489. continue;
  1490. fLastParameterValues[i] = value;
  1491. // TODO flag param only, notify listeners later on bg thread (sem_post etc)
  1492. event.mArgument.mParameter.mParameterID = i;
  1493. AUEventListenerNotify(NULL, NULL, &event);
  1494. notifyListeners('DPFp', kAudioUnitScope_Global, i);
  1495. }
  1496. }
  1497. }
  1498. void setLastRenderError(const OSStatus err)
  1499. {
  1500. if (fLastRenderError != noErr)
  1501. return;
  1502. fLastRenderError = err;
  1503. notifyListeners(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0);
  1504. }
  1505. // ----------------------------------------------------------------------------------------------------------------
  1506. CFMutableDictionaryRef retrieveClassInfo()
  1507. {
  1508. CFMutableDictionaryRef clsInfo = CFDictionaryCreateMutable(nullptr,
  1509. 0,
  1510. &kCFTypeDictionaryKeyCallBacks,
  1511. &kCFTypeDictionaryValueCallBacks);
  1512. SInt32 value;
  1513. value = 0;
  1514. if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value))
  1515. {
  1516. CFDictionarySetValue(clsInfo, CFSTR(kAUPresetVersionKey), num);
  1517. CFRelease(num);
  1518. }
  1519. value = kType;
  1520. if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value))
  1521. {
  1522. CFDictionarySetValue(clsInfo, CFSTR(kAUPresetTypeKey), num);
  1523. CFRelease(num);
  1524. }
  1525. value = kSubType;
  1526. if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value))
  1527. {
  1528. CFDictionarySetValue(clsInfo, CFSTR(kAUPresetSubtypeKey), num);
  1529. CFRelease(num);
  1530. }
  1531. value = kManufacturer;
  1532. if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value))
  1533. {
  1534. CFDictionarySetValue(clsInfo, CFSTR(kAUPresetManufacturerKey), num);
  1535. CFRelease(num);
  1536. }
  1537. CFDictionarySetValue(clsInfo, CFSTR(kAUPresetNameKey), fCurrentPreset.presetName);
  1538. if (const CFMutableDictionaryRef data = CFDictionaryCreateMutable(nullptr,
  1539. 0,
  1540. &kCFTypeDictionaryKeyCallBacks,
  1541. &kCFTypeDictionaryValueCallBacks))
  1542. {
  1543. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1544. const SInt32 program = fCurrentProgram;
  1545. if (const CFNumberRef programRef = CFNumberCreate(nullptr, kCFNumberSInt32Type, &program))
  1546. {
  1547. CFDictionarySetValue(data, CFSTR("program"), programRef);
  1548. CFRelease(programRef);
  1549. }
  1550. #endif
  1551. #if DISTRHO_PLUGIN_WANT_FULL_STATE
  1552. // Update current state
  1553. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  1554. {
  1555. const String& key(cit->first);
  1556. fStateMap[key] = fPlugin.getStateValue(key);
  1557. }
  1558. #endif
  1559. #if DISTRHO_PLUGIN_WANT_STATE
  1560. if (const CFMutableArrayRef statesRef = CFArrayCreateMutable(nullptr,
  1561. fStateCount,
  1562. &kCFTypeArrayCallBacks))
  1563. {
  1564. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  1565. {
  1566. const String& key(cit->first);
  1567. const String& value(cit->second);
  1568. #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && ! DISTRHO_PLUGIN_HAS_UI
  1569. bool wantStateKey = true;
  1570. for (uint32_t i=0; i<fStateCount; ++i)
  1571. {
  1572. if (fPlugin.getStateKey(i) == key)
  1573. {
  1574. if (fPlugin.getStateHints(i) & kStateIsOnlyForUI)
  1575. wantStateKey = false;
  1576. break;
  1577. }
  1578. }
  1579. if (! wantStateKey)
  1580. continue;
  1581. #endif
  1582. CFStringRef keyRef = CFStringCreateWithCString(nullptr, key, kCFStringEncodingASCII);
  1583. CFStringRef valueRef = CFStringCreateWithCString(nullptr, value, kCFStringEncodingUTF8);
  1584. if (CFDictionaryRef dictRef = CFDictionaryCreate(nullptr,
  1585. reinterpret_cast<const void**>(&keyRef),
  1586. reinterpret_cast<const void**>(&valueRef),
  1587. 1,
  1588. &kCFTypeDictionaryKeyCallBacks,
  1589. &kCFTypeDictionaryValueCallBacks))
  1590. {
  1591. CFArrayAppendValue(statesRef, dictRef);
  1592. CFRelease(dictRef);
  1593. }
  1594. CFRelease(keyRef);
  1595. CFRelease(valueRef);
  1596. }
  1597. CFDictionarySetValue(data, CFSTR("states"), statesRef);
  1598. CFRelease(statesRef);
  1599. }
  1600. #endif
  1601. if (const CFMutableArrayRef paramsRef = CFArrayCreateMutable(nullptr,
  1602. fParameterCount,
  1603. &kCFTypeArrayCallBacks))
  1604. {
  1605. for (uint32_t i=0; i<fParameterCount; ++i)
  1606. {
  1607. if (fPlugin.isParameterOutputOrTrigger(i))
  1608. continue;
  1609. const float value = fPlugin.getParameterValue(i);
  1610. CFStringRef keyRef = CFStringCreateWithCString(nullptr,
  1611. fPlugin.getParameterSymbol(i),
  1612. kCFStringEncodingASCII);
  1613. CFNumberRef valueRef = CFNumberCreate(nullptr, kCFNumberFloat32Type, &value);
  1614. if (CFDictionaryRef dictRef = CFDictionaryCreate(nullptr,
  1615. reinterpret_cast<const void**>(&keyRef),
  1616. reinterpret_cast<const void**>(&valueRef),
  1617. 1,
  1618. &kCFTypeDictionaryKeyCallBacks,
  1619. &kCFTypeDictionaryValueCallBacks))
  1620. {
  1621. CFArrayAppendValue(paramsRef, dictRef);
  1622. CFRelease(dictRef);
  1623. }
  1624. CFRelease(keyRef);
  1625. CFRelease(valueRef);
  1626. }
  1627. CFDictionarySetValue(data, CFSTR("params"), paramsRef);
  1628. CFRelease(paramsRef);
  1629. }
  1630. CFDictionarySetValue(clsInfo, CFSTR(kAUPresetDataKey), data);
  1631. CFRelease(data);
  1632. }
  1633. return clsInfo;
  1634. }
  1635. void restoreClassInfo(const CFDictionaryRef clsInfo)
  1636. {
  1637. CFDictionaryRef data = nullptr;
  1638. DISTRHO_SAFE_ASSERT_RETURN(CFDictionaryGetValueIfPresent(clsInfo,
  1639. CFSTR(kAUPresetDataKey),
  1640. reinterpret_cast<const void**>(&data)),);
  1641. DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(data) == CFDictionaryGetTypeID(),);
  1642. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1643. CFNumberRef programRef = nullptr;
  1644. if (CFDictionaryGetValueIfPresent(data, CFSTR("program"), reinterpret_cast<const void**>(&programRef))
  1645. && CFGetTypeID(programRef) == CFNumberGetTypeID())
  1646. {
  1647. SInt32 program = 0;
  1648. if (CFNumberGetValue(programRef, kCFNumberSInt32Type, &program) && program >= 0)
  1649. {
  1650. fCurrentProgram = program;
  1651. fPlugin.loadProgram(fCurrentProgram);
  1652. notifyListeners('DPFo', kAudioUnitScope_Global, 0);
  1653. }
  1654. }
  1655. #endif
  1656. #if DISTRHO_PLUGIN_WANT_STATE
  1657. CFArrayRef statesRef = nullptr;
  1658. if (CFDictionaryGetValueIfPresent(data, CFSTR("states"), reinterpret_cast<const void**>(&statesRef))
  1659. && CFGetTypeID(statesRef) == CFArrayGetTypeID())
  1660. {
  1661. const CFIndex numStates = CFArrayGetCount(statesRef);
  1662. char* key = nullptr;
  1663. char* value = nullptr;
  1664. CFIndex keyLen = -1;
  1665. CFIndex valueLen = -1;
  1666. for (CFIndex i=0; i<numStates; ++i)
  1667. {
  1668. const CFDictionaryRef state = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(statesRef, i));
  1669. DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(state) == CFDictionaryGetTypeID());
  1670. DISTRHO_SAFE_ASSERT_BREAK(CFDictionaryGetCount(state) == 1);
  1671. CFStringRef keyRef = nullptr;
  1672. CFStringRef valueRef = nullptr;
  1673. CFDictionaryGetKeysAndValues(state,
  1674. reinterpret_cast<const void**>(&keyRef),
  1675. reinterpret_cast<const void**>(&valueRef));
  1676. DISTRHO_SAFE_ASSERT_BREAK(keyRef != nullptr && CFGetTypeID(keyRef) == CFStringGetTypeID());
  1677. DISTRHO_SAFE_ASSERT_BREAK(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID());
  1678. const CFIndex keyRefLen = CFStringGetLength(keyRef);
  1679. if (keyLen < keyRefLen)
  1680. {
  1681. keyLen = keyRefLen;
  1682. key = static_cast<char*>(std::realloc(key, keyLen + 1));
  1683. }
  1684. DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, key, keyLen + 1, kCFStringEncodingASCII));
  1685. if (! fPlugin.wantStateKey(key))
  1686. continue;
  1687. const CFIndex valueRefLen = CFStringGetLength(valueRef);
  1688. if (valueLen < valueRefLen)
  1689. {
  1690. valueLen = valueRefLen;
  1691. value = static_cast<char*>(std::realloc(value, valueLen + 1));
  1692. }
  1693. DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8));
  1694. const String dkey(key);
  1695. fStateMap[dkey] = value;
  1696. fPlugin.setState(key, value);
  1697. for (uint32_t j=0; j<fStateCount; ++j)
  1698. {
  1699. if (fPlugin.getStateKey(j) == key)
  1700. {
  1701. if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0)
  1702. notifyListeners('DPFs', kAudioUnitScope_Global, j);
  1703. break;
  1704. }
  1705. }
  1706. }
  1707. std::free(key);
  1708. std::free(value);
  1709. }
  1710. #endif
  1711. CFArrayRef paramsRef = nullptr;
  1712. if (CFDictionaryGetValueIfPresent(data, CFSTR("params"), reinterpret_cast<const void**>(&paramsRef))
  1713. && CFGetTypeID(paramsRef) == CFArrayGetTypeID())
  1714. {
  1715. const CFIndex numParams = CFArrayGetCount(paramsRef);
  1716. char* symbol = nullptr;
  1717. CFIndex symbolLen = -1;
  1718. for (CFIndex i=0; i<numParams; ++i)
  1719. {
  1720. const CFDictionaryRef param = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(paramsRef, i));
  1721. DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(param) == CFDictionaryGetTypeID());
  1722. DISTRHO_SAFE_ASSERT_BREAK(CFDictionaryGetCount(param) == 1);
  1723. CFStringRef keyRef = nullptr;
  1724. CFNumberRef valueRef = nullptr;
  1725. CFDictionaryGetKeysAndValues(param,
  1726. reinterpret_cast<const void**>(&keyRef),
  1727. reinterpret_cast<const void**>(&valueRef));
  1728. DISTRHO_SAFE_ASSERT_BREAK(keyRef != nullptr && CFGetTypeID(keyRef) == CFStringGetTypeID());
  1729. DISTRHO_SAFE_ASSERT_BREAK(valueRef != nullptr && CFGetTypeID(valueRef) == CFNumberGetTypeID());
  1730. float value = 0.f;
  1731. DISTRHO_SAFE_ASSERT_BREAK(CFNumberGetValue(valueRef, kCFNumberFloat32Type, &value));
  1732. const CFIndex keyRefLen = CFStringGetLength(keyRef);
  1733. if (symbolLen < keyRefLen)
  1734. {
  1735. symbolLen = keyRefLen;
  1736. symbol = static_cast<char*>(std::realloc(symbol, symbolLen + 1));
  1737. }
  1738. DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, symbol, symbolLen + 1, kCFStringEncodingASCII));
  1739. for (uint32_t j=0; j<fParameterCount; ++j)
  1740. {
  1741. if (fPlugin.isParameterOutputOrTrigger(j))
  1742. continue;
  1743. if (fPlugin.getParameterSymbol(j) != symbol)
  1744. continue;
  1745. fLastParameterValues[j] = value;
  1746. fPlugin.setParameterValue(j, value);
  1747. notifyListeners('DPFp', kAudioUnitScope_Global, j);
  1748. if (fBypassParameterIndex == j)
  1749. notifyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
  1750. break;
  1751. }
  1752. }
  1753. std::free(symbol);
  1754. }
  1755. }
  1756. // ----------------------------------------------------------------------------------------------------------------
  1757. // DPF callbacks
  1758. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  1759. bool writeMidi(const MidiEvent& midiEvent)
  1760. {
  1761. DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("MIDI output unsupported", fMidiOutput.midiOutputCallback != nullptr, false);
  1762. if (midiEvent.size > sizeof(MIDIPacket::data))
  1763. return true;
  1764. if (fMidiOutputPackets.numPackets == kMaxMidiEvents)
  1765. return false;
  1766. const uint8_t* const midiData = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data;
  1767. MIDIPacket& packet(fMidiOutputPackets.packets[fMidiOutputPackets.numPackets++]);
  1768. packet.timeStamp = midiEvent.frame;
  1769. packet.length = midiEvent.size;
  1770. std::memcpy(packet.data, midiData, midiEvent.size);
  1771. return true;
  1772. }
  1773. static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent)
  1774. {
  1775. return static_cast<PluginAU*>(ptr)->writeMidi(midiEvent);
  1776. }
  1777. #endif
  1778. #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
  1779. bool requestParameterValueChange(const uint32_t index, const float value)
  1780. {
  1781. AudioUnitEvent event;
  1782. std::memset(&event, 0, sizeof(event));
  1783. event.mEventType = kAudioUnitEvent_ParameterValueChange;
  1784. event.mArgument.mParameter.mAudioUnit = fComponent;
  1785. event.mArgument.mParameter.mParameterID = index;
  1786. event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
  1787. fLastParameterValues[index] = value;
  1788. AUEventListenerNotify(NULL, NULL, &event);
  1789. notifyListeners('DPFp', kAudioUnitScope_Global, index);
  1790. return true;
  1791. }
  1792. static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value)
  1793. {
  1794. return static_cast<PluginAU*>(ptr)->requestParameterValueChange(index, value);
  1795. }
  1796. #endif
  1797. #if DISTRHO_PLUGIN_WANT_STATE
  1798. bool updateState(const char* const key, const char* const newValue)
  1799. {
  1800. fPlugin.setState(key, newValue);
  1801. for (uint32_t i=0; i<fStateCount; ++i)
  1802. {
  1803. if (fPlugin.getStateKey(i) == key)
  1804. {
  1805. const String dkey(key);
  1806. fStateMap[dkey] = newValue;
  1807. if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0)
  1808. notifyListeners('DPFs', kAudioUnitScope_Global, i);
  1809. return true;
  1810. }
  1811. }
  1812. d_stderr("Failed to find plugin state with key \"%s\"", key);
  1813. return false;
  1814. }
  1815. static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const newValue)
  1816. {
  1817. return static_cast<PluginAU*>(ptr)->updateState(key, newValue);
  1818. }
  1819. #endif
  1820. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginAU)
  1821. };
  1822. // --------------------------------------------------------------------------------------------------------------------
  1823. struct AudioComponentPlugInInstance {
  1824. AudioComponentPlugInInterface acpi;
  1825. PluginAU* plugin;
  1826. AudioComponentPlugInInstance() noexcept
  1827. : acpi(),
  1828. plugin(nullptr)
  1829. {
  1830. std::memset(&acpi, 0, sizeof(acpi));
  1831. acpi.Open = Open;
  1832. acpi.Close = Close;
  1833. acpi.Lookup = Lookup;
  1834. acpi.reserved = nullptr;
  1835. }
  1836. ~AudioComponentPlugInInstance()
  1837. {
  1838. delete plugin;
  1839. }
  1840. static OSStatus Open(void* const self, const AudioUnit component)
  1841. {
  1842. static_cast<AudioComponentPlugInInstance*>(self)->plugin = new PluginAU(component);
  1843. return noErr;
  1844. }
  1845. static OSStatus Close(void* const self)
  1846. {
  1847. delete static_cast<AudioComponentPlugInInstance*>(self);
  1848. return noErr;
  1849. }
  1850. static AudioComponentMethod Lookup(const SInt16 selector)
  1851. {
  1852. d_debug("AudioComponentPlugInInstance::Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector));
  1853. switch (selector)
  1854. {
  1855. case kAudioUnitInitializeSelect:
  1856. return reinterpret_cast<AudioComponentMethod>(Initialize);
  1857. case kAudioUnitUninitializeSelect:
  1858. return reinterpret_cast<AudioComponentMethod>(Uninitialize);
  1859. case kAudioUnitGetPropertyInfoSelect:
  1860. return reinterpret_cast<AudioComponentMethod>(GetPropertyInfo);
  1861. case kAudioUnitGetPropertySelect:
  1862. return reinterpret_cast<AudioComponentMethod>(GetProperty);
  1863. case kAudioUnitSetPropertySelect:
  1864. return reinterpret_cast<AudioComponentMethod>(SetProperty);
  1865. case kAudioUnitAddPropertyListenerSelect:
  1866. return reinterpret_cast<AudioComponentMethod>(AddPropertyListener);
  1867. case kAudioUnitRemovePropertyListenerSelect:
  1868. return reinterpret_cast<AudioComponentMethod>(RemovePropertyListener);
  1869. case kAudioUnitRemovePropertyListenerWithUserDataSelect:
  1870. return reinterpret_cast<AudioComponentMethod>(RemovePropertyListenerWithUserData);
  1871. case kAudioUnitAddRenderNotifySelect:
  1872. return reinterpret_cast<AudioComponentMethod>(AddRenderNotify);
  1873. case kAudioUnitRemoveRenderNotifySelect:
  1874. return reinterpret_cast<AudioComponentMethod>(RemoveRenderNotify);
  1875. case kAudioUnitGetParameterSelect:
  1876. return reinterpret_cast<AudioComponentMethod>(GetParameter);
  1877. case kAudioUnitSetParameterSelect:
  1878. return reinterpret_cast<AudioComponentMethod>(SetParameter);
  1879. case kAudioUnitScheduleParametersSelect:
  1880. return reinterpret_cast<AudioComponentMethod>(ScheduleParameters);
  1881. case kAudioUnitRenderSelect:
  1882. return reinterpret_cast<AudioComponentMethod>(Render);
  1883. /*
  1884. case kAudioUnitComplexRenderSelect:
  1885. return reinterpret_cast<AudioComponentMethod>(ComplexRender);
  1886. */
  1887. case kAudioUnitResetSelect:
  1888. return reinterpret_cast<AudioComponentMethod>(Reset);
  1889. /*
  1890. case kAudioUnitProcessSelect:
  1891. return reinterpret_cast<AudioComponentMethod>(Process);
  1892. case kAudioUnitProcessMultipleSelect:
  1893. return reinterpret_cast<AudioComponentMethod>(ProcessMultiple);
  1894. */
  1895. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1896. case kMusicDeviceMIDIEventSelect:
  1897. return reinterpret_cast<AudioComponentMethod>(MIDIEvent);
  1898. case kMusicDeviceSysExSelect:
  1899. return reinterpret_cast<AudioComponentMethod>(SysEx);
  1900. #else
  1901. case kMusicDeviceMIDIEventSelect:
  1902. case kMusicDeviceSysExSelect:
  1903. return nullptr;
  1904. #endif
  1905. }
  1906. d_stdout("TODO Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector));
  1907. return nullptr;
  1908. }
  1909. static OSStatus Initialize(AudioComponentPlugInInstance* const self)
  1910. {
  1911. d_debug("AudioComponentPlugInInstance::Initialize(%p)", self);
  1912. return self->plugin->auInitialize();
  1913. }
  1914. static OSStatus Uninitialize(AudioComponentPlugInInstance* const self)
  1915. {
  1916. d_debug("AudioComponentPlugInInstance::Uninitialize(%p)", self);
  1917. return self->plugin->auUninitialize();
  1918. }
  1919. static OSStatus GetPropertyInfo(AudioComponentPlugInInstance* const self,
  1920. const AudioUnitPropertyID inProp,
  1921. const AudioUnitScope inScope,
  1922. const AudioUnitElement inElement,
  1923. UInt32* const outDataSize,
  1924. Boolean* const outWritable)
  1925. {
  1926. d_debug("AudioComponentPlugInInstance::GetPropertyInfo(%p, %2d:%s, %d:%s, %d, ...)",
  1927. self, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);
  1928. UInt32 dataSize = 0;
  1929. Boolean writable = false;
  1930. const OSStatus res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, dataSize, writable);
  1931. if (outDataSize != nullptr)
  1932. *outDataSize = dataSize;
  1933. if (outWritable != nullptr)
  1934. *outWritable = writable;
  1935. return res;
  1936. }
  1937. static OSStatus GetProperty(AudioComponentPlugInInstance* const self,
  1938. const AudioUnitPropertyID inProp,
  1939. const AudioUnitScope inScope,
  1940. const AudioUnitElement inElement,
  1941. void* const outData,
  1942. UInt32* const ioDataSize)
  1943. {
  1944. d_debug("AudioComponentPlugInInstance::GetProperty (%p, %2d:%s, %d:%s, %d, ...)",
  1945. self, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);
  1946. DISTRHO_SAFE_ASSERT_RETURN(ioDataSize != nullptr, kAudio_ParamError);
  1947. Boolean writable;
  1948. UInt32 outDataSize = 0;
  1949. OSStatus res;
  1950. if (outData == nullptr)
  1951. {
  1952. res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, outDataSize, writable);
  1953. *ioDataSize = outDataSize;
  1954. return res;
  1955. }
  1956. const UInt32 inDataSize = *ioDataSize;
  1957. if (inDataSize == 0)
  1958. return kAudio_ParamError;
  1959. res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, outDataSize, writable);
  1960. if (res != noErr)
  1961. return res;
  1962. void* outBuffer;
  1963. uint8_t* tmpBuffer;
  1964. if (inDataSize < outDataSize)
  1965. {
  1966. tmpBuffer = new uint8_t[outDataSize];
  1967. outBuffer = tmpBuffer;
  1968. }
  1969. else
  1970. {
  1971. tmpBuffer = nullptr;
  1972. outBuffer = outData;
  1973. }
  1974. res = self->plugin->auGetProperty(inProp, inScope, inElement, outBuffer);
  1975. if (res != noErr)
  1976. {
  1977. *ioDataSize = 0;
  1978. return res;
  1979. }
  1980. if (tmpBuffer != nullptr)
  1981. {
  1982. memcpy(outData, tmpBuffer, inDataSize);
  1983. delete[] tmpBuffer;
  1984. }
  1985. else
  1986. {
  1987. *ioDataSize = outDataSize;
  1988. }
  1989. return noErr;
  1990. }
  1991. static OSStatus SetProperty(AudioComponentPlugInInstance* const self,
  1992. const AudioUnitPropertyID inProp,
  1993. const AudioUnitScope inScope,
  1994. const AudioUnitElement inElement,
  1995. const void* const inData,
  1996. const UInt32 inDataSize)
  1997. {
  1998. d_debug("AudioComponentPlugInInstance::SetProperty(%p, %d:%s, %d:%s, %d, %p, %u)",
  1999. self, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize);
  2000. return self->plugin->auSetProperty(inProp, inScope, inElement, inData, inDataSize);
  2001. }
  2002. static OSStatus AddPropertyListener(AudioComponentPlugInInstance* const self,
  2003. const AudioUnitPropertyID prop,
  2004. const AudioUnitPropertyListenerProc proc,
  2005. void* const userData)
  2006. {
  2007. d_debug("AudioComponentPlugInInstance::AddPropertyListener(%p, %d:%s, %p, %p)",
  2008. self, prop, AudioUnitPropertyID2Str(prop), proc, userData);
  2009. return self->plugin->auAddPropertyListener(prop, proc, userData);
  2010. }
  2011. static OSStatus RemovePropertyListener(AudioComponentPlugInInstance* const self,
  2012. const AudioUnitPropertyID prop,
  2013. const AudioUnitPropertyListenerProc proc)
  2014. {
  2015. d_debug("AudioComponentPlugInInstance::RemovePropertyListener(%p, %d:%s, %p)",
  2016. self, prop, AudioUnitPropertyID2Str(prop), proc);
  2017. return self->plugin->auRemovePropertyListener(prop, proc);
  2018. }
  2019. static OSStatus RemovePropertyListenerWithUserData(AudioComponentPlugInInstance* const self,
  2020. const AudioUnitPropertyID prop,
  2021. const AudioUnitPropertyListenerProc proc,
  2022. void* const userData)
  2023. {
  2024. d_debug("AudioComponentPlugInInstance::RemovePropertyListenerWithUserData(%p, %d:%s, %p, %p)",
  2025. self, prop, AudioUnitPropertyID2Str(prop), proc, userData);
  2026. return self->plugin->auRemovePropertyListenerWithUserData(prop, proc, userData);
  2027. }
  2028. static OSStatus AddRenderNotify(AudioComponentPlugInInstance* const self,
  2029. const AURenderCallback proc,
  2030. void* const userData)
  2031. {
  2032. d_debug("AudioComponentPlugInInstance::AddRenderNotify(%p, %p, %p)", self, proc, userData);
  2033. return self->plugin->auAddRenderNotify(proc, userData);
  2034. }
  2035. static OSStatus RemoveRenderNotify(AudioComponentPlugInInstance* const self,
  2036. const AURenderCallback proc,
  2037. void* const userData)
  2038. {
  2039. d_debug("AudioComponentPlugInInstance::RemoveRenderNotify(%p, %p, %p)", self, proc, userData);
  2040. return self->plugin->auRemoveRenderNotify(proc, userData);
  2041. }
  2042. static OSStatus GetParameter(AudioComponentPlugInInstance* const self,
  2043. const AudioUnitParameterID param,
  2044. const AudioUnitScope scope,
  2045. const AudioUnitElement elem,
  2046. AudioUnitParameterValue* const value)
  2047. {
  2048. d_debug("AudioComponentPlugInInstance::GetParameter(%p, %d, %d:%s, %d, %p)",
  2049. self, param, scope, AudioUnitScope2Str(scope), elem, value);
  2050. return self->plugin->auGetParameter(param, scope, elem, value);
  2051. }
  2052. static OSStatus SetParameter(AudioComponentPlugInInstance* const self,
  2053. const AudioUnitParameterID param,
  2054. const AudioUnitScope scope,
  2055. const AudioUnitElement elem,
  2056. const AudioUnitParameterValue value,
  2057. const UInt32 bufferOffset)
  2058. {
  2059. d_debug("AudioComponentPlugInInstance::SetParameter(%p, %d %d:%s, %d, %f, %u)",
  2060. self, param, scope, AudioUnitScope2Str(scope), elem, value, bufferOffset);
  2061. return self->plugin->auSetParameter(param, scope, elem, value, bufferOffset);
  2062. }
  2063. static OSStatus ScheduleParameters(AudioComponentPlugInInstance* const self,
  2064. const AudioUnitParameterEvent* const events,
  2065. const UInt32 numEvents)
  2066. {
  2067. d_debug("AudioComponentPlugInInstance::ScheduleParameters(%p, %p, %u)", self, events, numEvents);
  2068. return self->plugin->auScheduleParameters(events, numEvents);
  2069. }
  2070. static OSStatus Reset(AudioComponentPlugInInstance* const self,
  2071. const AudioUnitScope scope,
  2072. const AudioUnitElement elem)
  2073. {
  2074. d_debug("AudioComponentPlugInInstance::Reset(%p, %d:%s, %d)", self, scope, AudioUnitScope2Str(scope), elem);
  2075. return self->plugin->auReset(scope, elem);
  2076. }
  2077. static OSStatus Render(AudioComponentPlugInInstance* const self,
  2078. AudioUnitRenderActionFlags* ioActionFlags,
  2079. const AudioTimeStamp* const inTimeStamp,
  2080. const UInt32 inOutputBusNumber,
  2081. const UInt32 inNumberFrames,
  2082. AudioBufferList* const ioData)
  2083. {
  2084. DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError);
  2085. DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError);
  2086. return self->plugin->auRender(ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, *ioData);
  2087. }
  2088. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  2089. static OSStatus MIDIEvent(AudioComponentPlugInInstance* const self,
  2090. const UInt32 inStatus,
  2091. const UInt32 inData1,
  2092. const UInt32 inData2,
  2093. const UInt32 inOffsetSampleFrame)
  2094. {
  2095. return self->plugin->auMIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame);
  2096. }
  2097. static OSStatus SysEx(AudioComponentPlugInInstance* const self, const UInt8* const inData, const UInt32 inLength)
  2098. {
  2099. return self->plugin->auSysEx(inData, inLength);
  2100. }
  2101. #endif
  2102. DISTRHO_DECLARE_NON_COPYABLE(AudioComponentPlugInInstance)
  2103. };
  2104. // --------------------------------------------------------------------------------------------------------------------
  2105. END_NAMESPACE_DISTRHO
  2106. DISTRHO_PLUGIN_EXPORT
  2107. void* PluginAUFactory(const AudioComponentDescription* const desc)
  2108. {
  2109. USE_NAMESPACE_DISTRHO
  2110. DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentType == kType, desc->componentType, kType, nullptr);
  2111. DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentSubType == kSubType, desc->componentSubType, kSubType, nullptr);
  2112. DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentManufacturer == kManufacturer, desc->componentManufacturer, kManufacturer, nullptr);
  2113. if (d_nextBufferSize == 0)
  2114. d_nextBufferSize = 1156;
  2115. if (d_isZero(d_nextSampleRate))
  2116. d_nextSampleRate = 44100.0;
  2117. if (d_nextBundlePath == nullptr)
  2118. {
  2119. static String bundlePath;
  2120. String tmpPath(getBinaryFilename());
  2121. tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
  2122. tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
  2123. if (tmpPath.endsWith(DISTRHO_OS_SEP_STR "Contents"))
  2124. {
  2125. tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
  2126. bundlePath = tmpPath;
  2127. }
  2128. else
  2129. {
  2130. bundlePath = "error";
  2131. }
  2132. d_nextBundlePath = bundlePath.buffer();
  2133. }
  2134. d_nextCanRequestParameterValueChanges = true;
  2135. return new AudioComponentPlugInInstance();
  2136. }
  2137. // --------------------------------------------------------------------------------------------------------------------