Audio plugin host https://kx.studio/carla
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.

1070 lines
39KB

  1. // SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "CarlaPluginInternal.hpp"
  4. #include "CarlaEngine.hpp"
  5. #ifdef CARLA_OS_MAC
  6. # include "CarlaBackendUtils.hpp"
  7. # include "CarlaPluginUI.hpp"
  8. # include "CarlaMacUtils.hpp"
  9. # include <AudioToolbox/AudioUnit.h>
  10. # include <Foundation/Foundation.h>
  11. #endif
  12. CARLA_BACKEND_START_NAMESPACE
  13. // --------------------------------------------------------------------------------------------------------------------
  14. #ifdef CARLA_OS_MAC
  15. typedef AudioComponentPlugInInterface* (*FactoryFn)(const AudioComponentDescription*);
  16. typedef OSStatus (*InitializeFn)(void*);
  17. typedef OSStatus (*UninitializeFn)(void*);
  18. typedef OSStatus (*GetPropertyInfoFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, UInt32*, Boolean*);
  19. typedef OSStatus (*GetPropertyFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, void*, UInt32*);
  20. typedef OSStatus (*SetPropertyFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, const void*, UInt32);
  21. typedef OSStatus (*GetParameterFn)(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, AudioUnitParameterValue*);
  22. typedef OSStatus (*SetParameterFn)(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, AudioUnitParameterValue, UInt32);
  23. typedef OSStatus (*ScheduleParametersFn)(void*, const AudioUnitParameterEvent*, UInt32);
  24. typedef OSStatus (*ResetFn)(void*, AudioUnitScope, AudioUnitElement);
  25. typedef OSStatus (*RenderFn)(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32, UInt32, AudioBufferList*);
  26. typedef OSStatus (*MIDIEventFn)(void*, UInt32, UInt32, UInt32, UInt32);
  27. static constexpr FourCharCode getFourCharCodeFromString(const char str[4])
  28. {
  29. return (str[0] << 24) + (str[1] << 16) + (str[2] << 8) + str[3];
  30. }
  31. class CarlaPluginAU : public CarlaPlugin,
  32. private CarlaPluginUI::Callback
  33. {
  34. public:
  35. CarlaPluginAU(CarlaEngine* const engine, const uint id)
  36. : CarlaPlugin(engine, id),
  37. fInterface(nullptr),
  38. fAudioBufferData(nullptr)
  39. {
  40. carla_stdout("CarlaPluginAU::CarlaPluginAU(%p, %i)", engine, id);
  41. }
  42. ~CarlaPluginAU() override
  43. {
  44. carla_stdout("CarlaPluginAU::~CarlaPluginAU()");
  45. // close UI
  46. showCustomUI(false);
  47. pData->singleMutex.lock();
  48. pData->masterMutex.lock();
  49. if (pData->client != nullptr && pData->client->isActive())
  50. pData->client->deactivate(true);
  51. if (pData->active)
  52. {
  53. deactivate();
  54. pData->active = false;
  55. }
  56. if (fInterface != nullptr)
  57. {
  58. fInterface->Close(fInterface);
  59. fInterface = nullptr;
  60. }
  61. if (fAudioBufferData != nullptr)
  62. {
  63. std::free(fAudioBufferData);
  64. fAudioBufferData = nullptr;
  65. }
  66. // if (fLastChunk != nullptr)
  67. // {
  68. // std::free(fLastChunk);
  69. // fLastChunk = nullptr;
  70. // }
  71. clearBuffers();
  72. }
  73. // -------------------------------------------------------------------
  74. // Information (base)
  75. PluginType getType() const noexcept override
  76. {
  77. return PLUGIN_AU;
  78. }
  79. PluginCategory getCategory() const noexcept override
  80. {
  81. // TODO
  82. return PLUGIN_CATEGORY_NONE;
  83. }
  84. uint32_t getLatencyInFrames() const noexcept override
  85. {
  86. // TODO
  87. return 0;
  88. }
  89. // -------------------------------------------------------------------
  90. // Information (count)
  91. // -------------------------------------------------------------------
  92. // Information (current data)
  93. // -------------------------------------------------------------------
  94. // Information (per-plugin data)
  95. uint getOptionsAvailable() const noexcept override
  96. {
  97. // TODO
  98. return 0x0;
  99. }
  100. float getParameterValue(const uint32_t parameterId) const noexcept override
  101. {
  102. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr, 0.f);
  103. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, 0.f);
  104. const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
  105. AudioUnitParameterValue value = 0.f;
  106. if (fFunctions.getParameter(fInterface, paramId, kAudioUnitScope_Global, 0, &value) == noErr)
  107. return value;
  108. return 0.f;
  109. }
  110. bool getLabel(char* const strBuf) const noexcept override
  111. {
  112. std::strncpy(strBuf, fLabel.buffer(), STR_MAX);
  113. return true;
  114. }
  115. bool getMaker(char* const strBuf) const noexcept override
  116. {
  117. std::strncpy(strBuf, fMaker.buffer(), STR_MAX);
  118. return true;
  119. }
  120. bool getRealName(char* const strBuf) const noexcept override
  121. {
  122. std::strncpy(strBuf, fName.buffer(), STR_MAX);
  123. return true;
  124. }
  125. bool getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override
  126. {
  127. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr, false);
  128. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false);
  129. const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
  130. AudioUnitParameterInfo info = {};
  131. UInt32 outDataSize = sizeof(AudioUnitParameterInfo);
  132. if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramId, &info, &outDataSize) == noErr)
  133. {
  134. if (info.flags & kAudioUnitParameterFlag_HasCFNameString)
  135. {
  136. *strBuf = '\0';
  137. if (! CFStringGetCString(info.cfNameString, strBuf, std::min<int>(STR_MAX, CFStringGetLength(info.cfNameString) + 1), kCFStringEncodingUTF8))
  138. {
  139. carla_stdout("CFStringGetCString fail '%s'", info.name);
  140. std::strncpy(strBuf, info.name, STR_MAX);
  141. }
  142. else
  143. {
  144. carla_stdout("CFStringGetCString ok '%s' '%s'", info.name, strBuf);
  145. std::strncpy(strBuf, info.name, STR_MAX);
  146. }
  147. if (info.flags & kAudioUnitParameterFlag_CFNameRelease)
  148. CFRelease(info.cfNameString);
  149. return true;
  150. }
  151. carla_stdout("CFStringGetCString not used '%s'", info.name);
  152. std::strncpy(strBuf, info.name, STR_MAX);
  153. return true;
  154. }
  155. carla_safe_assert("fFunctions.getProperty(...)", __FILE__, __LINE__);
  156. return CarlaPlugin::getParameterName(parameterId, strBuf);
  157. }
  158. // -------------------------------------------------------------------
  159. // Set data (plugin-specific stuff)
  160. void setParameterValue(const uint32_t parameterId, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept override
  161. {
  162. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  163. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
  164. const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
  165. const float fixedValue = pData->param.getFixedValue(parameterId, value);
  166. fFunctions.setParameter(fInterface, paramId, kAudioUnitScope_Global, 0, value, 0);
  167. CarlaPlugin::setParameterValue(parameterId, fixedValue, sendGui, sendOsc, sendCallback);
  168. }
  169. void setParameterValueRT(const uint32_t parameterId, const float value, const uint32_t frameOffset, const bool sendCallbackLater) noexcept override
  170. {
  171. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  172. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
  173. const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
  174. const float fixedValue = pData->param.getFixedValue(parameterId, value);
  175. // TODO use scheduled events
  176. fFunctions.setParameter(fInterface, paramId, kAudioUnitScope_Global, 0, value, frameOffset);
  177. CarlaPlugin::setParameterValueRT(parameterId, fixedValue, frameOffset, sendCallbackLater);
  178. }
  179. // ----------------------------------------------------------------------------------------------------------------
  180. // Plugin state
  181. void reload() override
  182. {
  183. CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,);
  184. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  185. carla_debug("CarlaPluginAU::reload() - start");
  186. const EngineProcessMode processMode = pData->engine->getProccessMode();
  187. // Safely disable plugin for reload
  188. const ScopedDisabler sd(this);
  189. if (pData->active)
  190. deactivate();
  191. clearBuffers();
  192. uint32_t audioIns, audioOuts, parametersIns, parametersOuts;
  193. audioIns = audioOuts = parametersIns = parametersOuts = 0;
  194. bool needsCtrlIn, needsCtrlOut, hasMidiIn, hasMidiOut;
  195. needsCtrlIn = needsCtrlOut = hasMidiIn = hasMidiOut = false;
  196. CarlaString portName;
  197. const uint portNameSize = pData->engine->getMaxPortNameSize();
  198. UInt32 outDataSize;
  199. Boolean outWritable = false;
  200. // audio ports
  201. outDataSize = 0;
  202. if (fFunctions.getPropertyInfo(fInterface,
  203. kAudioUnitProperty_SupportedNumChannels,
  204. kAudioUnitScope_Global,
  205. 0, &outDataSize, &outWritable) == noErr
  206. /*
  207. && outDataSize != 0
  208. && outDataSize % sizeof(AUChannelInfo) == 0
  209. */
  210. )
  211. {
  212. const uint32_t numChannels = outDataSize / sizeof(AUChannelInfo);
  213. AUChannelInfo* const channelInfo = new AUChannelInfo[numChannels];
  214. carla_stdout("kAudioUnitProperty_SupportedNumChannels returns %u configs", numChannels);
  215. if (fFunctions.getProperty(fInterface,
  216. kAudioUnitProperty_SupportedNumChannels,
  217. kAudioUnitScope_Global,
  218. 0, channelInfo, &outDataSize) == noErr
  219. && outDataSize == numChannels * sizeof(AUChannelInfo))
  220. {
  221. AUChannelInfo* highestInfo = &channelInfo[0];
  222. carla_stdout("getProperty returns {%u,%u}... config",
  223. channelInfo[0].inChannels,
  224. channelInfo[0].outChannels);
  225. for (uint32_t i=1; i<numChannels; ++i)
  226. {
  227. if (channelInfo[i].inChannels > highestInfo->inChannels
  228. && channelInfo[i].outChannels > highestInfo->outChannels)
  229. {
  230. highestInfo = &channelInfo[i];
  231. }
  232. }
  233. audioIns = highestInfo->inChannels;
  234. audioOuts = highestInfo->outChannels;
  235. }
  236. else
  237. {
  238. carla_stdout("getProperty failed");
  239. }
  240. delete[] channelInfo;
  241. }
  242. else
  243. {
  244. carla_stdout("kAudioUnitProperty_SupportedNumChannels returns no configs, assume stereo");
  245. audioIns = audioOuts = 2;
  246. }
  247. if (audioIns > 0)
  248. {
  249. pData->audioIn.createNew(audioIns);
  250. }
  251. if (audioOuts > 0)
  252. {
  253. pData->audioOut.createNew(audioOuts);
  254. needsCtrlIn = true;
  255. }
  256. std::free(fAudioBufferData);
  257. if (const uint32_t numBuffers = std::max(audioIns, audioOuts))
  258. {
  259. fAudioBufferData = static_cast<AudioBufferList*>(std::malloc(sizeof(uint32_t) + sizeof(AudioBuffer) * numBuffers));
  260. fAudioBufferData->mNumberBuffers = numBuffers;
  261. for (uint32_t i = 0; i < numBuffers; ++i)
  262. fAudioBufferData->mBuffers[i].mNumberChannels = 1;
  263. }
  264. else
  265. {
  266. fAudioBufferData = static_cast<AudioBufferList*>(std::malloc(sizeof(uint32_t)));
  267. fAudioBufferData->mNumberBuffers = 0;
  268. }
  269. // Audio Ins
  270. for (uint32_t i=0; i < audioIns; ++i)
  271. {
  272. portName.clear();
  273. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  274. {
  275. portName = pData->name;
  276. portName += ":";
  277. }
  278. if (audioIns > 1)
  279. {
  280. portName += "input_";
  281. portName += CarlaString(i + 1);
  282. }
  283. else
  284. portName += "input";
  285. portName.truncate(portNameSize);
  286. pData->audioIn.ports[i].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, true, i);
  287. pData->audioIn.ports[i].rindex = i;
  288. }
  289. // Audio Outs
  290. for (uint32_t i=0; i < audioOuts; ++i)
  291. {
  292. portName.clear();
  293. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  294. {
  295. portName = pData->name;
  296. portName += ":";
  297. }
  298. if (audioOuts > 1)
  299. {
  300. portName += "output_";
  301. portName += CarlaString(i + 1);
  302. }
  303. else
  304. portName += "output";
  305. portName.truncate(portNameSize);
  306. pData->audioOut.ports[i].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false, i);
  307. pData->audioOut.ports[i].rindex = i;
  308. }
  309. // parameters
  310. outDataSize = 0;
  311. if (fFunctions.getPropertyInfo(fInterface,
  312. kAudioUnitProperty_ParameterList,
  313. kAudioUnitScope_Global,
  314. 0, &outDataSize, &outWritable) == noErr
  315. && outDataSize != 0
  316. && outDataSize % sizeof(AudioUnitParameterID) == 0)
  317. {
  318. const uint32_t numParams = outDataSize / sizeof(AudioUnitParameterID);
  319. AudioUnitParameterID* const paramIds = new AudioUnitParameterID[numParams];
  320. if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, paramIds, &outDataSize) == noErr && outDataSize == numParams * sizeof(AudioUnitParameterID))
  321. {
  322. pData->param.createNew(numParams, false);
  323. AudioUnitParameterInfo info;
  324. float min, max, def, step, stepSmall, stepLarge;
  325. for (uint32_t i=0; i<numParams; ++i)
  326. {
  327. carla_zeroStruct(info);
  328. outDataSize = 0;
  329. if (fFunctions.getPropertyInfo(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramIds[i], &outDataSize, &outWritable) != noErr)
  330. break;
  331. if (outDataSize != sizeof(AudioUnitParameterInfo))
  332. break;
  333. if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramIds[i], &info, &outDataSize) != noErr)
  334. break;
  335. if (info.flags & kAudioUnitParameterFlag_CFNameRelease)
  336. CFRelease(info.cfNameString);
  337. pData->param.data[i].index = static_cast<int32_t>(i);
  338. pData->param.data[i].rindex = static_cast<int32_t>(paramIds[i]);
  339. if (info.flags & kAudioUnitParameterFlag_IsWritable)
  340. {
  341. pData->param.data[i].type = PARAMETER_INPUT;
  342. needsCtrlIn = true;
  343. }
  344. else if (info.flags & (kAudioUnitParameterFlag_IsReadable|kAudioUnitParameterFlag_MeterReadOnly))
  345. {
  346. pData->param.data[i].type = PARAMETER_OUTPUT;
  347. needsCtrlOut = true;
  348. }
  349. else
  350. {
  351. pData->param.data[i].type = PARAMETER_UNKNOWN;
  352. continue;
  353. }
  354. min = info.minValue;
  355. max = info.maxValue;
  356. def = info.defaultValue;
  357. if (min > max)
  358. max = min;
  359. if (carla_isEqual(min, max))
  360. {
  361. carla_stderr2("WARNING - Broken plugin parameter '%s': max == min", info.name);
  362. max = min + 0.1f;
  363. }
  364. if (def < min)
  365. def = min;
  366. else if (def > max)
  367. def = max;
  368. pData->param.data[i].hints |= PARAMETER_IS_ENABLED;
  369. if ((info.flags & kAudioUnitParameterFlag_NonRealTime) == 0)
  370. {
  371. pData->param.data[i].hints |= PARAMETER_IS_AUTOMATABLE;
  372. pData->param.data[i].hints |= PARAMETER_CAN_BE_CV_CONTROLLED;
  373. }
  374. if (info.unit == kAudioUnitParameterUnit_Boolean)
  375. {
  376. step = max - min;
  377. stepSmall = step;
  378. stepLarge = step;
  379. pData->param.data[i].hints |= PARAMETER_IS_BOOLEAN;
  380. }
  381. else if (info.unit == kAudioUnitParameterUnit_Indexed)
  382. {
  383. step = 1.0f;
  384. stepSmall = 1.0f;
  385. stepLarge = 10.0f;
  386. pData->param.data[i].hints |= PARAMETER_IS_INTEGER;
  387. }
  388. else
  389. {
  390. float range = max - min;
  391. step = range/100.0f;
  392. stepSmall = range/1000.0f;
  393. stepLarge = range/10.0f;
  394. }
  395. pData->param.ranges[i].min = min;
  396. pData->param.ranges[i].max = max;
  397. pData->param.ranges[i].def = def;
  398. pData->param.ranges[i].step = step;
  399. pData->param.ranges[i].stepSmall = stepSmall;
  400. pData->param.ranges[i].stepLarge = stepLarge;
  401. }
  402. }
  403. delete[] paramIds;
  404. }
  405. if (needsCtrlIn || hasMidiIn)
  406. {
  407. portName.clear();
  408. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  409. {
  410. portName = pData->name;
  411. portName += ":";
  412. }
  413. portName += "events-in";
  414. portName.truncate(portNameSize);
  415. pData->event.portIn = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, true, 0);
  416. #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
  417. pData->event.cvSourcePorts = pData->client->createCVSourcePorts();
  418. #endif
  419. }
  420. if (needsCtrlOut || hasMidiOut)
  421. {
  422. portName.clear();
  423. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  424. {
  425. portName = pData->name;
  426. portName += ":";
  427. }
  428. portName += "events-out";
  429. portName.truncate(portNameSize);
  430. pData->event.portOut = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, false, 0);
  431. }
  432. // plugin hints
  433. pData->hints = 0x0;
  434. if (audioOuts > 0 && (audioIns == audioOuts || audioIns == 1))
  435. pData->hints |= PLUGIN_CAN_DRYWET;
  436. if (audioOuts > 0)
  437. pData->hints |= PLUGIN_CAN_VOLUME;
  438. if (audioOuts >= 2 && audioOuts % 2 == 0)
  439. pData->hints |= PLUGIN_CAN_BALANCE;
  440. // extra plugin hints
  441. pData->extraHints = 0x0;
  442. bufferSizeChanged(pData->engine->getBufferSize());
  443. reloadPrograms(true);
  444. if (pData->active)
  445. activate();
  446. carla_debug("CarlaPluginAU::reload() - end");
  447. }
  448. // -------------------------------------------------------------------
  449. // Plugin processing
  450. void activate() noexcept override
  451. {
  452. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  453. AudioStreamBasicDescription streamFormat = {
  454. .mFormatID = kAudioFormatLinearPCM,
  455. .mBitsPerChannel = 32,
  456. .mBytesPerFrame = sizeof(float),
  457. .mBytesPerPacket = sizeof(float),
  458. .mFramesPerPacket = 1,
  459. .mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved,
  460. .mChannelsPerFrame = 0,
  461. .mSampleRate = pData->engine->getSampleRate(),
  462. };
  463. if (pData->audioIn.count != 0)
  464. {
  465. streamFormat.mChannelsPerFrame = pData->audioIn.count;
  466. CARLA_SAFE_ASSERT_RETURN(fFunctions.setProperty(fInterface,
  467. kAudioUnitProperty_StreamFormat,
  468. kAudioUnitScope_Input,
  469. 0, &streamFormat, sizeof(streamFormat)) == noErr,);
  470. }
  471. if (pData->audioOut.count != 0)
  472. {
  473. streamFormat.mChannelsPerFrame = pData->audioOut.count;
  474. CARLA_SAFE_ASSERT_RETURN(fFunctions.setProperty(fInterface,
  475. kAudioUnitProperty_StreamFormat,
  476. kAudioUnitScope_Output,
  477. 0, &streamFormat, sizeof(streamFormat)) == noErr,);
  478. }
  479. fFunctions.initialize(fInterface);
  480. }
  481. void deactivate() noexcept override
  482. {
  483. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  484. fFunctions.uninitialize(fInterface);
  485. }
  486. void process(const float* const* const audioIn,
  487. float** const audioOut,
  488. const float* const* const,
  489. float** const,
  490. const uint32_t frames) override
  491. {
  492. // ------------------------------------------------------------------------------------------------------------
  493. // Check if active
  494. if (! pData->active)
  495. {
  496. // disable any output sound
  497. for (uint32_t i=0; i < pData->audioOut.count; ++i)
  498. carla_zeroFloats(audioOut[i], frames);
  499. return;
  500. }
  501. // ------------------------------------------------------------------------------------------------------------
  502. // Check buffers
  503. CARLA_SAFE_ASSERT_RETURN(frames > 0,);
  504. if (pData->audioIn.count > 0)
  505. {
  506. CARLA_SAFE_ASSERT_RETURN(audioIn != nullptr,);
  507. }
  508. if (pData->audioOut.count > 0)
  509. {
  510. CARLA_SAFE_ASSERT_RETURN(audioOut != nullptr,);
  511. }
  512. // ------------------------------------------------------------------------------------------------------------
  513. // Try lock, silence otherwise
  514. if (pData->engine->isOffline())
  515. {
  516. pData->singleMutex.lock();
  517. }
  518. else if (! pData->singleMutex.tryLock())
  519. {
  520. for (uint32_t i=0; i < pData->audioOut.count; ++i)
  521. carla_zeroFloats(audioOut[i], frames);
  522. return;
  523. }
  524. // ------------------------------------------------------------------------------------------------------------
  525. // Check if needs reset
  526. if (pData->needsReset)
  527. {
  528. // TODO
  529. }
  530. // ------------------------------------------------------------------------------------------------------------
  531. // Event Input (main port)
  532. if (pData->event.portIn != nullptr)
  533. {
  534. // TODO
  535. }
  536. // ------------------------------------------------------------------------------------------------------------
  537. // Plugin processing
  538. const EngineTimeInfo timeInfo(pData->engine->getTimeInfo());
  539. AudioUnitRenderActionFlags actionFlags = kAudioUnitRenderAction_DoNotCheckRenderArgs;
  540. AudioTimeStamp timeStamp = {};
  541. timeStamp.mFlags = kAudioTimeStampSampleTimeValid;
  542. timeStamp.mSampleTime = timeInfo.frame;
  543. const UInt32 inBusNumber = 0;
  544. {
  545. uint32_t i = 0;
  546. for (; i < pData->audioOut.count; ++i)
  547. {
  548. fAudioBufferData->mBuffers[i].mData = audioOut[i];
  549. fAudioBufferData->mBuffers[i].mDataByteSize = sizeof(float) * frames;
  550. if (audioOut[i] != audioIn[i])
  551. std::memcpy(audioOut[i], audioIn[i], sizeof(float) * frames);
  552. }
  553. for (; i < pData->audioIn.count; ++i)
  554. {
  555. fAudioBufferData->mBuffers[i].mData = audioOut[i];
  556. fAudioBufferData->mBuffers[i].mDataByteSize = sizeof(float) * frames;
  557. }
  558. }
  559. fFunctions.render(fInterface, &actionFlags, &timeStamp, inBusNumber, frames, fAudioBufferData);
  560. // ------------------------------------------------------------------------------------------------------------
  561. pData->singleMutex.unlock();
  562. #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
  563. // ------------------------------------------------------------------------------------------------------------
  564. // Control Output
  565. // TODO
  566. #endif
  567. // ------------------------------------------------------------------------------------------------------------
  568. // Events/MIDI Output
  569. // TODO
  570. }
  571. // ----------------------------------------------------------------------------------------------------------------
  572. protected:
  573. void handlePluginUIClosed() override
  574. {
  575. carla_stdout("CarlaPluginAU::handlePluginUIClosed()");
  576. // TODO
  577. }
  578. void handlePluginUIResized(const uint width, const uint height) override
  579. {
  580. // TODO
  581. }
  582. // -------------------------------------------------------------------
  583. public:
  584. bool init(const CarlaPluginPtr plugin,
  585. const char* const filename,
  586. const char* const label,
  587. const char* const name,
  588. const uint options)
  589. {
  590. CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false);
  591. // ---------------------------------------------------------------
  592. // first checks
  593. if (pData->client != nullptr)
  594. {
  595. pData->engine->setLastError("Plugin client is already registered");
  596. return false;
  597. }
  598. if (filename == nullptr || filename[0] == '\0')
  599. {
  600. pData->engine->setLastError("null filename");
  601. return false;
  602. }
  603. // ---------------------------------------------------------------
  604. // load bundle information
  605. if (! fBundleLoader.load(filename))
  606. {
  607. pData->engine->setLastError("Failed to load AU bundle executable");
  608. return false;
  609. }
  610. const CFTypeRef componentsRef = CFBundleGetValueForInfoDictionaryKey(fBundleLoader.getRef(), CFSTR("AudioComponents"));
  611. if (componentsRef == nullptr || CFGetTypeID(componentsRef) != CFArrayGetTypeID())
  612. {
  613. pData->engine->setLastError("Not an AU component");
  614. return false;
  615. }
  616. // ---------------------------------------------------------------
  617. // find binary matching requested label
  618. CFStringRef componentName;
  619. AudioComponentDescription desc = {};
  620. FactoryFn factoryFn;
  621. const CFArrayRef components = static_cast<CFArrayRef>(componentsRef);
  622. for (uint32_t c = 0, count = CFArrayGetCount(components); c < count; ++c)
  623. {
  624. // reset
  625. desc.componentType = 0;
  626. const CFTypeRef componentRef = CFArrayGetValueAtIndex(components, c);
  627. CARLA_SAFE_ASSERT_CONTINUE(componentRef != nullptr);
  628. CARLA_SAFE_ASSERT_CONTINUE(CFGetTypeID(componentRef) == CFDictionaryGetTypeID());
  629. const CFDictionaryRef component = static_cast<CFDictionaryRef>(componentRef);
  630. componentName = nullptr;
  631. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("name"), (const void **)&componentName));
  632. CFStringRef componentFactoryFunction = nullptr;
  633. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("factoryFunction"), (const void **)&componentFactoryFunction));
  634. CFStringRef componentType = nullptr;
  635. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("type"), (const void **)&componentType));
  636. CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentType) == 4);
  637. CFStringRef componentSubType = nullptr;
  638. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("subtype"), (const void **)&componentSubType));
  639. CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentSubType) == 4);
  640. CFStringRef componentManufacturer = nullptr;
  641. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("manufacturer"), (const void **)&componentManufacturer));
  642. CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentManufacturer) == 4);
  643. factoryFn = fBundleLoader.getSymbol<FactoryFn>(componentFactoryFunction);
  644. CARLA_SAFE_ASSERT_CONTINUE(factoryFn != nullptr);
  645. char clabel[15] = {};
  646. CFStringGetCString(componentType, clabel, 5, kCFStringEncodingASCII);
  647. CFStringGetCString(componentSubType, clabel + 5, 5, kCFStringEncodingASCII);
  648. CFStringGetCString(componentManufacturer, clabel + 10, 5, kCFStringEncodingASCII);
  649. desc.componentType = getFourCharCodeFromString(clabel);
  650. desc.componentSubType = getFourCharCodeFromString(clabel + 5);
  651. desc.componentManufacturer = getFourCharCodeFromString(clabel + 10);
  652. CARLA_SAFE_ASSERT_CONTINUE(desc.componentType != 0);
  653. CARLA_SAFE_ASSERT_CONTINUE(desc.componentSubType != 0);
  654. CARLA_SAFE_ASSERT_CONTINUE(desc.componentManufacturer != 0);
  655. clabel[4] = clabel[9] = ',';
  656. if (label == nullptr || label[0] == '\0' || std::strcmp(label, clabel) == 0)
  657. break;
  658. }
  659. if (desc.componentType == 0)
  660. {
  661. pData->engine->setLastError("Failed to find request plugin in Component bundle");
  662. return false;
  663. }
  664. // ---------------------------------------------------------------
  665. // load binary
  666. fInterface = factoryFn(&desc);
  667. if (fInterface == nullptr)
  668. {
  669. pData->engine->setLastError("Component failed to create new interface");
  670. return false;
  671. }
  672. if (! fFunctions.init(fInterface))
  673. {
  674. pData->engine->setLastError("Component does not provide all necessary functions");
  675. fInterface = nullptr;
  676. return false;
  677. }
  678. if (fInterface->Open(fInterface, (AudioUnit)(void*)0x1) != noErr)
  679. {
  680. pData->engine->setLastError("Component failed to open");
  681. fInterface->Close(fInterface);
  682. fInterface = nullptr;
  683. return false;
  684. }
  685. // ---------------------------------------------------------------
  686. // get info
  687. const CFIndex componentNameLen = CFStringGetLength(componentName);
  688. char* const nameBuffer = new char[componentNameLen + 1];
  689. if (CFStringGetCString(componentName, nameBuffer, componentNameLen + 1, kCFStringEncodingUTF8))
  690. {
  691. if (char* const sep = std::strstr(nameBuffer, ": "))
  692. {
  693. sep[0] = sep[1] = '\0';
  694. fName = sep + 2;
  695. fMaker = nameBuffer;
  696. }
  697. else
  698. {
  699. fName = nameBuffer;
  700. fMaker = nameBuffer + componentNameLen;
  701. }
  702. }
  703. fLabel = label;
  704. pData->name = pData->engine->getUniquePluginName(name != nullptr && name[0] != '\0' ? name : fName.buffer());
  705. pData->filename = carla_strdup(filename);
  706. delete[] nameBuffer;
  707. // ---------------------------------------------------------------
  708. // register client
  709. pData->client = pData->engine->addClient(plugin);
  710. if (pData->client == nullptr || ! pData->client->isOk())
  711. {
  712. pData->engine->setLastError("Failed to register plugin client");
  713. return false;
  714. }
  715. // ------------------------------------------------------------------------------------------------------------
  716. // init component
  717. {
  718. const UInt32 bufferSize = pData->engine->getBufferSize();
  719. if (fFunctions.setProperty(fInterface,
  720. kAudioUnitProperty_MaximumFramesPerSlice,
  721. kAudioUnitScope_Global,
  722. 0, &bufferSize, sizeof(bufferSize)) != noErr)
  723. {
  724. pData->engine->setLastError("Failed to set Component maximum frames per slice");
  725. return false;
  726. }
  727. }
  728. {
  729. const Float64 sampleRate = pData->engine->getSampleRate();
  730. // input scope
  731. UInt32 outDataSize = 0;
  732. Boolean outWritable = false;
  733. if (fFunctions.getPropertyInfo(fInterface,
  734. kAudioUnitProperty_ElementCount,
  735. kAudioUnitScope_Input,
  736. 0, &outDataSize, &outWritable) == noErr
  737. && outDataSize == sizeof(UInt32))
  738. {
  739. UInt32 outData = 0;
  740. if (fFunctions.getProperty(fInterface,
  741. kAudioUnitProperty_ElementCount,
  742. kAudioUnitScope_Input,
  743. 0, &outData, &outDataSize) == noErr
  744. && outData != 0)
  745. {
  746. if (fFunctions.setProperty(fInterface,
  747. kAudioUnitProperty_SampleRate,
  748. kAudioUnitScope_Input,
  749. 0, &sampleRate, sizeof(sampleRate)) != noErr)
  750. {
  751. pData->engine->setLastError("Failed to set Component input sample rate");
  752. return false;
  753. }
  754. }
  755. }
  756. // output scope
  757. outDataSize = 0;
  758. outWritable = false;
  759. if (fFunctions.getPropertyInfo(fInterface,
  760. kAudioUnitProperty_ElementCount,
  761. kAudioUnitScope_Output,
  762. 0, &outDataSize, &outWritable) == noErr
  763. && outDataSize == sizeof(UInt32))
  764. {
  765. UInt32 outData = 0;
  766. if (fFunctions.getProperty(fInterface,
  767. kAudioUnitProperty_ElementCount,
  768. kAudioUnitScope_Output,
  769. 0, &outData, &outDataSize) == noErr
  770. && outData != 0)
  771. {
  772. if (fFunctions.setProperty(fInterface,
  773. kAudioUnitProperty_SampleRate,
  774. kAudioUnitScope_Output,
  775. 0, &sampleRate, sizeof(sampleRate)) != noErr)
  776. {
  777. pData->engine->setLastError("Failed to set Component output sample rate");
  778. return false;
  779. }
  780. }
  781. }
  782. }
  783. // ------------------------------------------------------------------------------------------------------------
  784. // set default options
  785. pData->options = PLUGIN_OPTION_FIXED_BUFFERS;
  786. return true;
  787. }
  788. private:
  789. BundleLoader fBundleLoader;
  790. AudioComponentPlugInInterface* fInterface;
  791. AudioBufferList* fAudioBufferData;
  792. CarlaString fName;
  793. CarlaString fLabel;
  794. CarlaString fMaker;
  795. struct Functions {
  796. InitializeFn initialize;
  797. UninitializeFn uninitialize;
  798. GetPropertyInfoFn getPropertyInfo;
  799. GetPropertyFn getProperty;
  800. SetPropertyFn setProperty;
  801. GetParameterFn getParameter;
  802. SetParameterFn setParameter;
  803. ScheduleParametersFn scheduleParameters;
  804. ResetFn reset;
  805. RenderFn render;
  806. MIDIEventFn midiEvent;
  807. Functions()
  808. : initialize(nullptr),
  809. uninitialize(nullptr),
  810. getPropertyInfo(nullptr),
  811. getProperty(nullptr),
  812. setProperty(nullptr),
  813. getParameter(nullptr),
  814. setParameter(nullptr),
  815. scheduleParameters(nullptr),
  816. reset(nullptr),
  817. render(nullptr),
  818. midiEvent(nullptr) {}
  819. bool init(AudioComponentPlugInInterface* const interface)
  820. {
  821. initialize = (InitializeFn)interface->Lookup(kAudioUnitInitializeSelect);
  822. uninitialize = (UninitializeFn)interface->Lookup(kAudioUnitUninitializeSelect);
  823. getPropertyInfo = (GetPropertyInfoFn)interface->Lookup(kAudioUnitGetPropertyInfoSelect);
  824. getProperty = (GetPropertyFn)interface->Lookup(kAudioUnitGetPropertySelect);
  825. setProperty = (SetPropertyFn)interface->Lookup(kAudioUnitSetPropertySelect);
  826. getParameter = (GetParameterFn)interface->Lookup(kAudioUnitGetParameterSelect);
  827. setParameter = (SetParameterFn)interface->Lookup(kAudioUnitSetParameterSelect);
  828. scheduleParameters = (ScheduleParametersFn)interface->Lookup(kAudioUnitScheduleParametersSelect);
  829. reset = (ResetFn)interface->Lookup(kAudioUnitResetSelect);
  830. render = (RenderFn)interface->Lookup(kAudioUnitRenderSelect);
  831. midiEvent = (MIDIEventFn)interface->Lookup(kMusicDeviceMIDIEventSelect);
  832. return initialize != nullptr
  833. && uninitialize != nullptr
  834. && getPropertyInfo != nullptr
  835. && getProperty != nullptr
  836. && setProperty != nullptr
  837. && getParameter != nullptr
  838. && setParameter != nullptr
  839. && scheduleParameters != nullptr
  840. && render != nullptr;
  841. }
  842. } fFunctions;
  843. };
  844. #endif
  845. // --------------------------------------------------------------------------------------------------------------------
  846. CarlaPluginPtr CarlaPlugin::newAU(const Initializer& init)
  847. {
  848. carla_stdout("CarlaPlugin::newAU({%p, \"%s\", \"%s\", \"%s\", " P_INT64 "})",
  849. init.engine, init.filename, init.label, init.name, init.uniqueId);
  850. #ifdef CARLA_OS_MAC
  851. std::shared_ptr<CarlaPluginAU> plugin(new CarlaPluginAU(init.engine, init.id));
  852. if (! plugin->init(plugin, init.filename, init.label, init.name, init.options))
  853. return nullptr;
  854. return plugin;
  855. #else
  856. init.engine->setLastError("AU support not available");
  857. return nullptr;
  858. #endif
  859. }
  860. // --------------------------------------------------------------------------------------------------------------------
  861. CARLA_BACKEND_END_NAMESPACE