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.

372 lines
8.8KB

  1. /*
  2. * DISTRHO Plugin Toolkit (DPT)
  3. * Copyright (C) 2012-2013 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 Lesser General Public
  7. * License as published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Lesser General Public License for more details.
  13. *
  14. * For a full copy of the license see the LGPL.txt file
  15. */
  16. #include "DistrhoDefines.h"
  17. #if defined(DISTRHO_PLUGIN_TARGET_LV2) && DISTRHO_PLUGIN_HAS_UI
  18. #include "DistrhoUIInternal.h"
  19. #include "lv2-sdk/lv2.h"
  20. //#include "lv2-sdk/atom.h"
  21. //#include "lv2-sdk/atom-util.h"
  22. //#include "lv2-sdk/midi.h"
  23. #include "lv2-sdk/patch.h"
  24. #include "lv2-sdk/programs.h"
  25. //#include "lv2-sdk/state.h"
  26. #include "lv2-sdk/urid.h"
  27. #include "lv2-sdk/ui.h"
  28. #include <cassert>
  29. #include <cstring>
  30. #include <thread>
  31. #ifdef DISTRHO_UI_QT4
  32. # include <QtCore/QTimerEvent>
  33. #endif
  34. #ifndef DISTRHO_PLUGIN_URI
  35. # error DISTRHO_PLUGIN_URI undefined!
  36. #endif
  37. #define DISTRHO_LV2UI_USE_EXTENSION_DATA (DISTRHO_PLUGIN_WANT_PROGRAMS)
  38. // -------------------------------------------------
  39. START_NAMESPACE_DISTRHO
  40. #ifdef DISTRHO_UI_QT4
  41. class UILv2 : public QObject
  42. #else
  43. class UILv2
  44. #endif
  45. {
  46. public:
  47. UILv2(intptr_t parent, LV2UI_Controller controller, LV2UI_Write_Function writeFunction, LV2UI_Widget* widget, const LV2_Feature* const* features)
  48. : ui(this, parent, setParameterCallback, setStateCallback, uiNoteCallback, uiResizeCallback),
  49. lv2Controller(controller),
  50. lv2WriteFunction(writeFunction),
  51. lv2UiResize(nullptr),
  52. #if DISTRHO_PLUGIN_WANT_STATE
  53. uridIdPatchMessage(0),
  54. #endif
  55. #ifdef DISTRHO_UI_QT4
  56. uiTimer(0)
  57. #else
  58. threadExitNow(false),
  59. threadRunning(false),
  60. thread(uiThreadCallback, this)
  61. #endif
  62. {
  63. // Get Features
  64. for (uint32_t i = 0; features[i]; i++)
  65. {
  66. if (strcmp(features[i]->URI, LV2_URID__map) == 0)
  67. {
  68. #if DISTRHO_PLUGIN_WANT_STATE
  69. LV2_URID_Map* uridMap = (LV2_URID_Map*)features[i]->data;
  70. uridIdPatchMessage = uridMap->map(uridMap->handle, LV2_PATCH__Message);
  71. #endif
  72. }
  73. else if (strcmp(features[i]->URI, LV2_UI__resize) == 0)
  74. lv2UiResize = (LV2UI_Resize*)features[i]->data;
  75. }
  76. #ifndef DISTRHO_UI_QT4
  77. assert(parent);
  78. if (parent == 0)
  79. return;
  80. #endif
  81. if (lv2UiResize)
  82. lv2UiResize->ui_resize(lv2UiResize->handle, ui.getWidth(), ui.getHeight());
  83. #ifdef DISTRHO_UI_QT4
  84. uiTimer = startTimer(30);
  85. #endif
  86. *widget = (void*)ui.getWindowId();
  87. }
  88. ~UILv2()
  89. {
  90. #ifdef DISTRHO_UI_QT4
  91. if (uiTimer)
  92. killTimer(uiTimer);
  93. #else
  94. uiThreadClose();
  95. #endif
  96. }
  97. // ---------------------------------------------
  98. void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
  99. {
  100. if (format == 0)
  101. {
  102. if (bufferSize != sizeof(float))
  103. return;
  104. if (int32_t(portIndex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS) < 0)
  105. return;
  106. float value = *(float*)buffer;
  107. ui.parameterChanged(portIndex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS, value);
  108. }
  109. // TODO - atom events
  110. }
  111. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  112. void lv2ui_select_program(uint32_t bank, uint32_t program)
  113. {
  114. const uint32_t index = bank * 128 + program;
  115. ui.programChanged(index);
  116. }
  117. #endif
  118. // ---------------------------------------------
  119. protected:
  120. void setParameterValue(uint32_t index, float value)
  121. {
  122. if (lv2WriteFunction)
  123. lv2WriteFunction(lv2Controller, DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + index, sizeof(float), 0, &value);
  124. }
  125. void setState(const char* key, const char* value)
  126. {
  127. // TODO
  128. (void)key;
  129. (void)value;
  130. //if (lv2WriteFunction && uridMap)
  131. {
  132. }
  133. }
  134. void uiResize(unsigned int width, unsigned int height)
  135. {
  136. if (lv2UiResize)
  137. lv2UiResize->ui_resize(lv2UiResize->handle, width, height);
  138. }
  139. #ifdef DISTRHO_UI_QT4
  140. void timerEvent(QTimerEvent* event)
  141. {
  142. if (event->timerId() == uiTimer)
  143. ui.idle();
  144. QObject::timerEvent(event);
  145. }
  146. #else
  147. void uiThreadRun()
  148. {
  149. threadRunning = true;
  150. while (! threadExitNow)
  151. {
  152. ui.idle();
  153. d_msleep(1000 / 25); // 25 FPS
  154. }
  155. thread.detach();
  156. threadRunning = false;
  157. }
  158. void uiThreadClose()
  159. {
  160. threadExitNow = true;
  161. while (threadRunning)
  162. d_msleep(1000 / 25); // 25 FPS
  163. }
  164. #endif
  165. private:
  166. UIInternal ui;
  167. // LV2 Stuff
  168. LV2UI_Controller const lv2Controller;
  169. LV2UI_Write_Function const lv2WriteFunction;
  170. const LV2UI_Resize* lv2UiResize;
  171. #if DISTRHO_PLUGIN_WANT_STATE
  172. LV2_URID uridIdPatchMessage;
  173. #endif
  174. #ifdef DISTRHO_UI_QT4
  175. int uiTimer;
  176. #else
  177. // UI Thread
  178. bool threadExitNow;
  179. bool threadRunning;
  180. std::thread thread;
  181. #endif
  182. // ---------------------------------------------
  183. // Callbacks
  184. static void setParameterCallback(void* ptr, uint32_t index, float value)
  185. {
  186. UILv2* _this_ = (UILv2*)ptr;
  187. assert(_this_);
  188. _this_->setParameterValue(index, value);
  189. }
  190. static void setStateCallback(void* ptr, const char* key, const char* value)
  191. {
  192. #if DISTRHO_PLUGIN_WANT_STATE
  193. UILv2* _this_ = (UILv2*)ptr;
  194. assert(_this_);
  195. _this_->setState(key, value);
  196. #else
  197. // unused
  198. (void)ptr;
  199. (void)key;
  200. (void)value;
  201. #endif
  202. }
  203. static void uiNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity)
  204. {
  205. #if DISTRHO_PLUGIN_IS_SYNTH
  206. UILv2* _this_ = (UILv2*)ptr;
  207. assert(_this_);
  208. _this_->uiNote(onOff, channel, note, velocity);
  209. #else
  210. // unused
  211. (void)ptr;
  212. (void)onOff;
  213. (void)channel;
  214. (void)note;
  215. (void)velocity;
  216. #endif
  217. }
  218. static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height)
  219. {
  220. UILv2* _this_ = (UILv2*)ptr;
  221. assert(_this_);
  222. _this_->uiResize(width, height);
  223. }
  224. #ifndef DISTRHO_UI_QT4
  225. static void uiThreadCallback(void* ptr)
  226. {
  227. UILv2* _this_ = (UILv2*)ptr;
  228. assert(_this_);
  229. _this_->uiThreadRun();
  230. }
  231. #endif
  232. };
  233. // -------------------------------------------------
  234. static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, const char*, LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
  235. {
  236. if (strcmp(uri, DISTRHO_PLUGIN_URI) != 0)
  237. return nullptr;
  238. // Get parent
  239. intptr_t parent = 0;
  240. for (uint32_t i = 0; features[i]; i++)
  241. {
  242. if (strcmp(features[i]->URI, LV2_UI__parent) == 0)
  243. {
  244. parent = (intptr_t)features[i]->data;
  245. break;
  246. }
  247. }
  248. return new UILv2(parent, controller, writeFunction, widget, features);
  249. }
  250. static void lv2ui_cleanup(LV2UI_Handle instance)
  251. {
  252. UILv2* ui = (UILv2*)instance;
  253. assert(ui);
  254. delete ui;
  255. }
  256. static void lv2ui_port_event(LV2UI_Handle instance, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
  257. {
  258. UILv2* ui = (UILv2*)instance;
  259. assert(ui);
  260. ui->lv2ui_port_event(portIndex, bufferSize, format, buffer);
  261. }
  262. #if DISTRHO_LV2UI_USE_EXTENSION_DATA
  263. # if DISTRHO_PLUGIN_WANT_PROGRAMS
  264. static void lv2ui_select_program(LV2_Handle instance, uint32_t bank, uint32_t program)
  265. {
  266. UILv2* ui = (UILv2*)instance;
  267. assert(ui);
  268. ui->lv2ui_select_program(bank, program);
  269. }
  270. # endif
  271. static const void* lv2ui_extension_data(const char* uri)
  272. {
  273. # if DISTRHO_PLUGIN_WANT_PROGRAMS
  274. static const LV2_Programs_UI_Interface programs = { lv2ui_select_program };
  275. if (strcmp(uri, LV2_PROGRAMS__UIInterface) == 0)
  276. return &programs;
  277. # endif
  278. return nullptr;
  279. }
  280. #endif
  281. // -------------------------------------------------
  282. static LV2UI_Descriptor uidescriptor = {
  283. DISTRHO_UI_URI,
  284. lv2ui_instantiate,
  285. lv2ui_cleanup,
  286. lv2ui_port_event,
  287. #if DISTRHO_LV2UI_USE_EXTENSION_DATA
  288. lv2ui_extension_data
  289. #else
  290. /* extension_data */ nullptr
  291. #endif
  292. };
  293. END_NAMESPACE_DISTRHO
  294. // -------------------------------------------------
  295. DISTRHO_PLUGIN_EXPORT
  296. const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
  297. {
  298. USE_NAMESPACE_DISTRHO
  299. return (index == 0) ? &uidescriptor : nullptr;
  300. }
  301. // -------------------------------------------------
  302. #endif // DISTRHO_PLUGIN_TARGET_LV2 && DISTRHO_PLUGIN_HAS_UI