summaryrefslogtreecommitdiffstats
path: root/src/osc
diff options
context:
space:
mode:
Diffstat (limited to 'src/osc')
-rw-r--r--src/osc/OSCClientReceiver.cpp303
-rw-r--r--src/osc/OSCClientReceiver.hpp90
-rw-r--r--src/osc/OSCClientSender.hpp3
-rw-r--r--src/osc/OSCEngineReceiver.hpp56
-rw-r--r--src/osc/OSCEngineSender.cpp241
-rw-r--r--src/osc/OSCEngineSender.hpp118
-rw-r--r--src/osc/OSCSender.cpp111
-rw-r--r--src/osc/OSCSender.hpp52
-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"