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.

437 lines
13KB

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