diff options
Diffstat (limited to 'src/osc')
-rw-r--r-- | src/osc/OSCClientReceiver.cpp | 303 | ||||
-rw-r--r-- | src/osc/OSCClientReceiver.hpp | 90 | ||||
-rw-r--r-- | src/osc/OSCClientSender.hpp | 3 | ||||
-rw-r--r-- | src/osc/OSCEngineReceiver.hpp | 56 | ||||
-rw-r--r-- | src/osc/OSCEngineSender.cpp | 241 | ||||
-rw-r--r-- | src/osc/OSCEngineSender.hpp | 118 | ||||
-rw-r--r-- | src/osc/OSCSender.cpp | 111 | ||||
-rw-r--r-- | src/osc/OSCSender.hpp | 52 | ||||
-rw-r--r-- | src/osc/ingen_osc_server.cpp (renamed from src/osc/ingen_osc.cpp) | 4 |
9 files changed, 942 insertions, 36 deletions
diff --git a/src/osc/OSCClientReceiver.cpp b/src/osc/OSCClientReceiver.cpp new file mode 100644 index 00000000..4fef1d1c --- /dev/null +++ b/src/osc/OSCClientReceiver.cpp @@ -0,0 +1,303 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include "raul/log.hpp" +#include "raul/AtomLiblo.hpp" +#include "raul/Path.hpp" + +#include "ingen-config.h" +#include "OSCClientReceiver.hpp" + +#define LOG(s) s << "[OSCClientReceiver] " + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Client { + +OSCClientReceiver::OSCClientReceiver(int listen_port, + SharedPtr<ClientInterface> target) + : _target(target) + , _st(NULL) + , _listen_port(listen_port) +{ +#ifdef RAUL_LOG_DEBUG + start(true); +#else + start(false); // true = dump, false = shutup +#endif +} + +OSCClientReceiver::~OSCClientReceiver() +{ + stop(); +} + +void +OSCClientReceiver::start(bool dump_osc) +{ + if (_st != NULL) + return; + + // Attempt preferred port + if (_listen_port != 0) { + char port_str[8]; + snprintf(port_str, 8, "%d", _listen_port); + _st = lo_server_thread_new(port_str, lo_error_cb); + } + + // Find a free port + if (!_st) { + _st = lo_server_thread_new(NULL, lo_error_cb); + _listen_port = lo_server_thread_get_port(_st); + } + + if (_st == NULL) { + LOG(error) << "Could not start OSC listener. Aborting." << endl; + exit(EXIT_FAILURE); + } else { + LOG(info) << "Started OSC listener on port " << lo_server_thread_get_port(_st) << endl; + } + + // Print all incoming messages + if (dump_osc) + lo_server_thread_add_method(_st, NULL, NULL, generic_cb, NULL); + + setup_callbacks(); + + // Display any uncaught messages to the console + //lo_server_thread_add_method(_st, NULL, NULL, unknown_cb, NULL); + + lo_server_thread_start(_st); +} + +void +OSCClientReceiver::stop() +{ + if (_st != NULL) { + //unregister_client(); + lo_server_thread_free(_st); + _st = NULL; + } +} + +int +OSCClientReceiver::generic_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data) +{ + printf("[OSCClientReceiver] %s (%s)\t", path, types); + + for (int i=0; i < argc; ++i) { + lo_arg_pp(lo_type(types[i]), argv[i]); + printf("\t"); + } + printf("\n"); + + return 1; // not handled +} + +void +OSCClientReceiver::lo_error_cb(int num, const char* msg, const char* path) +{ + LOG(error) << "Got error from server: " << msg << endl; +} + +int +OSCClientReceiver::unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data) +{ + std::string msg = "Received unknown OSC message: "; + msg += path; + + LOG(error) << msg << endl; + + return 0; +} + +void +OSCClientReceiver::setup_callbacks() +{ + if (!_target) + return; + + lo_server_thread_add_method(_st, "/ok", "i", response_ok_cb, this); + lo_server_thread_add_method(_st, "/error", "is", response_error_cb, this); + lo_server_thread_add_method(_st, "/plugin", "sss", plugin_cb, this); + lo_server_thread_add_method(_st, "/put", NULL, put_cb, this); + lo_server_thread_add_method(_st, "/delta_begin", NULL, delta_begin_cb, this); + lo_server_thread_add_method(_st, "/delta_remove", NULL, delta_remove_cb, this); + lo_server_thread_add_method(_st, "/delta_add", NULL, delta_add_cb, this); + lo_server_thread_add_method(_st, "/delta_end", NULL, delta_end_cb, this); + lo_server_thread_add_method(_st, "/move", "ss", move_cb, this); + lo_server_thread_add_method(_st, "/delete", "s", del_cb, this); + lo_server_thread_add_method(_st, "/connect", "ss", connection_cb, this); + lo_server_thread_add_method(_st, "/disconnect", "ss", disconnection_cb, this); + lo_server_thread_add_method(_st, "/set_property", NULL, set_property_cb, this); + lo_server_thread_add_method(_st, "/activity", "sT", activity_cb, this); + lo_server_thread_add_method(_st, "/activity", "sf", activity_cb, this); +} + +/** Catches errors that aren't a direct result of a client request. + */ +int +OSCClientReceiver::_error_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _target->error((char*)argv[0]); + return 0; +} + +int +OSCClientReceiver::_del_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _target->del((const char*)&argv[0]->s); + return 0; +} + +int +OSCClientReceiver::_put_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* obj_path = &argv[0]->s; + const char* ctx = &argv[1]->s; + Resource::Properties prop; + for (int i = 2; i < argc-1; i += 2) + prop.insert(make_pair(&argv[i]->s, + AtomLiblo::lo_arg_to_atom(types[i+1], argv[i+1]))); + _target->put(obj_path, prop, Resource::uri_to_graph(ctx)); + return 0; +} + +int +OSCClientReceiver::_delta_begin_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* obj_path = &argv[0]->s; + assert(_delta_remove.empty()); + assert(_delta_add.empty()); + _delta_uri = obj_path; + return 0; +} + +int +OSCClientReceiver::_delta_remove_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _delta_remove.insert(make_pair(&argv[0]->s, + AtomLiblo::lo_arg_to_atom(types[1], argv[1]))); + return 0; +} + +int +OSCClientReceiver::_delta_add_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _delta_add.insert(make_pair(&argv[0]->s, + AtomLiblo::lo_arg_to_atom(types[1], argv[1]))); + return 0; +} + +int +OSCClientReceiver::_delta_end_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _target->delta(_delta_uri, _delta_remove, _delta_add); + _delta_uri = Raul::URI(); + _delta_remove.clear(); + _delta_add.clear(); + return 0; +} + +int +OSCClientReceiver::_move_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* old_path = &argv[1]->s; + const char* new_path = &argv[2]->s; + + _target->move(old_path, new_path); + return 0; +} + +int +OSCClientReceiver::_connection_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* const src_port_path = &argv[0]->s; + const char* const dst_port_path = &argv[1]->s; + + _target->connect(src_port_path, dst_port_path); + + return 0; +} + +int +OSCClientReceiver::_disconnection_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* src_uri = &argv[0]->s; + const char* dst_uri = &argv[1]->s; + + _target->disconnect(src_uri, dst_uri); + + return 0; +} + +/** Notification of a new or updated property. + */ +int +OSCClientReceiver::_set_property_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + if (argc != 3 || types[0] != 's' || types[1] != 's') + return 1; + + const char* obj_uri = &argv[0]->s; + const char* key = &argv[1]->s; + + Atom value = AtomLiblo::lo_arg_to_atom(types[2], argv[2]); + + _target->set_property(obj_uri, key, value); + + return 0; +} + +int +OSCClientReceiver::_activity_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* const port_path = &argv[0]->s; + + Atom value = AtomLiblo::lo_arg_to_atom(types[1], argv[1]); + + _target->activity(port_path, value); + + return 0; +} + +int +OSCClientReceiver::_response_ok_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + assert(!strcmp(types, "i")); + _target->response_ok(argv[0]->i); + + return 0; +} + +int +OSCClientReceiver::_response_error_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + assert(!strcmp(types, "is")); + _target->response_error(argv[0]->i, &argv[1]->s); + + return 0; +} + +} // namespace Client +} // namespace Ingen diff --git a/src/osc/OSCClientReceiver.hpp b/src/osc/OSCClientReceiver.hpp new file mode 100644 index 00000000..46afc81a --- /dev/null +++ b/src/osc/OSCClientReceiver.hpp @@ -0,0 +1,90 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INGEN_CLIENT_OSCCLIENTRECEIVER_HPP +#define INGEN_CLIENT_OSCCLIENTRECEIVER_HPP + +#include <cstdlib> + +#include <boost/utility.hpp> +#include <lo/lo.h> + +#include "ingen/ClientInterface.hpp" +#include "raul/Deletable.hpp" +#include "raul/SharedPtr.hpp" + +#include "macros.h" + +namespace Ingen { +namespace Client { + +/** Client-side receiver for OSC messages from the engine. + * + * \ingroup IngenClient + */ +class OSCClientReceiver : public boost::noncopyable, public Raul::Deletable +{ +public: + OSCClientReceiver(int listen_port, SharedPtr<ClientInterface> target); + ~OSCClientReceiver(); + + std::string uri() const { return lo_server_thread_get_url(_st); } + + void start(bool dump_osc); + void stop(); + + int listen_port() { return _listen_port; } + std::string listen_url() { return lo_server_thread_get_url(_st); } + +private: + void setup_callbacks(); + + static void lo_error_cb(int num, const char* msg, const char* path); + + static int generic_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data); + static int unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* osc_receiver); + + SharedPtr<ClientInterface> _target; + lo_server_thread _st; + Raul::URI _delta_uri; + Resource::Properties _delta_remove; + Resource::Properties _delta_add; + int _listen_port; + + LO_HANDLER(OSCClientReceiver, error); + LO_HANDLER(OSCClientReceiver, response_ok); + LO_HANDLER(OSCClientReceiver, response_error); + LO_HANDLER(OSCClientReceiver, plugin); + LO_HANDLER(OSCClientReceiver, plugin_list_end); + LO_HANDLER(OSCClientReceiver, new_patch); + LO_HANDLER(OSCClientReceiver, del); + LO_HANDLER(OSCClientReceiver, move); + LO_HANDLER(OSCClientReceiver, connection); + LO_HANDLER(OSCClientReceiver, disconnection); + LO_HANDLER(OSCClientReceiver, put); + LO_HANDLER(OSCClientReceiver, delta_begin); + LO_HANDLER(OSCClientReceiver, delta_remove); + LO_HANDLER(OSCClientReceiver, delta_add); + LO_HANDLER(OSCClientReceiver, delta_end); + LO_HANDLER(OSCClientReceiver, set_property); + LO_HANDLER(OSCClientReceiver, activity); +}; + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_OSCCLIENTRECEIVER_HPP diff --git a/src/osc/OSCClientSender.hpp b/src/osc/OSCClientSender.hpp index ffc3b202..c391e54a 100644 --- a/src/osc/OSCClientSender.hpp +++ b/src/osc/OSCClientSender.hpp @@ -25,7 +25,8 @@ #include "ingen/ClientInterface.hpp" #include "ingen/GraphObject.hpp" -#include "../shared/OSCSender.hpp" + +#include "OSCSender.hpp" namespace Ingen { diff --git a/src/osc/OSCEngineReceiver.hpp b/src/osc/OSCEngineReceiver.hpp index fe16ac1a..72388bc2 100644 --- a/src/osc/OSCEngineReceiver.hpp +++ b/src/osc/OSCEngineReceiver.hpp @@ -28,6 +28,7 @@ #include "ingen/Resource.hpp" #include "ingen-config.h" +#include "macros.h" namespace Ingen { @@ -37,17 +38,6 @@ namespace Server { class Engine; -/* Some boilerplate killing macros... */ -#define LO_HANDLER_ARGS const char* path, const char* types, lo_arg** argv, int argc, lo_message msg - -/* Defines a static handler to be passed to lo_add_method, which is a trivial - * wrapper around a non-static method that does the real work. Makes a whoole - * lot of ugly boiler plate go away */ -#define LO_HANDLER(name) \ -int _##name##_cb (LO_HANDLER_ARGS);\ -inline static int name##_cb(LO_HANDLER_ARGS, void* myself)\ -{ return ((OSCEngineReceiver*)myself)->_##name##_cb(path, types, argv, argc, msg); } - /* FIXME: Make this receive and preprocess in the same thread? */ /** Receive OSC messages and call interface functions. @@ -96,28 +86,28 @@ private: static int generic_cb(LO_HANDLER_ARGS, void* myself); static int unknown_cb(LO_HANDLER_ARGS, void* myself); - LO_HANDLER(quit); - LO_HANDLER(ping); - LO_HANDLER(ping_slow); - LO_HANDLER(register_client); - LO_HANDLER(unregister_client); - LO_HANDLER(get); - LO_HANDLER(put); - LO_HANDLER(delta_begin); - LO_HANDLER(delta_remove); - LO_HANDLER(delta_add); - LO_HANDLER(delta_end); - LO_HANDLER(move); - LO_HANDLER(del); - LO_HANDLER(connect); - LO_HANDLER(disconnect); - LO_HANDLER(disconnect_all); - LO_HANDLER(note_on); - LO_HANDLER(note_off); - LO_HANDLER(all_notes_off); - LO_HANDLER(learn); - LO_HANDLER(set_property); - LO_HANDLER(property_set); + LO_HANDLER(OSCEngineReceiver, quit); + LO_HANDLER(OSCEngineReceiver, ping); + LO_HANDLER(OSCEngineReceiver, ping_slow); + LO_HANDLER(OSCEngineReceiver, register_client); + LO_HANDLER(OSCEngineReceiver, unregister_client); + LO_HANDLER(OSCEngineReceiver, get); + LO_HANDLER(OSCEngineReceiver, put); + LO_HANDLER(OSCEngineReceiver, delta_begin); + LO_HANDLER(OSCEngineReceiver, delta_remove); + LO_HANDLER(OSCEngineReceiver, delta_add); + LO_HANDLER(OSCEngineReceiver, delta_end); + LO_HANDLER(OSCEngineReceiver, move); + LO_HANDLER(OSCEngineReceiver, del); + LO_HANDLER(OSCEngineReceiver, connect); + LO_HANDLER(OSCEngineReceiver, disconnect); + LO_HANDLER(OSCEngineReceiver, disconnect_all); + LO_HANDLER(OSCEngineReceiver, note_on); + LO_HANDLER(OSCEngineReceiver, note_off); + LO_HANDLER(OSCEngineReceiver, all_notes_off); + LO_HANDLER(OSCEngineReceiver, learn); + LO_HANDLER(OSCEngineReceiver, set_property); + LO_HANDLER(OSCEngineReceiver, property_set); Engine& _engine; SharedPtr<ServerInterface> _interface; diff --git a/src/osc/OSCEngineSender.cpp b/src/osc/OSCEngineSender.cpp new file mode 100644 index 00000000..e2436af7 --- /dev/null +++ b/src/osc/OSCEngineSender.cpp @@ -0,0 +1,241 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "raul/log.hpp" +#include "raul/AtomLiblo.hpp" +#include "raul/Path.hpp" + +#include "ingen/Patch.hpp" +#include "ingen/Port.hpp" +#include "ingen/Plugin.hpp" + +#include "OSCEngineSender.hpp" + +#define LOG(s) s << "[OSCEngineSender] " + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Client { + +/** Note the sending port is implicitly set by liblo, lo_send by default sends + * from the most recently created server, so create the OSC listener before + * this to have it all happen on the same port. Yeah, this is a big magic :/ + */ +OSCEngineSender::OSCEngineSender(const URI& engine_url, + size_t max_packet_size, + SharedPtr<Raul::Deletable> receiver) + : Shared::OSCSender(max_packet_size) + , _receiver(receiver) + , _engine_url(engine_url) + , _id(0) +{ + _address = lo_address_new_from_url(engine_url.c_str()); +} + +OSCEngineSender::~OSCEngineSender() +{ + lo_address_free(_address); +} + +/** Attempt to connect to the engine (by pinging it). + * + * This doesn't register a client (or otherwise affect the client/engine state). + * To check for success wait for the ping response with id @a ping_id (using the + * relevant OSCClientReceiver). + * + * Passing a client_port of 0 will automatically choose a free port. If the + * @a block parameter is true, this function will not return until a connection + * has successfully been made. + */ +void +OSCEngineSender::attach(int32_t ping_id, bool block) +{ + if (!_address) + _address = lo_address_new_from_url(_engine_url.c_str()); + + if (_address == NULL) { + LOG(error) << "Unable to connect to " << _engine_url << endl; + exit(EXIT_FAILURE); + } + + LOG(info) << "Attempting to contact engine at " << _engine_url << " ..." << endl; + + _id = ping_id; + this->ping(); +} + +/* *** ServerInterface implementation below here *** */ + +/** Register with the engine via OSC. + * + * Note that this does not actually use 'client', since the engine creates + * it's own key for OSC clients (namely the incoming URL), for NAT + * traversal. It is a parameter to remain compatible with ServerInterface. + */ +void +OSCEngineSender::register_client(ClientInterface* client) +{ + send("/register_client", "i", next_id(), LO_ARGS_END); +} + +void +OSCEngineSender::unregister_client(const URI& uri) +{ + send("/unregister_client", "i", next_id(), LO_ARGS_END); +} + +// Object commands + +void +OSCEngineSender::put(const Raul::URI& path, + const Resource::Properties& properties, + Resource::Graph ctx) +{ + typedef Resource::Properties::const_iterator iterator; + lo_message m = lo_message_new(); + lo_message_add_int32(m, next_id()); + lo_message_add_string(m, path.c_str()); + lo_message_add_string(m, Resource::graph_to_uri(ctx).c_str()); + for (iterator i = properties.begin(); i != properties.end(); ++i) { + lo_message_add_string(m, i->first.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, i->second); + } + send_message("/put", m); +} + +void +OSCEngineSender::delta(const Raul::URI& path, + const Resource::Properties& remove, + const Resource::Properties& add) +{ + typedef Resource::Properties::const_iterator iterator; + + const bool bundle = !_bundle; + if (bundle) + bundle_begin(); + + const int32_t id = next_id(); + send("/delta_begin", "is", id, path.c_str(), LO_ARGS_END); + + for (iterator i = remove.begin(); i != remove.end(); ++i) { + lo_message m = lo_message_new(); + lo_message_add_string(m, i->first.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, i->second); + send_message("/delta_remove", m); + } + + for (iterator i = add.begin(); i != add.end(); ++i) { + lo_message m = lo_message_new(); + lo_message_add_string(m, i->first.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, i->second); + send_message("/delta_add", m); + } + + send("/delta_end", "i", id, LO_ARGS_END); + + if (bundle) + bundle_end(); +} + +void +OSCEngineSender::move(const Path& old_path, + const Path& new_path) +{ + send("/move", "iss", + next_id(), + old_path.c_str(), + new_path.c_str(), + LO_ARGS_END); +} + +void +OSCEngineSender::del(const URI& uri) +{ + send("/delete", "is", + next_id(), + uri.c_str(), + LO_ARGS_END); +} + +void +OSCEngineSender::connect(const Path& src_port_path, + const Path& dst_port_path) +{ + send("/connect", "iss", + next_id(), + src_port_path.c_str(), + dst_port_path.c_str(), + LO_ARGS_END); +} + +void +OSCEngineSender::disconnect(const URI& src, + const URI& dst) +{ + send("/disconnect", "iss", + next_id(), + src.c_str(), + dst.c_str(), + LO_ARGS_END); +} + +void +OSCEngineSender::disconnect_all(const Path& parent_patch_path, + const Path& path) +{ + send("/disconnect_all", "iss", + next_id(), + parent_patch_path.c_str(), + path.c_str(), + LO_ARGS_END); +} + +void +OSCEngineSender::set_property(const URI& subject, + const URI& predicate, + const Atom& value) +{ + lo_message m = lo_message_new(); + lo_message_add_int32(m, next_id()); + lo_message_add_string(m, subject.c_str()); + lo_message_add_string(m, predicate.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, value); + send_message("/set_property", m); +} + +// Requests // + +void +OSCEngineSender::ping() +{ + send("/ping", "i", next_id(), LO_ARGS_END); +} + +void +OSCEngineSender::get(const URI& uri) +{ + send("/get", "is", + next_id(), + uri.c_str(), + LO_ARGS_END); +} + +} // namespace Client +} // namespace Ingen + diff --git a/src/osc/OSCEngineSender.hpp b/src/osc/OSCEngineSender.hpp new file mode 100644 index 00000000..5aeeed7b --- /dev/null +++ b/src/osc/OSCEngineSender.hpp @@ -0,0 +1,118 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INGEN_CLIENT_OSCENGINESENDER_HPP +#define INGEN_CLIENT_OSCENGINESENDER_HPP + +#include <stddef.h> + +#include <string> + +#include <lo/lo.h> + +#include "raul/Deletable.hpp" + +#include "ingen/ServerInterface.hpp" + +#include "OSCSender.hpp" + +namespace Ingen { + +namespace Client { + +/* OSC (via liblo) interface to the engine. + * + * Clients can use this opaquely as an ServerInterface* to control the engine + * over OSC (whether over a network or not, etc). + * + * \ingroup IngenClient + */ +class OSCEngineSender : public ServerInterface, public Shared::OSCSender { +public: + OSCEngineSender(const Raul::URI& engine_url, + size_t max_packet_size, + SharedPtr<Raul::Deletable> receiver); + + ~OSCEngineSender(); + + Raul::URI uri() const { return _engine_url; } + + inline int32_t next_id() + { int32_t ret = (_id == -1) ? -1 : _id++; return ret; } + + void respond_to(ClientInterface* client, int32_t id) { _id = id; } + void disable_responses() { _id = -1; } + + void attach(int32_t ping_id, bool block); + + /* *** ServerInterface implementation below here *** */ + + void enable() { _enabled = true; } + void disable() { _enabled = false; } + + void bundle_begin() { OSCSender::bundle_begin(); } + void bundle_end() { OSCSender::bundle_end(); } + + // Client registration + void register_client(ClientInterface* client); + void unregister_client(const Raul::URI& uri); + + // Object commands + + virtual void put(const Raul::URI& path, + const Resource::Properties& properties, + Resource::Graph ctx=Resource::DEFAULT); + + virtual void delta(const Raul::URI& path, + const Resource::Properties& remove, + const Resource::Properties& add); + + virtual void del(const Raul::URI& uri); + + virtual void move(const Raul::Path& old_path, + const Raul::Path& new_path); + + virtual void connect(const Raul::Path& src_port_path, + const Raul::Path& dst_port_path); + + virtual void disconnect(const Raul::URI& src, + const Raul::URI& dst); + + virtual void disconnect_all(const Raul::Path& parent_patch_path, + const Raul::Path& path); + + virtual void set_property(const Raul::URI& subject_path, + const Raul::URI& predicate, + const Raul::Atom& value); + + // Requests // + void ping(); + void get(const Raul::URI& uri); + +protected: + SharedPtr<Raul::Deletable> _receiver; + + const Raul::URI _engine_url; + int _client_port; + int32_t _id; +}; + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_OSCENGINESENDER_HPP + diff --git a/src/osc/OSCSender.cpp b/src/osc/OSCSender.cpp new file mode 100644 index 00000000..6c07551b --- /dev/null +++ b/src/osc/OSCSender.cpp @@ -0,0 +1,111 @@ +/* This file is part of Ingen. + * Copyright 2008-2011 David Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <assert.h> +#include <unistd.h> +#include <stdarg.h> + +#include "raul/log.hpp" + +#include "OSCSender.hpp" +#include "ingen-config.h" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Shared { + +OSCSender::OSCSender(size_t max_packet_size) + : _bundle(NULL) + , _address(NULL) + , _max_packet_size(max_packet_size) + , _enabled(true) +{ +} + +void +OSCSender::bundle_begin() +{ + assert(!_bundle); + lo_timetag t; + lo_timetag_now(&t); + _bundle = lo_bundle_new(t); +} + +void +OSCSender::bundle_end() +{ + assert(_bundle); + lo_send_bundle(_address, _bundle); + lo_bundle_free_messages(_bundle); + _bundle = NULL; +} + +int +OSCSender::send(const char *path, const char *types, ...) +{ + if (!_enabled) + return 0; + +#ifdef RAUL_LOG_DEBUG + info << "[OSCSender] " << path << " (" << types << ")" << endl; +#endif + + va_list args; + va_start(args, types); + + lo_message msg = lo_message_new(); + int ret = lo_message_add_varargs(msg, types, args); + + if (!ret) + send_message(path, msg); + else + lo_message_free(msg); + + va_end(args); + + return ret; +} + +void +OSCSender::send_message(const char* path, lo_message msg) +{ + if (!_enabled) { + lo_message_free(msg); + return; + } + + if (_bundle) { + if (lo_bundle_length(_bundle) + lo_message_length(msg, path) > _max_packet_size) { + warn << "Maximum bundle size reached, bundle split" << endl; + lo_send_bundle(_address, _bundle); + lo_bundle_free_messages(_bundle); + lo_timetag t; + lo_timetag_now(&t); + _bundle = lo_bundle_new(t); + } + lo_bundle_add_message(_bundle, path, msg); + + } else { + lo_send_message(_address, path, msg); + lo_message_free(msg); + } +} + +} // namespace Shared +} // namespace Ingen diff --git a/src/osc/OSCSender.hpp b/src/osc/OSCSender.hpp new file mode 100644 index 00000000..b2febb70 --- /dev/null +++ b/src/osc/OSCSender.hpp @@ -0,0 +1,52 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INGEN_SHARED_OSCSENDER_HPP +#define INGEN_SHARED_OSCSENDER_HPP + +#include <stddef.h> + +#include <lo/lo.h> + +namespace Ingen { +namespace Shared { + +class OSCSender { +public: + OSCSender(size_t max_packet_size); + virtual ~OSCSender() {} + + lo_address address() const { return _address; } + + void bundle_begin(); + void bundle_end(); + +protected: + int send(const char *path, const char *types, ...); + void send_message(const char* path, lo_message m); + + lo_bundle _bundle; + lo_address _address; + size_t _max_packet_size; + bool _enabled; +}; + +} // namespace Shared +} // namespace Ingen + +#endif // INGEN_SHARED_OSCSENDER_HPP + diff --git a/src/osc/ingen_osc.cpp b/src/osc/ingen_osc_server.cpp index 4a8946c0..fd4455f0 100644 --- a/src/osc/ingen_osc.cpp +++ b/src/osc/ingen_osc_server.cpp @@ -26,7 +26,7 @@ using namespace std; using namespace Ingen; -struct IngenOSCModule : public Ingen::Shared::Module { +struct IngenOSCServerModule : public Ingen::Shared::Module { void load(Ingen::Shared::World* world) { Server::Engine* engine = (Server::Engine*)world->local_engine().get(); SharedPtr<Server::ServerInterfaceImpl> interface( @@ -47,7 +47,7 @@ extern "C" { Ingen::Shared::Module* ingen_module_load() { - return new IngenOSCModule(); + return new IngenOSCServerModule(); } } // extern "C" |