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