Mercurial > hg > pub > prymula > com
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 // ----------------------------------------------------------------------- |