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.

766 lines
22KB

  1. /*
  2. * Carla Plugin Engine (Native)
  3. * Copyright (C) 2013 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #ifndef BUILD_BRIDGE
  18. #include "CarlaEngineInternal.hpp"
  19. #include "CarlaStateUtils.hpp"
  20. #include "CarlaNative.hpp"
  21. CARLA_BACKEND_START_NAMESPACE
  22. #if 0
  23. // -----------------------------------------------------------------------
  24. class CarlaEngineNativeThread : public QThread
  25. {
  26. public:
  27. enum UiState {
  28. UiNone = 0,
  29. UiHide,
  30. UiShow,
  31. UiCrashed
  32. };
  33. CarlaEngineNativeThread(CarlaEngine* const engine)
  34. : kEngine(engine),
  35. fBinary("carla-control"),
  36. fProcess(nullptr),
  37. fUiState(UiNone)
  38. {
  39. carla_debug("CarlaEngineNativeThread::CarlaEngineNativeThread(engine:\"%s\")", engine->getName());
  40. }
  41. ~CarlaEngineNativeThread()
  42. {
  43. CARLA_ASSERT_INT(fUiState == UiNone, fUiState);
  44. carla_debug("CarlaEngineNativeThread::~CarlaEngineNativeThread()");
  45. if (fProcess != nullptr)
  46. {
  47. delete fProcess;
  48. fProcess = nullptr;
  49. }
  50. }
  51. void setOscData(const char* const binary)
  52. {
  53. fBinary = binary;
  54. }
  55. UiState getUiState()
  56. {
  57. const UiState state(fUiState);
  58. fUiState = UiNone;
  59. return state;
  60. }
  61. void stop()
  62. {
  63. if (fProcess == nullptr)
  64. return;
  65. fUiState = UiNone;
  66. fProcess->kill();
  67. //fProcess->close();
  68. }
  69. protected:
  70. void run()
  71. {
  72. carla_debug("CarlaEngineNativeThread::run() - binary:\"%s\"", (const char*)fBinary);
  73. if (fProcess == nullptr)
  74. {
  75. fProcess = new QProcess(nullptr);
  76. fProcess->setProcessChannelMode(QProcess::ForwardedChannels);
  77. }
  78. else if (fProcess->state() == QProcess::Running)
  79. {
  80. carla_stderr("CarlaEngineNativeThread::run() - already running, giving up...");
  81. fUiState = UiCrashed;
  82. fProcess->terminate();
  83. //kEngine->callback(CarlaBackend::CALLBACK_SHOW_GUI, kPlugin->id(), -1, 0, 0.0f, nullptr);
  84. // TODO: tell master to hide UI
  85. return;
  86. }
  87. QStringList arguments;
  88. arguments << kEngine->getOscServerPathTCP();
  89. fProcess->start((const char*)fBinary, arguments);
  90. fProcess->waitForStarted();
  91. fUiState = UiShow;
  92. fProcess->waitForFinished(-1);
  93. if (fProcess->exitCode() == 0)
  94. {
  95. // Hide
  96. fUiState = UiHide;
  97. carla_stdout("CarlaEngineNativeThread::run() - GUI closed");
  98. }
  99. else
  100. {
  101. // Kill
  102. fUiState = UiCrashed;
  103. carla_stderr("CarlaEngineNativeThread::run() - GUI crashed while running");
  104. }
  105. }
  106. private:
  107. CarlaEngine* const kEngine;
  108. CarlaString fBinary;
  109. QProcess* fProcess;
  110. UiState fUiState;
  111. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineNativeThread)
  112. };
  113. // -----------------------------------------------------------------------
  114. class CarlaEngineNative : public PluginClass,
  115. public CarlaEngine
  116. {
  117. public:
  118. CarlaEngineNative(const HostDescriptor* const host)
  119. : PluginClass(host),
  120. CarlaEngine(),
  121. fIsRunning(true),
  122. fThread(this)
  123. {
  124. carla_debug("CarlaEngineNative::CarlaEngineNative()");
  125. // set-up engine
  126. fOptions.processMode = PROCESS_MODE_CONTINUOUS_RACK;
  127. fOptions.transportMode = TRANSPORT_MODE_PLUGIN;
  128. fOptions.forceStereo = true;
  129. fOptions.preferPluginBridges = false;
  130. fOptions.preferUiBridges = false;
  131. init("Carla-Plugin");
  132. // set control thread binary
  133. CarlaString threadBinary(getResourceDir());
  134. threadBinary += "/../";
  135. threadBinary += "carla_control.py";
  136. fThread.setOscData(threadBinary);
  137. // TESTING
  138. // if (! addPlugin(PLUGIN_INTERNAL, nullptr, "MIDI Transpose", "midiTranspose"))
  139. // carla_stdout("TESTING PLUG1 ERROR:\n%s", getLastError());
  140. // if (! addPlugin(PLUGIN_INTERNAL, nullptr, "ZynAddSubFX", "zynaddsubfx"))
  141. // carla_stdout("TESTING PLUG2 ERROR:\n%s", getLastError());
  142. // if (! addPlugin(PLUGIN_INTERNAL, nullptr, "Ping Pong Pan", "PingPongPan"))
  143. // carla_stdout("TESTING PLUG3 ERROR:\n%s", getLastError());
  144. }
  145. ~CarlaEngineNative() override
  146. {
  147. carla_debug("CarlaEngineNative::~CarlaEngineNative()");
  148. fIsRunning = false;
  149. setAboutToClose();
  150. removeAllPlugins();
  151. close();
  152. }
  153. protected:
  154. // -------------------------------------
  155. // CarlaEngine virtual calls
  156. bool init(const char* const clientName) override
  157. {
  158. carla_debug("CarlaEngineNative::init(\"%s\")", clientName);
  159. fBufferSize = PluginClass::getBufferSize();
  160. fSampleRate = PluginClass::getSampleRate();
  161. CarlaEngine::init(clientName);
  162. return true;
  163. }
  164. bool close() override
  165. {
  166. carla_debug("CarlaEngineNative::close()");
  167. runPendingRtEvents();
  168. return CarlaEngine::close();
  169. }
  170. bool isRunning() const noexcept override
  171. {
  172. return fIsRunning;
  173. }
  174. bool isOffline() const noexcept override
  175. {
  176. return false;
  177. }
  178. EngineType getType() const noexcept override
  179. {
  180. return kEngineTypePlugin;
  181. }
  182. // -------------------------------------------------------------------
  183. // Plugin parameter calls
  184. uint32_t getParameterCount() const override
  185. {
  186. if (pData->curPluginCount == 0 || pData->plugins == nullptr)
  187. return 0;
  188. CarlaPlugin* const plugin(pData->plugins[0].plugin);
  189. if (plugin == nullptr || ! plugin->isEnabled())
  190. return 0;
  191. return pData->plugins[0].plugin->getParameterCount();
  192. }
  193. const Parameter* getParameterInfo(const uint32_t index) const override
  194. {
  195. if (index >= getParameterCount())
  196. return nullptr;
  197. CarlaPlugin* const plugin(pData->plugins[0].plugin);
  198. if (plugin == nullptr || ! plugin->isEnabled())
  199. return nullptr;
  200. static ::Parameter param;
  201. static char strBufName[STR_MAX+1];
  202. static char strBufUnit[STR_MAX+1];
  203. const ParameterData& paramData(plugin->getParameterData(index));
  204. const ParameterRanges& paramRanges(plugin->getParameterRanges(index));
  205. plugin->getParameterName(index, strBufName);
  206. plugin->getParameterUnit(index, strBufUnit);
  207. unsigned int hints = 0x0;
  208. if (paramData.hints & PARAMETER_IS_BOOLEAN)
  209. hints |= ::PARAMETER_IS_BOOLEAN;
  210. if (paramData.hints & PARAMETER_IS_INTEGER)
  211. hints |= ::PARAMETER_IS_INTEGER;
  212. if (paramData.hints & PARAMETER_IS_LOGARITHMIC)
  213. hints |= ::PARAMETER_IS_LOGARITHMIC;
  214. if (paramData.hints & PARAMETER_IS_AUTOMABLE)
  215. hints |= ::PARAMETER_IS_AUTOMABLE;
  216. if (paramData.hints & PARAMETER_USES_SAMPLERATE)
  217. hints |= ::PARAMETER_USES_SAMPLE_RATE;
  218. if (paramData.hints & PARAMETER_USES_SCALEPOINTS)
  219. hints |= ::PARAMETER_USES_SCALEPOINTS;
  220. if (paramData.hints & PARAMETER_USES_CUSTOM_TEXT)
  221. hints |= ::PARAMETER_USES_CUSTOM_TEXT;
  222. if (paramData.type == PARAMETER_INPUT || paramData.type == PARAMETER_OUTPUT)
  223. {
  224. if (paramData.hints & PARAMETER_IS_ENABLED)
  225. hints |= ::PARAMETER_IS_ENABLED;
  226. if (paramData.type == PARAMETER_OUTPUT)
  227. hints |= ::PARAMETER_IS_OUTPUT;
  228. }
  229. param.hints = static_cast< ::ParameterHints>(hints);
  230. param.name = strBufName;
  231. param.unit = strBufUnit;
  232. param.ranges.def = paramRanges.def;
  233. param.ranges.min = paramRanges.min;
  234. param.ranges.max = paramRanges.max;
  235. param.ranges.step = paramRanges.step;
  236. param.ranges.stepSmall = paramRanges.stepSmall;
  237. param.ranges.stepLarge = paramRanges.stepLarge;
  238. param.scalePointCount = 0; // TODO
  239. param.scalePoints = nullptr;
  240. return &param;
  241. }
  242. float getParameterValue(const uint32_t index) const override
  243. {
  244. if (index >= getParameterCount())
  245. return 0.0f;
  246. CarlaPlugin* const plugin(pData->plugins[0].plugin);
  247. if (plugin == nullptr || ! plugin->isEnabled())
  248. return 0.0f;
  249. return plugin->getParameterValue(index);
  250. }
  251. const char* getParameterText(const uint32_t index, const float value) const override // FIXME - use value
  252. {
  253. if (index >= getParameterCount())
  254. return nullptr;
  255. CarlaPlugin* const plugin(pData->plugins[0].plugin);
  256. if (plugin == nullptr || ! plugin->isEnabled())
  257. return nullptr;
  258. static char strBuf[STR_MAX+1];
  259. plugin->getParameterText(index, strBuf);
  260. return strBuf;
  261. }
  262. // -------------------------------------------------------------------
  263. // Plugin midi-program calls
  264. uint32_t getMidiProgramCount() const override
  265. {
  266. if (pData->curPluginCount == 0 || pData->plugins == nullptr)
  267. return 0;
  268. CarlaPlugin* const plugin(pData->plugins[0].plugin);
  269. if (plugin == nullptr || ! plugin->isEnabled())
  270. return 0.0f;
  271. return plugin->getMidiProgramCount();
  272. }
  273. const MidiProgram* getMidiProgramInfo(const uint32_t index) const override
  274. {
  275. if (index >= getMidiProgramCount())
  276. return nullptr;
  277. CarlaPlugin* const plugin(pData->plugins[0].plugin);
  278. if (plugin == nullptr || ! plugin->isEnabled())
  279. return nullptr;
  280. static ::MidiProgram midiProg;
  281. {
  282. const MidiProgramData& midiProgData(plugin->getMidiProgramData(index));
  283. midiProg.bank = midiProgData.bank;
  284. midiProg.program = midiProgData.program;
  285. midiProg.name = midiProgData.name;
  286. }
  287. return &midiProg;
  288. }
  289. // -------------------------------------------------------------------
  290. // Plugin state calls
  291. void setParameterValue(const uint32_t index, const float value) override
  292. {
  293. if (index >= getParameterCount())
  294. return;
  295. CarlaPlugin* const plugin(pData->plugins[0].plugin);
  296. if (plugin == nullptr || ! plugin->isEnabled())
  297. return;
  298. plugin->setParameterValue(index, value, false, false, false);
  299. }
  300. void setMidiProgram(const uint8_t, const uint32_t bank, const uint32_t program) override
  301. {
  302. if (pData->curPluginCount == 0 || pData->plugins == nullptr)
  303. return;
  304. CarlaPlugin* const plugin(pData->plugins[0].plugin);
  305. if (plugin == nullptr || ! plugin->isEnabled())
  306. return;
  307. plugin->setMidiProgramById(bank, program, false, false, false);
  308. }
  309. void setCustomData(const char* const key, const char* const value) override
  310. {
  311. CARLA_ASSERT(key != nullptr);
  312. CARLA_ASSERT(value != nullptr);
  313. return;
  314. // TODO
  315. // unused
  316. (void)key;
  317. (void)value;
  318. }
  319. // -------------------------------------------------------------------
  320. // Plugin process calls
  321. void activate() override
  322. {
  323. for (uint32_t i=0; i < pData->curPluginCount; ++i)
  324. {
  325. CarlaPlugin* const plugin(pData->plugins[i].plugin);
  326. if (plugin == nullptr || ! plugin->isEnabled())
  327. continue;
  328. plugin->setActive(true, true, false);
  329. }
  330. }
  331. void deactivate() override
  332. {
  333. for (uint32_t i=0; i < pData->curPluginCount; ++i)
  334. {
  335. CarlaPlugin* const plugin(pData->plugins[i].plugin);
  336. if (plugin == nullptr || ! plugin->isEnabled())
  337. continue;
  338. plugin->setActive(false, true, false);
  339. }
  340. // just in case
  341. runPendingRtEvents();
  342. }
  343. void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const ::MidiEvent* const midiEvents, const uint32_t midiEventCount) override
  344. {
  345. if (pData->curPluginCount == 0)
  346. {
  347. FloatVectorOperations::clear(outBuffer[0], frames);
  348. FloatVectorOperations::clear(outBuffer[1], frames);
  349. return runPendingRtEvents();;
  350. }
  351. // ---------------------------------------------------------------
  352. // Time Info
  353. const ::TimeInfo* timeInfo(PluginClass::getTimeInfo());
  354. fTimeInfo.playing = timeInfo->playing;
  355. fTimeInfo.frame = timeInfo->frame;
  356. fTimeInfo.usecs = timeInfo->usecs;
  357. fTimeInfo.valid = 0x0;
  358. if (timeInfo->bbt.valid)
  359. {
  360. fTimeInfo.valid |= EngineTimeInfo::ValidBBT;
  361. fTimeInfo.bbt.bar = timeInfo->bbt.bar;
  362. fTimeInfo.bbt.beat = timeInfo->bbt.beat;
  363. fTimeInfo.bbt.tick = timeInfo->bbt.tick;
  364. fTimeInfo.bbt.barStartTick = timeInfo->bbt.barStartTick;
  365. fTimeInfo.bbt.beatsPerBar = timeInfo->bbt.beatsPerBar;
  366. fTimeInfo.bbt.beatType = timeInfo->bbt.beatType;
  367. fTimeInfo.bbt.ticksPerBeat = timeInfo->bbt.ticksPerBeat;
  368. fTimeInfo.bbt.beatsPerMinute = timeInfo->bbt.beatsPerMinute;
  369. }
  370. // ---------------------------------------------------------------
  371. // initialize input events
  372. carla_zeroStruct<EngineEvent>(pData->bufEvents.in, INTERNAL_EVENT_COUNT);
  373. {
  374. uint32_t engineEventIndex = 0;
  375. for (uint32_t i=0; i < midiEventCount && engineEventIndex < INTERNAL_EVENT_COUNT; ++i)
  376. {
  377. const ::MidiEvent& midiEvent(midiEvents[i]);
  378. if (midiEvent.size > 4)
  379. continue;
  380. const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent.data);
  381. const uint8_t channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent.data);
  382. // we don't want some events
  383. if (status == MIDI_STATUS_PROGRAM_CHANGE)
  384. continue;
  385. // handle note/sound off properly
  386. if (status == MIDI_STATUS_CONTROL_CHANGE)
  387. {
  388. const uint8_t control = midiEvent.data[1];
  389. if (MIDI_IS_CONTROL_BANK_SELECT(control))
  390. continue;
  391. if (control == MIDI_CONTROL_ALL_SOUND_OFF || control == MIDI_CONTROL_ALL_NOTES_OFF)
  392. {
  393. EngineEvent& engineEvent(pData->bufEvents.in[engineEventIndex++]);
  394. engineEvent.clear();
  395. engineEvent.type = kEngineEventTypeControl;
  396. engineEvent.time = midiEvent.time;
  397. engineEvent.channel = channel;
  398. engineEvent.ctrl.type = (control == MIDI_CONTROL_ALL_SOUND_OFF) ? kEngineControlEventTypeAllSoundOff : kEngineControlEventTypeAllNotesOff;
  399. engineEvent.ctrl.param = 0;
  400. engineEvent.ctrl.value = 0.0f;
  401. continue;
  402. }
  403. }
  404. EngineEvent& engineEvent(pData->bufEvents.in[engineEventIndex++]);
  405. engineEvent.clear();
  406. engineEvent.type = kEngineEventTypeMidi;
  407. engineEvent.time = midiEvent.time;
  408. engineEvent.channel = channel;
  409. engineEvent.midi.data[0] = MIDI_GET_STATUS_FROM_DATA(midiEvent.data);
  410. engineEvent.midi.data[1] = midiEvent.data[1];
  411. engineEvent.midi.data[2] = midiEvent.data[2];
  412. engineEvent.midi.data[3] = midiEvent.data[3];
  413. engineEvent.midi.size = midiEvent.size;
  414. }
  415. }
  416. // ---------------------------------------------------------------
  417. // create audio buffers
  418. float* inBuf[2] = { inBuffer[0], inBuffer[1] };
  419. float* outBuf[2] = { outBuffer[0], outBuffer[1] };
  420. // ---------------------------------------------------------------
  421. // process
  422. processRack(inBuf, outBuf, frames);
  423. runPendingRtEvents();
  424. }
  425. // -------------------------------------------------------------------
  426. // Plugin UI calls
  427. void uiShow(const bool show) override
  428. {
  429. if (show)
  430. {
  431. fThread.start();
  432. }
  433. else
  434. {
  435. #if 0
  436. for (uint32_t i=0; i < pData->curPluginCount; ++i)
  437. {
  438. CarlaPlugin* const plugin(pData->plugins[i].plugin);
  439. if (plugin == nullptr || ! plugin->enabled())
  440. continue;
  441. plugin->showGui(false);
  442. }
  443. #endif
  444. fThread.stop();
  445. }
  446. }
  447. void uiIdle() override
  448. {
  449. CarlaEngine::idle();
  450. switch(fThread.getUiState())
  451. {
  452. case CarlaEngineNativeThread::UiNone:
  453. case CarlaEngineNativeThread::UiShow:
  454. break;
  455. case CarlaEngineNativeThread::UiCrashed:
  456. hostUiUnavailable();
  457. break;
  458. case CarlaEngineNativeThread::UiHide:
  459. uiClosed();
  460. break;
  461. }
  462. }
  463. void uiSetParameterValue(const uint32_t index, const float value) override
  464. {
  465. if (index >= getParameterCount())
  466. return;
  467. CarlaPlugin* const plugin(pData->plugins[0].plugin);
  468. if (plugin == nullptr || ! plugin->isEnabled())
  469. return;
  470. plugin->uiParameterChange(index, value);
  471. }
  472. void uiSetMidiProgram(const uint8_t channel, const uint32_t bank, const uint32_t program) override
  473. {
  474. return;
  475. // TODO
  476. // unused
  477. (void)channel;
  478. (void)bank;
  479. (void)program;
  480. }
  481. void uiSetCustomData(const char* const key, const char* const value) override
  482. {
  483. CARLA_ASSERT(key != nullptr);
  484. CARLA_ASSERT(value != nullptr);
  485. return;
  486. // TODO
  487. // unused
  488. (void)key;
  489. (void)value;
  490. }
  491. // -------------------------------------------------------------------
  492. // Plugin state calls
  493. char* getState() const override
  494. {
  495. QString string;
  496. QTextStream out(&string);
  497. out << "<?xml version='1.0' encoding='UTF-8'?>\n";
  498. out << "<!DOCTYPE CARLA-PROJECT>\n";
  499. out << "<CARLA-PROJECT VERSION='1.0'>\n";
  500. bool firstPlugin = true;
  501. char strBuf[STR_MAX+1];
  502. for (unsigned int i=0; i < pData->curPluginCount; ++i)
  503. {
  504. CarlaPlugin* const plugin(pData->plugins[i].plugin);
  505. if (plugin != nullptr && plugin->isEnabled())
  506. {
  507. if (! firstPlugin)
  508. out << "\n";
  509. plugin->getRealName(strBuf);
  510. if (*strBuf != 0)
  511. out << QString(" <!-- %1 -->\n").arg(xmlSafeString(strBuf, true));
  512. QString content;
  513. fillXmlStringFromSaveState(content, plugin->getSaveState());
  514. out << " <Plugin>\n";
  515. out << content;
  516. out << " </Plugin>\n";
  517. firstPlugin = false;
  518. }
  519. }
  520. out << "</CARLA-PROJECT>\n";
  521. return strdup(string.toUtf8().constData());
  522. }
  523. void setState(const char* const data) override
  524. {
  525. QDomDocument xml;
  526. xml.setContent(QString(data));
  527. QDomNode xmlNode(xml.documentElement());
  528. if (xmlNode.toElement().tagName() != "CARLA-PROJECT")
  529. {
  530. carla_stderr2("Not a valid Carla project");
  531. return;
  532. }
  533. QDomNode node(xmlNode.firstChild());
  534. while (! node.isNull())
  535. {
  536. if (node.toElement().tagName() == "Plugin")
  537. {
  538. SaveState saveState;
  539. fillSaveStateFromXmlNode(saveState, node);
  540. CARLA_SAFE_ASSERT_CONTINUE(saveState.type != nullptr)
  541. const void* extraStuff = nullptr;
  542. // FIXME
  543. //if (std::strcmp(saveState.type, "DSSI") == 0)
  544. // extraStuff = findDSSIGUI(saveState.binary, saveState.label);
  545. // TODO - proper find&load plugins
  546. if (addPlugin(getPluginTypeFromString(saveState.type), saveState.binary, saveState.name, saveState.label, extraStuff))
  547. {
  548. if (CarlaPlugin* plugin = getPlugin(pData->curPluginCount-1))
  549. plugin->loadSaveState(saveState);
  550. }
  551. }
  552. node = node.nextSibling();
  553. }
  554. }
  555. // -------------------------------------------------------------------
  556. private:
  557. bool fIsRunning;
  558. CarlaEngineNativeThread fThread;
  559. PluginClassEND(CarlaEngineNative)
  560. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineNative)
  561. };
  562. // -----------------------------------------------------------------------
  563. static const PluginDescriptor carlaDesc = {
  564. /* category */ ::PLUGIN_CATEGORY_OTHER,
  565. /* hints */ static_cast< ::PluginHints>(::PLUGIN_IS_SYNTH|::PLUGIN_HAS_GUI|::PLUGIN_USES_SINGLE_THREAD|::PLUGIN_USES_STATE),
  566. /* supports */ static_cast<PluginSupports>(PLUGIN_SUPPORTS_EVERYTHING),
  567. /* audioIns */ 2,
  568. /* audioOuts */ 2,
  569. /* midiIns */ 1,
  570. /* midiOuts */ 1,
  571. /* paramIns */ 0,
  572. /* paramOuts */ 0,
  573. /* name */ "Carla-Plugin (TESTING)",
  574. /* label */ "carla",
  575. /* maker */ "falkTX",
  576. /* copyright */ "GNU GPL v2+",
  577. PluginDescriptorFILL(CarlaEngineNative)
  578. };
  579. #endif
  580. CARLA_EXPORT
  581. void carla_register_native_plugin_carla()
  582. {
  583. //carla_register_native_plugin(&carlaDesc);
  584. }
  585. CARLA_BACKEND_END_NAMESPACE
  586. // -----------------------------------------------------------------------
  587. #endif // ! BUILD_BRIDGE