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.

812 lines
29KB

  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. {
  39. carla_stdout("CarlaPluginAU::CarlaPluginAU(%p, %i)", engine, id);
  40. }
  41. ~CarlaPluginAU() override
  42. {
  43. carla_stdout("CarlaPluginAU::~CarlaPluginAU()");
  44. // close UI
  45. showCustomUI(false);
  46. pData->singleMutex.lock();
  47. pData->masterMutex.lock();
  48. if (pData->client != nullptr && pData->client->isActive())
  49. pData->client->deactivate(true);
  50. if (pData->active)
  51. {
  52. deactivate();
  53. pData->active = false;
  54. }
  55. if (fInterface != nullptr)
  56. {
  57. fInterface->Close(fInterface);
  58. fInterface = nullptr;
  59. }
  60. // if (fLastChunk != nullptr)
  61. // {
  62. // std::free(fLastChunk);
  63. // fLastChunk = nullptr;
  64. // }
  65. clearBuffers();
  66. }
  67. // -------------------------------------------------------------------
  68. // Information (base)
  69. PluginType getType() const noexcept override
  70. {
  71. return PLUGIN_AU;
  72. }
  73. PluginCategory getCategory() const noexcept override
  74. {
  75. // TODO
  76. return PLUGIN_CATEGORY_NONE;
  77. }
  78. uint32_t getLatencyInFrames() const noexcept override
  79. {
  80. // TODO
  81. return 0;
  82. }
  83. // -------------------------------------------------------------------
  84. // Information (count)
  85. // -------------------------------------------------------------------
  86. // Information (current data)
  87. // -------------------------------------------------------------------
  88. // Information (per-plugin data)
  89. uint getOptionsAvailable() const noexcept override
  90. {
  91. // TODO
  92. return 0x0;
  93. }
  94. float getParameterValue(const uint32_t parameterId) const noexcept override
  95. {
  96. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr, 0.f);
  97. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, 0.f);
  98. const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
  99. AudioUnitParameterValue value = 0.f;
  100. if (fFunctions.getParameter(fInterface, paramId, kAudioUnitScope_Global, 0, &value) == noErr)
  101. return value;
  102. return 0.f;
  103. }
  104. bool getLabel(char* const strBuf) const noexcept override
  105. {
  106. std::strncpy(strBuf, fLabel.buffer(), STR_MAX);
  107. return true;
  108. }
  109. bool getMaker(char* const strBuf) const noexcept override
  110. {
  111. std::strncpy(strBuf, fMaker.buffer(), STR_MAX);
  112. return true;
  113. }
  114. bool getRealName(char* const strBuf) const noexcept override
  115. {
  116. std::strncpy(strBuf, fName.buffer(), STR_MAX);
  117. return true;
  118. }
  119. bool getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override
  120. {
  121. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr, false);
  122. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false);
  123. const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
  124. AudioUnitParameterInfo info = {};
  125. UInt32 outDataSize = sizeof(AudioUnitParameterInfo);
  126. if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramId, &info, &outDataSize) == noErr)
  127. {
  128. if (info.flags & kAudioUnitParameterFlag_HasCFNameString)
  129. {
  130. *strBuf = '\0';
  131. if (! CFStringGetCString(info.cfNameString, strBuf, std::min<int>(STR_MAX, CFStringGetLength(info.cfNameString) + 1), kCFStringEncodingUTF8))
  132. {
  133. carla_stdout("CFStringGetCString fail '%s'", info.name);
  134. std::strncpy(strBuf, info.name, STR_MAX);
  135. }
  136. else
  137. {
  138. carla_stdout("CFStringGetCString ok '%s' '%s'", info.name, strBuf);
  139. std::strncpy(strBuf, info.name, STR_MAX);
  140. }
  141. if (info.flags & kAudioUnitParameterFlag_CFNameRelease)
  142. CFRelease(info.cfNameString);
  143. return true;
  144. }
  145. carla_stdout("CFStringGetCString not used '%s'", info.name);
  146. std::strncpy(strBuf, info.name, STR_MAX);
  147. return true;
  148. }
  149. carla_safe_assert("fFunctions.getProperty(...)", __FILE__, __LINE__);
  150. return CarlaPlugin::getParameterName(parameterId, strBuf);
  151. }
  152. // -------------------------------------------------------------------
  153. // Set data (plugin-specific stuff)
  154. void setParameterValue(const uint32_t parameterId, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept override
  155. {
  156. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  157. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
  158. const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
  159. const float fixedValue = pData->param.getFixedValue(parameterId, value);
  160. fFunctions.setParameter(fInterface, paramId, kAudioUnitScope_Global, 0, value, 0);
  161. CarlaPlugin::setParameterValue(parameterId, fixedValue, sendGui, sendOsc, sendCallback);
  162. }
  163. void setParameterValueRT(const uint32_t parameterId, const float value, const uint32_t frameOffset, const bool sendCallbackLater) noexcept override
  164. {
  165. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  166. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
  167. const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
  168. const float fixedValue = pData->param.getFixedValue(parameterId, value);
  169. // TODO use scheduled events
  170. fFunctions.setParameter(fInterface, paramId, kAudioUnitScope_Global, 0, value, 0);
  171. CarlaPlugin::setParameterValueRT(parameterId, fixedValue, frameOffset, sendCallbackLater);
  172. }
  173. // -------------------------------------------------------------------
  174. // Plugin state
  175. void reload() override
  176. {
  177. CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,);
  178. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  179. carla_debug("CarlaPluginAU::reload() - start");
  180. const EngineProcessMode processMode = pData->engine->getProccessMode();
  181. // Safely disable plugin for reload
  182. const ScopedDisabler sd(this);
  183. if (pData->active)
  184. deactivate();
  185. clearBuffers();
  186. uint32_t audioIns, audioOuts, parametersIns, parametersOuts;
  187. audioIns = audioOuts = parametersIns = parametersOuts = 0;
  188. bool needsCtrlIn, needsCtrlOut, hasMidiIn, hasMidiOut;
  189. needsCtrlIn = needsCtrlOut = hasMidiIn = hasMidiOut = false;
  190. CarlaString portName;
  191. const uint portNameSize(pData->engine->getMaxPortNameSize());
  192. UInt32 outDataSize;
  193. Boolean outWritable = false;
  194. // audio ports
  195. outDataSize = 0;
  196. if (fFunctions.getPropertyInfo(fInterface, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, 0, &outDataSize, &outWritable) == noErr && outDataSize != 0 && outDataSize % sizeof(AUChannelInfo) == 0)
  197. {
  198. const uint32_t numChannels = outDataSize / sizeof(AUChannelInfo);
  199. AUChannelInfo* const channelInfo = new AUChannelInfo[numChannels];
  200. if (fFunctions.getProperty(fInterface, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, 0, channelInfo, &outDataSize) == noErr && outDataSize == numChannels * sizeof(AUChannelInfo))
  201. {
  202. AUChannelInfo* highestInfo = &channelInfo[0];
  203. for (uint32_t i=1; i<numChannels; ++i)
  204. {
  205. if (channelInfo[i].inChannels > highestInfo->inChannels && channelInfo[i].outChannels > highestInfo->outChannels)
  206. highestInfo = &channelInfo[i];
  207. }
  208. audioIns = highestInfo->inChannels;
  209. audioOuts = highestInfo->outChannels;
  210. }
  211. }
  212. if (audioIns > 0)
  213. {
  214. pData->audioIn.createNew(audioIns);
  215. }
  216. if (audioOuts > 0)
  217. {
  218. pData->audioOut.createNew(audioOuts);
  219. needsCtrlIn = true;
  220. }
  221. // Audio Ins
  222. for (uint32_t i=0; i < audioIns; ++i)
  223. {
  224. portName.clear();
  225. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  226. {
  227. portName = pData->name;
  228. portName += ":";
  229. }
  230. if (audioIns > 1)
  231. {
  232. portName += "input_";
  233. portName += CarlaString(i+1);
  234. }
  235. else
  236. portName += "input";
  237. portName.truncate(portNameSize);
  238. pData->audioIn.ports[i].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, true, i);
  239. pData->audioIn.ports[i].rindex = i;
  240. }
  241. // Audio Outs
  242. for (uint32_t i=0; i < audioOuts; ++i)
  243. {
  244. portName.clear();
  245. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  246. {
  247. portName = pData->name;
  248. portName += ":";
  249. }
  250. if (audioOuts > 1)
  251. {
  252. portName += "output_";
  253. portName += CarlaString(i+1);
  254. }
  255. else
  256. portName += "output";
  257. portName.truncate(portNameSize);
  258. pData->audioOut.ports[i].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false, i);
  259. pData->audioOut.ports[i].rindex = i;
  260. }
  261. // parameters
  262. outDataSize = 0;
  263. if (fFunctions.getPropertyInfo(fInterface, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, &outDataSize, &outWritable) == noErr && outDataSize != 0 && outDataSize % sizeof(AudioUnitParameterID) == 0)
  264. {
  265. const uint32_t numParams = outDataSize / sizeof(AudioUnitParameterID);
  266. AudioUnitParameterID* const paramIds = new AudioUnitParameterID[numParams];
  267. if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, paramIds, &outDataSize) == noErr && outDataSize == numParams * sizeof(AudioUnitParameterID))
  268. {
  269. pData->param.createNew(numParams, false);
  270. AudioUnitParameterInfo info;
  271. float min, max, def, step, stepSmall, stepLarge;
  272. for (uint32_t i=0; i<numParams; ++i)
  273. {
  274. carla_zeroStruct(info);
  275. outDataSize = 0;
  276. if (fFunctions.getPropertyInfo(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramIds[i], &outDataSize, &outWritable) != noErr)
  277. break;
  278. if (outDataSize != sizeof(AudioUnitParameterInfo))
  279. break;
  280. if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramIds[i], &info, &outDataSize) != noErr)
  281. break;
  282. if (info.flags & kAudioUnitParameterFlag_CFNameRelease)
  283. CFRelease(info.cfNameString);
  284. pData->param.data[i].index = static_cast<int32_t>(i);
  285. pData->param.data[i].rindex = static_cast<int32_t>(paramIds[i]);
  286. if (info.flags & kAudioUnitParameterFlag_IsWritable)
  287. {
  288. pData->param.data[i].type = PARAMETER_INPUT;
  289. needsCtrlIn = true;
  290. }
  291. else if (info.flags & (kAudioUnitParameterFlag_IsReadable|kAudioUnitParameterFlag_MeterReadOnly))
  292. {
  293. pData->param.data[i].type = PARAMETER_OUTPUT;
  294. needsCtrlOut = true;
  295. }
  296. else
  297. {
  298. pData->param.data[i].type = PARAMETER_UNKNOWN;
  299. continue;
  300. }
  301. min = info.minValue;
  302. max = info.maxValue;
  303. def = info.defaultValue;
  304. if (min > max)
  305. max = min;
  306. if (carla_isEqual(min, max))
  307. {
  308. carla_stderr2("WARNING - Broken plugin parameter '%s': max == min", info.name);
  309. max = min + 0.1f;
  310. }
  311. if (def < min)
  312. def = min;
  313. else if (def > max)
  314. def = max;
  315. pData->param.data[i].hints |= PARAMETER_IS_ENABLED;
  316. if ((info.flags & kAudioUnitParameterFlag_NonRealTime) == 0)
  317. {
  318. pData->param.data[i].hints |= PARAMETER_IS_AUTOMATABLE;
  319. pData->param.data[i].hints |= PARAMETER_CAN_BE_CV_CONTROLLED;
  320. }
  321. if (info.unit == kAudioUnitParameterUnit_Boolean)
  322. {
  323. step = max - min;
  324. stepSmall = step;
  325. stepLarge = step;
  326. pData->param.data[i].hints |= PARAMETER_IS_BOOLEAN;
  327. }
  328. else if (info.unit == kAudioUnitParameterUnit_Indexed)
  329. {
  330. step = 1.0f;
  331. stepSmall = 1.0f;
  332. stepLarge = 10.0f;
  333. pData->param.data[i].hints |= PARAMETER_IS_INTEGER;
  334. }
  335. else
  336. {
  337. float range = max - min;
  338. step = range/100.0f;
  339. stepSmall = range/1000.0f;
  340. stepLarge = range/10.0f;
  341. }
  342. pData->param.ranges[i].min = min;
  343. pData->param.ranges[i].max = max;
  344. pData->param.ranges[i].def = def;
  345. pData->param.ranges[i].step = step;
  346. pData->param.ranges[i].stepSmall = stepSmall;
  347. pData->param.ranges[i].stepLarge = stepLarge;
  348. }
  349. }
  350. delete[] paramIds;
  351. }
  352. if (needsCtrlIn || hasMidiIn)
  353. {
  354. portName.clear();
  355. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  356. {
  357. portName = pData->name;
  358. portName += ":";
  359. }
  360. portName += "events-in";
  361. portName.truncate(portNameSize);
  362. pData->event.portIn = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, true, 0);
  363. #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
  364. pData->event.cvSourcePorts = pData->client->createCVSourcePorts();
  365. #endif
  366. }
  367. if (needsCtrlOut || hasMidiOut)
  368. {
  369. portName.clear();
  370. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  371. {
  372. portName = pData->name;
  373. portName += ":";
  374. }
  375. portName += "events-out";
  376. portName.truncate(portNameSize);
  377. pData->event.portOut = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, false, 0);
  378. }
  379. // plugin hints
  380. pData->hints = 0x0;
  381. if (audioOuts > 0 && (audioIns == audioOuts || audioIns == 1))
  382. pData->hints |= PLUGIN_CAN_DRYWET;
  383. if (audioOuts > 0)
  384. pData->hints |= PLUGIN_CAN_VOLUME;
  385. if (audioOuts >= 2 && audioOuts % 2 == 0)
  386. pData->hints |= PLUGIN_CAN_BALANCE;
  387. // extra plugin hints
  388. pData->extraHints = 0x0;
  389. bufferSizeChanged(pData->engine->getBufferSize());
  390. reloadPrograms(true);
  391. if (pData->active)
  392. activate();
  393. carla_debug("CarlaPluginAU::reload() - end");
  394. }
  395. // -------------------------------------------------------------------
  396. // Plugin processing
  397. void activate() noexcept override
  398. {
  399. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  400. // TODO
  401. }
  402. void deactivate() noexcept override
  403. {
  404. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  405. // TODO
  406. }
  407. void process(const float* const* const audioIn,
  408. float** const audioOut,
  409. const float* const* const cvIn,
  410. float** const,
  411. const uint32_t frames) override
  412. {
  413. // TODO
  414. }
  415. // -------------------------------------------------------------------
  416. protected:
  417. void handlePluginUIClosed() override
  418. {
  419. carla_stdout("CarlaPluginCLAP::handlePluginUIClosed()");
  420. // TODO
  421. }
  422. void handlePluginUIResized(const uint width, const uint height) override
  423. {
  424. // TODO
  425. }
  426. // -------------------------------------------------------------------
  427. public:
  428. bool init(const CarlaPluginPtr plugin,
  429. const char* const filename,
  430. const char* const label,
  431. const char* const name,
  432. const uint options)
  433. {
  434. CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false);
  435. // ---------------------------------------------------------------
  436. // first checks
  437. if (pData->client != nullptr)
  438. {
  439. pData->engine->setLastError("Plugin client is already registered");
  440. return false;
  441. }
  442. if (filename == nullptr || filename[0] == '\0')
  443. {
  444. pData->engine->setLastError("null filename");
  445. return false;
  446. }
  447. if (label == nullptr)
  448. {
  449. pData->engine->setLastError("null label");
  450. return false;
  451. }
  452. // ---------------------------------------------------------------
  453. // load bundle information
  454. if (! fBundleLoader.load(filename))
  455. {
  456. pData->engine->setLastError("Failed to load AU bundle executable");
  457. return false;
  458. }
  459. const CFTypeRef componentsRef = CFBundleGetValueForInfoDictionaryKey(fBundleLoader.getRef(), CFSTR("AudioComponents"));
  460. if (componentsRef == nullptr || CFGetTypeID(componentsRef) != CFArrayGetTypeID())
  461. {
  462. pData->engine->setLastError("Not an AU component");
  463. return false;
  464. }
  465. // ---------------------------------------------------------------
  466. // find binary matching requested label
  467. CFStringRef componentName;
  468. AudioComponentDescription desc = {};
  469. FactoryFn factoryFn;
  470. const CFArrayRef components = static_cast<CFArrayRef>(componentsRef);
  471. for (uint32_t c = 0, count = CFArrayGetCount(components); c < count; ++c)
  472. {
  473. // reset
  474. desc.componentType = 0;
  475. const CFTypeRef componentRef = CFArrayGetValueAtIndex(components, c);
  476. CARLA_SAFE_ASSERT_CONTINUE(componentRef != nullptr);
  477. CARLA_SAFE_ASSERT_CONTINUE(CFGetTypeID(componentRef) == CFDictionaryGetTypeID());
  478. const CFDictionaryRef component = static_cast<CFDictionaryRef>(componentRef);
  479. componentName = nullptr;
  480. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("name"), (const void **)&componentName));
  481. CFStringRef componentFactoryFunction = nullptr;
  482. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("factoryFunction"), (const void **)&componentFactoryFunction));
  483. CFStringRef componentType = nullptr;
  484. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("type"), (const void **)&componentType));
  485. CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentType) == 4);
  486. CFStringRef componentSubType = nullptr;
  487. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("subtype"), (const void **)&componentSubType));
  488. CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentSubType) == 4);
  489. CFStringRef componentManufacturer = nullptr;
  490. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("manufacturer"), (const void **)&componentManufacturer));
  491. CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentManufacturer) == 4);
  492. factoryFn = fBundleLoader.getSymbol<FactoryFn>(componentFactoryFunction);
  493. CARLA_SAFE_ASSERT_CONTINUE(factoryFn != nullptr);
  494. char clabel[15] = {};
  495. CFStringGetCString(componentType, clabel, 5, kCFStringEncodingASCII);
  496. CFStringGetCString(componentSubType, clabel + 5, 5, kCFStringEncodingASCII);
  497. CFStringGetCString(componentManufacturer, clabel + 10, 5, kCFStringEncodingASCII);
  498. desc.componentType = getFourCharCodeFromString(clabel);
  499. desc.componentSubType = getFourCharCodeFromString(clabel + 5);
  500. desc.componentManufacturer = getFourCharCodeFromString(clabel + 10);
  501. CARLA_SAFE_ASSERT_CONTINUE(desc.componentType != 0);
  502. CARLA_SAFE_ASSERT_CONTINUE(desc.componentSubType != 0);
  503. CARLA_SAFE_ASSERT_CONTINUE(desc.componentManufacturer != 0);
  504. clabel[4] = clabel[9] = ',';
  505. if (label[0] == '\0' || std::strcmp(label, clabel) == 0)
  506. break;
  507. }
  508. if (desc.componentType == 0)
  509. {
  510. pData->engine->setLastError("Failed to find request plugin in Component bundle");
  511. return false;
  512. }
  513. // ---------------------------------------------------------------
  514. // load binary
  515. fInterface = factoryFn(&desc);
  516. if (fInterface == nullptr)
  517. {
  518. pData->engine->setLastError("Component failed to create new interface");
  519. return false;
  520. }
  521. if (! fFunctions.init(fInterface))
  522. {
  523. pData->engine->setLastError("Component does not provide all necessary functions");
  524. fInterface = nullptr;
  525. return false;
  526. }
  527. if (fInterface->Open(fInterface, (AudioUnit)(void*)0x1) != noErr)
  528. {
  529. pData->engine->setLastError("Component failed to open");
  530. fInterface->Close(fInterface);
  531. fInterface = nullptr;
  532. return false;
  533. }
  534. // ---------------------------------------------------------------
  535. // get info
  536. const CFIndex componentNameLen = CFStringGetLength(componentName);
  537. char* const nameBuffer = new char[componentNameLen + 1];
  538. if (CFStringGetCString(componentName, nameBuffer, componentNameLen + 1, kCFStringEncodingUTF8))
  539. {
  540. if (char* const sep = std::strstr(nameBuffer, ": "))
  541. {
  542. sep[0] = sep[1] = '\0';
  543. fName = sep + 2;
  544. fMaker = nameBuffer;
  545. }
  546. else
  547. {
  548. fName = nameBuffer;
  549. fMaker = nameBuffer + componentNameLen;
  550. }
  551. }
  552. fLabel = label;
  553. pData->name = pData->engine->getUniquePluginName(name != nullptr && name[0] != '\0' ? name : fName.buffer());
  554. pData->filename = carla_strdup(filename);
  555. delete[] nameBuffer;
  556. // ---------------------------------------------------------------
  557. // register client
  558. pData->client = pData->engine->addClient(plugin);
  559. if (pData->client == nullptr || ! pData->client->isOk())
  560. {
  561. pData->engine->setLastError("Failed to register plugin client");
  562. return false;
  563. }
  564. // ---------------------------------------------------------------
  565. // set default options
  566. pData->options = PLUGIN_OPTION_FIXED_BUFFERS;
  567. return true;
  568. }
  569. private:
  570. BundleLoader fBundleLoader;
  571. AudioComponentPlugInInterface* fInterface;
  572. CarlaString fName;
  573. CarlaString fLabel;
  574. CarlaString fMaker;
  575. struct Functions {
  576. InitializeFn initialize;
  577. UninitializeFn uninitialize;
  578. GetPropertyInfoFn getPropertyInfo;
  579. GetPropertyFn getProperty;
  580. SetPropertyFn setProperty;
  581. GetParameterFn getParameter;
  582. SetParameterFn setParameter;
  583. ScheduleParametersFn scheduleParameters;
  584. ResetFn reset;
  585. RenderFn render;
  586. MIDIEventFn midiEvent;
  587. Functions()
  588. : initialize(nullptr),
  589. uninitialize(nullptr),
  590. getPropertyInfo(nullptr),
  591. getProperty(nullptr),
  592. setProperty(nullptr),
  593. getParameter(nullptr),
  594. setParameter(nullptr),
  595. scheduleParameters(nullptr),
  596. reset(nullptr),
  597. render(nullptr),
  598. midiEvent(nullptr) {}
  599. bool init(AudioComponentPlugInInterface* const interface)
  600. {
  601. initialize = (InitializeFn)interface->Lookup(kAudioUnitInitializeSelect);
  602. uninitialize = (UninitializeFn)interface->Lookup(kAudioUnitUninitializeSelect);
  603. getPropertyInfo = (GetPropertyInfoFn)interface->Lookup(kAudioUnitGetPropertyInfoSelect);
  604. getProperty = (GetPropertyFn)interface->Lookup(kAudioUnitGetPropertySelect);
  605. setProperty = (SetPropertyFn)interface->Lookup(kAudioUnitSetPropertySelect);
  606. getParameter = (GetParameterFn)interface->Lookup(kAudioUnitGetParameterSelect);
  607. setParameter = (SetParameterFn)interface->Lookup(kAudioUnitSetParameterSelect);
  608. scheduleParameters = (ScheduleParametersFn)interface->Lookup(kAudioUnitScheduleParametersSelect);
  609. reset = (ResetFn)interface->Lookup(kAudioUnitResetSelect);
  610. render = (RenderFn)interface->Lookup(kAudioUnitRenderSelect);
  611. midiEvent = (MIDIEventFn)interface->Lookup(kMusicDeviceMIDIEventSelect);
  612. return initialize != nullptr
  613. && uninitialize != nullptr
  614. && getPropertyInfo != nullptr
  615. && getProperty != nullptr
  616. && setProperty != nullptr
  617. && getParameter != nullptr
  618. && setParameter != nullptr
  619. && scheduleParameters != nullptr
  620. && render != nullptr;
  621. }
  622. } fFunctions;
  623. };
  624. #endif
  625. // -------------------------------------------------------------------------------------------------------------------
  626. CarlaPluginPtr CarlaPlugin::newAU(const Initializer& init)
  627. {
  628. carla_stdout("CarlaPlugin::newAU({%p, \"%s\", \"%s\", \"%s\", " P_INT64 "})",
  629. init.engine, init.filename, init.label, init.name, init.uniqueId);
  630. #ifdef CARLA_OS_MAC
  631. std::shared_ptr<CarlaPluginAU> plugin(new CarlaPluginAU(init.engine, init.id));
  632. if (! plugin->init(plugin, init.filename, init.label, init.name, init.options))
  633. return nullptr;
  634. return plugin;
  635. #else
  636. init.engine->setLastError("AU support not available");
  637. return nullptr;
  638. #endif
  639. }
  640. // -------------------------------------------------------------------------------------------------------------------
  641. CARLA_BACKEND_END_NAMESPACE