1 //  osc handler for supercollider-style communication, implementation
2 //  Copyright (C) 2009, 2010 Tim Blechmann
3 //
4 //  This program is free software; you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation; either version 2 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; see the file COPYING.  If not, write to
16 //  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 //  Boston, MA 02111-1307, USA.
18 
19 #include <iostream>
20 
21 // AppleClang workaround
22 #if defined(__apple_build_version__) && __apple_build_version__ > 11000000
23 #    define BOOST_ASIO_HAS_STD_STRING_VIEW 1
24 #endif
25 
26 #include <boost/asio/placeholders.hpp>
27 #include <boost/asio/read.hpp>
28 #include <boost/bind.hpp>
29 
30 
31 #include "osc/OscOutboundPacketStream.h"
32 #include "osc/OscPrintReceivedElements.h"
33 
34 #include "sc_msg_iter.h"
35 #include "sc_osc_handler.hpp"
36 #include "../server/server.hpp"
37 #include "utilities/sized_array.hpp"
38 
39 #include "SC_OSC_Commands.h"
40 #include "SC_Version.hpp"
41 #include "SC_FifoMsg.h"
42 
43 #ifdef _WIN32
44 #    include "malloc.h" // for alloca
45 #endif
46 
47 namespace nova {
48 
49 using namespace std;
50 using nova::detail::endpoint_ptr;
51 using nova::detail::nova_endpoint;
52 
53 namespace {
54 
55 int32_t last_generated = 0;
56 
find_node(int32_t target_id)57 server_node* find_node(int32_t target_id) {
58     if (target_id == -1)
59         target_id = last_generated;
60 
61     server_node* node = instance->find_node(target_id);
62 
63     if (node == nullptr)
64         log_printf("node not found: %d\n", target_id);
65 
66     return node;
67 }
68 
find_group(int32_t target_id)69 abstract_group* find_group(int32_t target_id) {
70     if (target_id == -1)
71         target_id = last_generated;
72 
73     abstract_group* node = instance->find_group(target_id);
74 
75     if (node == nullptr)
76         log("node not found or not a group\n");
77     return node;
78 }
79 
check_node_id(int node_id)80 bool check_node_id(int node_id) {
81     if (!instance->node_id_available(node_id)) {
82         log_printf("node id %d already in use\n", node_id);
83         return false;
84     }
85     return true;
86 }
87 
fill_notification(const server_node * node,osc::OutboundPacketStream & p)88 void fill_notification(const server_node* node, osc::OutboundPacketStream& p) {
89     p << node->id();
90 
91     /* parent */
92     const abstract_group* parent_node = node->get_parent();
93     assert(parent_node);
94     p << parent_node->id();
95 
96     /* previous/next */
97     if (parent_node->is_parallel())
98         p << -2 << -2; /* we are in a parallel group, so we have no notion of previous/next */
99     else {
100         const server_node* prev_node = node->previous_node();
101         if (prev_node)
102             p << prev_node->id();
103         else
104             p << -1;
105 
106         const server_node* next_node = node->next_node();
107         if (next_node)
108             p << next_node->id();
109         else
110             p << -1;
111     }
112 
113     /* is_synth, head, tail */
114     if (node->is_synth())
115         p << 0;
116     else {
117         const abstract_group* node_group = static_cast<const abstract_group*>(node);
118         p << 1;
119 
120         if (node_group->is_parallel())
121             p << -2 << -2;
122         else {
123             const group* node_real_group = static_cast<const group*>(node_group);
124             if (node_real_group->empty())
125                 p << -1 << -1;
126             else
127                 p << node_real_group->head_node()->id() << node_real_group->tail_node()->id();
128         }
129     }
130 
131     p << osc::EndMessage;
132 }
133 
134 spin_lock system_callback_allocator_lock;
135 
136 struct movable_string {
137     /** allocate new string, only allowed to be called from the rt context */
movable_stringnova::__anon8089e4d40111::movable_string138     explicit movable_string(const char* str) {
139         size_t length = strlen(str) + 1; /* terminating \0 */
140         char* data = (char*)system_callback::allocate(length);
141         strcpy(data, str);
142         data_ = data;
143     }
144 
145     movable_string(movable_string const& rhs) = delete;
146     movable_string operator=(movable_string const& rhs) = delete;
147 
movable_stringnova::__anon8089e4d40111::movable_string148     movable_string(movable_string&& rhs) {
149         data_ = rhs.data_;
150         const_cast<movable_string&>(rhs).data_ = nullptr;
151     }
152 
~movable_stringnova::__anon8089e4d40111::movable_string153     ~movable_string(void) {
154         if (data_)
155             system_callback::deallocate((char*)data_);
156     }
157 
c_strnova::__anon8089e4d40111::movable_string158     const char* c_str(void) const { return data_; }
159 
160 private:
161     const char* data_ = nullptr;
162 };
163 
164 template <typename T> struct movable_array {
165     /** allocate new array, only allowed to be called from the rt context */
movable_arraynova::__anon8089e4d40111::movable_array166     movable_array(size_t length, const T* data, bool locked = false): length_(length) {
167         data_ = (T*)system_callback::allocate(length * sizeof(T));
168         for (size_t i = 0; i != length; ++i)
169             data_[i] = data[i];
170     }
171 
172     movable_array& operator=(movable_array const& rhs) = delete;
173     movable_array(movable_array const& rhs) = delete;
174 
movable_arraynova::__anon8089e4d40111::movable_array175     movable_array(movable_array&& rhs): length_(rhs.length_), data_(rhs.data_) { rhs.data_ = nullptr; }
176 
operator =nova::__anon8089e4d40111::movable_array177     movable_array& operator=(movable_array&& rhs) {
178         if (data_)
179             system_callback::deallocate(data_);
180 
181         length_ = rhs.length_;
182         data_ = rhs.data_;
183 
184         rhs.data_ = nullptr;
185 
186         return *this;
187     }
188 
~movable_arraynova::__anon8089e4d40111::movable_array189     ~movable_array(void) {
190         if (!data_)
191             return;
192         system_callback::deallocate(data_);
193         data_ = nullptr;
194     }
195 
datanova::__anon8089e4d40111::movable_array196     const T* data(void) const { return data_; }
197 
operator []nova::__anon8089e4d40111::movable_array198     const T& operator[](size_t index) const { return data_[index]; }
199 
sizenova::__anon8089e4d40111::movable_array200     size_t size(void) const { return length_; }
201 
202 private:
203     size_t length_ = 0;
204     T* data_;
205 };
206 
consume(T && object)207 template <typename T> static inline void consume(T&& object) {
208     T sink(std::forward<T>(object)); // move object here (and destroy)
209 }
210 
send_done_message(endpoint_ptr const & endpoint,const char * cmd)211 void send_done_message(endpoint_ptr const& endpoint, const char* cmd) {
212     char buffer[1024];
213     osc::OutboundPacketStream p(buffer, 1024);
214     p << osc::BeginMessage("/done") << cmd << osc::EndMessage;
215 
216     endpoint->send(p.Data(), p.Size());
217 }
218 
send_done_message(endpoint_ptr const & endpoint,const char * cmd,osc::int32 index)219 void send_done_message(endpoint_ptr const& endpoint, const char* cmd, osc::int32 index) {
220     char buffer[1024];
221     osc::OutboundPacketStream p(buffer, 1024);
222     p << osc::BeginMessage("/done") << cmd << index << osc::EndMessage;
223 
224     endpoint->send(p.Data(), p.Size());
225 }
226 
send_fail_message(endpoint_ptr const & endpoint,const char * cmd,const char * content)227 void send_fail_message(endpoint_ptr const& endpoint, const char* cmd, const char* content) {
228     char buffer[8192];
229     osc::OutboundPacketStream p(buffer, 8192);
230     p << osc::BeginMessage("/fail") << cmd << content << osc::EndMessage;
231 
232     endpoint->send(p.Data(), p.Size());
233 }
234 
send_fail_message(endpoint_ptr const & endpoint,const char * cmd,const char * content,int id)235 void send_fail_message(endpoint_ptr const& endpoint, const char* cmd, const char* content, int id) {
236     char buffer[8192];
237     osc::OutboundPacketStream p(buffer, 8192);
238     p << osc::BeginMessage("/fail") << cmd << content << (osc::int32)id << osc::EndMessage;
239 
240     endpoint->send(p.Data(), p.Size());
241 }
242 
243 
244 template <typename Functor> struct fn_system_callback : public system_callback {
fn_system_callbacknova::__anon8089e4d40111::fn_system_callback245     fn_system_callback(Functor const& fn): fn_(fn) {}
246 
fn_system_callbacknova::__anon8089e4d40111::fn_system_callback247     fn_system_callback(Functor&& fn): fn_(std::forward<Functor>(fn)) {}
248 
runnova::__anon8089e4d40111::fn_system_callback249     void run(void) override { fn_(); }
250 
251     Functor fn_;
252 };
253 
254 template <typename Functor> struct fn_sync_callback : public audio_sync_callback {
fn_sync_callbacknova::__anon8089e4d40111::fn_sync_callback255     fn_sync_callback(Functor const& fn): fn_(fn) {}
256 
fn_sync_callbacknova::__anon8089e4d40111::fn_sync_callback257     fn_sync_callback(Functor&& fn): fn_(std::forward<Functor>(fn)) {}
258 
runnova::__anon8089e4d40111::fn_sync_callback259     void run(void) override { fn_(); }
260 
261     Functor fn_;
262 };
263 
264 /** helper class for dispatching real-time and non real-time osc command callbacks
265  *
266  *  uses template specialization to avoid unnecessary callback rescheduling
267  */
268 template <typename DerivedClass> struct cmd_dispatcher_base {
free_in_rt_threadnova::__anon8089e4d40111::cmd_dispatcher_base269     template <typename A> static void free_in_rt_thread(A&& object) {
270         DerivedClass::fire_rt_callback([object = std::move(object)]() mutable { consume(std::move(object)); });
271     }
272 
free_in_rt_threadnova::__anon8089e4d40111::cmd_dispatcher_base273     template <typename A, typename B> static void free_in_rt_thread(A&& object1, B&& object2) {
274         DerivedClass::fire_rt_callback([object1 = std::move(object1), object2 = std::move(object2)]() mutable {
275             consume(std::move(object1));
276             consume(std::move(object2));
277         });
278     }
279 
free_in_rt_threadnova::__anon8089e4d40111::cmd_dispatcher_base280     template <typename A, typename B, typename C> static void free_in_rt_thread(A&& object1, B&& object2, C&& object3) {
281         DerivedClass::fire_rt_callback(
282             [object1 = std::move(object1), object2 = std::move(object2), object3 = std::move(object3)]() mutable {
283                 consume(std::move(object1));
284                 consume(std::move(object2));
285                 consume(std::move(object3));
286             });
287     }
288 
289     template <typename A, typename B, typename C, typename D>
free_in_rt_threadnova::__anon8089e4d40111::cmd_dispatcher_base290     static void free_in_rt_thread(A&& object1, B&& object2, C&& object3, D&& object4) {
291         DerivedClass::fire_rt_callback([object1 = std::move(object1), object2 = std::move(object2),
292                                         object3 = std::move(object3), object4 = std::move(object4)]() mutable {
293             consume(std::move(object1));
294             consume(std::move(object2));
295             consume(std::move(object3));
296             consume(std::move(object4));
297         });
298     }
299 
fire_done_messagenova::__anon8089e4d40111::cmd_dispatcher_base300     static void fire_done_message(endpoint_ptr const& endpoint_ref, const char* cmd, osc::int32 index) {
301         if (endpoint_ref) {
302             DerivedClass::fire_io_callback(
303                 [=, endpoint = endpoint_ptr(endpoint_ref)]() { send_done_message(endpoint, cmd, index); });
304         }
305     }
306 
fire_messagenova::__anon8089e4d40111::cmd_dispatcher_base307     static void fire_message(endpoint_ptr const& endpoint_ref, movable_array<char>&& message) {
308         if (endpoint_ref) {
309             DerivedClass::fire_io_callback(
310                 [=, message = std::move(message), endpoint = endpoint_ptr(endpoint_ref)]() mutable {
311                     endpoint->send(message.data(), message.size());
312 
313                     DerivedClass::fire_rt_callback(
314                         [=, message = std::move(message)]() mutable { consume(std::move(message)); });
315                 });
316         }
317     }
318 };
319 
320 
321 template <bool realtime> struct cmd_dispatcher : public cmd_dispatcher_base<cmd_dispatcher<realtime>> {
fire_system_callbacknova::__anon8089e4d40111::cmd_dispatcher322     template <typename Functor> static void fire_system_callback(Functor const& f) {
323         instance->add_system_callback(new fn_system_callback<Functor>(f));
324     }
325 
fire_system_callbacknova::__anon8089e4d40111::cmd_dispatcher326     template <typename Functor> static void fire_system_callback(Functor&& f) {
327         instance->add_system_callback(new fn_system_callback<Functor>(std::forward<Functor>(f)));
328     }
329 
fire_io_callbacknova::__anon8089e4d40111::cmd_dispatcher330     template <typename Functor> static void fire_io_callback(Functor const& f) {
331         instance->add_io_callback(new fn_system_callback<Functor>(f));
332     }
333 
fire_io_callbacknova::__anon8089e4d40111::cmd_dispatcher334     template <typename Functor> static void fire_io_callback(Functor&& f) {
335         instance->add_io_callback(new fn_system_callback<Functor>(std::forward<Functor>(f)));
336     }
337 
fire_rt_callbacknova::__anon8089e4d40111::cmd_dispatcher338     template <typename Functor> static void fire_rt_callback(Functor const& f) {
339         instance->add_sync_callback(new fn_sync_callback<Functor>(f));
340     }
341 
fire_rt_callbacknova::__anon8089e4d40111::cmd_dispatcher342     template <typename Functor> static void fire_rt_callback(Functor&& f) {
343         instance->add_sync_callback(new fn_sync_callback<Functor>(std::forward<Functor>(f)));
344     }
345 };
346 
347 
348 template <> struct cmd_dispatcher<false> : public cmd_dispatcher_base<cmd_dispatcher<false>> {
fire_system_callbacknova::__anon8089e4d40111::cmd_dispatcher349     template <typename Functor> static void fire_system_callback(Functor const& f) { f(); }
350 
fire_system_callbacknova::__anon8089e4d40111::cmd_dispatcher351     template <typename Functor> static void fire_system_callback(Functor&& f) {
352         Functor fn(std::forward<Functor>(f));
353         fn();
354     }
355 
fire_rt_callbacknova::__anon8089e4d40111::cmd_dispatcher356     template <typename Functor> static void fire_rt_callback(Functor const& f) { f(); }
357 
fire_rt_callbacknova::__anon8089e4d40111::cmd_dispatcher358     template <typename Functor> static void fire_rt_callback(Functor&& f) {
359         Functor fn(std::forward<Functor>(f));
360         fn();
361     }
362 
fire_io_callbacknova::__anon8089e4d40111::cmd_dispatcher363     template <typename Functor> static void fire_io_callback(Functor const& f) { f(); }
364 
fire_io_callbacknova::__anon8089e4d40111::cmd_dispatcher365     template <typename Functor> static void fire_io_callback(Functor&& f) {
366         Functor fn(std::forward<Functor>(f));
367         fn();
368     }
369 };
370 
report_failure(endpoint_ptr const & endpoint,std::exception const & error,const char * command,int bufnum)371 void report_failure(endpoint_ptr const& endpoint, std::exception const& error, const char* command, int bufnum) {
372     std::cout << error.what() << std::endl;
373     send_fail_message(endpoint, command, error.what(), bufnum);
374 }
375 
376 } /* namespace */
377 
378 namespace detail {
379 using nova::log;
380 
add_observer(endpoint_ptr const & ep)381 int sc_notify_observers::add_observer(endpoint_ptr const& ep) {
382     auto it = find(ep);
383     if (it != observers.end())
384         return already_registered;
385 
386     observers.push_back(ep);
387     return observers.size() - 1;
388 }
389 
remove_observer(endpoint_ptr const & ep)390 int sc_notify_observers::remove_observer(endpoint_ptr const& ep) {
391     auto it = find(ep);
392 
393     if (it == observers.end())
394         return not_registered;
395 
396     const int observerIndex = it - observers.begin();
397     observers.erase(it);
398     return observerIndex;
399 }
400 
error_string(error_code error)401 const char* sc_notify_observers::error_string(error_code error) {
402     switch (error) {
403     case no_error:
404         return "";
405 
406     case already_registered:
407         return "notify: already registered";
408 
409     case not_registered:
410         return "notify: not registered";
411 
412     default:
413         assert(false);
414         return "";
415     }
416 }
417 
find(endpoint_ptr const & ep)418 sc_notify_observers::observer_vector::iterator sc_notify_observers::find(endpoint_ptr const& ep) {
419     for (auto it = observers.begin(); it != observers.end(); ++it) {
420         udp_endpoint* elemUDP = dynamic_cast<udp_endpoint*>(it->get());
421         udp_endpoint* testUDP = dynamic_cast<udp_endpoint*>(ep.get());
422         if (elemUDP && testUDP) {
423             if (*elemUDP == *testUDP)
424                 return it;
425         }
426 
427         typedef sc_osc_handler::tcp_connection tcp_connection;
428 
429         tcp_connection* elemTCP = dynamic_cast<tcp_connection*>(it->get());
430         tcp_connection* testTCP = dynamic_cast<tcp_connection*>(ep.get());
431         if (elemTCP && testTCP) {
432             if (*elemTCP == *testTCP)
433                 return it;
434         }
435     }
436     return observers.end();
437 }
438 
439 
notify(const char * address_pattern,const server_node * node) const440 void sc_notify_observers::notify(const char* address_pattern, const server_node* node) const {
441     char buffer[128]; // 128 byte should be enough
442     osc::OutboundPacketStream p(buffer, 128);
443     p << osc::BeginMessage(address_pattern);
444     fill_notification(node, p);
445 
446     movable_array<char> message(p.Size(), p.Data());
447 
448     cmd_dispatcher<true>::fire_io_callback([=, message = std::move(message)]() mutable {
449         instance->send_notification(message.data(), message.size());
450 
451         cmd_dispatcher<true>::fire_rt_callback(
452             [=, message = std::move(message)]() mutable { consume(std::move(message)); });
453     });
454 }
455 
fire_trigger(int32_t node_id,int32_t trigger_id,float value)456 void fire_trigger(int32_t node_id, int32_t trigger_id, float value) {
457     char buffer[128]; // 128 byte should be enough
458     osc::OutboundPacketStream p(buffer, 128);
459     p << osc::BeginMessage("/tr") << osc::int32(node_id) << osc::int32(trigger_id) << value << osc::EndMessage;
460 
461     instance->send_notification(p.Data(), p.Size());
462 }
463 
send_trigger(int32_t node_id,int32_t trigger_id,float value)464 void sc_notify_observers::send_trigger(int32_t node_id, int32_t trigger_id, float value) {
465     // called from rt helper threads, so we need to lock the memory pool (for system_callback allocation)
466     spin_lock::scoped_lock lock(system_callback_allocator_lock);
467     cmd_dispatcher<true>::fire_io_callback([=]() { fire_trigger(node_id, trigger_id, value); });
468 }
469 
send_node_reply(int32_t node_id,int reply_id,const char * command_name,int argument_count,const float * values)470 void sc_notify_observers::send_node_reply(int32_t node_id, int reply_id, const char* command_name, int argument_count,
471                                           const float* values) {
472     // called from rt helper threads, so we need to lock the memory pool
473     spin_lock::scoped_lock lock(system_callback_allocator_lock);
474     movable_string cmd(command_name);
475     movable_array<float> value_array(argument_count, values);
476 
477     cmd_dispatcher<true>::fire_io_callback([=, value_array = std::move(value_array), cmd = std::move(cmd)]() mutable {
478         size_t buffer_size = 1024 + strlen(cmd.c_str()) + value_array.size() * sizeof(float);
479 
480         char* buffer = (buffer_size < 2048) ? (char*)alloca(buffer_size) : (char*)malloc(buffer_size);
481 
482         try {
483             osc::OutboundPacketStream p(buffer, buffer_size);
484             p << osc::BeginMessage(cmd.c_str()) << osc::int32(node_id) << osc::int32(reply_id);
485 
486             for (int i = 0; i != value_array.size(); ++i)
487                 p << value_array[i];
488 
489             p << osc::EndMessage;
490 
491             instance->send_notification(p.Data(), p.Size());
492         } catch (...) {
493         }
494 
495         cmd_dispatcher<true>::free_in_rt_thread(std::move(value_array), std::move(cmd));
496 
497         if (buffer_size >= 2048)
498             free(buffer);
499     });
500 }
501 
send_notification(const char * data,size_t length)502 void sc_notify_observers::send_notification(const char* data, size_t length) {
503     for (auto& observer : observers)
504         observer->send(data, length);
505 }
506 
send(const char * data,size_t length)507 void udp_endpoint::send(const char* data, size_t length) {
508     instance->sc_notify_observers::send_udp(data, length, endpoint_);
509 }
510 
send_udp(const char * data,size_t size,udp::endpoint const & receiver)511 void sc_notify_observers::send_udp(const char* data, size_t size, udp::endpoint const& receiver) {
512     std::lock_guard<std::mutex> lock(udp_mutex);
513     sc_notify_observers::udp_socket.send_to(boost::asio::buffer(data, size), receiver);
514 }
515 
516 
run(void)517 void sc_scheduled_bundles::bundle_node::run(void) {
518     typedef osc::ReceivedBundleElement bundle_element;
519     typedef osc::ReceivedBundle received_bundle;
520     typedef osc::ReceivedMessage ReceivedMessage;
521 
522     bundle_element element(data_);
523 
524     if (element.IsBundle()) {
525         received_bundle bundle(element);
526         instance->handle_bundle<true>(bundle, endpoint_);
527     } else {
528         ReceivedMessage message(element);
529         instance->handle_message<true>(message, element.Size(), endpoint_);
530     }
531 }
532 
insert_bundle(time_tag const & timeout,const char * data,size_t length,endpoint_ptr const & endpoint)533 void sc_scheduled_bundles::insert_bundle(time_tag const& timeout, const char* data, size_t length,
534                                          endpoint_ptr const& endpoint) {
535     /* allocate chunk from realtime pool */
536     void* chunk = rt_pool.malloc(sizeof(bundle_node) + length + 4);
537     bundle_node* node = (bundle_node*)chunk;
538     char* cpy = (char*)chunk + sizeof(bundle_node);
539 
540     memcpy(cpy, data - 4, length + 4);
541 
542     new (node) bundle_node(timeout, cpy, endpoint);
543 
544     bundle_q.insert(*node);
545 }
546 
execute_bundles(time_tag const & last,time_tag const & now)547 void sc_scheduled_bundles::execute_bundles(time_tag const& last, time_tag const& now) {
548     World* world = &sc_factory->world;
549 
550     while (!bundle_q.empty()) {
551         bundle_node& front = *bundle_q.top();
552         time_tag const& next_timestamp = front.timeout_;
553 
554         if (now < next_timestamp)
555             break;
556 
557         if (last < next_timestamp) {
558             // between last and now
559             time_tag time_since_last = next_timestamp - last;
560             float samples_since_last = time_since_last.to_samples(world->mSampleRate);
561 
562             float sample_offset;
563             float subsample_offset = std::modf(samples_since_last, &sample_offset);
564 
565             world->mSampleOffset = (int)sample_offset;
566             world->mSubsampleOffset = subsample_offset;
567         } else
568             world->mSampleOffset = world->mSubsampleOffset = 0;
569 
570         front.run();
571         bundle_q.erase_and_dispose(bundle_q.top(), &dispose_bundle);
572     }
573 
574     world->mSampleOffset = world->mSubsampleOffset = 0;
575 }
576 
577 
open_tcp_acceptor(tcp const & protocol,unsigned int port)578 void sc_osc_handler::open_tcp_acceptor(tcp const& protocol, unsigned int port) {
579     tcp_acceptor_.open(protocol);
580 
581     tcp_acceptor_.bind(tcp::endpoint(protocol, port));
582     tcp_acceptor_.listen();
583     start_tcp_accept();
584 }
585 
open_udp_socket(udp const & protocol,unsigned int port)586 void sc_osc_handler::open_udp_socket(udp const& protocol, unsigned int port) {
587     sc_notify_observers::udp_socket.open(protocol);
588     sc_notify_observers::udp_socket.bind(udp::endpoint(protocol, port));
589 }
590 
open_socket(int family,int type,int protocol,unsigned int port)591 bool sc_osc_handler::open_socket(int family, int type, int protocol, unsigned int port) {
592     if (protocol == IPPROTO_TCP) {
593         if (type != SOCK_STREAM)
594             return false;
595 
596         if (family == AF_INET)
597             open_tcp_acceptor(tcp::v4(), port);
598         else if (family == AF_INET6)
599             open_tcp_acceptor(tcp::v6(), port);
600         else
601             return false;
602         return true;
603     } else if (protocol == IPPROTO_UDP) {
604         if (type != SOCK_DGRAM)
605             return false;
606 
607         if (family == AF_INET)
608             open_udp_socket(udp::v4(), port);
609         else if (family == AF_INET6)
610             open_udp_socket(udp::v6(), port);
611         else
612             return false;
613         start_receive_udp();
614         return true;
615     }
616     return false;
617 }
618 
start_receive_udp()619 void sc_osc_handler::start_receive_udp() {
620     using namespace boost;
621     sc_notify_observers::udp_socket.async_receive_from(buffer(recv_buffer_), udp_remote_endpoint_,
622                                                        bind(&sc_osc_handler::handle_receive_udp, this,
623                                                             asio::placeholders::error,
624                                                             asio::placeholders::bytes_transferred));
625 }
626 
handle_receive_udp(const boost::system::error_code & error,std::size_t bytes_transferred)627 void sc_osc_handler::handle_receive_udp(const boost::system::error_code& error, std::size_t bytes_transferred) {
628     if (unlikely(error == error::operation_aborted))
629         return; /* we're done */
630 
631     if (error) {
632         std::cout << "sc_osc_handler received error code " << error << std::endl;
633         start_receive_udp();
634         return;
635     }
636 
637     handle_packet_async(recv_buffer_.begin(), bytes_transferred, make_shared<udp_endpoint>(udp_remote_endpoint_));
638 
639     start_receive_udp();
640     return;
641 }
642 
start(sc_osc_handler * self)643 void sc_osc_handler::tcp_connection::start(sc_osc_handler* self) {
644     using namespace boost;
645     typedef boost::endian::big_int32_t big_int32_t;
646     asio::ip::tcp::no_delay option(true);
647     socket_.set_option(option);
648 
649     const bool check_password = self->tcp_password_;
650 
651     if (check_password) {
652         std::array<char, 32> password;
653         big_int32_t msglen;
654         for (unsigned int i = 0; i != 4; ++i) {
655             size_t size = socket_.receive(asio::buffer(&msglen, 4));
656             if (size != sizeof(big_int32_t))
657                 return;
658 
659             if (msglen > password.size())
660                 return;
661 
662             size = socket_.receive(asio::buffer(password.data(), msglen));
663 
664             bool verified = true;
665             if (size != msglen || strcmp(password.data(), self->tcp_password_) != 0)
666                 verified = false;
667 
668             if (!verified)
669                 throw std::runtime_error("cannot verify password");
670         }
671     }
672 
673     osc_handler = self;
674 
675     async_read_msg_size();
676 }
677 
678 
send(const char * data,size_t length)679 void sc_osc_handler::tcp_connection::send(const char* data, size_t length) {
680     std::lock_guard<std::mutex> lock(socket_mutex_);
681     try {
682         boost::endian::big_int32_t len(length);
683 
684         socket_.send(boost::asio::buffer(&len, sizeof(len)));
685         size_t written = socket_.send(boost::asio::buffer(data, length));
686         assert(length == written);
687     } catch (std::exception const& err) {
688         std::cout << "Exception when sending message over TCP: " << err.what();
689     }
690 }
691 
692 
async_read_msg_size()693 void sc_osc_handler::tcp_connection::async_read_msg_size() {
694     namespace asio = boost::asio;
695     pointer ptr = std::static_pointer_cast<sc_osc_handler::tcp_connection>(shared_from_this());
696 
697     asio::async_read(socket_, asio::buffer(&msg_size_, 4),
698                      [=](const boost::system::error_code& error, std::size_t bytes_transferred) {
699                          if (error == boost::asio::error::eof)
700                              return; // connection closed
701 
702                          if (error) {
703                              cout << "tcp_connection received error: " << error.message() << endl;
704                              return;
705                          }
706 
707                          ptr->handle_message_size();
708                      });
709 }
710 
handle_message_size()711 void sc_osc_handler::tcp_connection::handle_message_size() {
712     msg_buffer_.resize(msg_size_);
713 
714     namespace asio = boost::asio;
715     pointer ptr = std::static_pointer_cast<sc_osc_handler::tcp_connection>(shared_from_this());
716     asio::async_read(socket_, asio::buffer(msg_buffer_),
717                      [=](const boost::system::error_code& error, std::size_t bytes_transferred) {
718                          if (error == boost::asio::error::eof)
719                              return; // connection closed
720 
721                          if (error) {
722                              cout << "tcp_connection received error: " << error.message() << endl;
723                              return;
724                          }
725                          assert(bytes_transferred == ptr->msg_size_);
726 
727                          ptr->handle_message();
728                      });
729 }
730 
handle_message()731 void sc_osc_handler::tcp_connection::handle_message() {
732     assert(msg_size_ == msg_buffer_.size());
733 
734     osc_handler->handle_packet_async(msg_buffer_.data(), msg_buffer_.size(), shared_from_this());
735     async_read_msg_size();
736 }
737 
738 
start_tcp_accept(void)739 void sc_osc_handler::start_tcp_accept(void) {
740     tcp_connection::pointer new_connection = tcp_connection::create(tcp_acceptor_.get_executor());
741 
742     tcp_acceptor_.async_accept(
743         new_connection->socket(),
744         boost::bind(&sc_osc_handler::handle_tcp_accept, this, new_connection, boost::asio::placeholders::error));
745 }
746 
handle_tcp_accept(tcp_connection::pointer new_connection,const boost::system::error_code & error)747 void sc_osc_handler::handle_tcp_accept(tcp_connection::pointer new_connection, const boost::system::error_code& error) {
748     if (!error)
749         new_connection->start(this);
750 
751     start_tcp_accept();
752 }
753 
754 
handle_packet_async(const char * data,size_t length,endpoint_ptr const & endpoint)755 void sc_osc_handler::handle_packet_async(const char* data, size_t length, endpoint_ptr const& endpoint) {
756     received_packet* p = received_packet::alloc_packet(data, length, endpoint);
757     if (!p)
758         return;
759 
760     if (dump_osc_packets == 1) {
761         ReceivedPacket packet(data, length);
762 
763         if (packet.IsMessage()) {
764             ReceivedMessage message(packet);
765 
766             const char* address = message.AddressPattern();
767             if (strcmp(address, "/status") != 0) // we ignore /status messages
768                 cout << "received osc message " << message << endl;
769         } else
770             cout << "received osc bundle " << packet << endl;
771     }
772 
773     instance->add_sync_callback(p);
774 }
775 
handle_bundle_nrt(const char * data,size_t length)776 time_tag sc_osc_handler::handle_bundle_nrt(const char* data, size_t length) {
777     ReceivedPacket packet(data, length);
778     if (!packet.IsBundle())
779         throw std::runtime_error("packet needs to be an osc bundle");
780 
781     ReceivedBundle bundle(packet);
782     handle_bundle<false>(bundle, nullptr);
783     return bundle.TimeTag();
784 }
785 
786 
alloc_packet(const char * data,size_t length,endpoint_ptr const & remote_endpoint)787 sc_osc_handler::received_packet* sc_osc_handler::received_packet::alloc_packet(const char* data, size_t length,
788                                                                                endpoint_ptr const& remote_endpoint) {
789     /* received_packet struct and data array are located in one memory chunk */
790     void* chunk = received_packet::allocate(sizeof(received_packet) + length);
791     if (!chunk) {
792         std::cerr << "Memory allocation failure: OSC message not handled\n";
793         return nullptr;
794     }
795 
796     received_packet* p = (received_packet*)chunk;
797     char* cpy = (char*)(chunk) + sizeof(received_packet);
798     memcpy(cpy, data, length);
799 
800     new (p) received_packet(cpy, length, remote_endpoint);
801     return p;
802 }
803 
run(void)804 void sc_osc_handler::received_packet::run(void) { instance->handle_packet(data, length, endpoint_); }
805 
handle_packet(const char * data,std::size_t length,endpoint_ptr const & endpoint)806 void sc_osc_handler::handle_packet(const char* data, std::size_t length, endpoint_ptr const& endpoint) {
807     ReceivedPacket packet(data, length);
808     if (packet.IsBundle()) {
809         ReceivedBundle bundle(packet);
810         handle_bundle<true>(bundle, endpoint);
811     } else {
812         ReceivedMessage message(packet);
813         handle_message<true>(message, packet.Size(), endpoint);
814     }
815 }
816 
817 template <bool realtime>
handle_bundle(ReceivedBundle const & bundle,endpoint_ptr const & endpoint)818 void sc_osc_handler::handle_bundle(ReceivedBundle const& bundle, endpoint_ptr const& endpoint) {
819     time_tag bundle_time = bundle.TimeTag();
820 
821     typedef osc::ReceivedBundleElementIterator bundle_iterator;
822     typedef osc::ReceivedBundleElement bundle_element;
823 
824     if (bundle_time <= now) {
825         if (!bundle_time.is_immediate()) {
826             time_tag late = now - bundle_time;
827             log_printf("late: %f\n", late.to_seconds());
828         };
829         for (bundle_iterator it = bundle.ElementsBegin(); it != bundle.ElementsEnd(); ++it) {
830             bundle_element const& element = *it;
831 
832             if (element.IsBundle()) {
833                 ReceivedBundle inner_bundle(element);
834                 handle_bundle<realtime>(inner_bundle, endpoint);
835             } else {
836                 ReceivedMessage message(element);
837                 handle_message<realtime>(message, element.Size(), endpoint);
838             }
839         }
840     } else {
841         for (bundle_iterator it = bundle.ElementsBegin(); it != bundle.ElementsEnd(); ++it) {
842             bundle_element const& element = *it;
843             scheduled_bundles.insert_bundle(bundle_time, element.Contents(), element.Size(), endpoint);
844         }
845     }
846 }
847 
848 template <bool realtime>
handle_message(ReceivedMessage const & message,size_t msg_size,endpoint_ptr const & endpoint)849 void sc_osc_handler::handle_message(ReceivedMessage const& message, size_t msg_size, endpoint_ptr const& endpoint) {
850     try {
851         if (message.AddressPatternIsUInt32())
852             handle_message_int_address<realtime>(message, msg_size, endpoint);
853         else
854             handle_message_sym_address<realtime>(message, msg_size, endpoint);
855     } catch (std::exception const& e) {
856         log_printf("exception in handle_message: %s\n", e.what());
857     }
858 }
859 
860 namespace {
861 
862 typedef osc::ReceivedMessage ReceivedMessage;
863 
addr_pattern_size(ReceivedMessage const & msg)864 int addr_pattern_size(ReceivedMessage const& msg) { return msg.TypeTags() - 1 - msg.AddressPattern(); }
865 
first_arg_as_int(ReceivedMessage const & message)866 int first_arg_as_int(ReceivedMessage const& message) {
867     osc::ReceivedMessageArgumentStream args = message.ArgumentStream();
868     osc::int32 val;
869 
870     args >> val;
871 
872     return val;
873 }
874 
handle_quit(endpoint_ptr endpoint)875 template <bool realtime> void handle_quit(endpoint_ptr endpoint) {
876     instance->quit_received = true;
877     cmd_dispatcher<realtime>::fire_io_callback([=]() {
878         instance->prepare_to_terminate();
879         send_done_message(endpoint, "/quit");
880         instance->terminate();
881     });
882 }
883 
handle_notify(ReceivedMessage const & message,endpoint_ptr const & endpoint)884 template <bool realtime> void handle_notify(ReceivedMessage const& message, endpoint_ptr const& endpoint) {
885     int enable = first_arg_as_int(message);
886 
887     cmd_dispatcher<realtime>::fire_io_callback([=, endpoint = endpoint_ptr(endpoint)]() {
888         int observer = 0;
889 
890         if (enable) {
891             observer = instance->add_observer(endpoint);
892 
893             if (observer < 0)
894                 send_fail_message(endpoint, "/notify",
895                                   sc_notify_observers::error_string((sc_notify_observers::error_code)observer));
896         } else {
897             observer = instance->remove_observer(endpoint);
898             if (observer < 0)
899                 send_fail_message(endpoint, "/notify",
900                                   sc_notify_observers::error_string((sc_notify_observers::error_code)observer));
901         }
902 
903         if (observer >= 0)
904             send_done_message(endpoint, "/notify", observer);
905     });
906 }
907 
handle_status(endpoint_ptr const & endpoint_ref)908 template <bool realtime> void handle_status(endpoint_ptr const& endpoint_ref) {
909     cmd_dispatcher<realtime>::fire_io_callback([=, endpoint = endpoint_ptr(endpoint_ref)]() {
910         if (unlikely(instance->quit_received)) // we don't reply once we are about to quit
911             return;
912 
913         char buffer[1024];
914         typedef osc::int32 i32;
915 
916         float peak_load, average_load;
917         instance->cpu_load(peak_load, average_load);
918 
919         osc::OutboundPacketStream p(buffer, 1024);
920         p << osc::BeginMessage("/status.reply") << (i32)1 /* unused */
921           << (i32)sc_factory->ugen_count() /* ugens */
922           << (i32)instance->synth_count() /* synths */
923           << (i32)instance->group_count() /* groups */
924           << (i32)instance->definition_count() /* synthdefs */
925           << average_load /* average cpu % */
926           << peak_load /* peak cpu % */
927           << instance->get_samplerate() /* nominal samplerate */
928           << instance->smooth_samplerate /* actual samplerate */
929           << osc::EndMessage;
930 
931         endpoint->send(p.Data(), p.Size());
932     });
933 }
934 
handle_dumpOSC(ReceivedMessage const & message)935 void handle_dumpOSC(ReceivedMessage const& message) {
936     int val = first_arg_as_int(message);
937     val = min(1, val); /* we just support one way of dumping osc messages */
938 
939     instance->dumpOSC(val); /* thread-safe */
940 }
941 
handle_sync(ReceivedMessage const & message,endpoint_ptr const & endpoint)942 template <bool realtime> void handle_sync(ReceivedMessage const& message, endpoint_ptr const& endpoint) {
943     int id = first_arg_as_int(message);
944 
945     // ping pong: we go through the nrt->rt channel to ensure that earlier messages have been completely dispatched
946     cmd_dispatcher<realtime>::fire_system_callback([=, endpoint = endpoint_ptr(endpoint)]() {
947         cmd_dispatcher<realtime>::fire_rt_callback([=, endpoint = endpoint_ptr(endpoint)]() {
948             cmd_dispatcher<realtime>::fire_io_callback([=, endpoint = endpoint_ptr(endpoint)]() {
949                 char buffer[128];
950                 osc::OutboundPacketStream p(buffer, 128);
951                 p << osc::BeginMessage("/synced") << id << osc::EndMessage;
952 
953                 endpoint->send(p.Data(), p.Size());
954             });
955         });
956     });
957 }
958 
handle_clearSched(void)959 void handle_clearSched(void) { instance->clear_scheduled_bundles(); }
960 
handle_error(ReceivedMessage const & message)961 void handle_error(ReceivedMessage const& message) {
962     int val = first_arg_as_int(message);
963 
964     instance->set_error_posting(val); /* thread-safe */
965 }
966 
handle_version(endpoint_ptr const & endpoint_ref)967 template <bool realtime> void handle_version(endpoint_ptr const& endpoint_ref) {
968     cmd_dispatcher<realtime>::fire_io_callback([=, endpoint = endpoint_ptr(endpoint_ref)]() {
969         if (unlikely(instance->quit_received))
970             return;
971 
972         char buffer[4096];
973         typedef osc::int32 i32;
974 
975         osc::OutboundPacketStream p(buffer, 4096);
976         p << osc::BeginMessage("/version.reply") << "supernova" << (i32)SC_VersionMajor << (i32)SC_VersionMinor
977           << SC_VersionPostfix << SC_Branch << SC_CommitHash << osc::EndMessage;
978         endpoint->send(p.Data(), p.Size());
979     });
980 }
981 
handle_unhandled_message(ReceivedMessage const & msg)982 void handle_unhandled_message(ReceivedMessage const& msg) {
983     log_printf("unhandled message: %s\n", msg.AddressPattern());
984 }
985 
node_position_sanity_check(node_position_constraint const & constraint)986 static bool node_position_sanity_check(node_position_constraint const& constraint) {
987     switch (constraint.second) {
988     case head:
989     case tail:
990     case insert: {
991         server_node* target = constraint.first;
992         if (!target->is_group()) {
993             log_printf("Invalid position constraint (target: %d, addAction: %d)\n", target->id(), constraint.second);
994             return false;
995         }
996         break;
997     }
998     case before:
999     case after:
1000     case replace:
1001         break;
1002     }
1003 
1004     return true;
1005 }
1006 
add_synth(const char * name,int node_id,int action,int target_id)1007 sc_synth* add_synth(const char* name, int node_id, int action, int target_id) {
1008     if (!check_node_id(node_id))
1009         return nullptr;
1010 
1011     server_node* target = find_node(target_id);
1012     if (target == nullptr)
1013         return nullptr;
1014 
1015     node_position_constraint pos = make_pair(target, node_position(action));
1016     if (!node_position_sanity_check(pos))
1017         return nullptr;
1018 
1019     abstract_synth* synth = instance->add_synth(name, node_id, pos);
1020     if (!synth)
1021         log_printf("Cannot create synth (synthdef: %s, node id: %d)\n", name, node_id);
1022 
1023     last_generated = node_id;
1024     return static_cast<sc_synth*>(synth);
1025 }
1026 
1027 /* extract float or int32 as float from argument iterator */
extract_float_argument(osc::ReceivedMessageArgumentIterator const & it)1028 inline float extract_float_argument(osc::ReceivedMessageArgumentIterator const& it) {
1029     if (it->IsFloat())
1030         return it->AsFloatUnchecked();
1031     if (it->IsInt32())
1032         return float(it->AsInt32Unchecked());
1033     if (it->IsInt64())
1034         return float(it->AsInt64Unchecked());
1035 
1036     throw std::runtime_error("type cannot be converted to float");
1037 }
1038 
verify_argument(osc::ReceivedMessageArgumentIterator const & it,osc::ReceivedMessageArgumentIterator const & end)1039 inline void verify_argument(osc::ReceivedMessageArgumentIterator const& it,
1040                             osc::ReceivedMessageArgumentIterator const& end) {
1041     if (it == end)
1042         throw std::runtime_error("unexpected end of argument list");
1043 }
1044 
1045 template <bool IsAudio, typename slot_type>
1046 static void apply_control_bus_mapping(server_node& node, slot_type slot, int bus_index);
1047 
1048 template <typename control_id_type>
set_control_array(server_node * node,control_id_type control,osc::ReceivedMessageArgumentIterator & it)1049 void set_control_array(server_node* node, control_id_type control, osc::ReceivedMessageArgumentIterator& it) {
1050     size_t array_size = it->ComputeArrayItemCount();
1051     ++it;
1052 
1053     if (it->IsArrayBegin()) {
1054         // nested arrays are basically user errors, but we handle them like normal arrays
1055         log("Warning in /s_new handler: nested array argument detected");
1056         set_control_array<control_id_type>(node, control, it);
1057         return;
1058     } else {
1059         for (size_t i = 0; i != array_size; ++i) {
1060             if (it->IsString() || it->IsSymbol()) {
1061                 char const* name = it->AsStringUnchecked();
1062                 ++it;
1063                 int bus_id;
1064 
1065                 switch (name[0]) {
1066                 case 'c':
1067                     bus_id = atoi(name + 1);
1068                     static_cast<sc_synth*>(node)->map_control_bus<false>(control, i, bus_id);
1069                     break;
1070 
1071                 case 'a':
1072                     bus_id = atoi(name + 1);
1073                     static_cast<sc_synth*>(node)->map_control_bus<true>(control, i, bus_id);
1074                     break;
1075 
1076                 default:
1077                     throw runtime_error("invalid name for control mapping");
1078                 }
1079             } else {
1080                 float value = extract_float_argument(it++);
1081                 node->set_control_array_element(control, i, value);
1082             }
1083         }
1084     }
1085 
1086     if (!it->IsArrayEnd())
1087         throw runtime_error("missing array end tag");
1088     ++it; // skip array end
1089 }
1090 
1091 template <typename ControlSpecifier>
set_control(server_node * node,ControlSpecifier const & control,osc::ReceivedMessageArgumentIterator & it)1092 void set_control(server_node* node, ControlSpecifier const& control, osc::ReceivedMessageArgumentIterator& it) {
1093     if (it->IsArrayBegin())
1094         set_control_array(node, control, it);
1095     else if (it->IsString() || it->IsSymbol()) {
1096         char const* name = it->AsStringUnchecked();
1097         ++it;
1098         int bus_id;
1099 
1100         switch (name[0]) {
1101         case 'c':
1102             bus_id = atoi(name + 1);
1103             apply_control_bus_mapping<false>(*node, control, bus_id);
1104             break;
1105 
1106         case 'a':
1107             bus_id = atoi(name + 1);
1108             apply_control_bus_mapping<true>(*node, control, bus_id);
1109             break;
1110 
1111         default:
1112             throw runtime_error("invalid name for control mapping");
1113         }
1114 
1115     } else {
1116         float value = extract_float_argument(it++);
1117         node->set(control, value);
1118     }
1119 }
1120 
1121 /* set control values of node from string/float or int/float pair */
set_control(server_node * node,osc::ReceivedMessageArgumentIterator & it,osc::ReceivedMessageArgumentIterator end)1122 void set_control(server_node* node, osc::ReceivedMessageArgumentIterator& it,
1123                  osc::ReceivedMessageArgumentIterator end) {
1124     if (it->IsInt32()) {
1125         osc::int32 index = it->AsInt32Unchecked();
1126         ++it;
1127         if (it == end)
1128             return; // sclang sometimes uses an integer instead of an empty argument list
1129         set_control(node, index, it);
1130     } else if (it->IsString()) {
1131         const char* str = it->AsStringUnchecked();
1132         ++it;
1133         set_control(node, str, it);
1134     } else
1135         throw runtime_error("invalid argument");
1136 }
1137 
handle_s_new(ReceivedMessage const & msg)1138 void handle_s_new(ReceivedMessage const& msg) {
1139     osc::ReceivedMessageArgumentIterator args = msg.ArgumentsBegin(), end = msg.ArgumentsEnd();
1140 
1141     const char* def_name = args->AsString();
1142     ++args;
1143     int32_t id = args->AsInt32();
1144     ++args;
1145 
1146     if (id == -1)
1147         id = instance->generate_node_id();
1148 
1149     int32_t action, target;
1150 
1151     if (args != end) {
1152         action = args->AsInt32();
1153         ++args;
1154     } else
1155         action = 0;
1156 
1157     if (args != end) {
1158         target = args->AsInt32();
1159         ++args;
1160     } else
1161         target = 0;
1162 
1163     sc_synth* synth = add_synth(def_name, id, action, target);
1164 
1165     if (synth == nullptr)
1166         return;
1167 
1168     try {
1169         while (args != end)
1170             set_control(synth, args, end);
1171     } catch (std::exception& e) {
1172         log_printf("exception in /s_new: %s\n", e.what());
1173     }
1174 }
1175 
1176 
handle_g_new(ReceivedMessage const & msg)1177 void handle_g_new(ReceivedMessage const& msg) {
1178     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1179 
1180     while (!args.Eos()) {
1181         osc::int32 node_id, action, target_id;
1182         args >> node_id >> action >> target_id;
1183 
1184         if (node_id == -1)
1185             node_id = instance->generate_node_id();
1186         else if (!check_node_id(node_id))
1187             continue;
1188 
1189         server_node* target = find_node(target_id);
1190 
1191         if (!target)
1192             continue;
1193 
1194         node_position_constraint pos = make_pair(target, node_position(action));
1195         if (!node_position_sanity_check(pos))
1196             continue;
1197 
1198         instance->add_group(node_id, pos);
1199         last_generated = node_id;
1200     }
1201 }
1202 
handle_g_freeall(ReceivedMessage const & msg)1203 void handle_g_freeall(ReceivedMessage const& msg) {
1204     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1205 
1206     while (!args.Eos()) {
1207         osc::int32 id;
1208         args >> id;
1209 
1210         abstract_group* group = find_group(id);
1211         if (!group)
1212             continue;
1213 
1214         instance->group_free_all(group);
1215     }
1216 }
1217 
handle_g_deepFree(ReceivedMessage const & msg)1218 void handle_g_deepFree(ReceivedMessage const& msg) {
1219     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1220 
1221     while (!args.Eos()) {
1222         osc::int32 id;
1223         args >> id;
1224 
1225         abstract_group* group = find_group(id);
1226         if (!group)
1227             continue;
1228 
1229         instance->group_free_deep(group);
1230     }
1231 }
1232 
g_query_tree_fill_node(osc::OutboundPacketStream & p,bool flag,server_node const & node)1233 void g_query_tree_fill_node(osc::OutboundPacketStream& p, bool flag, server_node const& node) {
1234     p << osc::int32(node.id());
1235     if (node.is_synth())
1236         p << -1;
1237     else
1238         p << osc::int32(static_cast<abstract_group const&>(node).child_count());
1239 
1240     if (node.is_synth()) {
1241         sc_synth const& scsynth = static_cast<sc_synth const&>(node);
1242         p << scsynth.definition_name();
1243 
1244         if (flag) {
1245             osc::int32 controls = scsynth.mNumControls;
1246             p << controls;
1247 
1248             for (int i = 0; i != controls; ++i) {
1249                 const char* name_of_slot = scsynth.name_of_slot(i);
1250                 if (name_of_slot)
1251                     p << name_of_slot;
1252                 else
1253                     p << osc::int32(i);
1254 
1255                 char str[10];
1256                 if (scsynth.getMappedSymbol(i, str))
1257                     p << str;
1258                 else
1259                     p << scsynth.mControls[i];
1260             }
1261         }
1262     } else {
1263         abstract_group const& group = static_cast<abstract_group const&>(node);
1264 
1265         group.apply_on_children([&](server_node const& node) { g_query_tree_fill_node(p, flag, node); });
1266     }
1267 }
1268 
g_query_tree(int node_id,bool flag,endpoint_ptr endpoint)1269 template <bool realtime> void g_query_tree(int node_id, bool flag, endpoint_ptr endpoint) {
1270     server_node* node = find_node(node_id);
1271     if (!node || node->is_synth())
1272         return;
1273 
1274     abstract_group* group = static_cast<abstract_group*>(node);
1275 
1276     size_t max_msg_size = 1 << 16;
1277     for (;;) {
1278         try {
1279             if (max_msg_size > 1 << 22)
1280                 return;
1281 
1282             sized_array<char, rt_pool_allocator<char>> data(max_msg_size);
1283 
1284             osc::OutboundPacketStream p(data.c_array(), max_msg_size);
1285             p << osc::BeginMessage("/g_queryTree.reply") << (flag ? 1 : 0) << node_id
1286               << osc::int32(group->child_count());
1287 
1288             group->apply_on_children([&](server_node const& node) { g_query_tree_fill_node(p, flag, node); });
1289             p << osc::EndMessage;
1290 
1291             movable_array<char> message(p.Size(), data.c_array());
1292             cmd_dispatcher<realtime>::fire_message(endpoint, std::move(message));
1293             return;
1294         } catch (...) {
1295             max_msg_size *= 2; /* if we run out of memory, retry with doubled memory resources */
1296         }
1297     }
1298 }
1299 
handle_g_queryTree(ReceivedMessage const & msg,endpoint_ptr endpoint)1300 template <bool realtime> void handle_g_queryTree(ReceivedMessage const& msg, endpoint_ptr endpoint) {
1301     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1302 
1303     while (!args.Eos()) {
1304         try {
1305             osc::int32 id, flag;
1306             args >> id >> flag;
1307             g_query_tree<realtime>(id, flag, endpoint);
1308         } catch (std::exception& e) {
1309             log_printf("exception in handle_g_queryTree: %s\n", e.what());
1310         }
1311     }
1312 }
1313 
1314 typedef std::basic_stringstream <char,
1315                                  std::char_traits <char>/*,
1316                                  rt_pool_allocator<char>*/ > rt_string_stream;
1317 
fill_spaces(rt_string_stream & stream,int level)1318 void fill_spaces(rt_string_stream& stream, int level) {
1319     for (int i = 0; i != level * 3; ++i)
1320         stream << ' ';
1321 }
1322 
dump_controls(rt_string_stream & stream,abstract_synth const & synth,int indentation_level)1323 void dump_controls(rt_string_stream& stream, abstract_synth const& synth, int indentation_level) {
1324     const size_t number_of_slots = synth.number_of_slots();
1325 
1326     bool eol_pending = false;
1327 
1328     for (size_t control_index = 0; control_index != number_of_slots; ++control_index) {
1329         const char* name_of_slot = synth.name_of_slot(control_index);
1330 
1331         if (name_of_slot) {
1332             if (eol_pending) {
1333                 stream << endl;
1334                 eol_pending = false;
1335             }
1336 
1337             fill_spaces(stream, indentation_level);
1338             stream << synth.name_of_slot(control_index) << ": ";
1339             eol_pending = true;
1340         } else
1341             stream << ", ";
1342 
1343         char str[10];
1344         if (synth.getMappedSymbol(control_index, str))
1345             stream << str;
1346         else
1347             stream << synth.get(control_index);
1348     }
1349     if (eol_pending)
1350         stream << endl;
1351 }
1352 
g_dump_node(rt_string_stream & stream,server_node & node,bool flag,int level)1353 void g_dump_node(rt_string_stream& stream, server_node& node, bool flag, int level) {
1354     using namespace std;
1355     fill_spaces(stream, level);
1356 
1357     if (node.is_synth()) {
1358         abstract_synth const& synth = static_cast<abstract_synth const&>(node);
1359         stream << synth.id() << " " << synth.definition_name() << endl;
1360 
1361         if (flag)
1362             dump_controls(stream, synth, level + 1);
1363 
1364     } else {
1365         abstract_group& group = static_cast<abstract_group&>(node);
1366         stream << group.id();
1367 
1368         if (group.is_parallel())
1369             stream << " parallel group";
1370         else
1371             stream << " group";
1372         stream << endl;
1373 
1374         group.apply_on_children([&](server_node& node) { g_dump_node(stream, node, flag, level + 1); });
1375     }
1376 }
1377 
g_dump_tree(int id,bool flag)1378 void g_dump_tree(int id, bool flag) {
1379     server_node* node = find_node(id);
1380     if (!node)
1381         return;
1382 
1383     // FIXME: can we completely avoid all internal allocations?
1384     rt_string_stream stream;
1385     stream << "NODE TREE Group " << id << std::endl;
1386 
1387     g_dump_node(stream, *node, flag, 1);
1388     log(stream.str().c_str(), stream.str().size());
1389 }
1390 
handle_g_dumpTree(ReceivedMessage const & msg)1391 void handle_g_dumpTree(ReceivedMessage const& msg) {
1392     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1393 
1394     while (!args.Eos()) {
1395         try {
1396             osc::int32 id, flag;
1397             args >> id >> flag;
1398             g_dump_tree(id, flag);
1399         } catch (std::exception& e) {
1400             log_printf("exception in /g_dumpTree: %s\n", e.what());
1401         }
1402     }
1403 }
1404 
handle_n_free(ReceivedMessage const & msg)1405 void handle_n_free(ReceivedMessage const& msg) {
1406     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1407 
1408     while (!args.Eos()) {
1409         try {
1410             osc::int32 id;
1411             args >> id;
1412 
1413             server_node* node = find_node(id);
1414             if (!node)
1415                 continue;
1416 
1417             instance->free_node(node);
1418         } catch (std::exception& e) {
1419             log_printf("exception in /n_free: %s\n", e.what());
1420         }
1421     }
1422 }
1423 
1424 /** macro to define an os command handler with a starting node id
1425  *
1426  *  it is mainly intended as decorator to avoid duplicate error handling code
1427  */
1428 #define HANDLE_N_DECORATOR(cmd, function)                                                                              \
1429     void handle_n_##cmd(ReceivedMessage const& msg) {                                                                  \
1430         osc::ReceivedMessageArgumentIterator it = msg.ArgumentsBegin();                                                \
1431         osc::int32 id = it->AsInt32();                                                                                 \
1432         ++it;                                                                                                          \
1433                                                                                                                        \
1434         server_node* node = find_node(id);                                                                             \
1435         if (!node)                                                                                                     \
1436             return;                                                                                                    \
1437                                                                                                                        \
1438         try {                                                                                                          \
1439             while (it != msg.ArgumentsEnd())                                                                           \
1440                 function(node, it);                                                                                    \
1441         } catch (std::exception & e) {                                                                                 \
1442             log_printf("Exception during /n_" #cmd "handler: %s\n", e.what());                                         \
1443         }                                                                                                              \
1444     }
1445 
set_control(server_node * node,osc::ReceivedMessageArgumentIterator & it)1446 void set_control(server_node* node, osc::ReceivedMessageArgumentIterator& it) {
1447     if (it->IsInt32()) {
1448         osc::int32 index = it->AsInt32Unchecked();
1449         ++it;
1450         set_control(node, index, it);
1451     } else if (it->IsString()) {
1452         const char* str = it->AsStringUnchecked();
1453         ++it;
1454         set_control(node, str, it);
1455     } else
1456         throw runtime_error("invalid argument");
1457 }
1458 
1459 
HANDLE_N_DECORATOR(set,set_control)1460 HANDLE_N_DECORATOR(set, set_control)
1461 
1462 void set_control_n(server_node* node, osc::ReceivedMessageArgumentIterator& it) {
1463     if (it->IsInt32()) {
1464         osc::int32 index = it->AsInt32Unchecked();
1465         ++it;
1466         osc::int32 count = it->AsInt32();
1467         ++it;
1468 
1469         for (int i = 0; i != count; ++i)
1470             node->set(index + i, extract_float_argument(it++));
1471     } else if (it->IsString()) {
1472         const char* str = it->AsStringUnchecked();
1473         ++it;
1474         osc::int32 count = it->AsInt32();
1475         ++it;
1476 
1477         sized_array<float> values(count);
1478         for (int i = 0; i != count; ++i)
1479             values[i] = extract_float_argument(it++);
1480 
1481         node->set_control_array(str, count, values.c_array());
1482     } else
1483         throw runtime_error("invalid argument");
1484 }
1485 
HANDLE_N_DECORATOR(setn,set_control_n)1486 HANDLE_N_DECORATOR(setn, set_control_n)
1487 
1488 void fill_control(server_node* node, osc::ReceivedMessageArgumentIterator& it) {
1489     if (it->IsInt32()) {
1490         osc::int32 index = it->AsInt32Unchecked();
1491         ++it;
1492         osc::int32 count = it->AsInt32();
1493         ++it;
1494         float value = extract_float_argument(it++);
1495 
1496         for (int i = 0; i != count; ++i)
1497             node->set(index + i, value);
1498     } else if (it->IsString()) {
1499         const char* str = it->AsStringUnchecked();
1500         ++it;
1501         osc::int32 count = it->AsInt32();
1502         ++it;
1503         float value = extract_float_argument(it++);
1504 
1505         sized_array<float> values(count);
1506         for (int i = 0; i != count; ++i)
1507             values[i] = value;
1508 
1509         node->set_control_array(str, count, values.c_array());
1510     } else
1511         throw runtime_error("invalid argument");
1512 }
1513 
HANDLE_N_DECORATOR(fill,fill_control)1514 HANDLE_N_DECORATOR(fill, fill_control)
1515 
1516 template <bool IsAudio, typename slot_type>
1517 void apply_control_bus_mapping(server_node& node, slot_type slot, int bus_index) {
1518     if (node.is_synth())
1519         static_cast<sc_synth&>(node).map_control_bus<IsAudio>(slot, bus_index);
1520     else {
1521         static_cast<abstract_group&>(node).apply_on_children(
1522             [&](server_node& node) { apply_control_bus_mapping<IsAudio, slot_type>(node, slot, bus_index); });
1523     }
1524 }
1525 
1526 template <bool IsAudio, typename slot_type>
apply_control_busn_mapping(server_node & node,slot_type slot,int bus_index,int count)1527 void apply_control_busn_mapping(server_node& node, slot_type slot, int bus_index, int count) {
1528     if (node.is_synth())
1529         static_cast<sc_synth&>(node).map_control_buses<IsAudio>(slot, bus_index, count);
1530     else {
1531         static_cast<abstract_group&>(node).apply_on_children(
1532             [&](server_node& node) { apply_control_busn_mapping<IsAudio, slot_type>(node, slot, bus_index, count); });
1533     }
1534 }
1535 
map_control(server_node * node,osc::ReceivedMessageArgumentIterator & it)1536 template <bool IsAudio> void map_control(server_node* node, osc::ReceivedMessageArgumentIterator& it) {
1537     if (it->IsInt32()) {
1538         osc::int32 control_index = it->AsInt32Unchecked();
1539         ++it;
1540         osc::int32 control_bus_index = it->AsInt32();
1541         ++it;
1542 
1543         apply_control_bus_mapping<IsAudio>(*node, control_index, control_bus_index);
1544     } else if (it->IsString()) {
1545         const char* control_name = it->AsStringUnchecked();
1546         ++it;
1547         osc::int32 control_bus_index = it->AsInt32();
1548         ++it;
1549 
1550         apply_control_bus_mapping<IsAudio>(*node, control_name, control_bus_index);
1551     } else
1552         throw runtime_error("invalid argument");
1553 }
1554 
mapn_control(server_node * node,osc::ReceivedMessageArgumentIterator & it)1555 template <bool IsAudio> void mapn_control(server_node* node, osc::ReceivedMessageArgumentIterator& it) {
1556     if (it->IsInt32()) {
1557         osc::int32 control_index = it->AsInt32Unchecked();
1558         ++it;
1559         osc::int32 bus_index = it->AsInt32();
1560         ++it;
1561         osc::int32 count = it->AsInt32();
1562         ++it;
1563 
1564         apply_control_busn_mapping<IsAudio>(*node, control_index, bus_index, count);
1565     } else if (it->IsString()) {
1566         const char* control_name = it->AsStringUnchecked();
1567         ++it;
1568         osc::int32 bus_index = it->AsInt32();
1569         ++it;
1570         osc::int32 count = it->AsInt32();
1571         ++it;
1572 
1573         apply_control_busn_mapping<IsAudio>(*node, control_name, bus_index, count);
1574     } else
1575         throw runtime_error("invalid argument");
1576 }
1577 
1578 
HANDLE_N_DECORATOR(map,map_control<false>)1579 HANDLE_N_DECORATOR(map, map_control<false>)
1580 HANDLE_N_DECORATOR(mapa, map_control<true>)
1581 HANDLE_N_DECORATOR(mapn, mapn_control<false>)
1582 HANDLE_N_DECORATOR(mapan, mapn_control<true>)
1583 
1584 template <nova::node_position Relation> void handle_n_before_or_after(ReceivedMessage const& msg) {
1585     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1586 
1587     while (!args.Eos()) {
1588         osc::int32 node_a, node_b;
1589         args >> node_a >> node_b;
1590 
1591         server_node* node = find_node(node_a);
1592         if (!node)
1593             continue;
1594 
1595         server_node* target_node = find_node(node_b);
1596         if (!target_node)
1597             continue;
1598 
1599         abstract_group::move_before_or_after<Relation>(node, target_node);
1600         instance->notification_node_moved(node);
1601     }
1602 
1603     instance->request_dsp_queue_update();
1604 }
1605 
handle_g_head_or_tail(ReceivedMessage const & msg)1606 template <nova::node_position Position> void handle_g_head_or_tail(ReceivedMessage const& msg) {
1607     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1608 
1609     while (!args.Eos()) {
1610         osc::int32 node_id, target_id;
1611         args >> target_id >> node_id;
1612 
1613         server_node* node = find_node(node_id);
1614         if (!node)
1615             continue;
1616 
1617         abstract_group* target_group = find_group(target_id);
1618         if (!target_group)
1619             continue;
1620 
1621         abstract_group::move_to_head_or_tail<Position>(node, target_group);
1622         instance->notification_node_moved(node);
1623     }
1624     instance->request_dsp_queue_update();
1625 }
1626 
handle_n_query(ReceivedMessage const & msg,endpoint_ptr endpoint)1627 template <bool realtime> void handle_n_query(ReceivedMessage const& msg, endpoint_ptr endpoint) {
1628     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1629 
1630     while (!args.Eos()) {
1631         osc::int32 node_id;
1632         args >> node_id;
1633 
1634         server_node* node = find_node(node_id);
1635         if (!node)
1636             continue;
1637 
1638         char buffer[128]; // 128 byte should be enough
1639         osc::OutboundPacketStream p(buffer, 128);
1640         p << osc::BeginMessage("/n_info");
1641         fill_notification(node, p);
1642 
1643         movable_array<char> message(p.Size(), p.Data());
1644         cmd_dispatcher<realtime>::fire_message(endpoint, std::move(message));
1645     }
1646 }
1647 
handle_n_order(ReceivedMessage const & msg)1648 void handle_n_order(ReceivedMessage const& msg) {
1649     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1650 
1651     osc::int32 action, target_id;
1652     args >> action >> target_id;
1653 
1654     server_node* target = find_node(target_id);
1655 
1656     if (target == nullptr)
1657         return;
1658 
1659     abstract_group* target_parent;
1660     if (action == before || action == after)
1661         target_parent = target->get_parent();
1662     else {
1663         if (target->is_synth())
1664             throw std::runtime_error("invalid argument for n_order: argument is no synth");
1665         target_parent = static_cast<abstract_group*>(target);
1666     }
1667 
1668     while (!args.Eos()) {
1669         osc::int32 node_id;
1670         args >> node_id;
1671 
1672         server_node* node = find_node(node_id);
1673         if (node == nullptr)
1674             continue;
1675 
1676         abstract_group* node_parent = node->get_parent();
1677 
1678         /** TODO: this can be optimized if node_parent == target_parent */
1679         node_parent->remove_child(node);
1680         if (action == before || action == after)
1681             target_parent->add_child(node, make_pair(target, node_position(action)));
1682         else
1683             target_parent->add_child(node, node_position(action));
1684 
1685         instance->notification_node_moved(node);
1686     }
1687     instance->request_dsp_queue_update();
1688 }
1689 
1690 
handle_n_run(ReceivedMessage const & msg)1691 void handle_n_run(ReceivedMessage const& msg) {
1692     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1693 
1694     while (!args.Eos()) {
1695         osc::int32 node_id, run_flag;
1696         args >> node_id >> run_flag;
1697 
1698         server_node* node = find_node(node_id);
1699         if (!node)
1700             continue;
1701 
1702         if (run_flag)
1703             instance->node_resume(node);
1704         else
1705             instance->node_pause(node);
1706     }
1707 }
1708 
enable_tracing(server_node & node)1709 void enable_tracing(server_node& node) {
1710     if (node.is_synth()) {
1711         sc_synth& synth = static_cast<sc_synth&>(node);
1712         synth.enable_tracing();
1713     } else {
1714         abstract_group& group = static_cast<abstract_group&>(node);
1715         group.apply_on_children(enable_tracing);
1716     }
1717 }
1718 
handle_n_trace(ReceivedMessage const & msg)1719 void handle_n_trace(ReceivedMessage const& msg) {
1720     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1721 
1722     while (!args.Eos()) {
1723         osc::int32 node_id;
1724         args >> node_id;
1725 
1726         server_node* node = find_node(node_id);
1727         if (!node)
1728             continue;
1729 
1730         enable_tracing(*node);
1731     }
1732 }
1733 
1734 
handle_s_noid(ReceivedMessage const & msg)1735 void handle_s_noid(ReceivedMessage const& msg) {
1736     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1737 
1738     while (!args.Eos()) {
1739         osc::int32 node_id;
1740         args >> node_id;
1741         instance->synth_reassign_id(node_id);
1742     }
1743 }
1744 
get_control_index(sc_synth * s,osc::ReceivedMessageArgumentIterator & it,osc::OutboundPacketStream & p)1745 int32_t get_control_index(sc_synth* s, osc::ReceivedMessageArgumentIterator& it, osc::OutboundPacketStream& p) {
1746     int32_t control;
1747     if (it->IsInt32()) {
1748         control = it->AsInt32Unchecked();
1749         ++it;
1750         p << control;
1751     } else if (it->IsString()) {
1752         const char* control_str = it->AsStringUnchecked();
1753         ++it;
1754         control = s->resolve_slot(control_str);
1755         p << control_str;
1756     } else if (it->IsSymbol()) {
1757         const char* control_str = it->AsSymbolUnchecked();
1758         ++it;
1759         control = s->resolve_slot(control_str);
1760         p << osc::Symbol(control_str);
1761     } else
1762         throw std::runtime_error("wrong argument type");
1763     return control;
1764 }
1765 
handle_s_get(ReceivedMessage const & msg,size_t msg_size,endpoint_ptr endpoint)1766 template <bool realtime> void handle_s_get(ReceivedMessage const& msg, size_t msg_size, endpoint_ptr endpoint) {
1767     osc::ReceivedMessageArgumentIterator it = msg.ArgumentsBegin();
1768 
1769     if (!it->IsInt32())
1770         throw std::runtime_error("wrong argument type");
1771 
1772     int32_t node_id = it->AsInt32Unchecked();
1773     ++it;
1774 
1775     server_node* node = find_node(node_id);
1776     if (!node || !node->is_synth())
1777         throw std::runtime_error("node is not a synth");
1778 
1779     sc_synth* s = static_cast<sc_synth*>(node);
1780 
1781     size_t alloc_size = msg_size + sizeof(float) * (msg.ArgumentCount() - 1) + 128;
1782 
1783     sized_array<char, rt_pool_allocator<char>> return_message(alloc_size);
1784 
1785     osc::OutboundPacketStream p(return_message.c_array(), alloc_size);
1786     p << osc::BeginMessage("/n_set") << node_id;
1787 
1788     while (it != msg.ArgumentsEnd()) {
1789         int32_t control = get_control_index(s, it, p);
1790         p << s->get(control);
1791     }
1792     p << osc::EndMessage;
1793 
1794     movable_array<char> message(p.Size(), return_message.c_array());
1795     cmd_dispatcher<realtime>::fire_message(endpoint, std::move(message));
1796 }
1797 
handle_s_getn(ReceivedMessage const & msg,size_t msg_size,endpoint_ptr const & endpoint)1798 template <bool realtime> void handle_s_getn(ReceivedMessage const& msg, size_t msg_size, endpoint_ptr const& endpoint) {
1799     osc::ReceivedMessageArgumentIterator it = msg.ArgumentsBegin();
1800 
1801     if (!it->IsInt32())
1802         throw std::runtime_error("wrong argument type");
1803 
1804     int32_t node_id = it->AsInt32Unchecked();
1805     ++it;
1806 
1807     server_node* node = find_node(node_id);
1808     if (!node || !node->is_synth())
1809         throw std::runtime_error("node is not a synth");
1810 
1811     sc_synth* s = static_cast<sc_synth*>(node);
1812 
1813     /* count argument values */
1814     size_t argument_count = 0;
1815     for (osc::ReceivedMessageArgumentIterator local = it; local != msg.ArgumentsEnd(); ++local) {
1816         ++local; /* skip control */
1817         if (local == msg.ArgumentsEnd())
1818             break;
1819         if (!it->IsInt32())
1820             throw std::runtime_error("invalid count");
1821         argument_count += it->AsInt32Unchecked();
1822     }
1823 
1824     size_t alloc_size = msg_size + sizeof(float) * (argument_count) + 128;
1825 
1826     sized_array<char, rt_pool_allocator<char>> return_message(alloc_size);
1827 
1828     osc::OutboundPacketStream p(return_message.c_array(), alloc_size);
1829     p << osc::BeginMessage("/n_setn") << node_id;
1830 
1831     while (it != msg.ArgumentsEnd()) {
1832         int32_t control = get_control_index(s, it, p);
1833 
1834         if (!it->IsInt32())
1835             throw std::runtime_error("integer argument expected");
1836 
1837         int32_t control_count = it->AsInt32Unchecked();
1838         ++it;
1839         if (control_count < 0)
1840             break;
1841 
1842         p << control_count;
1843 
1844         for (int i = 0; i != control_count; ++i)
1845             p << s->get(control + i);
1846     }
1847     p << osc::EndMessage;
1848 
1849     movable_array<char> message(p.Size(), return_message.c_array());
1850     cmd_dispatcher<realtime>::fire_message(endpoint, std::move(message));
1851 }
1852 
1853 
1854 /** wrapper class for osc completion message
1855  */
1856 struct completion_message {
1857     /** constructor should only be used from the real-time thread */
completion_messagenova::detail::__anon8089e4d40f11::completion_message1858     completion_message(size_t size, const void* data): size_(size) {
1859         if (size) {
1860             data_ = system_callback::allocate(size);
1861             memcpy(data_, data, size);
1862         }
1863     }
1864 
1865     /** default constructor creates uninitialized object */
completion_messagenova::detail::__anon8089e4d40f11::completion_message1866     completion_message(void): size_(0) {}
1867 
1868     completion_message(completion_message const& rhs) = delete;
1869     completion_message operator=(completion_message const& rhs) = delete;
1870 
completion_messagenova::detail::__anon8089e4d40f11::completion_message1871     completion_message(completion_message&& rhs) { operator=(std::forward<completion_message>(rhs)); }
1872 
operator =nova::detail::__anon8089e4d40f11::completion_message1873     completion_message& operator=(completion_message&& rhs) {
1874         size_ = rhs.size_;
1875         data_ = rhs.data_;
1876         rhs.size_ = 0;
1877         return *this;
1878     }
1879 
~completion_messagenova::detail::__anon8089e4d40f11::completion_message1880     ~completion_message(void) {
1881         if (size_)
1882             system_callback::deallocate(data_);
1883     }
1884 
1885     /** handle package in the rt thread
1886      *  not to be called from the rt thread
1887      */
trigger_asyncnova::detail::__anon8089e4d40f11::completion_message1888     void trigger_async(endpoint_ptr const& endpoint) {
1889         if (size_) {
1890             sc_osc_handler::received_packet* p =
1891                 sc_osc_handler::received_packet::alloc_packet((char*)data_, size_, endpoint);
1892             if (p)
1893                 instance->add_sync_callback(p);
1894         }
1895     }
1896 
1897     /** handle package directly
1898      *  only to be called from the rt thread
1899      */
handlenova::detail::__anon8089e4d40f11::completion_message1900     void handle(endpoint_ptr const& endpoint) const {
1901         if (size_)
1902             instance->handle_packet((char*)data_, size_, endpoint);
1903     }
1904 
1905     size_t size_;
1906     void* data_;
1907 };
1908 
extract_completion_message(osc::ReceivedMessageArgumentStream & args)1909 completion_message extract_completion_message(osc::ReceivedMessageArgumentStream& args) {
1910     osc::Blob blob(nullptr, 0);
1911 
1912     if (!args.Eos()) {
1913         try {
1914             args >> blob;
1915         } catch (osc::WrongArgumentTypeException& e) {
1916         }
1917     }
1918 
1919     return completion_message(blob.size, blob.data);
1920 }
1921 
extract_completion_message(osc::ReceivedMessageArgumentIterator & it)1922 completion_message extract_completion_message(osc::ReceivedMessageArgumentIterator& it) {
1923     const void* data = nullptr;
1924     osc::osc_bundle_element_size_t length = 0;
1925 
1926     if (it->IsBlob())
1927         it->AsBlobUnchecked(data, length);
1928     ++it;
1929     return completion_message(length, data);
1930 }
1931 
1932 // must be called from rt thread
handle_completion_message(completion_message && message,endpoint_ptr endpoint)1933 void handle_completion_message(completion_message&& message, endpoint_ptr endpoint) {
1934     completion_message msg(std::forward<completion_message>(message));
1935     msg.handle(endpoint);
1936 }
1937 
1938 
handle_b_alloc(ReceivedMessage const & msg,endpoint_ptr endpoint)1939 template <bool realtime> void handle_b_alloc(ReceivedMessage const& msg, endpoint_ptr endpoint) {
1940     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1941 
1942     osc::int32 bufferIndex, frames, channels;
1943 
1944     args >> bufferIndex >> frames;
1945 
1946     if (!args.Eos())
1947         args >> channels;
1948     else
1949         channels = 1;
1950 
1951     completion_message message = extract_completion_message(args);
1952 
1953     cmd_dispatcher<realtime>::fire_system_callback([=, message = std::move(message)]() mutable {
1954         sc_ugen_factory::buffer_lock_t buffer_lock(sc_factory->buffer_guard(bufferIndex));
1955         try {
1956             sample* free_buf = sc_factory->get_nrt_mirror_buffer(bufferIndex);
1957             sc_factory->allocate_buffer(bufferIndex, frames, channels);
1958 
1959             cmd_dispatcher<realtime>::fire_rt_callback([=, message = std::move(message)]() mutable {
1960                 sc_factory->buffer_sync(bufferIndex);
1961                 handle_completion_message(std::move(message), endpoint);
1962 
1963                 cmd_dispatcher<realtime>::fire_io_callback([=] {
1964                     free_aligned(free_buf);
1965                     send_done_message(endpoint, "/b_alloc", bufferIndex);
1966                 });
1967             });
1968         } catch (std::exception const& error) {
1969             report_failure(endpoint, error, "/b_alloc", bufferIndex);
1970             cmd_dispatcher<realtime>::free_in_rt_thread(std::move(message));
1971         }
1972     });
1973 }
1974 
1975 
handle_b_free(ReceivedMessage const & msg,endpoint_ptr endpoint)1976 template <bool realtime> void handle_b_free(ReceivedMessage const& msg, endpoint_ptr endpoint) {
1977     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
1978 
1979     osc::int32 index;
1980     args >> index;
1981 
1982     completion_message message = extract_completion_message(args);
1983 
1984     cmd_dispatcher<realtime>::fire_system_callback([=, message = std::move(message)]() mutable {
1985         sc_ugen_factory::buffer_lock_t buffer_lock(sc_factory->buffer_guard(index));
1986         sample* free_buf = sc_factory->get_nrt_mirror_buffer(index);
1987         sc_factory->free_buffer(index);
1988 
1989         cmd_dispatcher<realtime>::fire_rt_callback([=, message = std::move(message)]() mutable {
1990             sc_factory->buffer_sync(index);
1991 
1992             handle_completion_message(std::move(message), endpoint);
1993 
1994             cmd_dispatcher<realtime>::fire_io_callback([=] {
1995                 free_aligned(free_buf);
1996                 send_done_message(endpoint, "/b_free", index);
1997             });
1998         });
1999     });
2000 }
2001 
2002 
handle_b_allocRead(ReceivedMessage const & msg,endpoint_ptr endpoint)2003 template <bool realtime> void handle_b_allocRead(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2004     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
2005 
2006     osc::int32 bufferIndex;
2007     const char* filenameString;
2008 
2009     osc::int32 start = 0;
2010     osc::int32 frames = 0;
2011 
2012     args >> bufferIndex >> filenameString;
2013 
2014     if (!args.Eos())
2015         args >> start;
2016 
2017     if (!args.Eos())
2018         args >> frames;
2019 
2020     completion_message message = extract_completion_message(args);
2021     movable_string filename(filenameString);
2022 
2023     cmd_dispatcher<realtime>::fire_system_callback(
2024         [=, filename = std::move(filename), message = std::move(message)]() mutable {
2025             sc_ugen_factory::buffer_lock_t buffer_lock(sc_factory->buffer_guard(bufferIndex));
2026             sample* free_buf = sc_factory->get_nrt_mirror_buffer(bufferIndex);
2027             try {
2028                 sc_factory->buffer_read_alloc(bufferIndex, filename.c_str(), start, frames);
2029 
2030                 cmd_dispatcher<realtime>::fire_rt_callback(
2031                     [=, filename = std::move(filename), message = std::move(message)]() mutable {
2032                         sc_factory->buffer_sync(bufferIndex);
2033 
2034                         handle_completion_message(std::move(message), endpoint);
2035                         consume(std::move(filename));
2036 
2037                         cmd_dispatcher<realtime>::fire_io_callback([=] {
2038                             free_aligned(free_buf);
2039                             send_done_message(endpoint, "/b_allocRead", bufferIndex);
2040                         });
2041                     });
2042 
2043             } catch (std::exception const& error) {
2044                 cmd_dispatcher<realtime>::free_in_rt_thread(std::move(message), std::move(filename));
2045                 report_failure(endpoint, error, "/b_allocRead", bufferIndex);
2046             }
2047         });
2048 }
2049 
2050 
handle_b_allocReadChannel(ReceivedMessage const & msg,endpoint_ptr endpoint)2051 template <bool realtime> void handle_b_allocReadChannel(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2052     osc::ReceivedMessageArgumentIterator arg = msg.ArgumentsBegin();
2053 
2054     osc::int32 bufnum = arg->AsInt32();
2055     arg++;
2056     const char* filenameString = arg->AsString();
2057     arg++;
2058 
2059     osc::int32 start = arg->AsInt32();
2060     arg++;
2061     size_t frames = arg->AsInt32();
2062     arg++;
2063 
2064     size_t channel_args = msg.ArgumentCount() - 4; /* we already consumed 4 elements */
2065 
2066     size_t channel_count = 0;
2067     sized_array<uint, rt_pool_allocator<uint>> channels(channel_args);
2068 
2069     // Any remaining Int arguments are channels, followed by an optional
2070     // completion message.
2071     for (uint i = 0; i < channel_args; ++i) {
2072         if (arg->IsInt32()) {
2073             channels[i] = arg->AsInt32Unchecked();
2074             arg++;
2075             ++channel_count;
2076         }
2077     }
2078 
2079     /* we reached the message blob */
2080     completion_message message = extract_completion_message(arg);
2081 
2082     movable_array<uint32_t> channel_mapping(channel_count, channels.c_array());
2083     movable_string filename(filenameString);
2084 
2085     cmd_dispatcher<realtime>::fire_system_callback([=, message = std::move(message),
2086                                                     channel_mapping = std::move(channel_mapping),
2087                                                     filename = std::move(filename)]() mutable {
2088         sc_ugen_factory::buffer_lock_t buffer_lock(sc_factory->buffer_guard(bufnum));
2089         sample* free_buf = sc_factory->get_nrt_mirror_buffer(bufnum);
2090 
2091         try {
2092             sc_factory->buffer_alloc_read_channels(bufnum, filename.c_str(), start, frames, channel_mapping.size(),
2093                                                    channel_mapping.data());
2094 
2095             cmd_dispatcher<realtime>::fire_rt_callback([=, message = std::move(message),
2096                                                         channel_mapping = std::move(channel_mapping),
2097                                                         filename = std::move(filename)]() mutable {
2098                 sc_factory->buffer_sync(bufnum);
2099                 consume(std::move(channel_mapping));
2100                 consume(std::move(filename));
2101                 handle_completion_message(std::move(message), endpoint);
2102 
2103                 cmd_dispatcher<realtime>::fire_io_callback([=] {
2104                     free_aligned(free_buf);
2105                     send_done_message(endpoint, "/b_allocReadChannel", bufnum);
2106                 });
2107             });
2108         } catch (std::exception const& error) {
2109             cmd_dispatcher<realtime>::free_in_rt_thread(std::move(message), std::move(channel_mapping),
2110                                                         std::move(filename));
2111             report_failure(endpoint, error, "/b_allocReadChannel", bufnum);
2112         }
2113     });
2114 }
2115 
2116 const char* b_write = "/b_write";
2117 
fire_b_write_exception(void)2118 void fire_b_write_exception(void) { throw std::runtime_error("wrong arguments for /b_write"); }
2119 
handle_b_write(ReceivedMessage const & msg,endpoint_ptr endpoint)2120 template <bool realtime> void handle_b_write(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2121     osc::ReceivedMessageArgumentIterator arg = msg.ArgumentsBegin();
2122     osc::ReceivedMessageArgumentIterator end = msg.ArgumentsEnd();
2123 
2124     /* required args */
2125     osc::int32 bufnum = arg->AsInt32();
2126     arg++;
2127     const char* filename = arg->AsString();
2128     arg++;
2129     const char* header_format = arg->AsString();
2130     arg++;
2131     const char* sample_format = arg->AsString();
2132     arg++;
2133 
2134     /* optional args */
2135     osc::int32 frames = -1;
2136     osc::int32 start = 0;
2137     osc::int32 leave_open = 0;
2138 
2139     completion_message message;
2140 
2141     if (arg != end) {
2142         if (!arg->IsInt32())
2143             fire_b_write_exception();
2144         frames = arg->AsInt32Unchecked();
2145         arg++;
2146     } else
2147         goto fire_callback;
2148 
2149     if (arg != end) {
2150         if (!arg->IsInt32())
2151             fire_b_write_exception();
2152         start = arg->AsInt32Unchecked();
2153         arg++;
2154     } else
2155         goto fire_callback;
2156 
2157     if (arg != end) {
2158         if (!arg->IsInt32())
2159             fire_b_write_exception();
2160         leave_open = arg->AsInt32Unchecked();
2161         arg++;
2162     } else
2163         goto fire_callback;
2164 
2165     if (arg != end)
2166         message = extract_completion_message(arg);
2167 
2168 fire_callback:
2169     movable_string filenameString(filename);
2170     movable_string headerString(header_format);
2171     movable_string sampleString(sample_format);
2172 
2173     cmd_dispatcher<realtime>::fire_system_callback(
2174         [=, message = std::move(message), filenameString = std::move(filenameString),
2175          headerString = std::move(headerString), sampleString = std::move(sampleString)]() mutable {
2176             sc_ugen_factory::buffer_lock_t buffer_lock(sc_factory->buffer_guard(bufnum));
2177             try {
2178                 sc_factory->buffer_write(bufnum, filenameString.c_str(), headerString.c_str(), sampleString.c_str(),
2179                                          start, frames, leave_open);
2180 
2181                 cmd_dispatcher<realtime>::fire_rt_callback(
2182                     [=, message = std::move(message), filenameString = std::move(filenameString),
2183                      headerString = std::move(headerString), sampleString = std::move(sampleString)]() mutable {
2184                         handle_completion_message(std::move(message), endpoint);
2185 
2186                         consume(std::move(filenameString));
2187                         consume(std::move(headerString));
2188                         consume(std::move(sampleString));
2189 
2190                         cmd_dispatcher<realtime>::fire_done_message(endpoint, b_write, bufnum);
2191                     });
2192             } catch (std::exception const& error) {
2193                 report_failure(endpoint, error, b_write, bufnum);
2194                 cmd_dispatcher<realtime>::free_in_rt_thread(std::move(message), std::move(filenameString),
2195                                                             std::move(headerString), std::move(sampleString));
2196             }
2197         });
2198 }
2199 
2200 
2201 const char* b_read = "/b_read";
2202 
fire_b_read_exception(void)2203 void fire_b_read_exception(void) { throw std::runtime_error("wrong arguments for /b_read"); }
2204 
handle_b_read(ReceivedMessage const & msg,endpoint_ptr endpoint)2205 template <bool realtime> void handle_b_read(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2206     osc::ReceivedMessageArgumentIterator arg = msg.ArgumentsBegin();
2207     osc::ReceivedMessageArgumentIterator end = msg.ArgumentsEnd();
2208 
2209     /* required args */
2210     osc::int32 bufnum = arg->AsInt32();
2211     arg++;
2212     const char* filename = arg->AsString();
2213     arg++;
2214 
2215     /* optional args */
2216     osc::int32 start_file = 0;
2217     osc::int32 frames = -1;
2218     osc::int32 start_buffer = 0;
2219     osc::int32 leave_open = 0;
2220 
2221     completion_message message;
2222 
2223     if (arg != end) {
2224         if (!arg->IsInt32())
2225             fire_b_read_exception();
2226         start_file = arg->AsInt32Unchecked();
2227         arg++;
2228     } else
2229         goto fire_callback;
2230 
2231     if (arg != end) {
2232         if (!arg->IsInt32())
2233             fire_b_read_exception();
2234         frames = arg->AsInt32Unchecked();
2235         arg++;
2236     } else
2237         goto fire_callback;
2238 
2239     if (arg != end) {
2240         if (!arg->IsInt32())
2241             fire_b_read_exception();
2242         start_buffer = arg->AsInt32Unchecked();
2243         arg++;
2244     } else
2245         goto fire_callback;
2246 
2247     if (arg != end) {
2248         if (!arg->IsInt32())
2249             fire_b_read_exception();
2250         leave_open = arg->AsInt32Unchecked();
2251         arg++;
2252     } else
2253         goto fire_callback;
2254 
2255     if (arg != end)
2256         message = extract_completion_message(arg);
2257 
2258 fire_callback:
2259     movable_string fname(filename);
2260 
2261     cmd_dispatcher<realtime>::fire_system_callback(
2262         [=, filename = std::move(fname), message = std::move(message)]() mutable {
2263             sc_ugen_factory::buffer_lock_t buffer_lock(sc_factory->buffer_guard(bufnum));
2264 
2265             try {
2266                 sc_factory->buffer_read(bufnum, filename.c_str(), start_file, frames, start_buffer, leave_open);
2267 
2268                 cmd_dispatcher<realtime>::fire_rt_callback(
2269                     [=, filename = std::move(filename), message = std::move(message)]() mutable {
2270                         sc_factory->buffer_sync(bufnum);
2271 
2272                         handle_completion_message(std::move(message), endpoint);
2273                         consume(std::move(filename));
2274 
2275                         cmd_dispatcher<realtime>::fire_done_message(endpoint, b_read, bufnum);
2276                     });
2277             } catch (std::exception const& error) {
2278                 report_failure(endpoint, error, b_read, bufnum);
2279                 cmd_dispatcher<realtime>::free_in_rt_thread(std::move(message), std::move(filename));
2280             }
2281         });
2282 }
2283 
2284 const char* b_readChannel = "/b_readChannel";
2285 
fire_b_readChannel_exception(void)2286 void fire_b_readChannel_exception(void) { throw std::runtime_error("wrong arguments for /b_readChannel"); }
2287 
handle_b_readChannel(ReceivedMessage const & msg,endpoint_ptr endpoint)2288 template <bool realtime> void handle_b_readChannel(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2289     osc::ReceivedMessageArgumentIterator arg = msg.ArgumentsBegin();
2290     osc::ReceivedMessageArgumentIterator end = msg.ArgumentsEnd();
2291 
2292     /* required args */
2293     osc::int32 bufnum = arg->AsInt32();
2294     arg++;
2295     const char* filename = arg->AsString();
2296     arg++;
2297 
2298     /* optional args */
2299     osc::int32 start_file = 0;
2300     osc::int32 frames = -1;
2301     osc::int32 start_buffer = 0;
2302     osc::int32 leave_open = 0;
2303 
2304     sized_array<uint32_t, rt_pool_allocator<uint32_t>> channel_mapping(
2305         int32_t(msg.ArgumentCount())); /* larger than required */
2306     uint32_t channel_count = 0;
2307 
2308     completion_message message;
2309 
2310     if (arg != end) {
2311         if (!arg->IsInt32())
2312             fire_b_read_exception();
2313         start_file = arg->AsInt32Unchecked();
2314         arg++;
2315     } else
2316         goto fire_callback;
2317 
2318     if (arg != end) {
2319         if (!arg->IsInt32())
2320             fire_b_read_exception();
2321         frames = arg->AsInt32Unchecked();
2322         arg++;
2323     } else
2324         goto fire_callback;
2325 
2326     if (arg != end) {
2327         if (!arg->IsInt32())
2328             fire_b_write_exception();
2329         start_buffer = arg->AsInt32Unchecked();
2330         arg++;
2331     } else
2332         goto fire_callback;
2333 
2334     if (arg != end) {
2335         if (!arg->IsInt32())
2336             fire_b_write_exception();
2337         leave_open = arg->AsInt32Unchecked();
2338         arg++;
2339     } else
2340         goto fire_callback;
2341 
2342     while (arg != end) {
2343         if (arg->IsBlob()) {
2344             message = extract_completion_message(arg);
2345             goto fire_callback;
2346         } else if (arg->IsInt32()) {
2347             channel_mapping[channel_count] = arg->AsInt32Unchecked();
2348             ++arg;
2349         } else
2350             fire_b_readChannel_exception();
2351     }
2352 
2353 fire_callback:
2354     movable_string fname(filename);
2355 
2356     movable_array<uint32_t> channel_map(channel_count, channel_mapping.c_array());
2357 
2358     cmd_dispatcher<realtime>::fire_system_callback([=, message = std::move(message), filename = std::move(fname),
2359                                                     channel_map = std::move(channel_map)]() mutable {
2360         try {
2361             sc_factory->buffer_read_channel(bufnum, filename.c_str(), start_file, frames, start_buffer, leave_open,
2362                                             channel_map.size(), channel_map.data());
2363 
2364             cmd_dispatcher<realtime>::fire_rt_callback([=, message = std::move(message), filename = std::move(filename),
2365                                                         channel_map = std::move(channel_map)]() mutable {
2366                 sc_factory->buffer_sync(bufnum);
2367                 handle_completion_message(std::move(message), endpoint);
2368                 consume(std::move(filename));
2369                 consume(std::move(channel_map));
2370 
2371                 cmd_dispatcher<realtime>::fire_done_message(endpoint, b_readChannel, bufnum);
2372             });
2373         } catch (std::exception const& error) {
2374             report_failure(endpoint, error, b_readChannel, bufnum);
2375             cmd_dispatcher<realtime>::free_in_rt_thread(std::move(filename), std::move(channel_map),
2376                                                         std::move(message));
2377         }
2378     });
2379 }
2380 
2381 
2382 const char* b_zero = "/b_zero";
2383 
handle_b_zero(ReceivedMessage const & msg,endpoint_ptr endpoint)2384 template <bool realtime> void handle_b_zero(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2385     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
2386 
2387     osc::int32 index;
2388     args >> index;
2389     completion_message message = extract_completion_message(args);
2390 
2391     cmd_dispatcher<realtime>::fire_system_callback([=, message = std::move(message)]() mutable {
2392         sc_factory->buffer_zero(index);
2393         cmd_dispatcher<realtime>::fire_rt_callback([=, message = std::move(message)]() mutable {
2394             sc_factory->increment_write_updates(index);
2395             handle_completion_message(std::move(message), endpoint);
2396             cmd_dispatcher<realtime>::fire_done_message(endpoint, b_zero, index);
2397         });
2398     });
2399 }
2400 
handle_b_set(ReceivedMessage const & msg)2401 void handle_b_set(ReceivedMessage const& msg) {
2402     osc::ReceivedMessageArgumentIterator it = msg.ArgumentsBegin();
2403     osc::ReceivedMessageArgumentIterator end = msg.ArgumentsEnd();
2404     verify_argument(it, end);
2405     osc::int32 buffer_index = it->AsInt32();
2406     ++it;
2407 
2408     buffer_wrapper::sample_t* data = sc_factory->get_buffer(buffer_index);
2409     if (!data) {
2410         log_printf("/b_set called on unallocated buffer\n");
2411         return;
2412     }
2413 
2414     while (it != end) {
2415         osc::int32 index = it->AsInt32();
2416         ++it;
2417         float value = extract_float_argument(it++);
2418 
2419         data[index] = value;
2420     }
2421 }
2422 
handle_b_setn(ReceivedMessage const & msg)2423 void handle_b_setn(ReceivedMessage const& msg) {
2424     osc::ReceivedMessageArgumentIterator it = msg.ArgumentsBegin();
2425     osc::ReceivedMessageArgumentIterator end = msg.ArgumentsEnd();
2426     verify_argument(it, end);
2427     osc::int32 buffer_index = it->AsInt32();
2428     ++it;
2429 
2430     buffer_wrapper::sample_t* data = sc_factory->get_buffer(buffer_index);
2431     if (!data) {
2432         log_printf("/b_setn called on unallocated buffer\n");
2433         return;
2434     }
2435 
2436     while (it != end) {
2437         osc::int32 index = it->AsInt32();
2438         ++it;
2439         verify_argument(it, end);
2440         osc::int32 samples = it->AsInt32();
2441         ++it;
2442 
2443         for (int i = 0; i != samples; ++i) {
2444             verify_argument(it, end);
2445             float value = extract_float_argument(it++);
2446             data[index + i] = value;
2447         }
2448     }
2449 }
2450 
handle_b_fill(ReceivedMessage const & msg)2451 void handle_b_fill(ReceivedMessage const& msg) {
2452     osc::ReceivedMessageArgumentIterator it = msg.ArgumentsBegin();
2453     osc::ReceivedMessageArgumentIterator end = msg.ArgumentsEnd();
2454     verify_argument(it, end);
2455     osc::int32 buffer_index = it->AsInt32();
2456     ++it;
2457 
2458     buffer_wrapper::sample_t* data = sc_factory->get_buffer(buffer_index);
2459     if (!data) {
2460         log_printf("/b_fill called on unallocated buffer\n");
2461         return;
2462     }
2463 
2464     int bufSamples = sc_factory->get_buffer_struct(buffer_index)->samples;
2465 
2466     while (it != end) {
2467         osc::int32 index = it->AsInt32();
2468         ++it;
2469         verify_argument(it, end);
2470         osc::int32 samples = it->AsInt32();
2471         ++it;
2472         verify_argument(it, end);
2473         float value = extract_float_argument(it++);
2474 
2475         for (int i = 0; i != samples; ++i) {
2476             data[index] = value;
2477             ++index;
2478             if (index >= bufSamples) {
2479                 break;
2480             }
2481         }
2482     }
2483 }
2484 
handle_b_query(ReceivedMessage const & msg,endpoint_ptr endpoint)2485 template <bool realtime> void handle_b_query(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2486     const size_t elem_size = 3 * sizeof(int) * sizeof(float);
2487 
2488     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
2489     size_t arg_count = msg.ArgumentCount();
2490 
2491     size_t size = elem_size * arg_count + 128; /* should be more than required */
2492     sized_array<char, rt_pool_allocator<char>> data(size);
2493 
2494     osc::OutboundPacketStream p(data.c_array(), size);
2495     p << osc::BeginMessage("/b_info");
2496 
2497     while (!args.Eos()) {
2498         osc::int32 buffer_index;
2499         args >> buffer_index;
2500 
2501         SndBuf* buf = sc_factory->get_buffer_struct(buffer_index);
2502 
2503         p << buffer_index << osc::int32(buf->frames) << osc::int32(buf->channels) << float(buf->samplerate);
2504     }
2505 
2506     p << osc::EndMessage;
2507 
2508     movable_array<char> message(p.Size(), data.c_array());
2509     cmd_dispatcher<realtime>::fire_message(endpoint, std::move(message));
2510 }
2511 
2512 
handle_b_close(ReceivedMessage const & msg,endpoint_ptr endpoint)2513 template <bool realtime> void handle_b_close(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2514     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
2515     osc::int32 index;
2516     args >> index;
2517 
2518     completion_message message = extract_completion_message(args);
2519     cmd_dispatcher<realtime>::fire_system_callback([=, message = std::move(message)]() mutable {
2520         sc_factory->buffer_close(index);
2521 
2522         cmd_dispatcher<realtime>::fire_rt_callback([=, message = std::move(message)]() mutable {
2523             handle_completion_message(std::move(message), endpoint);
2524             cmd_dispatcher<realtime>::fire_done_message(endpoint, "/b_close", index);
2525         });
2526     });
2527 }
2528 
handle_b_get(ReceivedMessage const & msg,endpoint_ptr endpoint)2529 template <bool realtime> void handle_b_get(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2530     const size_t elem_size = sizeof(int) * sizeof(float);
2531     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
2532     const size_t index_count = msg.ArgumentCount() - 1;
2533     const size_t alloc_size = index_count * elem_size + 128; /* hopefully enough */
2534 
2535     sized_array<char, rt_pool_allocator<char>> return_message(alloc_size);
2536 
2537     osc::int32 buffer_index;
2538     args >> buffer_index;
2539 
2540     const SndBuf* buf = sc_factory->get_buffer_struct(buffer_index);
2541     const sample* data = buf->data;
2542     if (!data) {
2543         log_printf("/b_get called on unallocated buffer\n");
2544         return;
2545     }
2546 
2547     const int max_sample = buf->frames * buf->channels;
2548 
2549     osc::OutboundPacketStream p(return_message.c_array(), alloc_size);
2550     p << osc::BeginMessage("/b_set") << buffer_index;
2551 
2552     while (!args.Eos()) {
2553         osc::int32 index;
2554         args >> index;
2555         p << index;
2556 
2557         if (index < max_sample)
2558             p << data[index];
2559         else
2560             p << 0.f;
2561     }
2562 
2563     p << osc::EndMessage;
2564 
2565     movable_array<char> message(p.Size(), return_message.c_array());
2566     cmd_dispatcher<realtime>::fire_message(endpoint, std::move(message));
2567 }
2568 
2569 template <typename Alloc> struct getn_data {
getn_datanova::detail::__anon8089e4d40f11::getn_data2570     getn_data(int start, int count, const float* data): start_index_(start), data_(count) {
2571         data_.reserve(count);
2572         for (int i = 0; i != count; ++i)
2573             data_[i] = data[i];
2574     }
2575 
2576     int start_index_;
2577     std::vector<float, Alloc> data_;
2578 };
2579 
handle_b_getn(ReceivedMessage const & msg,endpoint_ptr endpoint)2580 template <bool realtime> void handle_b_getn(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2581     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
2582 
2583     typedef getn_data<rt_pool_allocator<float>> getn_data;
2584     std::vector<getn_data, rt_pool_allocator<getn_data>> return_data;
2585 
2586     osc::int32 buffer_index;
2587     args >> buffer_index;
2588 
2589     const SndBuf* buf = sc_factory->get_buffer_struct(buffer_index);
2590     const sample* data = buf->data;
2591     if (!data) {
2592         log_printf("/b_getn called on unallocated buffer\n");
2593         return;
2594     }
2595     const int max_sample = buf->frames * buf->channels;
2596 
2597     while (!args.Eos()) {
2598         osc::int32 index, sample_count;
2599         args >> index >> sample_count;
2600 
2601         if (index + sample_count <= max_sample)
2602             return_data.push_back(getn_data(index, sample_count, data + index));
2603     }
2604 
2605     size_t alloc_size = 128;
2606     for (size_t i = 0; i != return_data.size(); ++i)
2607         alloc_size += return_data[i].data_.size() * (sizeof(float) + sizeof(int)) + 2 * sizeof(int);
2608 
2609     sized_array<char, rt_pool_allocator<char>> return_message(alloc_size);
2610 
2611     osc::OutboundPacketStream p(return_message.c_array(), alloc_size);
2612     p << osc::BeginMessage("/b_setn") << buffer_index;
2613 
2614     for (size_t i = 0; i != return_data.size(); ++i) {
2615         p << osc::int32(return_data[i].start_index_) << osc::int32(return_data[i].data_.size());
2616 
2617         for (size_t j = 0; j != return_data[i].data_.size(); ++j)
2618             p << return_data[i].data_[j];
2619     }
2620 
2621     p << osc::EndMessage;
2622 
2623     movable_array<char> message(p.Size(), return_message.c_array());
2624     cmd_dispatcher<realtime>::fire_message(endpoint, std::move(message));
2625 }
2626 
2627 
handle_b_gen(ReceivedMessage const & msg,size_t msg_size,endpoint_ptr endpoint)2628 template <bool realtime> void handle_b_gen(ReceivedMessage const& msg, size_t msg_size, endpoint_ptr endpoint) {
2629     int skip_bytes = addr_pattern_size(msg); // skip address pattern
2630     movable_array<char> cmd(msg_size - skip_bytes, msg.AddressPattern() + skip_bytes);
2631 
2632     cmd_dispatcher<realtime>::fire_system_callback([=, message = std::move(cmd)]() mutable {
2633         sc_msg_iter msg(message.size(), message.data());
2634 
2635         char nextTag = msg.nextTag();
2636         if (nextTag != 'i') {
2637             printf("/b_gen handler: invalid buffer index type %c\n", nextTag);
2638             return;
2639         }
2640         int index = msg.geti();
2641 
2642         const char* generator = (const char*)msg.gets4();
2643         if (!generator) {
2644             if (nextTag += 'i') {
2645                 printf("/b_gen handler: invalid bufgen name\n");
2646                 return;
2647             }
2648         }
2649 
2650         sample* free_buf = sc_factory->buffer_generate(index, generator, msg);
2651 
2652         cmd_dispatcher<realtime>::fire_rt_callback([=, message = std::move(message)]() mutable {
2653             consume(std::move(message));
2654             sc_factory->buffer_sync(index);
2655 
2656             cmd_dispatcher<realtime>::fire_io_callback([=] {
2657                 free_aligned(free_buf);
2658                 send_done_message(endpoint, "/b_gen", index);
2659             });
2660         });
2661     });
2662 }
2663 
2664 
handle_c_set(ReceivedMessage const & msg)2665 void handle_c_set(ReceivedMessage const& msg) {
2666     osc::ReceivedMessageArgumentIterator it = msg.ArgumentsBegin();
2667 
2668     while (it != msg.ArgumentsEnd()) {
2669         osc::int32 bus_index = it->AsInt32();
2670         ++it;
2671         if (it == msg.ArgumentsEnd())
2672             return;
2673 
2674         float value = extract_float_argument(it++);
2675 
2676         sc_factory->controlbus_set(bus_index, value);
2677     }
2678 }
2679 
handle_c_setn(ReceivedMessage const & msg)2680 void handle_c_setn(ReceivedMessage const& msg) {
2681     osc::ReceivedMessageArgumentIterator it = msg.ArgumentsBegin();
2682 
2683     while (it != msg.ArgumentsEnd()) {
2684         osc::int32 bus_index, bus_count;
2685         bus_index = it->AsInt32();
2686         ++it;
2687         bus_count = it->AsInt32();
2688         ++it;
2689 
2690         for (int i = 0; i != bus_count; ++i) {
2691             float value = extract_float_argument(it++);
2692             sc_factory->controlbus_set(bus_index + i, value);
2693         }
2694     }
2695 }
2696 
handle_c_fill(ReceivedMessage const & msg)2697 void handle_c_fill(ReceivedMessage const& msg) {
2698     osc::ReceivedMessageArgumentIterator it = msg.ArgumentsBegin();
2699 
2700     while (it != msg.ArgumentsEnd()) {
2701         osc::int32 bus_index, bus_count;
2702         bus_index = it->AsInt32();
2703         ++it;
2704         bus_count = it->AsInt32();
2705         ++it;
2706         float value = extract_float_argument(it++);
2707         sc_factory->controlbus_fill(bus_index, bus_count, value);
2708     }
2709 }
2710 
handle_c_get(ReceivedMessage const & msg,endpoint_ptr endpoint)2711 template <bool realtime> void handle_c_get(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2712     const size_t elem_size = sizeof(int) + sizeof(float);
2713     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
2714     const size_t index_count = msg.ArgumentCount();
2715     const size_t alloc_size = index_count * elem_size + 128; /* hopefully enough */
2716 
2717     sized_array<char, rt_pool_allocator<char>> return_message(alloc_size);
2718 
2719     osc::OutboundPacketStream p(return_message.c_array(), alloc_size);
2720     p << osc::BeginMessage("/c_set");
2721 
2722     while (!args.Eos()) {
2723         osc::int32 index;
2724         args >> index;
2725 
2726         p << index << sc_factory->controlbus_get(index);
2727     }
2728 
2729     p << osc::EndMessage;
2730 
2731     movable_array<char> message(p.Size(), return_message.c_array());
2732     cmd_dispatcher<realtime>::fire_message(endpoint, std::move(message));
2733 }
2734 
handle_c_getn(ReceivedMessage const & msg,endpoint_ptr endpoint)2735 template <bool realtime> void handle_c_getn(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2736     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
2737 
2738     /* we pessimize, but better to allocate too much than too little */
2739     const size_t alloc_size = 128 + (2 * sizeof(int) + 128 * sizeof(float)) * msg.ArgumentCount();
2740 
2741     sized_array<char, rt_pool_allocator<char>> return_message(alloc_size);
2742 
2743     osc::OutboundPacketStream p(return_message.c_array(), alloc_size);
2744     p << osc::BeginMessage("/c_setn");
2745 
2746     while (!args.Eos()) {
2747         osc::int32 bus_index, bus_count;
2748         args >> bus_index >> bus_count;
2749         p << bus_index << bus_count;
2750 
2751         for (int i = 0; i != bus_count; ++i) {
2752             float value = sc_factory->controlbus_get(bus_index + i);
2753             p << value;
2754         }
2755     }
2756 
2757     p << osc::EndMessage;
2758 
2759     movable_array<char> message(p.Size(), return_message.c_array());
2760     cmd_dispatcher<realtime>::fire_message(endpoint, std::move(message));
2761 }
2762 
wrapSynthdefs(std::vector<sc_synthdef> && synthdefs)2763 static std::vector<sc_synth_definition_ptr> wrapSynthdefs(std::vector<sc_synthdef>&& synthdefs) {
2764     std::vector<sc_synth_definition_ptr> wrappedSynthdefs;
2765     wrappedSynthdefs.reserve(synthdefs.size());
2766 
2767     for (sc_synthdef& synthdef : synthdefs) {
2768         sc_synth_definition_ptr ptr(new sc_synth_definition(std::move(synthdef)));
2769         wrappedSynthdefs.emplace_back(std::move(ptr));
2770     }
2771 
2772     return wrappedSynthdefs;
2773 }
2774 
handle_d_recv(ReceivedMessage const & msg,endpoint_ptr endpoint)2775 template <bool realtime> void handle_d_recv(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2776     const void* synthdef_data;
2777     osc::osc_bundle_element_size_t synthdef_size;
2778 
2779     osc::ReceivedMessageArgumentIterator args = msg.ArgumentsBegin();
2780 
2781     args->AsBlob(synthdef_data, synthdef_size);
2782     ++args;
2783     movable_array<char> def(synthdef_size, (const char*)synthdef_data);
2784     completion_message message = extract_completion_message(args);
2785 
2786     cmd_dispatcher<realtime>::fire_system_callback([=, def = std::move(def), message = std::move(message)]() mutable {
2787         std::vector<sc_synth_definition_ptr> wrappedSynthdefs =
2788             wrapSynthdefs(read_synthdefs(def.data(), def.data() + def.size()));
2789 
2790         cmd_dispatcher<realtime>::fire_rt_callback([=, def = std::move(def), message = std::move(message),
2791                                                     wrappedSynthdefs = std::move(wrappedSynthdefs)]() mutable {
2792             for (sc_synth_definition_ptr& definition : wrappedSynthdefs)
2793                 instance->register_definition(std::move(definition));
2794 
2795             handle_completion_message(std::move(message), endpoint);
2796             consume(std::move(def));
2797 
2798             cmd_dispatcher<realtime>::fire_io_callback([=, wrappedSynthdefs = std::move(wrappedSynthdefs)] {
2799                 consume(std::move(wrappedSynthdefs));
2800                 send_done_message(endpoint, "/d_recv");
2801             });
2802         });
2803     });
2804 }
2805 
2806 
handle_d_load(ReceivedMessage const & msg,endpoint_ptr endpoint)2807 template <bool realtime> void handle_d_load(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2808     osc::ReceivedMessageArgumentIterator args = msg.ArgumentsBegin();
2809     const char* path = args->AsString();
2810     args++;
2811     completion_message message = extract_completion_message(args);
2812     movable_string path_string(path);
2813 
2814     cmd_dispatcher<realtime>::fire_system_callback(
2815         [=, message = std::move(message), path_string = std::move(path_string)]() mutable {
2816             /* TODO: we need to implement some file name pattern matching */
2817             std::vector<sc_synth_definition_ptr> wrappedSynthdefs =
2818                 wrapSynthdefs(sc_read_synthdefs_file(path_string.c_str()));
2819 
2820             cmd_dispatcher<realtime>::fire_rt_callback([=, message = std::move(message),
2821                                                         path_string = std::move(path_string),
2822                                                         wrappedSynthdefs = std::move(wrappedSynthdefs)]() mutable {
2823                 for (sc_synth_definition_ptr& definition : wrappedSynthdefs)
2824                     instance->register_definition(std::move(definition));
2825 
2826                 handle_completion_message(std::move(message), endpoint);
2827                 consume(std::move(path_string));
2828 
2829                 cmd_dispatcher<realtime>::fire_io_callback([=, wrappedSynthdefs = std::move(wrappedSynthdefs)] {
2830                     consume(std::move(wrappedSynthdefs));
2831                     send_done_message(endpoint, "/d_load");
2832                 });
2833             });
2834         });
2835 }
2836 
2837 
handle_d_loadDir(ReceivedMessage const & msg,endpoint_ptr endpoint)2838 template <bool realtime> void handle_d_loadDir(ReceivedMessage const& msg, endpoint_ptr endpoint) {
2839     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
2840     const char* path;
2841 
2842     args >> path;
2843     completion_message message = extract_completion_message(args);
2844     movable_string path_string(path);
2845 
2846     cmd_dispatcher<realtime>::fire_system_callback(
2847         [=, message = std::move(message), path_string = std::move(path_string)]() mutable {
2848             std::vector<sc_synth_definition_ptr> wrappedSynthdefs =
2849                 wrapSynthdefs(sc_read_synthdefs_dir(path_string.c_str()));
2850 
2851             cmd_dispatcher<realtime>::fire_rt_callback([=, message = std::move(message),
2852                                                         path_string = std::move(path_string),
2853                                                         wrappedSynthdefs = std::move(wrappedSynthdefs)]() mutable {
2854                 for (sc_synth_definition_ptr& definition : wrappedSynthdefs)
2855                     instance->register_definition(std::move(definition));
2856 
2857                 handle_completion_message(std::move(message), endpoint);
2858                 consume(std::move(path_string));
2859 
2860                 cmd_dispatcher<realtime>::fire_io_callback([=, wrappedSynthdefs = std::move(wrappedSynthdefs)] {
2861                     consume(std::move(wrappedSynthdefs));
2862                     send_done_message(endpoint, "/d_loadDir");
2863                 });
2864             });
2865         });
2866 }
2867 
2868 
handle_d_free(ReceivedMessage const & msg)2869 void handle_d_free(ReceivedMessage const& msg) {
2870     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
2871 
2872     while (!args.Eos()) {
2873         const char* defname;
2874         args >> defname;
2875 
2876         instance->remove_definition(defname);
2877     }
2878 }
2879 
insert_parallel_group(int node_id,int action,int target_id)2880 void insert_parallel_group(int node_id, int action, int target_id) {
2881     if (node_id == -1)
2882         node_id = instance->generate_node_id();
2883     else if (!check_node_id(node_id))
2884         return;
2885 
2886     server_node* target = find_node(target_id);
2887     if (!target)
2888         return;
2889 
2890     node_position_constraint pos = make_pair(target, node_position(action));
2891     if (!node_position_sanity_check(pos))
2892         return;
2893 
2894     instance->add_parallel_group(node_id, pos);
2895     last_generated = node_id;
2896 }
2897 
handle_p_new(ReceivedMessage const & msg)2898 void handle_p_new(ReceivedMessage const& msg) {
2899     osc::ReceivedMessageArgumentStream args = msg.ArgumentStream();
2900 
2901     while (!args.Eos()) {
2902         osc::int32 id, action, target;
2903         args >> id >> action >> target;
2904 
2905         insert_parallel_group(id, action, target);
2906     }
2907 }
2908 
handle_u_cmd(ReceivedMessage const & msg,int size)2909 void handle_u_cmd(ReceivedMessage const& msg, int size) {
2910     int skip_bytes = addr_pattern_size(msg); // skip address pattern
2911     sc_msg_iter args(size - skip_bytes, msg.AddressPattern() + skip_bytes);
2912 
2913     int node_id = args.geti();
2914 
2915     server_node* target_synth = find_node(node_id);
2916 
2917     if (target_synth == nullptr || target_synth->is_group())
2918         return;
2919 
2920     sc_synth* synth = static_cast<sc_synth*>(target_synth);
2921 
2922     int ugen_index = args.geti();
2923     const char* cmd_name = args.gets();
2924 
2925     synth->apply_unit_cmd(cmd_name, ugen_index, &args);
2926 }
2927 
handle_cmd(ReceivedMessage const & msg,int size,endpoint_ptr endpoint)2928 void handle_cmd(ReceivedMessage const& msg, int size, endpoint_ptr endpoint) {
2929     int skip_bytes = addr_pattern_size(msg); // skip address pattern
2930     sc_msg_iter args(size - skip_bytes, msg.AddressPattern() + skip_bytes);
2931 
2932     const char* cmd = args.gets();
2933 
2934     sc_factory->run_cmd_plugin(&sc_factory->world, cmd, &args, endpoint.get());
2935 }
2936 
2937 } /* namespace */
2938 
2939 template <bool realtime>
handle_message_int_address(ReceivedMessage const & message,size_t msg_size,endpoint_ptr const & endpoint)2940 void sc_osc_handler::handle_message_int_address(ReceivedMessage const& message, size_t msg_size,
2941                                                 endpoint_ptr const& endpoint) {
2942     uint32_t address = message.AddressPatternAsUInt32();
2943 
2944     switch (address) {
2945     case cmd_none:
2946         break;
2947 
2948     case cmd_quit:
2949         handle_quit<realtime>(endpoint);
2950         break;
2951 
2952     case cmd_s_new:
2953         handle_s_new(message);
2954         break;
2955 
2956     case cmd_s_noid:
2957         handle_s_noid(message);
2958         break;
2959 
2960     case cmd_s_get:
2961         handle_s_get<realtime>(message, msg_size, endpoint);
2962         break;
2963 
2964     case cmd_s_getn:
2965         handle_s_getn<realtime>(message, msg_size, endpoint);
2966         break;
2967 
2968     case cmd_notify:
2969         handle_notify<realtime>(message, endpoint);
2970         break;
2971 
2972     case cmd_status:
2973         handle_status<realtime>(endpoint);
2974         break;
2975 
2976     case cmd_dumpOSC:
2977         handle_dumpOSC(message);
2978         break;
2979 
2980     case cmd_sync:
2981         handle_sync<realtime>(message, endpoint);
2982         break;
2983 
2984     case cmd_clearSched:
2985         handle_clearSched();
2986         break;
2987 
2988     case cmd_error:
2989         handle_error(message);
2990         break;
2991 
2992     case cmd_g_new:
2993         handle_g_new(message);
2994         break;
2995 
2996     case cmd_g_head:
2997         handle_g_head_or_tail<head>(message);
2998         break;
2999 
3000     case cmd_g_tail:
3001         handle_g_head_or_tail<tail>(message);
3002         break;
3003 
3004     case cmd_g_freeAll:
3005         handle_g_freeall(message);
3006         break;
3007 
3008     case cmd_g_deepFree:
3009         handle_g_deepFree(message);
3010         break;
3011 
3012     case cmd_g_queryTree:
3013         handle_g_queryTree<realtime>(message, endpoint);
3014         break;
3015 
3016     case cmd_g_dumpTree:
3017         handle_g_dumpTree(message);
3018         break;
3019 
3020     case cmd_n_free:
3021         handle_n_free(message);
3022         break;
3023 
3024     case cmd_n_set:
3025         handle_n_set(message);
3026         break;
3027 
3028     case cmd_n_setn:
3029         handle_n_setn(message);
3030         break;
3031 
3032     case cmd_n_fill:
3033         handle_n_fill(message);
3034         break;
3035 
3036     case cmd_n_map:
3037         handle_n_map(message);
3038         break;
3039 
3040     case cmd_n_mapn:
3041         handle_n_mapn(message);
3042         break;
3043 
3044     case cmd_n_mapa:
3045         handle_n_mapa(message);
3046         break;
3047 
3048     case cmd_n_mapan:
3049         handle_n_mapan(message);
3050         break;
3051 
3052     case cmd_n_query:
3053         handle_n_query<realtime>(message, endpoint);
3054         break;
3055 
3056     case cmd_n_order:
3057         handle_n_order(message);
3058         break;
3059 
3060     case cmd_n_run:
3061         handle_n_run(message);
3062         break;
3063 
3064     case cmd_n_before:
3065         handle_n_before_or_after<before>(message);
3066         break;
3067 
3068     case cmd_n_after:
3069         handle_n_before_or_after<after>(message);
3070         break;
3071 
3072     case cmd_n_trace:
3073         handle_n_trace(message);
3074         break;
3075 
3076     case cmd_b_alloc:
3077         handle_b_alloc<realtime>(message, endpoint);
3078         break;
3079 
3080     case cmd_u_cmd:
3081         handle_u_cmd(message, msg_size);
3082         break;
3083 
3084     case cmd_b_free:
3085         handle_b_free<realtime>(message, endpoint);
3086         break;
3087 
3088     case cmd_b_allocRead:
3089         handle_b_allocRead<realtime>(message, endpoint);
3090         break;
3091 
3092     case cmd_b_allocReadChannel:
3093         handle_b_allocReadChannel<realtime>(message, endpoint);
3094         break;
3095 
3096     case cmd_b_read:
3097         handle_b_read<realtime>(message, endpoint);
3098         break;
3099 
3100     case cmd_b_readChannel:
3101         handle_b_readChannel<realtime>(message, endpoint);
3102         break;
3103 
3104     case cmd_b_write:
3105         handle_b_write<realtime>(message, endpoint);
3106         break;
3107 
3108     case cmd_b_zero:
3109         handle_b_zero<realtime>(message, endpoint);
3110         break;
3111 
3112     case cmd_b_set:
3113         handle_b_set(message);
3114         break;
3115 
3116     case cmd_b_setn:
3117         handle_b_setn(message);
3118         break;
3119 
3120     case cmd_b_fill:
3121         handle_b_fill(message);
3122         break;
3123 
3124     case cmd_b_query:
3125         handle_b_query<realtime>(message, endpoint);
3126         break;
3127 
3128     case cmd_b_get:
3129         handle_b_get<realtime>(message, endpoint);
3130         break;
3131 
3132     case cmd_b_getn:
3133         handle_b_getn<realtime>(message, endpoint);
3134         break;
3135 
3136     case cmd_b_gen:
3137         handle_b_gen<realtime>(message, msg_size, endpoint);
3138         break;
3139 
3140     case cmd_b_close:
3141         handle_b_close<realtime>(message, endpoint);
3142         break;
3143 
3144     case cmd_c_set:
3145         handle_c_set(message);
3146         break;
3147 
3148     case cmd_c_setn:
3149         handle_c_setn(message);
3150         break;
3151 
3152     case cmd_c_fill:
3153         handle_c_fill(message);
3154         break;
3155 
3156     case cmd_c_get:
3157         handle_c_get<realtime>(message, endpoint);
3158         break;
3159 
3160     case cmd_c_getn:
3161         handle_c_getn<realtime>(message, endpoint);
3162         break;
3163 
3164     case cmd_d_recv:
3165         handle_d_recv<realtime>(message, endpoint);
3166         break;
3167 
3168     case cmd_d_load:
3169         handle_d_load<realtime>(message, endpoint);
3170         break;
3171 
3172     case cmd_d_loadDir:
3173         handle_d_loadDir<realtime>(message, endpoint);
3174         break;
3175 
3176     case cmd_d_free:
3177         handle_d_free(message);
3178         break;
3179 
3180     case cmd_p_new:
3181         handle_p_new(message);
3182         break;
3183 
3184     case cmd_cmd:
3185         handle_cmd(message, msg_size, endpoint);
3186         break;
3187 
3188     case cmd_version:
3189         handle_version<realtime>(endpoint);
3190         break;
3191 
3192     default:
3193         handle_unhandled_message(message);
3194     }
3195 }
3196 
3197 namespace {
3198 
3199 template <bool realtime>
dispatch_group_commands(const char * address,ReceivedMessage const & message,endpoint_ptr const & endpoint)3200 void dispatch_group_commands(const char* address, ReceivedMessage const& message, endpoint_ptr const& endpoint) {
3201     assert(address[1] == 'g');
3202     assert(address[2] == '_');
3203 
3204     if (strcmp(address + 3, "new") == 0) {
3205         handle_g_new(message);
3206         return;
3207     }
3208     if (strcmp(address + 3, "head") == 0) {
3209         handle_g_head_or_tail<head>(message);
3210         return;
3211     }
3212     if (strcmp(address + 3, "tail") == 0) {
3213         handle_g_head_or_tail<tail>(message);
3214         return;
3215     }
3216     if (strcmp(address + 3, "freeAll") == 0) {
3217         handle_g_freeall(message);
3218         return;
3219     }
3220     if (strcmp(address + 3, "deepFree") == 0) {
3221         handle_g_deepFree(message);
3222         return;
3223     }
3224     if (strcmp(address + 3, "queryTree") == 0) {
3225         handle_g_queryTree<realtime>(message, endpoint);
3226         return;
3227     }
3228 
3229     if (strcmp(address + 3, "dumpTree") == 0) {
3230         handle_g_dumpTree(message);
3231         return;
3232     }
3233 }
3234 
3235 template <bool realtime>
dispatch_node_commands(const char * address,ReceivedMessage const & message,endpoint_ptr const & endpoint)3236 void dispatch_node_commands(const char* address, ReceivedMessage const& message, endpoint_ptr const& endpoint) {
3237     assert(address[1] == 'n');
3238     assert(address[2] == '_');
3239 
3240     if (strcmp(address + 3, "free") == 0) {
3241         handle_n_free(message);
3242         return;
3243     }
3244 
3245     if (strcmp(address + 3, "set") == 0) {
3246         handle_n_set(message);
3247         return;
3248     }
3249 
3250     if (strcmp(address + 3, "setn") == 0) {
3251         handle_n_setn(message);
3252         return;
3253     }
3254 
3255     if (strcmp(address + 3, "fill") == 0) {
3256         handle_n_fill(message);
3257         return;
3258     }
3259 
3260     if (strcmp(address + 3, "map") == 0) {
3261         handle_n_map(message);
3262         return;
3263     }
3264 
3265     if (strcmp(address + 3, "mapn") == 0) {
3266         handle_n_mapn(message);
3267         return;
3268     }
3269 
3270     if (strcmp(address + 3, "mapa") == 0) {
3271         handle_n_mapa(message);
3272         return;
3273     }
3274 
3275     if (strcmp(address + 3, "mapan") == 0) {
3276         handle_n_mapan(message);
3277         return;
3278     }
3279 
3280     if (strcmp(address + 3, "run") == 0) {
3281         handle_n_run(message);
3282         return;
3283     }
3284 
3285     if (strcmp(address + 3, "before") == 0) {
3286         handle_n_before_or_after<before>(message);
3287         return;
3288     }
3289 
3290     if (strcmp(address + 3, "after") == 0) {
3291         handle_n_before_or_after<after>(message);
3292         return;
3293     }
3294 
3295     if (strcmp(address + 3, "order") == 0) {
3296         handle_n_order(message);
3297         return;
3298     }
3299 
3300     if (strcmp(address + 3, "query") == 0) {
3301         handle_n_query<realtime>(message, endpoint);
3302         return;
3303     }
3304 
3305     if (strcmp(address + 3, "trace") == 0) {
3306         handle_n_trace(message);
3307         return;
3308     }
3309 }
3310 
3311 template <bool realtime>
dispatch_buffer_commands(const char * address,ReceivedMessage const & message,size_t msg_size,endpoint_ptr const & endpoint)3312 void dispatch_buffer_commands(const char* address, ReceivedMessage const& message, size_t msg_size,
3313                               endpoint_ptr const& endpoint) {
3314     assert(address[1] == 'b');
3315     assert(address[2] == '_');
3316 
3317     if (strcmp(address + 3, "alloc") == 0) {
3318         handle_b_alloc<realtime>(message, endpoint);
3319         return;
3320     }
3321 
3322     if (strcmp(address + 3, "free") == 0) {
3323         handle_b_free<realtime>(message, endpoint);
3324         return;
3325     }
3326 
3327     if (strcmp(address + 3, "allocRead") == 0) {
3328         handle_b_allocRead<realtime>(message, endpoint);
3329         return;
3330     }
3331     if (strcmp(address + 3, "allocReadChannel") == 0) {
3332         handle_b_allocReadChannel<realtime>(message, endpoint);
3333         return;
3334     }
3335 
3336     if (strcmp(address + 3, "read") == 0) {
3337         handle_b_read<realtime>(message, endpoint);
3338         return;
3339     }
3340 
3341     if (strcmp(address + 3, "readChannel") == 0) {
3342         handle_b_readChannel<realtime>(message, endpoint);
3343         return;
3344     }
3345 
3346     if (strcmp(address + 3, "write") == 0) {
3347         handle_b_write<realtime>(message, endpoint);
3348         return;
3349     }
3350 
3351     if (strcmp(address + 3, "zero") == 0) {
3352         handle_b_zero<realtime>(message, endpoint);
3353         return;
3354     }
3355 
3356     if (strcmp(address + 3, "set") == 0) {
3357         handle_b_set(message);
3358         return;
3359     }
3360 
3361     if (strcmp(address + 3, "setn") == 0) {
3362         handle_b_setn(message);
3363         return;
3364     }
3365 
3366     if (strcmp(address + 3, "fill") == 0) {
3367         handle_b_fill(message);
3368         return;
3369     }
3370 
3371     if (strcmp(address + 3, "query") == 0) {
3372         handle_b_query<realtime>(message, endpoint);
3373         return;
3374     }
3375 
3376     if (strcmp(address + 3, "get") == 0) {
3377         handle_b_get<realtime>(message, endpoint);
3378         return;
3379     }
3380 
3381     if (strcmp(address + 3, "getn") == 0) {
3382         handle_b_getn<realtime>(message, endpoint);
3383         return;
3384     }
3385 
3386     if (strcmp(address + 3, "gen") == 0) {
3387         handle_b_gen<realtime>(message, msg_size, endpoint);
3388         return;
3389     }
3390 
3391     if (strcmp(address + 3, "close") == 0) {
3392         handle_b_close<realtime>(message, endpoint);
3393         return;
3394     }
3395 }
3396 
3397 template <bool realtime>
dispatch_control_bus_commands(const char * address,ReceivedMessage const & message,endpoint_ptr const & endpoint)3398 void dispatch_control_bus_commands(const char* address, ReceivedMessage const& message, endpoint_ptr const& endpoint) {
3399     assert(address[1] == 'c');
3400     assert(address[2] == '_');
3401 
3402     if (strcmp(address + 3, "set") == 0) {
3403         handle_c_set(message);
3404         return;
3405     }
3406 
3407     if (strcmp(address + 3, "setn") == 0) {
3408         handle_c_setn(message);
3409         return;
3410     }
3411 
3412     if (strcmp(address + 3, "fill") == 0) {
3413         handle_c_fill(message);
3414         return;
3415     }
3416 
3417     if (strcmp(address + 3, "get") == 0) {
3418         handle_c_get<realtime>(message, endpoint);
3419         return;
3420     }
3421 
3422     if (strcmp(address + 3, "getn") == 0) {
3423         handle_c_getn<realtime>(message, endpoint);
3424         return;
3425     }
3426 }
3427 
3428 template <bool realtime>
dispatch_synthdef_commands(const char * address,ReceivedMessage const & message,endpoint_ptr const & endpoint)3429 void dispatch_synthdef_commands(const char* address, ReceivedMessage const& message, endpoint_ptr const& endpoint) {
3430     assert(address[1] == 'd');
3431     assert(address[2] == '_');
3432 
3433     if (strcmp(address + 3, "recv") == 0) {
3434         handle_d_recv<realtime>(message, endpoint);
3435         return;
3436     }
3437 
3438     if (strcmp(address + 3, "load") == 0) {
3439         handle_d_load<realtime>(message, endpoint);
3440         return;
3441     }
3442 
3443     if (strcmp(address + 3, "loadDir") == 0) {
3444         handle_d_loadDir<realtime>(message, endpoint);
3445         return;
3446     }
3447 
3448     if (strcmp(address + 3, "free") == 0) {
3449         handle_d_free(message);
3450         return;
3451     }
3452 }
3453 
3454 template <bool realtime>
dispatch_synth_commands(const char * address,ReceivedMessage const & message,size_t msg_size,endpoint_ptr const & endpoint)3455 void dispatch_synth_commands(const char* address, ReceivedMessage const& message, size_t msg_size,
3456                              endpoint_ptr const& endpoint) {
3457     assert(address[1] == 's');
3458     assert(address[2] == '_');
3459 
3460     if (strcmp(address + 3, "new") == 0) {
3461         handle_s_new(message);
3462         return;
3463     }
3464 
3465     if (strcmp(address + 3, "noid") == 0) {
3466         handle_s_noid(message);
3467         return;
3468     }
3469 
3470     if (strcmp(address + 3, "get") == 0) {
3471         handle_s_get<realtime>(message, msg_size, endpoint);
3472         return;
3473     }
3474 
3475     if (strcmp(address + 3, "getn") == 0) {
3476         handle_s_getn<realtime>(message, msg_size, endpoint);
3477         return;
3478     }
3479 }
3480 
3481 } /* namespace */
3482 
3483 template <bool realtime>
handle_message_sym_address(ReceivedMessage const & message,size_t msg_size,endpoint_ptr const & endpoint)3484 void sc_osc_handler::handle_message_sym_address(ReceivedMessage const& message, size_t msg_size,
3485                                                 endpoint_ptr const& endpoint) {
3486     const char* address = message.AddressPattern();
3487 
3488     /* scsynth doesn't require the leading / */
3489     if (address[0] != '/')
3490         address -= 1;
3491 
3492     if (address[2] == '_') {
3493         if (address[1] == 'g') {
3494             dispatch_group_commands<realtime>(address, message, endpoint);
3495             return;
3496         }
3497 
3498         if (address[1] == 'n') {
3499             dispatch_node_commands<realtime>(address, message, endpoint);
3500             return;
3501         }
3502 
3503         if (address[1] == 'b') {
3504             dispatch_buffer_commands<realtime>(address, message, msg_size, endpoint);
3505             return;
3506         }
3507 
3508         if (address[1] == 'c') {
3509             dispatch_control_bus_commands<realtime>(address, message, endpoint);
3510             return;
3511         }
3512 
3513         if (address[1] == 'd') {
3514             dispatch_synthdef_commands<realtime>(address, message, endpoint);
3515             return;
3516         }
3517 
3518         if (address[1] == 's') {
3519             dispatch_synth_commands<realtime>(address, message, msg_size, endpoint);
3520             return;
3521         }
3522     }
3523 
3524     if (strcmp(address + 1, "p_new") == 0) {
3525         handle_p_new(message);
3526         return;
3527     }
3528 
3529     if (strcmp(address + 1, "u_cmd") == 0) {
3530         handle_u_cmd(message, msg_size);
3531         return;
3532     }
3533 
3534     if (strcmp(address + 1, "status") == 0) {
3535         handle_status<realtime>(endpoint);
3536         return;
3537     }
3538 
3539     if (strcmp(address + 1, "sync") == 0) {
3540         handle_sync<realtime>(message, endpoint);
3541         return;
3542     }
3543 
3544     if (strcmp(address + 1, "quit") == 0) {
3545         handle_quit<realtime>(endpoint);
3546         return;
3547     }
3548 
3549     if (strcmp(address + 1, "notify") == 0) {
3550         handle_notify<realtime>(message, endpoint);
3551         return;
3552     }
3553 
3554     if (strcmp(address + 1, "dumpOSC") == 0) {
3555         handle_dumpOSC(message);
3556         return;
3557     }
3558 
3559     if (strcmp(address + 1, "clearSched") == 0) {
3560         handle_clearSched();
3561         return;
3562     }
3563 
3564     if (strcmp(address + 1, "error") == 0) {
3565         handle_error(message);
3566         return;
3567     }
3568 
3569     if (strcmp(address + 1, "cmd") == 0) {
3570         handle_cmd(message, msg_size, endpoint);
3571         return;
3572     }
3573 
3574     if (strcmp(address + 1, "version") == 0) {
3575         handle_version<realtime>(endpoint);
3576         return;
3577     }
3578 
3579     if (strcmp(address + 1, "none") == 0)
3580         return;
3581 
3582     handle_unhandled_message(message);
3583 }
3584 
3585 
3586 template <bool realtime>
handle_asynchronous_command(World * world,const char * cmdName,void * cmdData,AsyncStageFn stage2,AsyncStageFn stage3,AsyncStageFn stage4,AsyncFreeFn cleanup,completion_message && message,endpoint_ptr endpoint)3587 void handle_asynchronous_command(World* world, const char* cmdName, void* cmdData, AsyncStageFn stage2,
3588                                  AsyncStageFn stage3, AsyncStageFn stage4, AsyncFreeFn cleanup,
3589                                  completion_message&& message, endpoint_ptr endpoint) {
3590     // Usually, this API function is called in response to plugin/unit commands (handled *before* DSP computation).
3591     // We lock the memory pool nevertheless, just in case it's called from RT helper threads.
3592     // Actually, it's not a good idea to call it from within the perform routine because fire_system_callback()
3593     // is not thread-safe (the system_interpreter is a SPSC FIFO). On the other hand, calling it in the constructor
3594     // seems to be safe because constructors are protected by a global lock and therefore never run in parallel.
3595     spin_lock::scoped_lock lock(system_callback_allocator_lock);
3596 
3597     cmd_dispatcher<realtime>::fire_system_callback(
3598         [=, message = std::move(message), endpoint = std::move(endpoint)]() mutable {
3599             // stage 2 (NRT thread)
3600             bool result2 = !stage2 || (stage2)(world, cmdData);
3601 
3602             if (result2) {
3603                 cmd_dispatcher<realtime>::fire_rt_callback(
3604                     [=, message = std::move(message), endpoint = std::move(endpoint)]() mutable {
3605                         // stage 3 (RT thread)
3606                         bool result3 = !stage3 || (stage3)(world, cmdData);
3607 
3608                         if (result3) {
3609                             handle_completion_message(std::move(message), endpoint);
3610 
3611                             cmd_dispatcher<realtime>::fire_io_callback([=, endpoint = std::move(endpoint)] {
3612                                 // stage 4 (NRT thread)
3613                                 bool result4 = !stage4 || (stage4)(world, cmdData);
3614 
3615                                 if (result4 && cmdName)
3616                                     send_done_message(endpoint, cmdName);
3617 
3618                                 // free in RT thread!
3619                                 cmd_dispatcher<realtime>::fire_rt_callback([=] {
3620                                     if (cleanup)
3621                                         (cleanup)(world, cmdData);
3622                                 });
3623                             });
3624                         } else {
3625                             if (cleanup)
3626                                 (cleanup)(world, cmdData);
3627                             consume(std::move(message));
3628                         }
3629                     });
3630             } else {
3631                 // free in RT thread!
3632                 cmd_dispatcher<realtime>::fire_rt_callback([=, message = std::move(message)]() mutable {
3633                     if (cleanup)
3634                         (cleanup)(world, cmdData);
3635                     consume(std::move(message));
3636                 });
3637             }
3638         });
3639 }
3640 
do_asynchronous_command(World * world,void * replyAddr,const char * cmdName,void * cmdData,AsyncStageFn stage2,AsyncStageFn stage3,AsyncStageFn stage4,AsyncFreeFn cleanup,int completionMsgSize,void * completionMsgData) const3641 void sc_osc_handler::do_asynchronous_command(World* world, void* replyAddr, const char* cmdName, void* cmdData,
3642                                              AsyncStageFn stage2, AsyncStageFn stage3, AsyncStageFn stage4,
3643                                              AsyncFreeFn cleanup, int completionMsgSize,
3644                                              void* completionMsgData) const {
3645     completion_message msg(completionMsgSize, completionMsgData);
3646     endpoint_ptr shared_endpoint;
3647 
3648     nova_endpoint* endpoint = replyAddr ? static_cast<nova_endpoint*>(replyAddr) : nullptr;
3649 
3650     if (endpoint)
3651         shared_endpoint = endpoint->shared_from_this();
3652 
3653     if (world->mRealTime)
3654         handle_asynchronous_command<true>(world, cmdName, cmdData, stage2, stage3, stage4, cleanup, std::move(msg),
3655                                           shared_endpoint);
3656     else
3657         handle_asynchronous_command<false>(world, cmdName, cmdData, stage2, stage3, stage4, cleanup, std::move(msg),
3658                                            shared_endpoint);
3659 }
3660 
3661 // called from RT thread, perform in NRT thread, free in RT thread
handle_message_from_RT(FifoMsg & msg)3662 template <bool realtime> void handle_message_from_RT(FifoMsg& msg) {
3663     // see handle_asynchronous_command()
3664     spin_lock::scoped_lock lock(system_callback_allocator_lock);
3665 
3666     cmd_dispatcher<realtime>::fire_system_callback([msg]() mutable {
3667         msg.Perform();
3668 
3669         cmd_dispatcher<realtime>::fire_rt_callback([msg]() mutable { msg.Free(); });
3670     });
3671 }
3672 
send_message_from_RT(const World * world,FifoMsg & msg) const3673 void sc_osc_handler::send_message_from_RT(const World* world, FifoMsg& msg) const {
3674     if (world->mRealTime)
3675         handle_message_from_RT<true>(msg);
3676     else
3677         handle_message_from_RT<false>(msg);
3678 }
3679 
3680 // called from NRT thread, perform in RT thread, free in NRT thread
handle_message_to_RT(FifoMsg & msg)3681 template <bool realtime> void handle_message_to_RT(FifoMsg& msg) {
3682     cmd_dispatcher<realtime>::fire_rt_callback([msg]() mutable {
3683         msg.Perform();
3684 
3685         cmd_dispatcher<realtime>::fire_system_callback([msg]() mutable { msg.Free(); });
3686     });
3687 }
3688 
send_message_to_RT(const World * world,FifoMsg & msg) const3689 void sc_osc_handler::send_message_to_RT(const World* world, FifoMsg& msg) const {
3690     if (world->mRealTime)
3691         handle_message_to_RT<true>(msg);
3692     else
3693         handle_message_to_RT<false>(msg);
3694 }
3695 
3696 } /* namespace detail */
3697 } /* namespace nova */
3698