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.

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