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.

872 lines
25KB

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