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.

carla-lv2-ui.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2013-2019 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 HAVE_PYQT
  18. # error This file should not be built
  19. #endif
  20. #include "CarlaLv2Utils.hpp"
  21. #include "CarlaPipeUtils.hpp"
  22. #include "CarlaScopeUtils.hpp"
  23. // --------------------------------------------------------------------------------------------------------------------
  24. class NativePluginUI : public LV2_External_UI_Widget_Compat
  25. {
  26. public:
  27. NativePluginUI(LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
  28. LV2UI_Widget* widget, const LV2_Feature* const* features)
  29. : fUridMap(nullptr),
  30. fUridUnmap(nullptr),
  31. fUridTranser(0),
  32. fUridTranser2(0),
  33. fUI()
  34. {
  35. run = extui_run;
  36. show = extui_show;
  37. hide = extui_hide;
  38. fUI.writeFunction = writeFunction;
  39. fUI.controller = controller;
  40. const LV2_URID_Map* uridMap = nullptr;
  41. const LV2_URID_Unmap* uridUnmap = nullptr;
  42. for (int i=0; features[i] != nullptr; ++i)
  43. {
  44. /**/ if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
  45. uridMap = (const LV2_URID_Map*)features[i]->data;
  46. else if (std::strcmp(features[i]->URI, LV2_URID__unmap) == 0)
  47. uridUnmap = (const LV2_URID_Unmap*)features[i]->data;
  48. }
  49. if (uridMap == nullptr)
  50. {
  51. carla_stderr("Host doesn't provide urid-map feature");
  52. return;
  53. }
  54. fUridMap = uridMap;
  55. fUridUnmap = uridUnmap;
  56. fUridTranser = uridMap->map(uridMap->handle, LV2_ATOM__eventTransfer);
  57. fUridTranser2 = uridMap->map(uridMap->handle, "urn:carla:transmitEv");
  58. // ---------------------------------------------------------------
  59. // see if the host supports external-ui
  60. for (int i=0; features[i] != nullptr; ++i)
  61. {
  62. if (std::strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host) == 0 ||
  63. std::strcmp(features[i]->URI, LV2_EXTERNAL_UI_DEPRECATED_URI) == 0)
  64. {
  65. fUI.host = (const LV2_External_UI_Host*)features[i]->data;
  66. break;
  67. }
  68. }
  69. if (fUI.host != nullptr)
  70. {
  71. fUI.name = carla_strdup(fUI.host->plugin_human_id);
  72. *widget = (LV2_External_UI_Widget_Compat*)this;
  73. return;
  74. }
  75. // ---------------------------------------------------------------
  76. // no external-ui support, use showInterface
  77. for (int i=0; features[i] != nullptr; ++i)
  78. {
  79. if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) != 0)
  80. continue;
  81. const LV2_Options_Option* const options((const LV2_Options_Option*)features[i]->data);
  82. CARLA_SAFE_ASSERT_BREAK(options != nullptr);
  83. for (int j=0; options[j].key != 0; ++j)
  84. {
  85. if (options[j].key != uridMap->map(uridMap->handle, LV2_UI__windowTitle))
  86. continue;
  87. const char* const title((const char*)options[j].value);
  88. CARLA_SAFE_ASSERT_BREAK(title != nullptr && title[0] != '\0');
  89. fUI.name = carla_strdup(title);
  90. break;
  91. }
  92. break;
  93. }
  94. if (fUI.name == nullptr)
  95. fUI.name = carla_strdup("Carla");
  96. *widget = nullptr;
  97. }
  98. ~NativePluginUI()
  99. {
  100. if (fUI.isVisible)
  101. writeAtomMessage("quit");
  102. fUI.host = nullptr;
  103. fUI.writeFunction = nullptr;
  104. fUI.controller = nullptr;
  105. }
  106. // ----------------------------------------------------------------------------------------------------------------
  107. void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
  108. {
  109. CARLA_SAFE_ASSERT_RETURN(buffer != nullptr,);
  110. if (format == 0)
  111. {
  112. char msg[128];
  113. const float* const valuePtr = (const float*)buffer;
  114. {
  115. const CarlaScopedLocale csl;
  116. std::snprintf(msg, 127, "control %u %.12g", portIndex, static_cast<double>(*valuePtr));
  117. }
  118. msg[127] = '\0';
  119. writeAtomMessage(msg);
  120. return;
  121. }
  122. if (format == fUridTranser)
  123. {
  124. CARLA_SAFE_ASSERT_RETURN(bufferSize > sizeof(LV2_Atom),);
  125. const LV2_Atom* const atom = (const LV2_Atom*)buffer;
  126. if (atom->type == fUridTranser2)
  127. {
  128. const char* const msg = (const char*)(atom + 1);
  129. if (std::strcmp(msg, "quit") == 0)
  130. {
  131. handleUiClosed();
  132. }
  133. return;
  134. }
  135. }
  136. if (fUridUnmap != nullptr)
  137. {
  138. carla_stdout("lv2ui_port_event %u %u %u:%s %p",
  139. portIndex, bufferSize, format, fUridUnmap->unmap(fUridUnmap->handle, format), buffer);
  140. }
  141. }
  142. // ----------------------------------------------------------------------------------------------------------------
  143. void lv2ui_select_program(uint32_t bank, uint32_t program) const
  144. {
  145. char msg[128];
  146. std::snprintf(msg, 127, "program %u %u", bank, program);
  147. msg[127] = '\0';
  148. writeAtomMessage(msg);
  149. }
  150. // ----------------------------------------------------------------------------------------------------------------
  151. int lv2ui_idle() const
  152. {
  153. if (! fUI.isVisible)
  154. return 1;
  155. handleUiRun();
  156. return 0;
  157. }
  158. int lv2ui_show()
  159. {
  160. handleUiShow();
  161. return 0;
  162. }
  163. int lv2ui_hide()
  164. {
  165. handleUiHide();
  166. return 0;
  167. }
  168. // ----------------------------------------------------------------------------------------------------------------
  169. protected:
  170. void handleUiShow()
  171. {
  172. writeAtomMessage("show");
  173. fUI.isVisible = true;
  174. }
  175. void handleUiHide()
  176. {
  177. if (fUI.isVisible)
  178. {
  179. fUI.isVisible = false;
  180. writeAtomMessage("hide");
  181. }
  182. }
  183. void handleUiRun() const
  184. {
  185. if (fUI.isVisible)
  186. writeAtomMessage("idle");
  187. }
  188. void handleUiClosed()
  189. {
  190. fUI.isVisible = false;
  191. if (fUI.host != nullptr && fUI.host->ui_closed != nullptr && fUI.controller != nullptr)
  192. fUI.host->ui_closed(fUI.controller);
  193. fUI.host = nullptr;
  194. fUI.writeFunction = nullptr;
  195. fUI.controller = nullptr;
  196. }
  197. bool writeAtomMessage(const char* const msg) const
  198. {
  199. CARLA_SAFE_ASSERT_RETURN(fUI.writeFunction != nullptr, false);
  200. CARLA_SAFE_ASSERT_RETURN(fUridTranser != 0, false);
  201. CARLA_SAFE_ASSERT_RETURN(fUridTranser2 != 0, false);
  202. #ifdef DEBUG
  203. if (std::strcmp(msg, "idle") != 0) {
  204. carla_debug("writeAtomMessage(%s)", msg);
  205. }
  206. #endif
  207. const size_t msgSize = std::strlen(msg)+1;
  208. const size_t atomSize = sizeof(LV2_Atom) + msgSize;
  209. if (atomSize <= 128)
  210. {
  211. char atomBuf[atomSize];
  212. carla_zeroChars(atomBuf, atomSize);
  213. LV2_Atom* const atom = (LV2_Atom*)atomBuf;
  214. atom->size = static_cast<uint32_t>(msgSize);
  215. atom->type = fUridTranser2;
  216. std::memcpy(atomBuf+sizeof(LV2_Atom), msg, msgSize);
  217. fUI.writeFunction(fUI.controller, 0, static_cast<uint32_t>(atomSize), fUridTranser, atomBuf);
  218. }
  219. else
  220. {
  221. char* const atomBuf = new char[atomSize];
  222. carla_zeroChars(atomBuf, atomSize);
  223. LV2_Atom* const atom = (LV2_Atom*)atomBuf;
  224. atom->size = static_cast<uint32_t>(msgSize);
  225. atom->type = fUridTranser2;
  226. std::memcpy(atomBuf+sizeof(LV2_Atom), msg, msgSize);
  227. fUI.writeFunction(fUI.controller, 0, static_cast<uint32_t>(atomSize), fUridTranser, atomBuf);
  228. delete[] atomBuf;
  229. }
  230. return true;
  231. }
  232. // ----------------------------------------------------------------------------------------------------------------
  233. private:
  234. const LV2_URID_Map* fUridMap;
  235. const LV2_URID_Unmap* fUridUnmap;
  236. LV2_URID fUridTranser, fUridTranser2;
  237. struct UI {
  238. const LV2_External_UI_Host* host;
  239. LV2UI_Write_Function writeFunction;
  240. LV2UI_Controller controller;
  241. const char* name;
  242. bool isVisible;
  243. UI()
  244. : host(nullptr),
  245. writeFunction(nullptr),
  246. controller(nullptr),
  247. name(nullptr),
  248. isVisible(false) {}
  249. ~UI()
  250. {
  251. if (name != nullptr)
  252. {
  253. delete[] name;
  254. name = nullptr;
  255. }
  256. }
  257. CARLA_DECLARE_NON_COPY_STRUCT(UI);
  258. } fUI;
  259. // ----------------------------------------------------------------------------------------------------------------
  260. #define handlePtr ((NativePluginUI*)handle)
  261. static void extui_run(LV2_External_UI_Widget_Compat* handle)
  262. {
  263. handlePtr->handleUiRun();
  264. }
  265. static void extui_show(LV2_External_UI_Widget_Compat* handle)
  266. {
  267. carla_debug("extui_show(%p)", handle);
  268. handlePtr->handleUiShow();
  269. }
  270. static void extui_hide(LV2_External_UI_Widget_Compat* handle)
  271. {
  272. carla_debug("extui_hide(%p)", handle);
  273. handlePtr->handleUiHide();
  274. }
  275. #undef handlePtr
  276. // -------------------------------------------------------------------
  277. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NativePluginUI)
  278. };
  279. // -----------------------------------------------------------------------
  280. // LV2 UI descriptor functions
  281. static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char*, const char*,
  282. LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
  283. LV2UI_Widget* widget, const LV2_Feature* const* features)
  284. {
  285. carla_debug("lv2ui_instantiate(..., %p, %p, %p)", writeFunction, controller, widget, features);
  286. NativePluginUI* const ui = new NativePluginUI(writeFunction, controller, widget, features);
  287. // TODO: check ok
  288. return (LV2UI_Handle)ui;
  289. }
  290. #define uiPtr ((NativePluginUI*)ui)
  291. static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
  292. {
  293. carla_debug("lv2ui_port_event(%p, %i, %i, %i, %p)", ui, portIndex, bufferSize, format, buffer);
  294. uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
  295. }
  296. static void lv2ui_cleanup(LV2UI_Handle ui)
  297. {
  298. carla_debug("lv2ui_cleanup(%p)", ui);
  299. delete uiPtr;
  300. }
  301. static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program)
  302. {
  303. carla_debug("lv2ui_select_program(%p, %i, %i)", ui, bank, program);
  304. uiPtr->lv2ui_select_program(bank, program);
  305. }
  306. static int lv2ui_idle(LV2UI_Handle ui)
  307. {
  308. return uiPtr->lv2ui_idle();
  309. }
  310. static int lv2ui_show(LV2UI_Handle ui)
  311. {
  312. carla_debug("lv2ui_show(%p)", ui);
  313. return uiPtr->lv2ui_show();
  314. }
  315. static int lv2ui_hide(LV2UI_Handle ui)
  316. {
  317. carla_debug("lv2ui_hide(%p)", ui);
  318. return uiPtr->lv2ui_hide();
  319. }
  320. static const void* lv2ui_extension_data(const char* uri)
  321. {
  322. carla_stdout("lv2ui_extension_data(\"%s\")", uri);
  323. static const LV2UI_Idle_Interface uiidle = { lv2ui_idle };
  324. static const LV2UI_Show_Interface uishow = { lv2ui_show, lv2ui_hide };
  325. static const LV2_Programs_UI_Interface uiprograms = { lv2ui_select_program };
  326. if (std::strcmp(uri, LV2_UI__idleInterface) == 0)
  327. return &uiidle;
  328. if (std::strcmp(uri, LV2_UI__showInterface) == 0)
  329. return &uishow;
  330. if (std::strcmp(uri, LV2_PROGRAMS__UIInterface) == 0)
  331. return &uiprograms;
  332. return nullptr;
  333. }
  334. #undef uiPtr
  335. // -----------------------------------------------------------------------
  336. // Startup code
  337. CARLA_EXPORT
  338. const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
  339. {
  340. carla_debug("lv2ui_descriptor(%i)", index);
  341. static const LV2UI_Descriptor lv2UiExtDesc = {
  342. /* URI */ "http://kxstudio.sf.net/carla/ui-bridge-ext",
  343. /* instantiate */ lv2ui_instantiate,
  344. /* cleanup */ lv2ui_cleanup,
  345. /* port_event */ lv2ui_port_event,
  346. /* extension_data */ lv2ui_extension_data
  347. };
  348. return (index == 0) ? &lv2UiExtDesc : nullptr;
  349. }
  350. // -----------------------------------------------------------------------
  351. #include "CarlaPipeUtils.cpp"
  352. // -----------------------------------------------------------------------