1  /*
2  * Carla Plugin Host
3  * Copyright (C) 2011-2019 Filipe Coelho <falktx@falktx.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * For a full copy of the GNU General Public License see the doc/GPL.txt file.
16  */
17 
18 #include "CarlaEngineOsc.hpp"
19 
20 #ifdef HAVE_LIBLO
21 
22 #include "CarlaEngineInternal.hpp"
23 #include "CarlaPlugin.hpp"
24 #include "CarlaMIDI.h"
25 
26 #include <cctype>
27 
28 CARLA_BACKEND_START_NAMESPACE
29 
30 // -----------------------------------------------------------------------
31 
handleMessage(const bool isTCP,const char * const path,const int argc,const lo_arg * const * const argv,const char * const types,const lo_message msg)32 int CarlaEngineOsc::handleMessage(const bool isTCP, const char* const path,
33                                   const int argc, const lo_arg* const* const argv, const char* const types,
34                                   const lo_message msg)
35 {
36     CARLA_SAFE_ASSERT_RETURN(fName.isNotEmpty(), 1);
37     CARLA_SAFE_ASSERT_RETURN(path != nullptr && path[0] != '\0', 1);
38     CARLA_SAFE_ASSERT_RETURN(path[0] == '/', 1);
39 #ifdef DEBUG
40     if (std::strncmp(path, "/bridge_pong", 12) != 0) {
41         carla_debug("CarlaEngineOsc::handleMessage(%s, \"%s\", %i, %p, \"%s\", %p)",
42                     bool2str(isTCP), path, argc, argv, types, msg);
43     }
44 #endif
45 
46     if (isTCP)
47     {
48         CARLA_SAFE_ASSERT_RETURN(fServerPathTCP.isNotEmpty(), 1);
49         CARLA_SAFE_ASSERT_RETURN(fServerTCP != nullptr, 1);
50     }
51     else
52     {
53         CARLA_SAFE_ASSERT_RETURN(fServerPathUDP.isNotEmpty(), 1);
54         CARLA_SAFE_ASSERT_RETURN(fServerUDP != nullptr, 1);
55     }
56 
57     const lo_address source = lo_message_get_source(msg);
58 
59     // Initial path check
60     if (std::strcmp(path, "/register") == 0)
61         return handleMsgRegister(isTCP, argc, argv, types, source);
62 
63     if (std::strcmp(path, "/unregister") == 0)
64         return handleMsgUnregister(isTCP, argc, argv, types);
65 
66     if (std::strncmp(path, "/ctrl/", 6) == 0)
67     {
68         CARLA_SAFE_ASSERT_RETURN(isTCP, 1);
69         return handleMsgControl(path + 6, argc, argv, types);
70     }
71 
72     // Check if message is for this client
73     std::size_t bytesAfterName;
74     if (fControlDataTCP.owner != nullptr && std::strcmp(lo_address_get_hostname(source), fControlDataTCP.owner) == 0)
75     {
76         if (const char* const slash = std::strchr(path+1, '/'))
77         {
78             bytesAfterName = static_cast<std::size_t>(slash - path);
79         }
80         else
81         {
82             carla_stderr("CarlaEngineOsc::handleMessage() - message '%s' is invalid", path);
83             return 1;
84         }
85     }
86     else
87     {
88         bytesAfterName = fName.length();
89 
90         if (std::strlen(path) <= bytesAfterName || std::strncmp(path+1, fName, bytesAfterName) != 0)
91         {
92             carla_stderr("CarlaEngineOsc::handleMessage() - message not for this client -> '%s' != '/%s/'",
93                         path, fName.buffer());
94             return 1;
95         }
96 
97         ++bytesAfterName;
98     }
99 
100     // Get plugin id from path, "/carla/23/method" -> 23
101     uint pluginId = 0;
102     std::size_t offset;
103 
104     if (! std::isdigit(path[bytesAfterName+1]))
105     {
106         carla_stderr("CarlaEngineOsc::handleMessage() - invalid message '%s'", path);
107         return 1;
108     }
109 
110     if (std::isdigit(path[bytesAfterName+2]))
111     {
112         if (std::isdigit(path[bytesAfterName+4]))
113         {
114             carla_stderr2("CarlaEngineOsc::handleMessage() - invalid plugin id, over 999? (value: \"%s\")",
115                           path+bytesAfterName);
116             return 1;
117         }
118         else if (std::isdigit(path[bytesAfterName+3]))
119         {
120             // 3 digits, /xyz/method
121             offset    = 5;
122             pluginId += uint(path[bytesAfterName+1]-'0')*100;
123             pluginId += uint(path[bytesAfterName+2]-'0')*10;
124             pluginId += uint(path[bytesAfterName+3]-'0');
125         }
126         else
127         {
128             // 2 digits, /xy/method
129             offset    = 4;
130             pluginId += uint(path[bytesAfterName+1]-'0')*10;
131             pluginId += uint(path[bytesAfterName+2]-'0');
132         }
133     }
134     else
135     {
136         // single digit, /x/method
137         offset    = 3;
138         pluginId += uint(path[bytesAfterName+1]-'0');
139     }
140 
141     if (pluginId > fEngine->getCurrentPluginCount())
142     {
143         carla_stderr("CarlaEngineOsc::handleMessage() - failed to get plugin, wrong id '%i'", pluginId);
144         return 0;
145     }
146 
147     // Get plugin
148     const CarlaPluginPtr plugin = fEngine->getPluginUnchecked(pluginId);
149 
150     if (plugin == nullptr || plugin->getId() != pluginId)
151     {
152         carla_stderr("CarlaEngineOsc::handleMessage() - invalid plugin id '%i', probably has been removed (path: '%s')", pluginId, path);
153         return 0;
154     }
155 
156     // Get method from path, "/Carla/i/method" -> "method"
157     char method[48];
158     std::strncpy(method, path + (bytesAfterName + offset), 47);
159     method[47] = '\0';
160 
161     if (method[0] == '\0')
162     {
163         carla_stderr("CarlaEngineOsc::handleMessage(%s, \"%s\", ...) - received message without method", bool2str(isTCP), path);
164         return 0;
165     }
166 
167     // Internal methods
168     if (std::strcmp(method, "set_option") == 0)
169         return 0; //handleMsgSetOption(plugin, argc, argv, types); // TODO
170     if (std::strcmp(method, "set_active") == 0)
171         return handleMsgSetActive(plugin, argc, argv, types);
172     if (std::strcmp(method, "set_drywet") == 0)
173         return handleMsgSetDryWet(plugin, argc, argv, types);
174     if (std::strcmp(method, "set_volume") == 0)
175         return handleMsgSetVolume(plugin, argc, argv, types);
176     if (std::strcmp(method, "set_balance_left") == 0)
177         return handleMsgSetBalanceLeft(plugin, argc, argv, types);
178     if (std::strcmp(method, "set_balance_right") == 0)
179         return handleMsgSetBalanceRight(plugin, argc, argv, types);
180     if (std::strcmp(method, "set_panning") == 0)
181         return handleMsgSetPanning(plugin, argc, argv, types);
182     if (std::strcmp(method, "set_ctrl_channel") == 0)
183         return 0; //handleMsgSetControlChannel(plugin, argc, argv, types); // TODO
184     if (std::strcmp(method, "set_parameter_value") == 0)
185         return handleMsgSetParameterValue(plugin, argc, argv, types);
186     if (std::strcmp(method, "set_parameter_mapped_control_index") == 0)
187         return handleMsgSetParameterMappedControlIndex(plugin, argc, argv, types);
188     if (std::strcmp(method, "set_parameter_mapped_range") == 0)
189         return handleMsgSetParameterMappedRange(plugin, argc, argv, types);
190     if (std::strcmp(method, "set_parameter_midi_channel") == 0)
191         return handleMsgSetParameterMidiChannel(plugin, argc, argv, types);
192     if (std::strcmp(method, "set_program") == 0)
193         return handleMsgSetProgram(plugin, argc, argv, types);
194     if (std::strcmp(method, "set_midi_program") == 0)
195         return handleMsgSetMidiProgram(plugin, argc, argv, types);
196     if (std::strcmp(method, "set_custom_data") == 0)
197         return 0; //handleMsgSetCustomData(plugin, argc, argv, types); // TODO
198     if (std::strcmp(method, "set_chunk") == 0)
199         return 0; //handleMsgSetChunk(plugin, argc, argv, types); // TODO
200     if (std::strcmp(method, "note_on") == 0)
201         return handleMsgNoteOn(plugin, argc, argv, types);
202     if (std::strcmp(method, "note_off") == 0)
203         return handleMsgNoteOff(plugin, argc, argv, types);
204 
205     // Send all other methods to plugins, TODO
206     plugin->handleOscMessage(method, argc, argv, types, msg);
207     return 0;
208 }
209 
210 // -----------------------------------------------------------------------
211 
handleMsgRegister(const bool isTCP,const int argc,const lo_arg * const * const argv,const char * const types,const lo_address source)212 int CarlaEngineOsc::handleMsgRegister(const bool isTCP,
213                                       const int argc, const lo_arg* const* const argv, const char* const types,
214                                       const lo_address source)
215 {
216     carla_debug("CarlaEngineOsc::handleMsgRegister()");
217     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(1, "s");
218 
219     const char* const url = &argv[0]->s;
220 
221     CarlaOscData& oscData(isTCP ? fControlDataTCP : fControlDataUDP);
222 
223     if (oscData.owner != nullptr)
224     {
225         carla_stderr("OSC backend already registered to %s", oscData.owner);
226 
227         char* const path = lo_url_get_path(url);
228         const size_t pathlen = std::strlen(path);
229         CARLA_SAFE_ASSERT_RETURN(pathlen < 32, 0);
230 
231         char targetPath[pathlen+12];
232         std::strcpy(targetPath, path);
233         std::strcat(targetPath, "/exit-error");
234 
235         lo_send_from(source, isTCP ? fServerTCP : fServerUDP, LO_TT_IMMEDIATE,
236                      targetPath, "s", "OSC already registered to another client");
237 
238         free(path);
239     }
240     else
241     {
242         const char* const host  = lo_address_get_hostname(source);
243         /* */ char* const port  = lo_url_get_port(url); // NOTE: lo_address_get_port is buggy against TCP
244         const lo_address target = lo_address_new_with_proto(isTCP ? LO_TCP : LO_UDP, host, port);
245 
246         oscData.owner  = carla_strdup_safe(host);
247         oscData.path   = carla_strdup_free(lo_url_get_path(url));
248         oscData.target = target;
249 
250         char* const targeturl = lo_address_get_url(target);
251         carla_stdout("OSC %s backend registered to %s, path: %s, target: %s (host: %s, port: %s)",
252                      isTCP ? "TCP" : "UDP", url, oscData.path, targeturl, host, port);
253         free(targeturl);
254         free(port);
255 
256         if (isTCP)
257         {
258             const EngineOptions& opts(fEngine->getOptions());
259 
260             fEngine->callback(false, true,
261                               ENGINE_CALLBACK_ENGINE_STARTED,
262                               fEngine->getCurrentPluginCount(),
263                               opts.processMode,
264                               opts.transportMode,
265                               static_cast<int>(fEngine->getBufferSize()),
266                               static_cast<float>(fEngine->getSampleRate()),
267                               fEngine->getCurrentDriverName());
268 
269             for (uint i=0, count=fEngine->getCurrentPluginCount(); i < count; ++i)
270             {
271                 const CarlaPluginPtr plugin = fEngine->getPluginUnchecked(i);
272                 CARLA_SAFE_ASSERT_CONTINUE(plugin != nullptr);
273 
274                 fEngine->callback(false, true, ENGINE_CALLBACK_PLUGIN_ADDED, i, 0, 0, 0, 0.0f, plugin->getName());
275             }
276 
277             fEngine->patchbayRefresh(false, true, fEngine->pData->graph.isUsingExternalOSC());
278         }
279     }
280 
281     return 0;
282 }
283 
handleMsgUnregister(const bool isTCP,const int argc,const lo_arg * const * const argv,const char * const types)284 int CarlaEngineOsc::handleMsgUnregister(const bool isTCP,
285                                         const int argc, const lo_arg* const* const argv, const char* const types)
286 {
287     carla_debug("CarlaEngineOsc::handleMsgUnregister()");
288     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(1, "s");
289 
290     CarlaOscData& oscData(isTCP ? fControlDataTCP : fControlDataUDP);
291 
292     if (oscData.owner == nullptr)
293     {
294         carla_stderr("OSC backend is not registered yet, unregister failed");
295         return 0;
296     }
297 
298     const char* const url = &argv[0]->s;
299 
300     if (std::strcmp(oscData.owner, url) == 0)
301     {
302         carla_stdout("OSC client %s unregistered", url);
303         oscData.clear();
304         return 0;
305     }
306 
307     carla_stderr("OSC backend unregister failed, current owner %s does not match requested %s", oscData.owner, url);
308     return 0;
309 }
310 
handleMsgControl(const char * const method,const int argc,const lo_arg * const * const argv,const char * const types)311 int CarlaEngineOsc::handleMsgControl(const char* const method,
312                                      const int argc, const lo_arg* const* const argv, const char* const types)
313 {
314     carla_debug("CarlaEngineOsc::handleMsgControl()");
315     CARLA_SAFE_ASSERT_RETURN(method != nullptr && method[0] != '\0', 0);
316     CARLA_SAFE_ASSERT_RETURN(types != nullptr, 0);
317     CARLA_SAFE_ASSERT_RETURN(types[0] == 'i', 0);
318 
319     if (fControlDataTCP.owner == nullptr)
320     {
321         carla_stderr("OSC backend is not registered yet, control failed");
322         return 0;
323     }
324 
325     const int32_t messageId = argv[0]->i;
326     bool ok;
327 
328 #define CARLA_SAFE_ASSERT_RETURN_OSC_ERR(cond) \
329     if (! (cond)) { carla_safe_assert(#cond, __FILE__, __LINE__); sendResponse(messageId, #cond); return 0; }
330 
331     /**/ if (std::strcmp(method, "clear_engine_xruns") == 0)
332     {
333         ok = true;
334         fEngine->clearXruns();
335     }
336     else if (std::strcmp(method, "cancel_engine_action") == 0)
337     {
338         ok = true;
339         fEngine->setActionCanceled(true);
340     }
341     else if (std::strcmp(method, "patchbay_connect") == 0)
342     {
343         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 6);
344         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[1] == 'i');
345         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[2] == 'i');
346         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[3] == 'i');
347         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[4] == 'i');
348         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[5] == 'i');
349 
350         const bool external = argv[1]->i != 0;
351 
352         const int32_t groupA = argv[2]->i;
353         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(groupA >= 0);
354 
355         const int32_t portA = argv[3]->i;
356         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(portA >= 0);
357 
358         const int32_t groupB = argv[4]->i;
359         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(groupB >= 0);
360 
361         const int32_t portB = argv[5]->i;
362         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(portB >= 0);
363 
364         ok = fEngine->patchbayConnect(external,
365                                       static_cast<uint32_t>(groupA),
366                                       static_cast<uint32_t>(portA),
367                                       static_cast<uint32_t>(groupB),
368                                       static_cast<uint32_t>(portB));
369     }
370     else if (std::strcmp(method, "patchbay_disconnect") == 0)
371     {
372         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 3);
373         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[1] == 'i');
374         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[2] == 'i');
375 
376         const bool external = argv[1]->i != 0;
377 
378         const int32_t connectionId = argv[2]->i;
379         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(connectionId >= 0);
380 
381         ok = fEngine->patchbayDisconnect(external, static_cast<uint32_t>(connectionId));
382     }
383     else if (std::strcmp(method, "patchbay_set_group_pos") == 0)
384     {
385         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 7);
386         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[1] == 'i');
387         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[2] == 'i');
388         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[3] == 'i');
389         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[4] == 'i');
390         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[5] == 'i');
391         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[6] == 'i');
392 
393         const bool external = argv[1]->i != 0;
394 
395         const int32_t groupId = argv[2]->i;
396         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(groupId >= 0);
397 
398         ok = fEngine->patchbaySetGroupPos(true, false,
399                                           external, static_cast<uint32_t>(groupId),
400                                           argv[3]->i, argv[4]->i, argv[5]->i, argv[6]->i);
401     }
402     else if (std::strcmp(method, "patchbay_refresh") == 0)
403     {
404         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 2);
405         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[1] == 'i');
406 
407         const bool external = argv[1]->i != 0;
408 
409         ok = fEngine->patchbayRefresh(false, true, external);
410     }
411     else if (std::strcmp(method, "transport_play") == 0)
412     {
413         ok = true;
414         fEngine->transportPlay();
415     }
416     else if (std::strcmp(method, "transport_pause") == 0)
417     {
418         ok = true;
419         fEngine->transportPause();
420     }
421     else if (std::strcmp(method, "transport_bpm") == 0)
422     {
423         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 2);
424         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[1] == 'f');
425 
426         const double bpm = argv[1]->f;
427         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(bpm >= 0.0);
428 
429         ok = true;
430         fEngine->transportBPM(bpm);
431     }
432     else if (std::strcmp(method, "transport_relocate") == 0)
433     {
434         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 2);
435 
436         uint64_t frame;
437 
438         /**/ if (types[1] == 'i')
439         {
440             const int32_t i = argv[1]->i;
441             CARLA_SAFE_ASSERT_RETURN_OSC_ERR(i >= 0);
442             frame = static_cast<uint64_t>(i);
443         }
444         else if (types[1] == 'h')
445         {
446             const int64_t h = argv[1]->h;
447             CARLA_SAFE_ASSERT_RETURN_OSC_ERR(h >= 0);
448             frame = static_cast<uint64_t>(h);
449         }
450         else
451         {
452             carla_stderr2("Wrong OSC type used for '%s'", method);
453             sendResponse(messageId, "Wrong OSC type");
454             return 0;
455         }
456 
457         ok = true;
458         fEngine->transportRelocate(frame);
459     }
460     else if (std::strcmp(method, "add_plugin") == 0)
461     {
462         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 8);
463         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[1] == 'i');
464         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[2] == 'i');
465         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[3] == 's');
466         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[4] == 's');
467         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[5] == 's');
468         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[7] == 'i');
469 
470         int32_t btype = argv[1]->i;
471         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(btype >= 0);
472 
473         const int32_t ptype = argv[2]->i;
474         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(ptype >= 0);
475 
476         // Force binary type to be native in some cases
477         switch (ptype)
478         {
479         case PLUGIN_INTERNAL:
480         case PLUGIN_LV2:
481         case PLUGIN_SF2:
482         case PLUGIN_SFZ:
483         case PLUGIN_JACK:
484             btype = BINARY_NATIVE;
485             break;
486         }
487 
488         const char* filename = &argv[3]->s;
489 
490         if (filename != nullptr && std::strcmp(filename, "(null)") == 0)
491             filename = nullptr;
492 
493         const char* name = &argv[4]->s;
494 
495         if (name != nullptr && std::strcmp(name, "(null)") == 0)
496             name = nullptr;
497 
498         const char* const label = &argv[5]->s;
499         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(label != nullptr && label[0] != '\0');
500 
501         int64_t uniqueId;
502 
503         /**/ if (types[6] == 'i')
504         {
505             uniqueId = argv[6]->i;
506         }
507         else if (types[6] == 'h')
508         {
509             uniqueId = argv[6]->h;
510         }
511         else
512         {
513             carla_stderr2("Wrong OSC type used for '%s' uniqueId", method);
514             sendResponse(messageId, "Wrong OSC type");
515             return 0;
516         }
517 
518         const int32_t options = argv[7]->i;
519         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(options >= 0);
520 
521         ok = fEngine->addPlugin(static_cast<BinaryType>(btype),
522                                 static_cast<PluginType>(ptype),
523                                 filename, name, label, uniqueId, nullptr, static_cast<uint32_t>(options));
524     }
525     else if (std::strcmp(method, "remove_plugin") == 0)
526     {
527         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 2);
528         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[1] == 'i');
529 
530         const int32_t id = argv[1]->i;
531         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(id >= 0);
532 
533         ok = fEngine->removePlugin(static_cast<uint32_t>(id));
534     }
535     else if (std::strcmp(method, "remove_all_plugins") == 0)
536     {
537         ok = fEngine->removeAllPlugins();
538     }
539     else if (std::strcmp(method, "rename_plugin") == 0)
540     {
541         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 3);
542         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[1] == 'i');
543         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[2] == 's');
544 
545         const int32_t id = argv[1]->i;
546         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(id >= 0);
547 
548         const char* const newName = &argv[2]->s;
549         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(newName != nullptr && newName[0] != '\0');
550 
551         ok = fEngine->renamePlugin(static_cast<uint32_t>(id), newName);
552     }
553     else if (std::strcmp(method, "clone_plugin") == 0)
554     {
555         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 2);
556         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[1] == 'i');
557 
558         const int32_t id = argv[1]->i;
559         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(id >= 0);
560 
561         ok = fEngine->clonePlugin(static_cast<uint32_t>(id));
562     }
563     else if (std::strcmp(method, "replace_plugin") == 0)
564     {
565         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 2);
566         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[1] == 'i');
567 
568         const int32_t id = argv[1]->i;
569         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(id >= 0);
570 
571         ok = fEngine->replacePlugin(static_cast<uint32_t>(id));
572     }
573     else if (std::strcmp(method, "switch_plugins") == 0)
574     {
575         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(argc == 3);
576         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[1] == 'i');
577         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(types[2] == 'i');
578 
579         const int32_t idA = argv[1]->i;
580         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(idA >= 0);
581 
582         const int32_t idB = argv[2]->i;
583         CARLA_SAFE_ASSERT_RETURN_OSC_ERR(idB >= 0);
584 
585         ok = fEngine->switchPlugins(static_cast<uint32_t>(idA), static_cast<uint32_t>(idB));
586     }
587     else
588     {
589         carla_stderr2("Unhandled OSC control for '%s'", method);
590         sendResponse(messageId, "Unhandled OSC control method");
591         return 0;
592     }
593 
594 #undef CARLA_SAFE_ASSERT_RETURN_OSC_ERR
595 
596     sendResponse(messageId, ok ? "" : fEngine->getLastError());
597     return 0;
598 }
599 
600 // -----------------------------------------------------------------------
601 
handleMsgSetActive(CARLA_ENGINE_OSC_HANDLE_ARGS)602 int CarlaEngineOsc::handleMsgSetActive(CARLA_ENGINE_OSC_HANDLE_ARGS)
603 {
604     carla_debug("CarlaEngineOsc::handleMsgSetActive()");
605     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(1, "i");
606 
607     const bool active = (argv[0]->i != 0);
608 
609     plugin->setActive(active, false, true);
610     return 0;
611 }
612 
handleMsgSetDryWet(CARLA_ENGINE_OSC_HANDLE_ARGS)613 int CarlaEngineOsc::handleMsgSetDryWet(CARLA_ENGINE_OSC_HANDLE_ARGS)
614 {
615     carla_debug("CarlaEngineOsc::handleMsgSetDryWet()");
616     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(1, "f");
617 
618     const float value = argv[0]->f;
619 
620     plugin->setDryWet(value, false, true);
621     return 0;
622 }
623 
handleMsgSetVolume(CARLA_ENGINE_OSC_HANDLE_ARGS)624 int CarlaEngineOsc::handleMsgSetVolume(CARLA_ENGINE_OSC_HANDLE_ARGS)
625 {
626     carla_debug("CarlaEngineOsc::handleMsgSetVolume()");
627     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(1, "f");
628 
629     const float value = argv[0]->f;
630 
631     plugin->setVolume(value, false, true);
632     return 0;
633 }
634 
handleMsgSetBalanceLeft(CARLA_ENGINE_OSC_HANDLE_ARGS)635 int CarlaEngineOsc::handleMsgSetBalanceLeft(CARLA_ENGINE_OSC_HANDLE_ARGS)
636 {
637     carla_debug("CarlaEngineOsc::handleMsgSetBalanceLeft()");
638     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(1, "f");
639 
640     const float value = argv[0]->f;
641 
642     plugin->setBalanceLeft(value, false, true);
643     return 0;
644 }
645 
handleMsgSetBalanceRight(CARLA_ENGINE_OSC_HANDLE_ARGS)646 int CarlaEngineOsc::handleMsgSetBalanceRight(CARLA_ENGINE_OSC_HANDLE_ARGS)
647 {
648     carla_debug("CarlaEngineOsc::handleMsgSetBalanceRight()");
649     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(1, "f");
650 
651     const float value = argv[0]->f;
652 
653     plugin->setBalanceRight(value, false, true);
654     return 0;
655 }
656 
handleMsgSetPanning(CARLA_ENGINE_OSC_HANDLE_ARGS)657 int CarlaEngineOsc::handleMsgSetPanning(CARLA_ENGINE_OSC_HANDLE_ARGS)
658 {
659     carla_debug("CarlaEngineOsc::handleMsgSetPanning()");
660     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(1, "f");
661 
662     const float value = argv[0]->f;
663 
664     plugin->setPanning(value, false, true);
665     return 0;
666 }
667 
handleMsgSetParameterValue(CARLA_ENGINE_OSC_HANDLE_ARGS)668 int CarlaEngineOsc::handleMsgSetParameterValue(CARLA_ENGINE_OSC_HANDLE_ARGS)
669 {
670     carla_debug("CarlaEngineOsc::handleMsgSetParameterValue()");
671     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(2, "if");
672 
673     const int32_t index = argv[0]->i;
674     const float   value = argv[1]->f;
675 
676     CARLA_SAFE_ASSERT_RETURN(index >= 0, 0);
677 
678     plugin->setParameterValue(static_cast<uint32_t>(index), value, true, false, true);
679     return 0;
680 }
681 
handleMsgSetParameterMappedControlIndex(CARLA_ENGINE_OSC_HANDLE_ARGS)682 int CarlaEngineOsc::handleMsgSetParameterMappedControlIndex(CARLA_ENGINE_OSC_HANDLE_ARGS)
683 {
684     carla_debug("CarlaEngineOsc::handleMsgSetParameterMappedIndex()");
685     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(2, "ii");
686 
687     const int32_t index = argv[0]->i;
688     const int32_t ctrl  = argv[1]->i;
689 
690     CARLA_SAFE_ASSERT_RETURN(index >= 0, 0);
691     CARLA_SAFE_ASSERT_RETURN(ctrl >= CONTROL_INDEX_NONE && ctrl <= CONTROL_INDEX_MAX_ALLOWED, 0);
692 
693     plugin->setParameterMappedControlIndex(static_cast<uint32_t>(index), static_cast<int16_t>(ctrl), false, true, true);
694     return 0;
695 }
696 
handleMsgSetParameterMappedRange(CARLA_ENGINE_OSC_HANDLE_ARGS)697 int CarlaEngineOsc::handleMsgSetParameterMappedRange(CARLA_ENGINE_OSC_HANDLE_ARGS)
698 {
699     carla_debug("CarlaEngineOsc::handleMsgSetParameterMappedRange()");
700     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(2, "iff");
701 
702     const int32_t index   = argv[0]->i;
703     const float   minimum = argv[1]->f;
704     const float   maximum = argv[2]->f;
705 
706     CARLA_SAFE_ASSERT_RETURN(index >= 0, 0);
707 
708     plugin->setParameterMappedRange(static_cast<uint32_t>(index), minimum, maximum, false, true);
709     return 0;
710 }
711 
handleMsgSetParameterMidiChannel(CARLA_ENGINE_OSC_HANDLE_ARGS)712 int CarlaEngineOsc::handleMsgSetParameterMidiChannel(CARLA_ENGINE_OSC_HANDLE_ARGS)
713 {
714     carla_debug("CarlaEngineOsc::handleMsgSetParameterMidiChannel()");
715     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(2, "ii");
716 
717     const int32_t index   = argv[0]->i;
718     const int32_t channel = argv[1]->i;
719 
720     CARLA_SAFE_ASSERT_RETURN(index >= 0, 0);
721     CARLA_SAFE_ASSERT_RETURN(channel >= 0 && channel < MAX_MIDI_CHANNELS, 0);
722 
723     plugin->setParameterMidiChannel(static_cast<uint32_t>(index), static_cast<uint8_t>(channel), false, true);
724     return 0;
725 }
726 
handleMsgSetProgram(CARLA_ENGINE_OSC_HANDLE_ARGS)727 int CarlaEngineOsc::handleMsgSetProgram(CARLA_ENGINE_OSC_HANDLE_ARGS)
728 {
729     carla_debug("CarlaEngineOsc::handleMsgSetProgram()");
730     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(1, "i");
731 
732     const int32_t index = argv[0]->i;
733 
734     CARLA_SAFE_ASSERT_RETURN(index >= -1, 0);
735 
736     plugin->setProgram(index, true, false, true);
737     return 0;
738 }
739 
handleMsgSetMidiProgram(CARLA_ENGINE_OSC_HANDLE_ARGS)740 int CarlaEngineOsc::handleMsgSetMidiProgram(CARLA_ENGINE_OSC_HANDLE_ARGS)
741 {
742     carla_debug("CarlaEngineOsc::handleMsgSetMidiProgram()");
743     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(1, "i");
744 
745     const int32_t index = argv[0]->i;
746 
747     CARLA_SAFE_ASSERT_RETURN(index >= -1, 0);
748 
749     plugin->setMidiProgram(index, true, false, true);
750     return 0;
751 }
752 
handleMsgNoteOn(CARLA_ENGINE_OSC_HANDLE_ARGS)753 int CarlaEngineOsc::handleMsgNoteOn(CARLA_ENGINE_OSC_HANDLE_ARGS)
754 {
755     carla_debug("CarlaEngineOsc::handleMsgNoteOn()");
756     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(3, "iii");
757 
758     const int32_t channel = argv[0]->i;
759     const int32_t note    = argv[1]->i;
760     const int32_t velo    = argv[2]->i;
761 
762     CARLA_SAFE_ASSERT_RETURN(channel >= 0 && channel < MAX_MIDI_CHANNELS, 0);
763     CARLA_SAFE_ASSERT_RETURN(note >= 0 && note < MAX_MIDI_NOTE, 0);
764     CARLA_SAFE_ASSERT_RETURN(velo >= 0 && velo < MAX_MIDI_VALUE, 0);
765 
766     plugin->sendMidiSingleNote(static_cast<uint8_t>(channel), static_cast<uint8_t>(note), static_cast<uint8_t>(velo), true, false, true);
767     return 0;
768 }
769 
handleMsgNoteOff(CARLA_ENGINE_OSC_HANDLE_ARGS)770 int CarlaEngineOsc::handleMsgNoteOff(CARLA_ENGINE_OSC_HANDLE_ARGS)
771 {
772     carla_debug("CarlaEngineOsc::handleMsgNoteOff()");
773     CARLA_ENGINE_OSC_CHECK_OSC_TYPES(2, "ii");
774 
775     const int32_t channel = argv[0]->i;
776     const int32_t note    = argv[1]->i;
777 
778     CARLA_SAFE_ASSERT_RETURN(channel >= 0 && channel < MAX_MIDI_CHANNELS, 0);
779     CARLA_SAFE_ASSERT_RETURN(note >= 0 && note < MAX_MIDI_NOTE, 0);
780 
781     plugin->sendMidiSingleNote(static_cast<uint8_t>(channel), static_cast<uint8_t>(note), 0, true, false, true);
782     return 0;
783 }
784 
785 // -----------------------------------------------------------------------
786 
787 CARLA_BACKEND_END_NAMESPACE
788 
789 #endif // HAVE_LIBLO
790