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.

839 lines
24KB

  1. /*
  2. * Carla LV2 Single Plugin
  3. * Copyright (C) 2017 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. # error This file should not be compiled if not building bridge
  19. #endif
  20. #include "engine/CarlaEngineInternal.hpp"
  21. #include "CarlaPlugin.hpp"
  22. #include "CarlaBackendUtils.hpp"
  23. #include "CarlaEngineUtils.hpp"
  24. #include "CarlaLv2Utils.hpp"
  25. #include "CarlaUtils.h"
  26. #include "AppConfig.h"
  27. #include "juce_core/juce_core.h"
  28. // ---------------------------------------------------------------------------------------------------------------------
  29. CARLA_BACKEND_START_NAMESPACE
  30. class CarlaEngineLV2Single : public CarlaEngine,
  31. public LV2_External_UI_Widget
  32. {
  33. public:
  34. CarlaEngineLV2Single(const uint32_t bufferSize, const double sampleRate, const char* const bundlePath, const LV2_URID_Map* uridMap)
  35. : fPlugin(nullptr),
  36. fIsActive(false),
  37. fIsOffline(false)
  38. {
  39. run = extui_run;
  40. show = extui_show;
  41. hide = extui_hide;
  42. CARLA_SAFE_ASSERT_RETURN(pData->curPluginCount == 0,)
  43. CARLA_SAFE_ASSERT_RETURN(pData->plugins[0].plugin == nullptr,);
  44. // xxxxx
  45. CarlaString binaryDir(bundlePath);
  46. binaryDir += CARLA_OS_SEP_STR "bin" CARLA_OS_SEP_STR;
  47. CarlaString resourceDir(bundlePath);
  48. resourceDir += CARLA_OS_SEP_STR "res" CARLA_OS_SEP_STR;
  49. pData->bufferSize = bufferSize;
  50. pData->sampleRate = sampleRate;
  51. pData->initTime(nullptr);
  52. pData->options.processMode = ENGINE_PROCESS_MODE_BRIDGE;
  53. pData->options.transportMode = ENGINE_TRANSPORT_MODE_PLUGIN;
  54. pData->options.forceStereo = false;
  55. pData->options.preferPluginBridges = false;
  56. pData->options.preferUiBridges = false;
  57. init("LV2-Export");
  58. if (pData->options.resourceDir != nullptr)
  59. delete[] pData->options.resourceDir;
  60. if (pData->options.binaryDir != nullptr)
  61. delete[] pData->options.binaryDir;
  62. pData->options.binaryDir = binaryDir.dup();
  63. pData->options.resourceDir = resourceDir.dup();
  64. setCallback(_engine_callback, this);
  65. using juce::File;
  66. const File pluginFile(File::getSpecialLocation(File::currentExecutableFile).withFileExtension("xml"));
  67. if (! loadProject(pluginFile.getFullPathName().toRawUTF8()))
  68. {
  69. carla_stderr2("Failed to init plugin, possible reasons: %s", getLastError());
  70. return;
  71. }
  72. CARLA_SAFE_ASSERT_RETURN(pData->curPluginCount == 1,)
  73. fPlugin = pData->plugins[0].plugin;
  74. CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
  75. CARLA_SAFE_ASSERT_RETURN(fPlugin->isEnabled(),);
  76. fPorts.init(fPlugin);
  77. }
  78. ~CarlaEngineLV2Single()
  79. {
  80. if (fPlugin != nullptr && fIsActive)
  81. fPlugin->setActive(false, false, false);
  82. close();
  83. }
  84. bool hasPlugin() noexcept
  85. {
  86. return fPlugin != nullptr;
  87. }
  88. // -----------------------------------------------------------------------------------------------------------------
  89. // LV2 functions
  90. void lv2_connect_port(const uint32_t port, void* const dataLocation) noexcept
  91. {
  92. fPorts.connectPort(port, dataLocation);
  93. }
  94. void lv2_activate() noexcept
  95. {
  96. CARLA_SAFE_ASSERT_RETURN(! fIsActive,);
  97. fPlugin->setActive(true, false, false);
  98. fIsActive = true;
  99. }
  100. void lv2_deactivate() noexcept
  101. {
  102. CARLA_SAFE_ASSERT_RETURN(fIsActive,);
  103. fIsActive = false;
  104. fPlugin->setActive(false, false, false);
  105. }
  106. void lv2_run(const uint32_t frames)
  107. {
  108. fIsOffline = (fPorts.freewheel != nullptr && *fPorts.freewheel >= 0.5f);
  109. // Check for updated parameters
  110. float curValue;
  111. for (uint32_t i=0; i < fPorts.numParams; ++i)
  112. {
  113. if (fPorts.paramsOut[i])
  114. continue;
  115. CARLA_SAFE_ASSERT_CONTINUE(fPorts.paramsPtr[i] != nullptr)
  116. curValue = *fPorts.paramsPtr[i];
  117. if (carla_isEqual(fPorts.paramsLast[i], curValue))
  118. continue;
  119. fPorts.paramsLast[i] = curValue;
  120. fPlugin->setParameterValue(i, curValue, false, false, false);
  121. }
  122. if (frames == 0)
  123. return fPorts.updateOutputs();
  124. if (fPlugin->tryLock(fIsOffline))
  125. {
  126. fPlugin->initBuffers();
  127. fPlugin->process(fPorts.audioIns, fPorts.audioOuts, nullptr, nullptr, frames);
  128. fPlugin->unlock();
  129. }
  130. else
  131. {
  132. for (uint32_t i=0; i<fPorts.numAudioOuts; ++i)
  133. carla_zeroFloats(fPorts.audioOuts[i], frames);
  134. }
  135. fPorts.updateOutputs();
  136. }
  137. // -----------------------------------------------------------------------------------------------------------------
  138. void lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
  139. LV2UI_Widget* widget, const LV2_Feature* const* features)
  140. {
  141. fUI.writeFunction = writeFunction;
  142. fUI.controller = controller;
  143. const LV2_URID_Map* uridMap = nullptr;
  144. // -------------------------------------------------------------------------------------------------------------
  145. // see if the host supports external-ui, get uridMap
  146. for (int i=0; features[i] != nullptr; ++i)
  147. {
  148. if (std::strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host) == 0 ||
  149. std::strcmp(features[i]->URI, LV2_EXTERNAL_UI_DEPRECATED_URI) == 0)
  150. {
  151. fUI.host = (const LV2_External_UI_Host*)features[i]->data;
  152. }
  153. else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
  154. {
  155. uridMap = (const LV2_URID_Map*)features[i]->data;
  156. }
  157. }
  158. if (fUI.host != nullptr)
  159. {
  160. fUI.name = fUI.host->plugin_human_id;
  161. *widget = (LV2_External_UI_Widget*)this;
  162. return;
  163. }
  164. // -------------------------------------------------------------------------------------------------------------
  165. // no external-ui support, use showInterface
  166. for (int i=0; features[i] != nullptr; ++i)
  167. {
  168. if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
  169. {
  170. const LV2_Options_Option* const options((const LV2_Options_Option*)features[i]->data);
  171. for (int j=0; options[j].key != 0; ++j)
  172. {
  173. if (options[j].key == uridMap->map(uridMap->handle, LV2_UI__windowTitle))
  174. {
  175. fUI.name = (const char*)options[j].value;
  176. break;
  177. }
  178. }
  179. break;
  180. }
  181. }
  182. if (fUI.name.isEmpty())
  183. fUI.name = fPlugin->getName();
  184. *widget = nullptr;
  185. }
  186. void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) const
  187. {
  188. if (format != 0 || bufferSize != sizeof(float) || buffer == nullptr)
  189. return;
  190. if (portIndex >= fPorts.indexOffset || ! fUI.visible)
  191. return;
  192. const float value(*(const float*)buffer);
  193. fPlugin->uiParameterChange(portIndex-fPorts.indexOffset, value);
  194. }
  195. void lv2ui_cleanup()
  196. {
  197. if (fUI.visible)
  198. handleUiHide();
  199. fUI.writeFunction = nullptr;
  200. fUI.controller = nullptr;
  201. fUI.host = nullptr;
  202. }
  203. // -----------------------------------------------------------------------------------------------------------------
  204. int lv2ui_idle() const
  205. {
  206. if (! fUI.visible)
  207. return 1;
  208. handleUiRun();
  209. return 0;
  210. }
  211. int lv2ui_show()
  212. {
  213. handleUiShow();
  214. return 0;
  215. }
  216. int lv2ui_hide()
  217. {
  218. handleUiHide();
  219. return 0;
  220. }
  221. protected:
  222. // -----------------------------------------------------------------------------------------------------------------
  223. // CarlaEngine virtual calls
  224. bool init(const char* const clientName) override
  225. {
  226. carla_stdout("CarlaEngineNative::init(\"%s\")", clientName);
  227. if (! pData->init(clientName))
  228. {
  229. close();
  230. setLastError("Failed to init internal data");
  231. return false;
  232. }
  233. return true;
  234. }
  235. bool isRunning() const noexcept override
  236. {
  237. return fIsActive;
  238. }
  239. bool isOffline() const noexcept override
  240. {
  241. return fIsOffline;
  242. }
  243. bool usesConstantBufferSize() const noexcept override
  244. {
  245. return true;
  246. }
  247. EngineType getType() const noexcept override
  248. {
  249. return kEngineTypePlugin;
  250. }
  251. const char* getCurrentDriverName() const noexcept override
  252. {
  253. return "LV2 Plugin";
  254. }
  255. void engineCallback(const EngineCallbackOpcode action, const uint pluginId, const int value1, const int value2, const float value3, const char* const valueStr)
  256. {
  257. switch (action)
  258. {
  259. case ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED:
  260. if (fUI.writeFunction != nullptr && fUI.controller != nullptr && fUI.visible)
  261. fUI.writeFunction(fUI.controller, value1+fPorts.indexOffset, sizeof(float), 0, &value3);
  262. break;
  263. case ENGINE_CALLBACK_UI_STATE_CHANGED:
  264. fUI.visible = value1 == 1;
  265. if (fUI.host != nullptr)
  266. fUI.host->ui_closed(fUI.controller);
  267. break;
  268. case ENGINE_CALLBACK_IDLE:
  269. break;
  270. default:
  271. carla_stdout("engineCallback(%i:%s, %u, %i, %i, %f, %s)", action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3, valueStr);
  272. break;
  273. }
  274. }
  275. // -----------------------------------------------------------------------------------------------------------------
  276. void handleUiRun() const
  277. {
  278. try {
  279. fPlugin->uiIdle();
  280. } CARLA_SAFE_EXCEPTION("fPlugin->uiIdle()")
  281. }
  282. void handleUiShow()
  283. {
  284. fPlugin->showCustomUI(true);
  285. fUI.visible = true;
  286. }
  287. void handleUiHide()
  288. {
  289. fUI.visible = false;
  290. fPlugin->showCustomUI(false);
  291. }
  292. // -----------------------------------------------------------------------------------------------------------------
  293. private:
  294. CarlaPlugin* fPlugin;
  295. // Lv2 host data
  296. bool fIsActive;
  297. bool fIsOffline;
  298. struct Ports {
  299. uint32_t numAudioIns;
  300. uint32_t numAudioOuts;
  301. uint32_t numParams;
  302. const float** audioIns;
  303. /* */ float** audioOuts;
  304. float* freewheel;
  305. float* paramsLast;
  306. float** paramsPtr;
  307. bool* paramsOut;
  308. uint32_t indexOffset;
  309. const CarlaPlugin* plugin;
  310. Ports()
  311. : numAudioIns(0),
  312. numAudioOuts(0),
  313. numParams(0),
  314. audioIns(nullptr),
  315. audioOuts(nullptr),
  316. freewheel(nullptr),
  317. paramsLast(nullptr),
  318. paramsPtr(nullptr),
  319. paramsOut(nullptr),
  320. indexOffset(1),
  321. plugin(nullptr) {}
  322. ~Ports()
  323. {
  324. if (audioIns != nullptr)
  325. {
  326. delete[] audioIns;
  327. audioIns = nullptr;
  328. }
  329. if (audioOuts != nullptr)
  330. {
  331. delete[] audioOuts;
  332. audioOuts = nullptr;
  333. }
  334. if (paramsLast != nullptr)
  335. {
  336. delete[] paramsLast;
  337. paramsLast = nullptr;
  338. }
  339. if (paramsPtr != nullptr)
  340. {
  341. delete[] paramsPtr;
  342. paramsPtr = nullptr;
  343. }
  344. if (paramsOut != nullptr)
  345. {
  346. delete[] paramsOut;
  347. paramsOut = nullptr;
  348. }
  349. }
  350. void init(const CarlaPlugin* const pl)
  351. {
  352. plugin = pl;
  353. if ((numAudioIns = plugin->getAudioInCount()) > 0)
  354. {
  355. audioIns = new const float*[numAudioIns];
  356. for (uint32_t i=0; i < numAudioIns; ++i)
  357. audioIns[i] = nullptr;
  358. }
  359. if ((numAudioOuts = plugin->getAudioOutCount()) > 0)
  360. {
  361. audioOuts = new float*[numAudioOuts];
  362. for (uint32_t i=0; i < numAudioOuts; ++i)
  363. audioOuts[i] = nullptr;
  364. }
  365. if ((numParams = plugin->getParameterCount()) > 0)
  366. {
  367. paramsLast = new float[numParams];
  368. paramsPtr = new float*[numParams];
  369. paramsOut = new bool[numParams];
  370. for (uint32_t i=0; i < numParams; ++i)
  371. {
  372. paramsLast[i] = plugin->getParameterValue(i);
  373. paramsPtr [i] = nullptr;
  374. paramsOut [i] = plugin->isParameterOutput(i);
  375. }
  376. }
  377. indexOffset = numAudioIns + numAudioOuts + 1;
  378. }
  379. void connectPort(const uint32_t port, void* const dataLocation) noexcept
  380. {
  381. uint32_t index = 0;
  382. for (uint32_t i=0; i < numAudioIns; ++i)
  383. {
  384. if (port == index++)
  385. {
  386. audioIns[i] = (float*)dataLocation;
  387. return;
  388. }
  389. }
  390. for (uint32_t i=0; i < numAudioOuts; ++i)
  391. {
  392. if (port == index++)
  393. {
  394. audioOuts[i] = (float*)dataLocation;
  395. return;
  396. }
  397. }
  398. if (port == index++)
  399. {
  400. freewheel = (float*)dataLocation;
  401. return;
  402. }
  403. for (uint32_t i=0; i < numParams; ++i)
  404. {
  405. if (port == index++)
  406. {
  407. paramsPtr[i] = (float*)dataLocation;
  408. return;
  409. }
  410. }
  411. }
  412. void updateOutputs() noexcept
  413. {
  414. for (uint32_t i=0; i < numParams; ++i)
  415. {
  416. if (! paramsOut[i])
  417. continue;
  418. paramsLast[i] = plugin->getParameterValue(i);
  419. if (paramsPtr[i] != nullptr)
  420. *paramsPtr[i] = paramsLast[i];
  421. }
  422. }
  423. CARLA_DECLARE_NON_COPY_STRUCT(Ports);
  424. } fPorts;
  425. struct UI {
  426. LV2UI_Write_Function writeFunction;
  427. LV2UI_Controller controller;
  428. const LV2_External_UI_Host* host;
  429. CarlaString name;
  430. bool visible;
  431. UI()
  432. : writeFunction(nullptr),
  433. controller(nullptr),
  434. host(nullptr),
  435. visible(false) {}
  436. } fUI;
  437. // -------------------------------------------------------------------
  438. #define handlePtr ((CarlaEngineLV2Single*)handle)
  439. static void extui_run(LV2_External_UI_Widget* handle)
  440. {
  441. handlePtr->handleUiRun();
  442. }
  443. static void extui_show(LV2_External_UI_Widget* handle)
  444. {
  445. handlePtr->handleUiShow();
  446. }
  447. static void extui_hide(LV2_External_UI_Widget* handle)
  448. {
  449. handlePtr->handleUiHide();
  450. }
  451. static void _engine_callback(void* handle, EngineCallbackOpcode action, uint pluginId, int value1, int value2, float value3, const char* valueStr)
  452. {
  453. handlePtr->engineCallback(action, pluginId, value1, value2, value3, valueStr);
  454. }
  455. #undef handlePtr
  456. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineLV2Single)
  457. };
  458. CARLA_BACKEND_END_NAMESPACE
  459. CARLA_BACKEND_USE_NAMESPACE
  460. // ---------------------------------------------------------------------------------------------------------------------
  461. // LV2 DSP functions
  462. static LV2_Handle lv2_instantiate(const LV2_Descriptor* lv2Descriptor, double sampleRate, const char* bundlePath, const LV2_Feature* const* features)
  463. {
  464. carla_stdout("lv2_instantiate(%p, %g, %s, %p)", lv2Descriptor, sampleRate, bundlePath, features);
  465. const LV2_Options_Option* options = nullptr;
  466. const LV2_URID_Map* uridMap = nullptr;
  467. const LV2_URID_Unmap* uridUnmap = nullptr;
  468. for (int i=0; features[i] != nullptr; ++i)
  469. {
  470. if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
  471. options = (const LV2_Options_Option*)features[i]->data;
  472. else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
  473. uridMap = (const LV2_URID_Map*)features[i]->data;
  474. else if (std::strcmp(features[i]->URI, LV2_URID__unmap) == 0)
  475. uridUnmap = (const LV2_URID_Unmap*)features[i]->data;
  476. }
  477. if (options == nullptr || uridMap == nullptr)
  478. {
  479. carla_stderr("Host doesn't provide option or urid-map features");
  480. return nullptr;
  481. }
  482. uint32_t bufferSize = 0;
  483. for (int i=0; options[i].key != 0; ++i)
  484. {
  485. if (uridUnmap != nullptr) {
  486. carla_stdout("Host option %i:\"%s\"", i, uridUnmap->unmap(uridUnmap->handle, options[i].key));
  487. }
  488. if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__nominalBlockLength))
  489. {
  490. if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int))
  491. {
  492. const int value(*(const int*)options[i].value);
  493. CARLA_SAFE_ASSERT_CONTINUE(value > 0);
  494. bufferSize = static_cast<uint32_t>(value);
  495. }
  496. else
  497. {
  498. carla_stderr("Host provides nominalBlockLength but has wrong value type");
  499. }
  500. break;
  501. }
  502. if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength))
  503. {
  504. if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int))
  505. {
  506. const int value(*(const int*)options[i].value);
  507. CARLA_SAFE_ASSERT_CONTINUE(value > 0);
  508. bufferSize = static_cast<uint32_t>(value);
  509. }
  510. else
  511. {
  512. carla_stderr("Host provides maxBlockLength but has wrong value type");
  513. }
  514. // no break, continue in case host supports nominalBlockLength
  515. }
  516. }
  517. if (bufferSize == 0)
  518. {
  519. carla_stderr("Host doesn't provide bufferSize feature");
  520. return nullptr;
  521. }
  522. CarlaEngineLV2Single* const instance(new CarlaEngineLV2Single(bufferSize, sampleRate, bundlePath, uridMap));
  523. if (instance->hasPlugin())
  524. return (LV2_Handle)instance;
  525. delete instance;
  526. return nullptr;
  527. }
  528. #define instancePtr ((CarlaEngineLV2Single*)instance)
  529. static void lv2_connect_port(LV2_Handle instance, uint32_t port, void* dataLocation)
  530. {
  531. instancePtr->lv2_connect_port(port, dataLocation);
  532. }
  533. static void lv2_activate(LV2_Handle instance)
  534. {
  535. carla_stdout("lv2_activate(%p)", instance);
  536. instancePtr->lv2_activate();
  537. }
  538. static void lv2_run(LV2_Handle instance, uint32_t sampleCount)
  539. {
  540. instancePtr->lv2_run(sampleCount);
  541. }
  542. static void lv2_deactivate(LV2_Handle instance)
  543. {
  544. carla_stdout("lv2_deactivate(%p)", instance);
  545. instancePtr->lv2_deactivate();
  546. }
  547. static void lv2_cleanup(LV2_Handle instance)
  548. {
  549. carla_stdout("lv2_cleanup(%p)", instance);
  550. delete instancePtr;
  551. }
  552. static const void* lv2_extension_data(const char* uri)
  553. {
  554. carla_stdout("lv2_extension_data(\"%s\")", uri);
  555. return nullptr;
  556. }
  557. #undef instancePtr
  558. // ---------------------------------------------------------------------------------------------------------------------
  559. // LV2 UI functions
  560. static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char*, const char*,
  561. LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
  562. LV2UI_Widget* widget, const LV2_Feature* const* features)
  563. {
  564. carla_debug("lv2ui_instantiate(..., %p, %p, %p)", writeFunction, controller, widget, features);
  565. CarlaEngineLV2Single* engine = nullptr;
  566. for (int i=0; features[i] != nullptr; ++i)
  567. {
  568. if (std::strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0)
  569. {
  570. engine = (CarlaEngineLV2Single*)features[i]->data;
  571. break;
  572. }
  573. }
  574. if (engine == nullptr)
  575. {
  576. carla_stderr("Host doesn't support instance-access, cannot show UI");
  577. return nullptr;
  578. }
  579. engine->lv2ui_instantiate(writeFunction, controller, widget, features);
  580. return (LV2UI_Handle)engine;
  581. }
  582. #define uiPtr ((CarlaEngineLV2Single*)ui)
  583. static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
  584. {
  585. carla_debug("lv2ui_port_event(%p, %i, %i, %i, %p)", ui, portIndex, bufferSize, format, buffer);
  586. uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
  587. }
  588. static void lv2ui_cleanup(LV2UI_Handle ui)
  589. {
  590. carla_debug("lv2ui_cleanup(%p)", ui);
  591. uiPtr->lv2ui_cleanup();
  592. }
  593. static int lv2ui_idle(LV2UI_Handle ui)
  594. {
  595. return uiPtr->lv2ui_idle();
  596. }
  597. static int lv2ui_show(LV2UI_Handle ui)
  598. {
  599. carla_debug("lv2ui_show(%p)", ui);
  600. return uiPtr->lv2ui_show();
  601. }
  602. static int lv2ui_hide(LV2UI_Handle ui)
  603. {
  604. carla_debug("lv2ui_hide(%p)", ui);
  605. return uiPtr->lv2ui_hide();
  606. }
  607. static const void* lv2ui_extension_data(const char* uri)
  608. {
  609. carla_stdout("lv2ui_extension_data(\"%s\")", uri);
  610. static const LV2UI_Idle_Interface uiidle = { lv2ui_idle };
  611. static const LV2UI_Show_Interface uishow = { lv2ui_show, lv2ui_hide };
  612. if (std::strcmp(uri, LV2_UI__idleInterface) == 0)
  613. return &uiidle;
  614. if (std::strcmp(uri, LV2_UI__showInterface) == 0)
  615. return &uishow;
  616. return nullptr;
  617. }
  618. #undef uiPtr
  619. // ---------------------------------------------------------------------------------------------------------------------
  620. // Startup code
  621. CARLA_EXPORT
  622. const LV2_Descriptor* lv2_descriptor(uint32_t index)
  623. {
  624. carla_stdout("lv2_descriptor(%i)", index);
  625. if (index != 0)
  626. return nullptr;
  627. static CarlaString ret;
  628. if (ret.isEmpty())
  629. {
  630. using namespace juce;
  631. const File file(File::getSpecialLocation(File::currentExecutableFile).withFileExtension("ttl"));
  632. ret = String("file://" + file.getFullPathName()).toRawUTF8();
  633. }
  634. static const LV2_Descriptor desc = {
  635. /* URI */ ret.buffer(),
  636. /* instantiate */ lv2_instantiate,
  637. /* connect_port */ lv2_connect_port,
  638. /* activate */ lv2_activate,
  639. /* run */ lv2_run,
  640. /* deactivate */ lv2_deactivate,
  641. /* cleanup */ lv2_cleanup,
  642. /* extension_data */ lv2_extension_data
  643. };
  644. return &desc;
  645. }
  646. CARLA_EXPORT
  647. const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
  648. {
  649. carla_stdout("lv2ui_descriptor(%i)", index);
  650. static CarlaString ret;
  651. if (ret.isEmpty())
  652. {
  653. using namespace juce;
  654. const File file(File::getSpecialLocation(File::currentExecutableFile).getSiblingFile("ext-ui"));
  655. ret = String("file://" + file.getFullPathName()).toRawUTF8();
  656. }
  657. static const LV2UI_Descriptor lv2UiExtDesc = {
  658. /* URI */ ret.buffer(),
  659. /* instantiate */ lv2ui_instantiate,
  660. /* cleanup */ lv2ui_cleanup,
  661. /* port_event */ lv2ui_port_event,
  662. /* extension_data */ lv2ui_extension_data
  663. };
  664. return (index == 0) ? &lv2UiExtDesc : nullptr;
  665. }
  666. // ---------------------------------------------------------------------------------------------------------------------