DISTRHO Plugin Framework
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.

720 lines
24KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include "DistrhoUIInternal.hpp"
  17. #include "../extra/String.hpp"
  18. #include "lv2/atom.h"
  19. #include "lv2/atom-util.h"
  20. #include "lv2/data-access.h"
  21. #include "lv2/instance-access.h"
  22. #include "lv2/midi.h"
  23. #include "lv2/options.h"
  24. #include "lv2/parameters.h"
  25. #include "lv2/patch.h"
  26. #include "lv2/ui.h"
  27. #include "lv2/urid.h"
  28. #include "lv2/lv2_kxstudio_properties.h"
  29. #include "lv2/lv2_programs.h"
  30. #ifndef DISTRHO_PLUGIN_LV2_STATE_PREFIX
  31. # define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:"
  32. #endif
  33. START_NAMESPACE_DISTRHO
  34. typedef struct _LV2_Atom_MidiEvent {
  35. LV2_Atom atom; /**< Atom header. */
  36. uint8_t data[3]; /**< MIDI data (body). */
  37. } LV2_Atom_MidiEvent;
  38. #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
  39. static const sendNoteFunc sendNoteCallback = nullptr;
  40. #endif
  41. // -----------------------------------------------------------------------
  42. template <class LV2F>
  43. static const LV2F* getLv2Feature(const LV2_Feature* const* features, const char* const uri)
  44. {
  45. for (int i=0; features[i] != nullptr; ++i)
  46. {
  47. if (std::strcmp(features[i]->URI, uri) == 0)
  48. return (const LV2F*)features[i]->data;
  49. }
  50. return nullptr;
  51. }
  52. class UiLv2
  53. {
  54. public:
  55. UiLv2(const char* const bundlePath,
  56. const intptr_t winId,
  57. const LV2_Options_Option* options,
  58. const LV2_URID_Map* const uridMap,
  59. const LV2_Feature* const* const features,
  60. const LV2UI_Controller controller,
  61. const LV2UI_Write_Function writeFunc,
  62. LV2UI_Widget* const widget,
  63. void* const dspPtr,
  64. const float sampleRate,
  65. const float scaleFactor,
  66. const uint32_t bgColor,
  67. const uint32_t fgColor)
  68. : fUI(this, winId, sampleRate,
  69. editParameterCallback,
  70. setParameterCallback,
  71. setStateCallback,
  72. sendNoteCallback,
  73. nullptr, // resize is very messy, hosts can do it without extensions
  74. fileRequestCallback,
  75. bundlePath,
  76. dspPtr,
  77. scaleFactor,
  78. bgColor,
  79. fgColor),
  80. fUridMap(uridMap),
  81. fUridUnmap(getLv2Feature<LV2_URID_Unmap>(features, LV2_URID__unmap)),
  82. fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)),
  83. fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)),
  84. fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)),
  85. fController(controller),
  86. fWriteFunction(writeFunc),
  87. fURIDs(uridMap),
  88. fBypassParameterIndex(fUiPortMap != nullptr ? fUiPortMap->port_index(fUiPortMap->handle, "lv2_enabled")
  89. : LV2UI_INVALID_PORT_INDEX),
  90. fWinIdWasNull(winId == 0)
  91. {
  92. if (widget != nullptr)
  93. *widget = (LV2UI_Widget)fUI.getNativeWindowHandle();
  94. #if DISTRHO_PLUGIN_WANT_STATE
  95. // tell the DSP we're ready to receive msgs
  96. setState("__dpf_ui_data__", "");
  97. #endif
  98. if (winId != 0)
  99. return;
  100. // if winId == 0 then options must not be null
  101. DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,);
  102. const LV2_URID uridWindowTitle = uridMap->map(uridMap->handle, LV2_UI__windowTitle);
  103. const LV2_URID uridTransientWinId = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId);
  104. bool hasTitle = false;
  105. for (int i=0; options[i].key != 0; ++i)
  106. {
  107. if (options[i].key == uridTransientWinId)
  108. {
  109. if (options[i].type == fURIDs.atomLong)
  110. {
  111. if (const int64_t transientWinId = *(const int64_t*)options[i].value)
  112. fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId));
  113. }
  114. else
  115. d_stderr("Host provides transientWinId but has wrong value type");
  116. }
  117. else if (options[i].key == uridWindowTitle)
  118. {
  119. if (options[i].type == fURIDs.atomString)
  120. {
  121. if (const char* const windowTitle = (const char*)options[i].value)
  122. {
  123. hasTitle = true;
  124. fUI.setWindowTitle(windowTitle);
  125. }
  126. }
  127. else
  128. d_stderr("Host provides windowTitle but has wrong value type");
  129. }
  130. }
  131. if (! hasTitle)
  132. fUI.setWindowTitle(DISTRHO_PLUGIN_NAME);
  133. }
  134. // -------------------------------------------------------------------
  135. void lv2ui_port_event(const uint32_t rindex, const uint32_t bufferSize, const uint32_t format, const void* const buffer)
  136. {
  137. if (format == 0)
  138. {
  139. const uint32_t parameterOffset = fUI.getParameterOffset();
  140. if (rindex < parameterOffset)
  141. return;
  142. DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),)
  143. float value = *(const float*)buffer;
  144. if (rindex == fBypassParameterIndex)
  145. value = 1.0f - value;
  146. fUI.parameterChanged(rindex-parameterOffset, value);
  147. }
  148. #if DISTRHO_PLUGIN_WANT_STATE
  149. else if (format == fURIDs.atomEventTransfer)
  150. {
  151. const LV2_Atom* const atom = (const LV2_Atom*)buffer;
  152. if (atom->type == fURIDs.dpfKeyValue)
  153. {
  154. const char* const key = (const char*)LV2_ATOM_BODY_CONST(atom);
  155. const char* const value = key+(std::strlen(key)+1);
  156. fUI.stateChanged(key, value);
  157. }
  158. else if (atom->type == fURIDs.atomObject && fUridUnmap != nullptr)
  159. {
  160. const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)atom;
  161. const LV2_Atom* property = nullptr;
  162. const LV2_Atom* atomvalue = nullptr;
  163. lv2_atom_object_get(obj, fURIDs.patchProperty, &property, fURIDs.patchValue, &atomvalue, 0);
  164. DISTRHO_SAFE_ASSERT_RETURN(property != nullptr,);
  165. DISTRHO_SAFE_ASSERT_RETURN(atomvalue != nullptr,);
  166. DISTRHO_SAFE_ASSERT_RETURN(property->type == fURIDs.atomURID,);
  167. DISTRHO_SAFE_ASSERT_RETURN(atomvalue->type == fURIDs.atomPath || atomvalue->type == fURIDs.atomString,);
  168. if (property != nullptr && property->type == fURIDs.atomURID &&
  169. atomvalue != nullptr && (atomvalue->type == fURIDs.atomPath || atomvalue->type == fURIDs.atomString))
  170. {
  171. const LV2_URID dpf_lv2_urid = ((const LV2_Atom_URID*)property)->body;
  172. DISTRHO_SAFE_ASSERT_RETURN(dpf_lv2_urid != 0,);
  173. const char* const dpf_lv2_key = fUridUnmap->unmap(fUridUnmap->handle, dpf_lv2_urid);
  174. DISTRHO_SAFE_ASSERT_RETURN(dpf_lv2_key != nullptr,);
  175. /*constexpr*/ const size_t reqLen = std::strlen(DISTRHO_PLUGIN_URI "#");
  176. DISTRHO_SAFE_ASSERT_RETURN(std::strlen(dpf_lv2_key) > reqLen,);
  177. const char* const key = dpf_lv2_key + reqLen;
  178. const char* const value = (const char*)LV2_ATOM_BODY_CONST(atomvalue);
  179. fUI.stateChanged(key, value);
  180. }
  181. }
  182. else
  183. {
  184. d_stdout("DPF :: received atom not handled");
  185. }
  186. }
  187. #endif
  188. }
  189. // -------------------------------------------------------------------
  190. int lv2ui_idle()
  191. {
  192. if (fWinIdWasNull)
  193. return (fUI.plugin_idle() && fUI.isVisible()) ? 0 : 1;
  194. return fUI.plugin_idle() ? 0 : 1;
  195. }
  196. int lv2ui_show()
  197. {
  198. return fUI.setWindowVisible(true) ? 0 : 1;
  199. }
  200. int lv2ui_hide()
  201. {
  202. return fUI.setWindowVisible(false) ? 0 : 1;
  203. }
  204. // -------------------------------------------------------------------
  205. uint32_t lv2_get_options(LV2_Options_Option* const /*options*/)
  206. {
  207. // currently unused
  208. return LV2_OPTIONS_ERR_UNKNOWN;
  209. }
  210. uint32_t lv2_set_options(const LV2_Options_Option* const options)
  211. {
  212. for (int i=0; options[i].key != 0; ++i)
  213. {
  214. if (options[i].key == fURIDs.paramSampleRate)
  215. {
  216. if (options[i].type == fURIDs.atomFloat)
  217. {
  218. const float sampleRate = *(const float*)options[i].value;
  219. fUI.setSampleRate(sampleRate);
  220. continue;
  221. }
  222. else
  223. {
  224. d_stderr("Host changed UI sample-rate but with wrong value type");
  225. continue;
  226. }
  227. }
  228. }
  229. return LV2_OPTIONS_SUCCESS;
  230. }
  231. // -------------------------------------------------------------------
  232. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  233. void lv2ui_select_program(const uint32_t bank, const uint32_t program)
  234. {
  235. const uint32_t realProgram = bank * 128 + program;
  236. fUI.programLoaded(realProgram);
  237. }
  238. #endif
  239. // -------------------------------------------------------------------
  240. protected:
  241. void editParameterValue(const uint32_t rindex, const bool started)
  242. {
  243. if (fUiTouch != nullptr && fUiTouch->touch != nullptr)
  244. fUiTouch->touch(fUiTouch->handle, rindex, started);
  245. }
  246. void setParameterValue(const uint32_t rindex, float value)
  247. {
  248. DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
  249. if (rindex == fBypassParameterIndex)
  250. value = 1.0f - value;
  251. fWriteFunction(fController, rindex, sizeof(float), 0, &value);
  252. }
  253. void setState(const char* const key, const char* const value)
  254. {
  255. DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
  256. const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
  257. // join key and value
  258. String tmpStr;
  259. tmpStr += key;
  260. tmpStr += "\xff";
  261. tmpStr += value;
  262. tmpStr[std::strlen(key)] = '\0';
  263. // set msg size (key + separator + value + null terminator)
  264. const uint32_t msgSize = static_cast<uint32_t>(tmpStr.length()) + 1U;
  265. // reserve atom space
  266. const uint32_t atomSize = sizeof(LV2_Atom) + msgSize;
  267. char* const atomBuf = (char*)malloc(atomSize);
  268. DISTRHO_SAFE_ASSERT_RETURN(atomBuf != nullptr,);
  269. std::memset(atomBuf, 0, atomSize);
  270. // set atom info
  271. LV2_Atom* const atom = (LV2_Atom*)atomBuf;
  272. atom->size = msgSize;
  273. atom->type = fURIDs.dpfKeyValue;
  274. // set atom data
  275. std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize);
  276. // send to DSP side
  277. fWriteFunction(fController, eventInPortIndex, atomSize, fURIDs.atomEventTransfer, atom);
  278. // free atom space
  279. free(atomBuf);
  280. }
  281. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  282. void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
  283. {
  284. DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
  285. if (channel > 0xF)
  286. return;
  287. const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
  288. LV2_Atom_MidiEvent atomMidiEvent;
  289. atomMidiEvent.atom.size = 3;
  290. atomMidiEvent.atom.type = fURIDs.midiEvent;
  291. atomMidiEvent.data[0] = channel + (velocity != 0 ? 0x90 : 0x80);
  292. atomMidiEvent.data[1] = note;
  293. atomMidiEvent.data[2] = velocity;
  294. // send to DSP side
  295. fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom),
  296. fURIDs.atomEventTransfer, &atomMidiEvent);
  297. }
  298. #endif
  299. bool fileRequest(const char* const key)
  300. {
  301. d_stdout("UI file request %s %p", key, fUiRequestValue);
  302. if (fUiRequestValue == nullptr)
  303. return false;
  304. String dpf_lv2_key(DISTRHO_PLUGIN_URI "#");
  305. dpf_lv2_key += key;
  306. const int r = fUiRequestValue->request(fUiRequestValue->handle,
  307. fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()),
  308. fURIDs.atomPath,
  309. nullptr);
  310. d_stdout("UI file request %s %p => %s %i", key, fUiRequestValue, dpf_lv2_key.buffer(), r);
  311. return r == LV2UI_REQUEST_VALUE_SUCCESS;
  312. }
  313. private:
  314. UIExporter fUI;
  315. // LV2 features
  316. const LV2_URID_Map* const fUridMap;
  317. const LV2_URID_Unmap* const fUridUnmap;
  318. const LV2UI_Port_Map* const fUiPortMap;
  319. const LV2UI_Request_Value* const fUiRequestValue;
  320. const LV2UI_Touch* const fUiTouch;
  321. // LV2 UI stuff
  322. const LV2UI_Controller fController;
  323. const LV2UI_Write_Function fWriteFunction;
  324. // LV2 URIDs
  325. const struct URIDs {
  326. const LV2_URID_Map* _uridMap;
  327. const LV2_URID dpfKeyValue;
  328. const LV2_URID atomEventTransfer;
  329. const LV2_URID atomFloat;
  330. const LV2_URID atomLong;
  331. const LV2_URID atomObject;
  332. const LV2_URID atomPath;
  333. const LV2_URID atomString;
  334. const LV2_URID atomURID;
  335. const LV2_URID midiEvent;
  336. const LV2_URID paramSampleRate;
  337. const LV2_URID patchProperty;
  338. const LV2_URID patchSet;
  339. const LV2_URID patchValue;
  340. URIDs(const LV2_URID_Map* const uridMap)
  341. : _uridMap(uridMap),
  342. dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")),
  343. atomEventTransfer(map(LV2_ATOM__eventTransfer)),
  344. atomFloat(map(LV2_ATOM__Float)),
  345. atomLong(map(LV2_ATOM__Long)),
  346. atomObject(map(LV2_ATOM__Object)),
  347. atomPath(map(LV2_ATOM__Path)),
  348. atomString(map(LV2_ATOM__String)),
  349. atomURID(map(LV2_ATOM__URID)),
  350. midiEvent(map(LV2_MIDI__MidiEvent)),
  351. paramSampleRate(map(LV2_PARAMETERS__sampleRate)),
  352. patchProperty(map(LV2_PATCH__property)),
  353. patchSet(map(LV2_PATCH__Set)),
  354. patchValue(map(LV2_PATCH__value)) {}
  355. inline LV2_URID map(const char* const uri) const
  356. {
  357. return _uridMap->map(_uridMap->handle, uri);
  358. }
  359. } fURIDs;
  360. // index of bypass parameter, if present
  361. const uint32_t fBypassParameterIndex;
  362. // using ui:showInterface if true
  363. const bool fWinIdWasNull;
  364. // -------------------------------------------------------------------
  365. // Callbacks
  366. #define uiPtr ((UiLv2*)ptr)
  367. static void editParameterCallback(void* ptr, uint32_t rindex, bool started)
  368. {
  369. uiPtr->editParameterValue(rindex, started);
  370. }
  371. static void setParameterCallback(void* ptr, uint32_t rindex, float value)
  372. {
  373. uiPtr->setParameterValue(rindex, value);
  374. }
  375. static void setStateCallback(void* ptr, const char* key, const char* value)
  376. {
  377. uiPtr->setState(key, value);
  378. }
  379. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  380. static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity)
  381. {
  382. uiPtr->sendNote(channel, note, velocity);
  383. }
  384. #endif
  385. static bool fileRequestCallback(void* ptr, const char* key)
  386. {
  387. return uiPtr->fileRequest(key);
  388. }
  389. #undef uiPtr
  390. };
  391. // -----------------------------------------------------------------------
  392. static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
  393. const char* const uri,
  394. const char* const bundlePath,
  395. const LV2UI_Write_Function writeFunction,
  396. const LV2UI_Controller controller,
  397. LV2UI_Widget* const widget,
  398. const LV2_Feature* const* const features)
  399. {
  400. if (uri == nullptr || std::strcmp(uri, DISTRHO_PLUGIN_URI) != 0)
  401. {
  402. d_stderr("Invalid plugin URI");
  403. return nullptr;
  404. }
  405. const LV2_Options_Option* options = nullptr;
  406. const LV2_URID_Map* uridMap = nullptr;
  407. void* parentId = nullptr;
  408. void* instance = nullptr;
  409. #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  410. struct LV2_DirectAccess_Interface {
  411. void* (*get_instance_pointer)(LV2_Handle handle);
  412. };
  413. const LV2_Extension_Data_Feature* extData = nullptr;
  414. #endif
  415. for (int i=0; features[i] != nullptr; ++i)
  416. {
  417. /**/ if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
  418. options = (const LV2_Options_Option*)features[i]->data;
  419. else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
  420. uridMap = (const LV2_URID_Map*)features[i]->data;
  421. else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0)
  422. parentId = features[i]->data;
  423. #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  424. else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0)
  425. extData = (const LV2_Extension_Data_Feature*)features[i]->data;
  426. else if (std::strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0)
  427. instance = features[i]->data;
  428. #endif
  429. }
  430. if (options == nullptr && parentId == nullptr)
  431. {
  432. d_stderr("Options feature missing (needed for show-interface), cannot continue!");
  433. return nullptr;
  434. }
  435. if (uridMap == nullptr)
  436. {
  437. d_stderr("URID Map feature missing, cannot continue!");
  438. return nullptr;
  439. }
  440. if (parentId == nullptr)
  441. {
  442. d_stdout("Parent Window Id missing, host should be using ui:showInterface...");
  443. }
  444. #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  445. if (extData == nullptr || instance == nullptr)
  446. {
  447. d_stderr("Data or instance access missing, cannot continue!");
  448. return nullptr;
  449. }
  450. if (const LV2_DirectAccess_Interface* const directAccess = (const LV2_DirectAccess_Interface*)extData->data_access(DISTRHO_PLUGIN_LV2_STATE_PREFIX "direct-access"))
  451. instance = directAccess->get_instance_pointer(instance);
  452. else
  453. instance = nullptr;
  454. if (instance == nullptr)
  455. {
  456. d_stderr("Failed to get direct access, cannot continue!");
  457. return nullptr;
  458. }
  459. #endif
  460. const intptr_t winId = (intptr_t)parentId;
  461. float sampleRate = 0.0f;
  462. float scaleFactor = 1.0f;
  463. uint32_t bgColor = 0;
  464. uint32_t fgColor = 0xffffffff;
  465. if (options != nullptr)
  466. {
  467. const LV2_URID uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int);
  468. const LV2_URID uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float);
  469. const LV2_URID uridSampleRate = uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate);
  470. const LV2_URID uridBgColor = uridMap->map(uridMap->handle, LV2_UI__backgroundColor);
  471. const LV2_URID uridFgColor = uridMap->map(uridMap->handle, LV2_UI__foregroundColor);
  472. const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor);
  473. for (int i=0; options[i].key != 0; ++i)
  474. {
  475. /**/ if (options[i].key == uridSampleRate)
  476. {
  477. if (options[i].type == uridAtomFloat)
  478. sampleRate = *(const float*)options[i].value;
  479. else
  480. d_stderr("Host provides UI sample-rate but has wrong value type");
  481. }
  482. else if (options[i].key == uridScaleFactor)
  483. {
  484. if (options[i].type == uridAtomFloat)
  485. scaleFactor = *(const float*)options[i].value;
  486. else
  487. d_stderr("Host provides UI scale factor but has wrong value type");
  488. }
  489. else if (options[i].key == uridBgColor)
  490. {
  491. if (options[i].type == uridAtomInt)
  492. bgColor = (uint32_t)*(const int32_t*)options[i].value;
  493. else
  494. d_stderr("Host provides UI background color but has wrong value type");
  495. }
  496. else if (options[i].key == uridFgColor)
  497. {
  498. if (options[i].type == uridAtomInt)
  499. fgColor = (uint32_t)*(const int32_t*)options[i].value;
  500. else
  501. d_stderr("Host provides UI foreground color but has wrong value type");
  502. }
  503. }
  504. }
  505. if (sampleRate < 1.0)
  506. {
  507. d_stdout("WARNING: this host does not send sample-rate information for LV2 UIs, using 44100 as fallback (this could be wrong)");
  508. sampleRate = 44100.0;
  509. }
  510. return new UiLv2(bundlePath, winId, options, uridMap, features,
  511. controller, writeFunction, widget, instance,
  512. sampleRate, scaleFactor, bgColor, fgColor);
  513. }
  514. #define uiPtr ((UiLv2*)ui)
  515. static void lv2ui_cleanup(LV2UI_Handle ui)
  516. {
  517. delete uiPtr;
  518. }
  519. static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
  520. {
  521. uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
  522. }
  523. // -----------------------------------------------------------------------
  524. static int lv2ui_idle(LV2UI_Handle ui)
  525. {
  526. return uiPtr->lv2ui_idle();
  527. }
  528. static int lv2ui_show(LV2UI_Handle ui)
  529. {
  530. return uiPtr->lv2ui_show();
  531. }
  532. static int lv2ui_hide(LV2UI_Handle ui)
  533. {
  534. return uiPtr->lv2ui_hide();
  535. }
  536. // -----------------------------------------------------------------------
  537. static uint32_t lv2_get_options(LV2UI_Handle ui, LV2_Options_Option* options)
  538. {
  539. return uiPtr->lv2_get_options(options);
  540. }
  541. static uint32_t lv2_set_options(LV2UI_Handle ui, const LV2_Options_Option* options)
  542. {
  543. return uiPtr->lv2_set_options(options);
  544. }
  545. // -----------------------------------------------------------------------
  546. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  547. static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program)
  548. {
  549. uiPtr->lv2ui_select_program(bank, program);
  550. }
  551. #endif
  552. // -----------------------------------------------------------------------
  553. static const void* lv2ui_extension_data(const char* uri)
  554. {
  555. static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options };
  556. static const LV2UI_Idle_Interface uiIdle = { lv2ui_idle };
  557. static const LV2UI_Show_Interface uiShow = { lv2ui_show, lv2ui_hide };
  558. if (std::strcmp(uri, LV2_OPTIONS__interface) == 0)
  559. return &options;
  560. if (std::strcmp(uri, LV2_UI__idleInterface) == 0)
  561. return &uiIdle;
  562. if (std::strcmp(uri, LV2_UI__showInterface) == 0)
  563. return &uiShow;
  564. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  565. static const LV2_Programs_UI_Interface uiPrograms = { lv2ui_select_program };
  566. if (std::strcmp(uri, LV2_PROGRAMS__UIInterface) == 0)
  567. return &uiPrograms;
  568. #endif
  569. return nullptr;
  570. }
  571. #undef instancePtr
  572. // -----------------------------------------------------------------------
  573. static const LV2UI_Descriptor sLv2UiDescriptor = {
  574. DISTRHO_UI_URI,
  575. lv2ui_instantiate,
  576. lv2ui_cleanup,
  577. lv2ui_port_event,
  578. lv2ui_extension_data
  579. };
  580. // -----------------------------------------------------------------------
  581. END_NAMESPACE_DISTRHO
  582. DISTRHO_PLUGIN_EXPORT
  583. const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
  584. {
  585. USE_NAMESPACE_DISTRHO
  586. return (index == 0) ? &sLv2UiDescriptor : nullptr;
  587. }
  588. // -----------------------------------------------------------------------