Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoUIDSSI.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 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
20 # error DSSI UIs do not support direct access! | |
21 #endif | |
22 | |
23 #include "../extra/Sleep.hpp" | |
24 | |
25 #include <lo/lo.h> | |
26 | |
27 START_NAMESPACE_DISTRHO | |
28 | |
29 #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
30 static constexpr const sendNoteFunc sendNoteCallback = nullptr; | |
31 #endif | |
32 | |
33 // ----------------------------------------------------------------------- | |
34 | |
35 struct OscData { | |
36 lo_address addr; | |
37 const char* path; | |
38 lo_server server; | |
39 | |
40 OscData() | |
41 : addr(nullptr), | |
42 path(nullptr), | |
43 server(nullptr) {} | |
44 | |
45 void idle() const | |
46 { | |
47 if (server == nullptr) | |
48 return; | |
49 | |
50 while (lo_server_recv_noblock(server, 0) != 0) {} | |
51 } | |
52 | |
53 void send_configure(const char* const key, const char* const value) const | |
54 { | |
55 char targetPath[std::strlen(path)+11]; | |
56 std::strcpy(targetPath, path); | |
57 std::strcat(targetPath, "/configure"); | |
58 lo_send(addr, targetPath, "ss", key, value); | |
59 } | |
60 | |
61 void send_control(const int32_t index, const float value) const | |
62 { | |
63 char targetPath[std::strlen(path)+9]; | |
64 std::strcpy(targetPath, path); | |
65 std::strcat(targetPath, "/control"); | |
66 lo_send(addr, targetPath, "if", index, value); | |
67 } | |
68 | |
69 void send_midi(uchar data[4]) const | |
70 { | |
71 char targetPath[std::strlen(path)+6]; | |
72 std::strcpy(targetPath, path); | |
73 std::strcat(targetPath, "/midi"); | |
74 lo_send(addr, targetPath, "m", data); | |
75 } | |
76 | |
77 void send_update(const char* const url) const | |
78 { | |
79 char targetPath[std::strlen(path)+8]; | |
80 std::strcpy(targetPath, path); | |
81 std::strcat(targetPath, "/update"); | |
82 lo_send(addr, targetPath, "s", url); | |
83 } | |
84 | |
85 void send_exiting() const | |
86 { | |
87 char targetPath[std::strlen(path)+9]; | |
88 std::strcpy(targetPath, path); | |
89 std::strcat(targetPath, "/exiting"); | |
90 lo_send(addr, targetPath, ""); | |
91 } | |
92 }; | |
93 | |
94 // ----------------------------------------------------------------------- | |
95 | |
96 class UIDssi : public DGL_NAMESPACE::IdleCallback | |
97 { | |
98 public: | |
99 UIDssi(const OscData& oscData, const char* const uiTitle, const double sampleRate) | |
100 : fUI(this, 0, sampleRate, nullptr, | |
101 setParameterCallback, setStateCallback, sendNoteCallback, nullptr, nullptr), | |
102 fHostClosed(false), | |
103 fOscData(oscData) | |
104 { | |
105 fUI.setWindowTitle(uiTitle); | |
106 } | |
107 | |
108 ~UIDssi() | |
109 { | |
110 if (fOscData.server != nullptr && ! fHostClosed) | |
111 fOscData.send_exiting(); | |
112 } | |
113 | |
114 void exec_start() | |
115 { | |
116 fUI.exec(this); | |
117 } | |
118 | |
119 void idleCallback() override | |
120 { | |
121 fOscData.idle(); | |
122 | |
123 if (fHostClosed) | |
124 return; | |
125 | |
126 fUI.exec_idle(); | |
127 } | |
128 | |
129 // ------------------------------------------------------------------- | |
130 | |
131 #if DISTRHO_PLUGIN_WANT_STATE | |
132 void dssiui_configure(const char* key, const char* value) | |
133 { | |
134 fUI.stateChanged(key, value); | |
135 } | |
136 #endif | |
137 | |
138 void dssiui_control(ulong index, float value) | |
139 { | |
140 fUI.parameterChanged(index, value); | |
141 } | |
142 | |
143 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
144 void dssiui_program(ulong bank, ulong program) | |
145 { | |
146 fUI.programLoaded(bank * 128 + program); | |
147 } | |
148 #endif | |
149 | |
150 void dssiui_samplerate(const double sampleRate) | |
151 { | |
152 fUI.setSampleRate(sampleRate, true); | |
153 } | |
154 | |
155 void dssiui_show(const bool focus = false) | |
156 { | |
157 fUI.setWindowVisible(true); | |
158 | |
159 if (focus) | |
160 fUI.focus(); | |
161 } | |
162 | |
163 void dssiui_hide() | |
164 { | |
165 fUI.setWindowVisible(false); | |
166 } | |
167 | |
168 void dssiui_quit() | |
169 { | |
170 fHostClosed = true; | |
171 fUI.quit(); | |
172 } | |
173 | |
174 // ------------------------------------------------------------------- | |
175 | |
176 protected: | |
177 void setParameterValue(const uint32_t rindex, const float value) | |
178 { | |
179 if (fOscData.server == nullptr) | |
180 return; | |
181 | |
182 fOscData.send_control(rindex, value); | |
183 } | |
184 | |
185 void setState(const char* const key, const char* const value) | |
186 { | |
187 if (fOscData.server == nullptr) | |
188 return; | |
189 | |
190 fOscData.send_configure(key, value); | |
191 } | |
192 | |
193 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
194 void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |
195 { | |
196 if (fOscData.server == nullptr) | |
197 return; | |
198 if (channel > 0xF) | |
199 return; | |
200 | |
201 uint8_t mdata[4] = { | |
202 0, | |
203 static_cast<uint8_t>(channel + (velocity != 0 ? 0x90 : 0x80)), | |
204 note, | |
205 velocity | |
206 }; | |
207 fOscData.send_midi(mdata); | |
208 } | |
209 #endif | |
210 | |
211 private: | |
212 UIExporter fUI; | |
213 bool fHostClosed; | |
214 | |
215 const OscData& fOscData; | |
216 | |
217 // ------------------------------------------------------------------- | |
218 // Callbacks | |
219 | |
220 #define uiPtr ((UIDssi*)ptr) | |
221 | |
222 static void setParameterCallback(void* ptr, uint32_t rindex, float value) | |
223 { | |
224 uiPtr->setParameterValue(rindex, value); | |
225 } | |
226 | |
227 static void setStateCallback(void* ptr, const char* key, const char* value) | |
228 { | |
229 uiPtr->setState(key, value); | |
230 } | |
231 | |
232 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
233 static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |
234 { | |
235 uiPtr->sendNote(channel, note, velocity); | |
236 } | |
237 #endif | |
238 | |
239 #undef uiPtr | |
240 }; | |
241 | |
242 // ----------------------------------------------------------------------- | |
243 | |
244 static OscData gOscData; | |
245 static const char* gUiTitle = nullptr; | |
246 static UIDssi* globalUI = nullptr; | |
247 static double sampleRate = 0.0; | |
248 | |
249 static void initUiIfNeeded() | |
250 { | |
251 if (globalUI != nullptr) | |
252 return; | |
253 | |
254 if (sampleRate == 0.0) | |
255 sampleRate = 44100.0; | |
256 | |
257 globalUI = new UIDssi(gOscData, gUiTitle, sampleRate); | |
258 } | |
259 | |
260 // ----------------------------------------------------------------------- | |
261 | |
262 int osc_debug_handler(const char* path, const char*, lo_arg**, int, lo_message, void*) | |
263 { | |
264 d_debug("osc_debug_handler(\"%s\")", path); | |
265 return 0; | |
266 | |
267 #ifndef DEBUG | |
268 // unused | |
269 (void)path; | |
270 #endif | |
271 } | |
272 | |
273 void osc_error_handler(int num, const char* msg, const char* path) | |
274 { | |
275 d_stderr("osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path); | |
276 } | |
277 | |
278 #if DISTRHO_PLUGIN_WANT_STATE | |
279 int osc_configure_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |
280 { | |
281 const char* const key = &argv[0]->s; | |
282 const char* const value = &argv[1]->s; | |
283 d_debug("osc_configure_handler(\"%s\", \"%s\")", key, value); | |
284 | |
285 initUiIfNeeded(); | |
286 | |
287 globalUI->dssiui_configure(key, value); | |
288 | |
289 return 0; | |
290 } | |
291 #endif | |
292 | |
293 int osc_control_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |
294 { | |
295 const int32_t rindex = argv[0]->i; | |
296 const float value = argv[1]->f; | |
297 d_debug("osc_control_handler(%i, %f)", rindex, value); | |
298 | |
299 int32_t index = rindex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS; | |
300 | |
301 // latency | |
302 #if DISTRHO_PLUGIN_WANT_LATENCY | |
303 index -= 1; | |
304 #endif | |
305 | |
306 if (index < 0) | |
307 return 0; | |
308 | |
309 initUiIfNeeded(); | |
310 | |
311 globalUI->dssiui_control(index, value); | |
312 | |
313 return 0; | |
314 } | |
315 | |
316 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
317 int osc_program_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |
318 { | |
319 const int32_t bank = argv[0]->i; | |
320 const int32_t program = argv[1]->f; | |
321 d_debug("osc_program_handler(%i, %i)", bank, program); | |
322 | |
323 initUiIfNeeded(); | |
324 | |
325 globalUI->dssiui_program(bank, program); | |
326 | |
327 return 0; | |
328 } | |
329 #endif | |
330 | |
331 int osc_sample_rate_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |
332 { | |
333 sampleRate = argv[0]->i; | |
334 d_debug("osc_sample_rate_handler(%f)", sampleRate); | |
335 | |
336 if (globalUI != nullptr) | |
337 globalUI->dssiui_samplerate(sampleRate); | |
338 | |
339 return 0; | |
340 } | |
341 | |
342 int osc_show_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |
343 { | |
344 d_debug("osc_show_handler()"); | |
345 | |
346 initUiIfNeeded(); | |
347 | |
348 globalUI->dssiui_show(); | |
349 | |
350 return 0; | |
351 } | |
352 | |
353 int osc_hide_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |
354 { | |
355 d_debug("osc_hide_handler()"); | |
356 | |
357 if (globalUI != nullptr) | |
358 globalUI->dssiui_hide(); | |
359 | |
360 return 0; | |
361 } | |
362 | |
363 int osc_quit_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |
364 { | |
365 d_debug("osc_quit_handler()"); | |
366 | |
367 if (globalUI != nullptr) | |
368 globalUI->dssiui_quit(); | |
369 | |
370 return 0; | |
371 } | |
372 | |
373 END_NAMESPACE_DISTRHO | |
374 | |
375 // ----------------------------------------------------------------------- | |
376 | |
377 int main(int argc, char* argv[]) | |
378 { | |
379 USE_NAMESPACE_DISTRHO | |
380 | |
381 // dummy test mode | |
382 if (argc == 1) | |
383 { | |
384 gUiTitle = "DSSI UI Test"; | |
385 | |
386 initUiIfNeeded(); | |
387 globalUI->dssiui_show(true); | |
388 globalUI->exec_start(); | |
389 | |
390 delete globalUI; | |
391 globalUI = nullptr; | |
392 | |
393 return 0; | |
394 } | |
395 | |
396 if (argc != 5) | |
397 { | |
398 fprintf(stderr, "Usage: %s <osc-url> <plugin-dll> <plugin-label> <instance-name>\n", argv[0]); | |
399 return 1; | |
400 } | |
401 | |
402 const char* oscUrl = argv[1]; | |
403 const char* uiTitle = argv[4]; | |
404 | |
405 char* const oscHost = lo_url_get_hostname(oscUrl); | |
406 char* const oscPort = lo_url_get_port(oscUrl); | |
407 char* const oscPath = lo_url_get_path(oscUrl); | |
408 size_t oscPathSize = strlen(oscPath); | |
409 lo_address oscAddr = lo_address_new(oscHost, oscPort); | |
410 lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, osc_error_handler); | |
411 | |
412 char* const oscServerPath = lo_server_get_url(oscServer); | |
413 | |
414 char pluginPath[strlen(oscServerPath)+oscPathSize]; | |
415 strcpy(pluginPath, oscServerPath); | |
416 strcat(pluginPath, oscPath+1); | |
417 | |
418 #if DISTRHO_PLUGIN_WANT_STATE | |
419 char oscPathConfigure[oscPathSize+11]; | |
420 strcpy(oscPathConfigure, oscPath); | |
421 strcat(oscPathConfigure, "/configure"); | |
422 lo_server_add_method(oscServer, oscPathConfigure, "ss", osc_configure_handler, nullptr); | |
423 #endif | |
424 | |
425 char oscPathControl[oscPathSize+9]; | |
426 strcpy(oscPathControl, oscPath); | |
427 strcat(oscPathControl, "/control"); | |
428 lo_server_add_method(oscServer, oscPathControl, "if", osc_control_handler, nullptr); | |
429 | |
430 d_stdout("oscServerPath: \"%s\"", oscServerPath); | |
431 d_stdout("pluginPath: \"%s\"", pluginPath); | |
432 d_stdout("oscPathControl: \"%s\"", oscPathControl); | |
433 | |
434 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
435 char oscPathProgram[oscPathSize+9]; | |
436 strcpy(oscPathProgram, oscPath); | |
437 strcat(oscPathProgram, "/program"); | |
438 lo_server_add_method(oscServer, oscPathProgram, "ii", osc_program_handler, nullptr); | |
439 #endif | |
440 | |
441 char oscPathSampleRate[oscPathSize+13]; | |
442 strcpy(oscPathSampleRate, oscPath); | |
443 strcat(oscPathSampleRate, "/sample-rate"); | |
444 lo_server_add_method(oscServer, oscPathSampleRate, "i", osc_sample_rate_handler, nullptr); | |
445 | |
446 char oscPathShow[oscPathSize+6]; | |
447 strcpy(oscPathShow, oscPath); | |
448 strcat(oscPathShow, "/show"); | |
449 lo_server_add_method(oscServer, oscPathShow, "", osc_show_handler, nullptr); | |
450 | |
451 char oscPathHide[oscPathSize+6]; | |
452 strcpy(oscPathHide, oscPath); | |
453 strcat(oscPathHide, "/hide"); | |
454 lo_server_add_method(oscServer, oscPathHide, "", osc_hide_handler, nullptr); | |
455 | |
456 char oscPathQuit[oscPathSize+6]; | |
457 strcpy(oscPathQuit, oscPath); | |
458 strcat(oscPathQuit, "/quit"); | |
459 lo_server_add_method(oscServer, oscPathQuit, "", osc_quit_handler, nullptr); | |
460 | |
461 lo_server_add_method(oscServer, nullptr, nullptr, osc_debug_handler, nullptr); | |
462 | |
463 gUiTitle = uiTitle; | |
464 | |
465 gOscData.addr = oscAddr; | |
466 gOscData.path = oscPath; | |
467 gOscData.server = oscServer; | |
468 gOscData.send_update(pluginPath); | |
469 | |
470 // wait for init | |
471 for (int i=0; i < 100; ++i) | |
472 { | |
473 lo_server_recv(oscServer); | |
474 | |
475 if (sampleRate != 0.0 || globalUI != nullptr) | |
476 break; | |
477 | |
478 d_msleep(50); | |
479 } | |
480 | |
481 int ret = 1; | |
482 | |
483 if (sampleRate != 0.0 || globalUI != nullptr) | |
484 { | |
485 initUiIfNeeded(); | |
486 | |
487 globalUI->exec_start(); | |
488 | |
489 delete globalUI; | |
490 globalUI = nullptr; | |
491 | |
492 ret = 0; | |
493 } | |
494 | |
495 #if DISTRHO_PLUGIN_WANT_STATE | |
496 lo_server_del_method(oscServer, oscPathConfigure, "ss"); | |
497 #endif | |
498 lo_server_del_method(oscServer, oscPathControl, "if"); | |
499 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
500 lo_server_del_method(oscServer, oscPathProgram, "ii"); | |
501 #endif | |
502 lo_server_del_method(oscServer, oscPathSampleRate, "i"); | |
503 lo_server_del_method(oscServer, oscPathShow, ""); | |
504 lo_server_del_method(oscServer, oscPathHide, ""); | |
505 lo_server_del_method(oscServer, oscPathQuit, ""); | |
506 lo_server_del_method(oscServer, nullptr, nullptr); | |
507 | |
508 std::free(oscServerPath); | |
509 std::free(oscHost); | |
510 std::free(oscPort); | |
511 std::free(oscPath); | |
512 | |
513 lo_address_free(oscAddr); | |
514 lo_server_free(oscServer); | |
515 | |
516 return ret; | |
517 } |