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

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