Collection of tools useful for audio production
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.

373 lines
8.8KB

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