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.

1122 lines
42KB

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