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 }