comparison DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoUILV2.cpp @ 3:84e66ea83026

DPF-Prymula-audioplugins-0.231015-2
author prymula <prymula76@outlook.com>
date Mon, 16 Oct 2023 21:53:34 +0200
parents
children
comparison
equal deleted inserted replaced
2:cf2cb71d31dd 3:84e66ea83026
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
17 #include "DistrhoUIInternal.hpp"
18
19 #include "../extra/String.hpp"
20
21 #include "lv2/atom.h"
22 #include "lv2/atom-util.h"
23 #include "lv2/data-access.h"
24 #include "lv2/instance-access.h"
25 #include "lv2/midi.h"
26 #include "lv2/options.h"
27 #include "lv2/parameters.h"
28 #include "lv2/patch.h"
29 #include "lv2/ui.h"
30 #include "lv2/urid.h"
31 #include "lv2/lv2_kxstudio_properties.h"
32 #include "lv2/lv2_programs.h"
33
34 #ifndef DISTRHO_PLUGIN_LV2_STATE_PREFIX
35 # define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:"
36 #endif
37
38 START_NAMESPACE_DISTRHO
39
40 typedef struct _LV2_Atom_MidiEvent {
41 LV2_Atom atom; /**< Atom header. */
42 uint8_t data[3]; /**< MIDI data (body). */
43 } LV2_Atom_MidiEvent;
44
45 #if ! DISTRHO_PLUGIN_WANT_STATE
46 static constexpr const setStateFunc setStateCallback = nullptr;
47 #endif
48 #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
49 static constexpr const sendNoteFunc sendNoteCallback = nullptr;
50 #endif
51
52 // -----------------------------------------------------------------------
53
54 template <class LV2F>
55 static const LV2F* getLv2Feature(const LV2_Feature* const* features, const char* const uri)
56 {
57 for (int i=0; features[i] != nullptr; ++i)
58 {
59 if (std::strcmp(features[i]->URI, uri) == 0)
60 return (const LV2F*)features[i]->data;
61 }
62
63 return nullptr;
64 }
65
66 class UiLv2
67 {
68 public:
69 UiLv2(const char* const bundlePath,
70 const intptr_t winId,
71 const LV2_Options_Option* options,
72 const LV2_URID_Map* const uridMap,
73 const LV2_Feature* const* const features,
74 const LV2UI_Controller controller,
75 const LV2UI_Write_Function writeFunc,
76 LV2UI_Widget* const widget,
77 void* const dspPtr,
78 const float sampleRate,
79 const float scaleFactor,
80 const uint32_t bgColor,
81 const uint32_t fgColor,
82 const char* const appClassName)
83 : fUridMap(uridMap),
84 fUridUnmap(getLv2Feature<LV2_URID_Unmap>(features, LV2_URID__unmap)),
85 fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)),
86 fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)),
87 fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)),
88 fController(controller),
89 fWriteFunction(writeFunc),
90 fURIDs(uridMap),
91 fBypassParameterIndex(fUiPortMap != nullptr ? fUiPortMap->port_index(fUiPortMap->handle, "lv2_enabled")
92 : LV2UI_INVALID_PORT_INDEX),
93 fWinIdWasNull(winId == 0),
94 fUI(this, winId, sampleRate,
95 editParameterCallback,
96 setParameterCallback,
97 setStateCallback,
98 sendNoteCallback,
99 nullptr, // resize is very messy, hosts can do it without extensions
100 fileRequestCallback,
101 bundlePath, dspPtr, scaleFactor, bgColor, fgColor, appClassName)
102 {
103 if (widget != nullptr)
104 *widget = (LV2UI_Widget)fUI.getNativeWindowHandle();
105
106 #if DISTRHO_PLUGIN_WANT_STATE
107 // tell the DSP we're ready to receive msgs
108 setState("__dpf_ui_data__", "");
109 #endif
110
111 if (winId != 0)
112 return;
113
114 // if winId == 0 then options must not be null
115 DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,);
116
117 #ifndef __EMSCRIPTEN__
118 const LV2_URID uridWindowTitle = uridMap->map(uridMap->handle, LV2_UI__windowTitle);
119 const LV2_URID uridTransientWinId = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId);
120
121 const char* windowTitle = nullptr;
122
123 for (int i=0; options[i].key != 0; ++i)
124 {
125 if (options[i].key == uridTransientWinId)
126 {
127 if (options[i].type == fURIDs.atomLong)
128 {
129 if (const int64_t transientWinId = *(const int64_t*)options[i].value)
130 fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId));
131 }
132 else
133 d_stderr("Host provides transientWinId but has wrong value type");
134 }
135 else if (options[i].key == uridWindowTitle)
136 {
137 if (options[i].type == fURIDs.atomString)
138 {
139 windowTitle = (const char*)options[i].value;
140 }
141 else
142 d_stderr("Host provides windowTitle but has wrong value type");
143 }
144 }
145
146 if (windowTitle == nullptr)
147 windowTitle = DISTRHO_PLUGIN_NAME;
148
149 fUI.setWindowTitle(windowTitle);
150 #endif
151 }
152
153 // -------------------------------------------------------------------
154
155 void lv2ui_port_event(const uint32_t rindex, const uint32_t bufferSize, const uint32_t format, const void* const buffer)
156 {
157 if (format == 0)
158 {
159 const uint32_t parameterOffset = fUI.getParameterOffset();
160
161 if (rindex < parameterOffset)
162 return;
163
164 DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),)
165
166 float value = *(const float*)buffer;
167
168 if (rindex == fBypassParameterIndex)
169 value = 1.0f - value;
170
171 fUI.parameterChanged(rindex-parameterOffset, value);
172 }
173 #if DISTRHO_PLUGIN_WANT_STATE
174 else if (format == fURIDs.atomEventTransfer)
175 {
176 const LV2_Atom* const atom = (const LV2_Atom*)buffer;
177
178 if (atom->type == fURIDs.dpfKeyValue)
179 {
180 const char* const key = (const char*)LV2_ATOM_BODY_CONST(atom);
181 const char* const value = key+(std::strlen(key)+1);
182
183 fUI.stateChanged(key, value);
184 }
185 else if (atom->type == fURIDs.atomObject && fUridUnmap != nullptr)
186 {
187 const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)atom;
188
189 const LV2_Atom* property = nullptr;
190 const LV2_Atom* atomvalue = nullptr;
191 lv2_atom_object_get(obj, fURIDs.patchProperty, &property, fURIDs.patchValue, &atomvalue, 0);
192
193 DISTRHO_SAFE_ASSERT_RETURN(property != nullptr,);
194 DISTRHO_SAFE_ASSERT_RETURN(atomvalue != nullptr,);
195
196 DISTRHO_SAFE_ASSERT_RETURN(property->type == fURIDs.atomURID,);
197 DISTRHO_SAFE_ASSERT_RETURN(atomvalue->type == fURIDs.atomPath || atomvalue->type == fURIDs.atomString,);
198
199 if (property != nullptr && property->type == fURIDs.atomURID &&
200 atomvalue != nullptr && (atomvalue->type == fURIDs.atomPath || atomvalue->type == fURIDs.atomString))
201 {
202 const LV2_URID dpf_lv2_urid = ((const LV2_Atom_URID*)property)->body;
203 DISTRHO_SAFE_ASSERT_RETURN(dpf_lv2_urid != 0,);
204
205 const char* const dpf_lv2_key = fUridUnmap->unmap(fUridUnmap->handle, dpf_lv2_urid);
206 DISTRHO_SAFE_ASSERT_RETURN(dpf_lv2_key != nullptr,);
207
208 /*constexpr*/ const size_t reqLen = std::strlen(DISTRHO_PLUGIN_URI "#");
209 DISTRHO_SAFE_ASSERT_RETURN(std::strlen(dpf_lv2_key) > reqLen,);
210
211 const char* const key = dpf_lv2_key + reqLen;
212 const char* const value = (const char*)LV2_ATOM_BODY_CONST(atomvalue);
213
214 fUI.stateChanged(key, value);
215 }
216 }
217 else if (atom->type == fURIDs.midiEvent)
218 {
219 // ignore
220 }
221 else
222 {
223 d_stdout("DPF :: received atom not handled :: %s",
224 fUridUnmap != nullptr ? fUridUnmap->unmap(fUridUnmap->handle, atom->type) : "(null)");
225 }
226 }
227 #endif
228 }
229
230 // -------------------------------------------------------------------
231
232 int lv2ui_idle()
233 {
234 if (fWinIdWasNull)
235 return (fUI.plugin_idle() && fUI.isVisible()) ? 0 : 1;
236
237 return fUI.plugin_idle() ? 0 : 1;
238 }
239
240 int lv2ui_show()
241 {
242 return fUI.setWindowVisible(true) ? 0 : 1;
243 }
244
245 int lv2ui_hide()
246 {
247 return fUI.setWindowVisible(false) ? 0 : 1;
248 }
249
250 // -------------------------------------------------------------------
251
252 uint32_t lv2_get_options(LV2_Options_Option* const /*options*/)
253 {
254 // currently unused
255 return LV2_OPTIONS_ERR_UNKNOWN;
256 }
257
258 uint32_t lv2_set_options(const LV2_Options_Option* const options)
259 {
260 for (int i=0; options[i].key != 0; ++i)
261 {
262 if (options[i].key == fURIDs.paramSampleRate)
263 {
264 if (options[i].type == fURIDs.atomFloat)
265 {
266 const float sampleRate = *(const float*)options[i].value;
267 fUI.setSampleRate(sampleRate, true);
268 continue;
269 }
270 else
271 {
272 d_stderr("Host changed UI sample-rate but with wrong value type");
273 continue;
274 }
275 }
276 }
277
278 return LV2_OPTIONS_SUCCESS;
279 }
280
281 // -------------------------------------------------------------------
282
283 #if DISTRHO_PLUGIN_WANT_PROGRAMS
284 void lv2ui_select_program(const uint32_t bank, const uint32_t program)
285 {
286 const uint32_t realProgram = bank * 128 + program;
287
288 fUI.programLoaded(realProgram);
289 }
290 #endif
291
292 // -------------------------------------------------------------------
293
294 private:
295 // LV2 features
296 const LV2_URID_Map* const fUridMap;
297 const LV2_URID_Unmap* const fUridUnmap;
298 const LV2UI_Port_Map* const fUiPortMap;
299 const LV2UI_Request_Value* const fUiRequestValue;
300 const LV2UI_Touch* const fUiTouch;
301
302 // LV2 UI stuff
303 const LV2UI_Controller fController;
304 const LV2UI_Write_Function fWriteFunction;
305
306 // LV2 URIDs
307 const struct URIDs {
308 const LV2_URID_Map* _uridMap;
309 const LV2_URID dpfKeyValue;
310 const LV2_URID atomEventTransfer;
311 const LV2_URID atomFloat;
312 const LV2_URID atomLong;
313 const LV2_URID atomObject;
314 const LV2_URID atomPath;
315 const LV2_URID atomString;
316 const LV2_URID atomURID;
317 const LV2_URID midiEvent;
318 const LV2_URID paramSampleRate;
319 const LV2_URID patchProperty;
320 const LV2_URID patchSet;
321 const LV2_URID patchValue;
322
323 URIDs(const LV2_URID_Map* const uridMap)
324 : _uridMap(uridMap),
325 dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")),
326 atomEventTransfer(map(LV2_ATOM__eventTransfer)),
327 atomFloat(map(LV2_ATOM__Float)),
328 atomLong(map(LV2_ATOM__Long)),
329 atomObject(map(LV2_ATOM__Object)),
330 atomPath(map(LV2_ATOM__Path)),
331 atomString(map(LV2_ATOM__String)),
332 atomURID(map(LV2_ATOM__URID)),
333 midiEvent(map(LV2_MIDI__MidiEvent)),
334 paramSampleRate(map(LV2_PARAMETERS__sampleRate)),
335 patchProperty(map(LV2_PATCH__property)),
336 patchSet(map(LV2_PATCH__Set)),
337 patchValue(map(LV2_PATCH__value)) {}
338
339 inline LV2_URID map(const char* const uri) const
340 {
341 return _uridMap->map(_uridMap->handle, uri);
342 }
343 } fURIDs;
344
345 // index of bypass parameter, if present
346 const uint32_t fBypassParameterIndex;
347
348 // using ui:showInterface if true
349 const bool fWinIdWasNull;
350
351 // Plugin UI (after LV2 stuff so the UI can call into us during its constructor)
352 UIExporter fUI;
353
354 // ----------------------------------------------------------------------------------------------------------------
355 // DPF callbacks
356
357 void editParameterValue(const uint32_t rindex, const bool started)
358 {
359 if (fUiTouch != nullptr && fUiTouch->touch != nullptr)
360 fUiTouch->touch(fUiTouch->handle, rindex, started);
361 }
362
363 static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started)
364 {
365 static_cast<UiLv2*>(ptr)->editParameterValue(rindex, started);
366 }
367
368 void setParameterValue(const uint32_t rindex, float value)
369 {
370 DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
371
372 if (rindex == fBypassParameterIndex)
373 value = 1.0f - value;
374
375 fWriteFunction(fController, rindex, sizeof(float), 0, &value);
376 }
377
378 static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value)
379 {
380 static_cast<UiLv2*>(ptr)->setParameterValue(rindex, value);
381 }
382
383 #if DISTRHO_PLUGIN_WANT_STATE
384 void setState(const char* const key, const char* const value)
385 {
386 DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
387
388 const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
389
390 // join key and value
391 String tmpStr;
392 tmpStr += key;
393 tmpStr += "\xff";
394 tmpStr += value;
395
396 tmpStr[std::strlen(key)] = '\0';
397
398 // set msg size (key + separator + value + null terminator)
399 const uint32_t msgSize = static_cast<uint32_t>(tmpStr.length()) + 1U;
400
401 // reserve atom space
402 const uint32_t atomSize = sizeof(LV2_Atom) + msgSize;
403 char* const atomBuf = (char*)malloc(atomSize);
404 DISTRHO_SAFE_ASSERT_RETURN(atomBuf != nullptr,);
405
406 std::memset(atomBuf, 0, atomSize);
407
408 // set atom info
409 LV2_Atom* const atom = (LV2_Atom*)atomBuf;
410 atom->size = msgSize;
411 atom->type = fURIDs.dpfKeyValue;
412
413 // set atom data
414 std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize);
415
416 // send to DSP side
417 fWriteFunction(fController, eventInPortIndex, atomSize, fURIDs.atomEventTransfer, atom);
418
419 // free atom space
420 free(atomBuf);
421 }
422
423 static void setStateCallback(void* const ptr, const char* const key, const char* const value)
424 {
425 static_cast<UiLv2*>(ptr)->setState(key, value);
426 }
427 #endif
428
429 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
430 void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
431 {
432 DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
433
434 if (channel > 0xF)
435 return;
436
437 const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
438
439 LV2_Atom_MidiEvent atomMidiEvent;
440 atomMidiEvent.atom.size = 3;
441 atomMidiEvent.atom.type = fURIDs.midiEvent;
442
443 atomMidiEvent.data[0] = channel + (velocity != 0 ? 0x90 : 0x80);
444 atomMidiEvent.data[1] = note;
445 atomMidiEvent.data[2] = velocity;
446
447 // send to DSP side
448 fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom),
449 fURIDs.atomEventTransfer, &atomMidiEvent);
450 }
451
452 static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity)
453 {
454 static_cast<UiLv2*>(ptr)->sendNote(channel, note, velocity);
455 }
456 #endif
457
458 bool fileRequest(const char* const key)
459 {
460 d_stdout("UI file request %s %p", key, fUiRequestValue);
461
462 if (fUiRequestValue == nullptr)
463 return false;
464
465 String dpf_lv2_key(DISTRHO_PLUGIN_URI "#");
466 dpf_lv2_key += key;
467
468 const int r = fUiRequestValue->request(fUiRequestValue->handle,
469 fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()),
470 fURIDs.atomPath,
471 nullptr);
472
473 d_stdout("UI file request %s %p => %s %i", key, fUiRequestValue, dpf_lv2_key.buffer(), r);
474 return r == LV2UI_REQUEST_VALUE_SUCCESS;
475 }
476
477 static bool fileRequestCallback(void* ptr, const char* key)
478 {
479 return static_cast<UiLv2*>(ptr)->fileRequest(key);
480 }
481 };
482
483 // -----------------------------------------------------------------------
484
485 static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
486 const char* const uri,
487 const char* const bundlePath,
488 const LV2UI_Write_Function writeFunction,
489 const LV2UI_Controller controller,
490 LV2UI_Widget* const widget,
491 const LV2_Feature* const* const features)
492 {
493 if (uri == nullptr || std::strcmp(uri, DISTRHO_PLUGIN_URI) != 0)
494 {
495 d_stderr("Invalid plugin URI");
496 return nullptr;
497 }
498
499 const LV2_Options_Option* options = nullptr;
500 const LV2_URID_Map* uridMap = nullptr;
501 void* parentId = nullptr;
502 void* instance = nullptr;
503
504 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
505 struct LV2_DirectAccess_Interface {
506 void* (*get_instance_pointer)(LV2_Handle handle);
507 };
508 const LV2_Extension_Data_Feature* extData = nullptr;
509 #endif
510
511 for (int i=0; features[i] != nullptr; ++i)
512 {
513 /**/ if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
514 options = (const LV2_Options_Option*)features[i]->data;
515 else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
516 uridMap = (const LV2_URID_Map*)features[i]->data;
517 else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0)
518 parentId = features[i]->data;
519 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
520 else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0)
521 extData = (const LV2_Extension_Data_Feature*)features[i]->data;
522 else if (std::strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0)
523 instance = features[i]->data;
524 #endif
525 }
526
527 if (options == nullptr && parentId == nullptr)
528 {
529 d_stderr("Options feature missing (needed for show-interface), cannot continue!");
530 return nullptr;
531 }
532
533 if (uridMap == nullptr)
534 {
535 d_stderr("URID Map feature missing, cannot continue!");
536 return nullptr;
537 }
538
539 if (parentId == nullptr)
540 {
541 d_stdout("Parent Window Id missing, host should be using ui:showInterface...");
542 }
543
544 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
545 if (extData == nullptr || instance == nullptr)
546 {
547 d_stderr("Data or instance access missing, cannot continue!");
548 return nullptr;
549 }
550
551 if (const LV2_DirectAccess_Interface* const directAccess = (const LV2_DirectAccess_Interface*)extData->data_access(DISTRHO_PLUGIN_LV2_STATE_PREFIX "direct-access"))
552 instance = directAccess->get_instance_pointer(instance);
553 else
554 instance = nullptr;
555
556 if (instance == nullptr)
557 {
558 d_stderr("Failed to get direct access, cannot continue!");
559 return nullptr;
560 }
561 #endif
562
563 const intptr_t winId = (intptr_t)parentId;
564 float sampleRate = 0.0f;
565 float scaleFactor = 0.0f;
566 uint32_t bgColor = 0;
567 uint32_t fgColor = 0xffffffff;
568 const char* appClassName = nullptr;
569
570 if (options != nullptr)
571 {
572 const LV2_URID uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int);
573 const LV2_URID uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float);
574 const LV2_URID uridAtomString = uridMap->map(uridMap->handle, LV2_ATOM__String);
575 const LV2_URID uridSampleRate = uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate);
576 const LV2_URID uridBgColor = uridMap->map(uridMap->handle, LV2_UI__backgroundColor);
577 const LV2_URID uridFgColor = uridMap->map(uridMap->handle, LV2_UI__foregroundColor);
578 #ifndef DISTRHO_OS_MAC
579 const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor);
580 #endif
581 const LV2_URID uridClassName = uridMap->map(uridMap->handle, "urn:distrho:className");
582
583 for (int i=0; options[i].key != 0; ++i)
584 {
585 /**/ if (options[i].key == uridSampleRate)
586 {
587 if (options[i].type == uridAtomFloat)
588 sampleRate = *(const float*)options[i].value;
589 else
590 d_stderr("Host provides UI sample-rate but has wrong value type");
591 }
592 else if (options[i].key == uridBgColor)
593 {
594 if (options[i].type == uridAtomInt)
595 bgColor = (uint32_t)*(const int32_t*)options[i].value;
596 else
597 d_stderr("Host provides UI background color but has wrong value type");
598 }
599 else if (options[i].key == uridFgColor)
600 {
601 if (options[i].type == uridAtomInt)
602 fgColor = (uint32_t)*(const int32_t*)options[i].value;
603 else
604 d_stderr("Host provides UI foreground color but has wrong value type");
605 }
606 #ifndef DISTRHO_OS_MAC
607 else if (options[i].key == uridScaleFactor)
608 {
609 if (options[i].type == uridAtomFloat)
610 scaleFactor = *(const float*)options[i].value;
611 else
612 d_stderr("Host provides UI scale factor but has wrong value type");
613 }
614 #endif
615 else if (options[i].key == uridClassName)
616 {
617 if (options[i].type == uridAtomString)
618 appClassName = (const char*)options[i].value;
619 else
620 d_stderr("Host provides UI scale factor but has wrong value type");
621 }
622 }
623 }
624
625 if (sampleRate < 1.0)
626 {
627 d_stdout("WARNING: this host does not send sample-rate information for LV2 UIs, using 44100 as fallback (this could be wrong)");
628 sampleRate = 44100.0;
629 }
630
631 return new UiLv2(bundlePath, winId, options, uridMap, features,
632 controller, writeFunction, widget, instance,
633 sampleRate, scaleFactor, bgColor, fgColor, appClassName);
634 }
635
636 #define uiPtr ((UiLv2*)ui)
637
638 static void lv2ui_cleanup(LV2UI_Handle ui)
639 {
640 delete uiPtr;
641 }
642
643 static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
644 {
645 uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
646 }
647
648 // -----------------------------------------------------------------------
649
650 static int lv2ui_idle(LV2UI_Handle ui)
651 {
652 return uiPtr->lv2ui_idle();
653 }
654
655 static int lv2ui_show(LV2UI_Handle ui)
656 {
657 return uiPtr->lv2ui_show();
658 }
659
660 static int lv2ui_hide(LV2UI_Handle ui)
661 {
662 return uiPtr->lv2ui_hide();
663 }
664
665 // -----------------------------------------------------------------------
666
667 static uint32_t lv2_get_options(LV2UI_Handle ui, LV2_Options_Option* options)
668 {
669 return uiPtr->lv2_get_options(options);
670 }
671
672 static uint32_t lv2_set_options(LV2UI_Handle ui, const LV2_Options_Option* options)
673 {
674 return uiPtr->lv2_set_options(options);
675 }
676
677 // -----------------------------------------------------------------------
678
679 #if DISTRHO_PLUGIN_WANT_PROGRAMS
680 static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program)
681 {
682 uiPtr->lv2ui_select_program(bank, program);
683 }
684 #endif
685
686 // -----------------------------------------------------------------------
687
688 static const void* lv2ui_extension_data(const char* uri)
689 {
690 static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options };
691 static const LV2UI_Idle_Interface uiIdle = { lv2ui_idle };
692 static const LV2UI_Show_Interface uiShow = { lv2ui_show, lv2ui_hide };
693
694 if (std::strcmp(uri, LV2_OPTIONS__interface) == 0)
695 return &options;
696 if (std::strcmp(uri, LV2_UI__idleInterface) == 0)
697 return &uiIdle;
698 if (std::strcmp(uri, LV2_UI__showInterface) == 0)
699 return &uiShow;
700
701 #if DISTRHO_PLUGIN_WANT_PROGRAMS
702 static const LV2_Programs_UI_Interface uiPrograms = { lv2ui_select_program };
703
704 if (std::strcmp(uri, LV2_PROGRAMS__UIInterface) == 0)
705 return &uiPrograms;
706 #endif
707
708 return nullptr;
709 }
710
711 #undef instancePtr
712
713 // -----------------------------------------------------------------------
714
715 static const LV2UI_Descriptor sLv2UiDescriptor = {
716 DISTRHO_UI_URI,
717 lv2ui_instantiate,
718 lv2ui_cleanup,
719 lv2ui_port_event,
720 lv2ui_extension_data
721 };
722
723 // -----------------------------------------------------------------------
724
725 END_NAMESPACE_DISTRHO
726
727 DISTRHO_PLUGIN_EXPORT
728 const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
729 {
730 USE_NAMESPACE_DISTRHO
731 return (index == 0) ? &sLv2UiDescriptor : nullptr;
732 }
733
734 #if defined(__MOD_DEVICES__) && defined(__EMSCRIPTEN__)
735 #include <emscripten/html5.h>
736 #include <string>
737
738 typedef void (*_custom_param_set)(uint32_t port_index, float value);
739 typedef void (*_custom_patch_set)(const char* uri, const char* value);
740
741 struct ModguiHandle {
742 LV2UI_Handle handle;
743 long loop_id;
744 _custom_param_set param_set;
745 _custom_patch_set patch_set;
746 };
747
748 enum URIs {
749 kUriNull,
750 kUriAtomEventTransfer,
751 kUriDpfKeyValue,
752 };
753
754 static std::vector<std::string> kURIs;
755
756 static LV2_URID lv2_urid_map(LV2_URID_Map_Handle, const char* const uri)
757 {
758 for (size_t i=0, size=kURIs.size(); i<size; ++i)
759 {
760 if (kURIs[i] == uri)
761 return i;
762 }
763
764 kURIs.push_back(uri);
765 return kURIs.size() - 1u;
766 }
767
768 static const char* lv2_urid_unmap(LV2_URID_Map_Handle, const LV2_URID urid)
769 {
770 return kURIs[urid].c_str();
771 }
772
773 static void lv2ui_write_function(LV2UI_Controller controller,
774 uint32_t port_index,
775 uint32_t buffer_size,
776 uint32_t port_protocol,
777 const void* buffer)
778 {
779 DISTRHO_SAFE_ASSERT_RETURN(buffer_size >= 1,);
780
781 // d_stdout("lv2ui_write_function %p %u %u %u %p", controller, port_index, buffer_size, port_protocol, buffer);
782 ModguiHandle* const mhandle = static_cast<ModguiHandle*>(controller);
783
784 switch (port_protocol)
785 {
786 case kUriNull:
787 mhandle->param_set(port_index, *static_cast<const float*>(buffer));
788 break;
789 case kUriAtomEventTransfer:
790 if (const LV2_Atom* const atom = static_cast<const LV2_Atom*>(buffer))
791 {
792 // d_stdout("lv2ui_write_function %u %u:%s", atom->size, atom->type, kURIs[atom->type].c_str());
793
794 // if (kURIs[atom->type] == "urn:distrho:KeyValueState")
795 {
796 const char* const key = (const char*)(atom + 1);
797 const char* const value = key + (std::strlen(key) + 1U);
798 // d_stdout("lv2ui_write_function %s %s", key, value);
799
800 String urikey;
801 urikey = DISTRHO_PLUGIN_URI "#";
802 urikey += key;
803
804 mhandle->patch_set(urikey, value);
805 }
806 }
807 break;
808 }
809 }
810
811 static void app_idle(void* const handle)
812 {
813 static_cast<UiLv2*>(handle)->lv2ui_idle();
814 }
815
816 DISTRHO_PLUGIN_EXPORT
817 LV2UI_Handle modgui_init(const char* const className, _custom_param_set param_set, _custom_patch_set patch_set)
818 {
819 d_stdout("init \"%s\"", className);
820 DISTRHO_SAFE_ASSERT_RETURN(className != nullptr, nullptr);
821
822 static LV2_URID_Map uridMap = { nullptr, lv2_urid_map };
823 static LV2_URID_Unmap uridUnmap = { nullptr, lv2_urid_unmap };
824
825 // known first URIDs, matching URIs
826 if (kURIs.empty())
827 {
828 kURIs.push_back("");
829 kURIs.push_back("http://lv2plug.in/ns/ext/atom#eventTransfer");
830 kURIs.push_back(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState");
831 }
832
833 static float sampleRateValue = 48000.f;
834 static LV2_Options_Option options[3] = {
835 {
836 LV2_OPTIONS_INSTANCE,
837 0,
838 uridMap.map(uridMap.handle, LV2_PARAMETERS__sampleRate),
839 sizeof(float),
840 uridMap.map(uridMap.handle, LV2_ATOM__Float),
841 &sampleRateValue
842 },
843 {
844 LV2_OPTIONS_INSTANCE,
845 0,
846 uridMap.map(uridMap.handle, "urn:distrho:className"),
847 std::strlen(className) + 1,
848 uridMap.map(uridMap.handle, LV2_ATOM__String),
849 className
850 },
851 {}
852 };
853
854 static const LV2_Feature optionsFt = { LV2_OPTIONS__options, static_cast<void*>(options) };
855 static const LV2_Feature uridMapFt = { LV2_URID__map, static_cast<void*>(&uridMap) };
856 static const LV2_Feature uridUnmapFt = { LV2_URID__unmap, static_cast<void*>(&uridUnmap) };
857
858 static const LV2_Feature* features[] = {
859 &optionsFt,
860 &uridMapFt,
861 &uridUnmapFt,
862 nullptr
863 };
864
865 ModguiHandle* const mhandle = new ModguiHandle;
866 mhandle->handle = nullptr;
867 mhandle->loop_id = 0;
868 mhandle->param_set = param_set;
869 mhandle->patch_set = patch_set;
870
871 LV2UI_Widget widget;
872 const LV2UI_Handle handle = lv2ui_instantiate(&sLv2UiDescriptor,
873 DISTRHO_PLUGIN_URI,
874 "", // bundlePath
875 lv2ui_write_function,
876 mhandle,
877 &widget,
878 features);
879 mhandle->handle = handle;
880
881 static_cast<UiLv2*>(handle)->lv2ui_show();
882 mhandle->loop_id = emscripten_set_interval(app_idle, 1000.0/60, handle);
883
884 return mhandle;
885 }
886
887 DISTRHO_PLUGIN_EXPORT
888 void modgui_param_set(const LV2UI_Handle handle, const uint32_t index, const float value)
889 {
890 lv2ui_port_event(static_cast<ModguiHandle*>(handle)->handle, index, sizeof(float), kUriNull, &value);
891 }
892
893 DISTRHO_PLUGIN_EXPORT
894 void modgui_patch_set(const LV2UI_Handle handle, const char* const uri, const char* const value)
895 {
896 static const constexpr uint32_t URI_PREFIX_LEN = sizeof(DISTRHO_PLUGIN_URI);
897 DISTRHO_SAFE_ASSERT_RETURN(std::strncmp(uri, DISTRHO_PLUGIN_URI "#", URI_PREFIX_LEN) == 0,);
898
899 const uint32_t keySize = std::strlen(uri + URI_PREFIX_LEN) + 1;
900 const uint32_t valueSize = std::strlen(value) + 1;
901 const uint32_t atomSize = sizeof(LV2_Atom) + keySize + valueSize;
902
903 LV2_Atom* const atom = static_cast<LV2_Atom*>(std::malloc(atomSize));
904 atom->size = atomSize;
905 atom->type = kUriDpfKeyValue;
906
907 std::memcpy(static_cast<uint8_t*>(static_cast<void*>(atom + 1)), uri + URI_PREFIX_LEN, keySize);
908 std::memcpy(static_cast<uint8_t*>(static_cast<void*>(atom + 1)) + keySize, value, valueSize);
909
910 lv2ui_port_event(static_cast<ModguiHandle*>(handle)->handle,
911 DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS, // events input port
912 atomSize, kUriAtomEventTransfer, atom);
913
914 std::free(atom);
915 }
916
917 DISTRHO_PLUGIN_EXPORT
918 void modgui_cleanup(const LV2UI_Handle handle)
919 {
920 d_stdout("cleanup");
921 ModguiHandle* const mhandle = static_cast<ModguiHandle*>(handle);
922 if (mhandle->loop_id != 0)
923 emscripten_clear_interval(mhandle->loop_id);
924 lv2ui_cleanup(mhandle->handle);
925 delete mhandle;
926 }
927 #endif
928
929 // -----------------------------------------------------------------------