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.

516 lines
15KB

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