From 43d51948ccae71b8f0a1c1710e25cf36da8d7d7c Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 8 Sep 2006 06:23:25 +0000 Subject: Renamed communications classes for consistency. Removed engine dependency on OSC (mostly). git-svn-id: http://svn.drobilla.net/lad/ingen@120 a436a847-0d15-0410-975c-d299462d15a1 --- src/libs/client/Makefile.am | 8 +- src/libs/client/ModelEngineInterface.h | 1 - src/libs/client/OSCClientReceiver.cpp | 415 +++++++++++++ src/libs/client/OSCClientReceiver.h | 120 ++++ src/libs/client/OSCEngineInterface.cpp | 348 ----------- src/libs/client/OSCEngineInterface.h | 145 ----- src/libs/client/OSCEngineSender.cpp | 348 +++++++++++ src/libs/client/OSCEngineSender.h | 145 +++++ src/libs/client/OSCListener.cpp | 415 ------------- src/libs/client/OSCListener.h | 121 ---- src/libs/client/OSCModelEngineInterface.cpp | 4 +- src/libs/client/OSCModelEngineInterface.h | 13 +- src/libs/client/Store.cpp | 2 +- src/libs/engine/ClientBroadcaster.cpp | 12 +- src/libs/engine/Engine.cpp | 10 +- src/libs/engine/Engine.h | 9 +- src/libs/engine/EventSource.h | 7 +- src/libs/engine/JackAudioDriver.cpp | 50 +- src/libs/engine/JackAudioDriver.h | 2 - src/libs/engine/Makefile.am | 8 +- src/libs/engine/OSCClient.cpp | 500 --------------- src/libs/engine/OSCClient.h | 131 ---- src/libs/engine/OSCClientSender.cpp | 487 +++++++++++++++ src/libs/engine/OSCClientSender.h | 131 ++++ src/libs/engine/OSCEngineReceiver.cpp | 909 ++++++++++++++++++++++++++++ src/libs/engine/OSCEngineReceiver.h | 129 ++++ src/libs/engine/OSCReceiver.cpp | 909 ---------------------------- src/libs/engine/OSCReceiver.h | 129 ---- src/libs/engine/QueuedEventSource.cpp | 36 +- src/libs/engine/QueuedEventSource.h | 9 +- 30 files changed, 2761 insertions(+), 2792 deletions(-) create mode 100644 src/libs/client/OSCClientReceiver.cpp create mode 100644 src/libs/client/OSCClientReceiver.h delete mode 100644 src/libs/client/OSCEngineInterface.cpp delete mode 100644 src/libs/client/OSCEngineInterface.h create mode 100644 src/libs/client/OSCEngineSender.cpp create mode 100644 src/libs/client/OSCEngineSender.h delete mode 100644 src/libs/client/OSCListener.cpp delete mode 100644 src/libs/client/OSCListener.h delete mode 100644 src/libs/engine/OSCClient.cpp delete mode 100644 src/libs/engine/OSCClient.h create mode 100644 src/libs/engine/OSCClientSender.cpp create mode 100644 src/libs/engine/OSCClientSender.h create mode 100644 src/libs/engine/OSCEngineReceiver.cpp create mode 100644 src/libs/engine/OSCEngineReceiver.h delete mode 100644 src/libs/engine/OSCReceiver.cpp delete mode 100644 src/libs/engine/OSCReceiver.h (limited to 'src/libs') diff --git a/src/libs/client/Makefile.am b/src/libs/client/Makefile.am index 01cff357..f8a9a1cf 100644 --- a/src/libs/client/Makefile.am +++ b/src/libs/client/Makefile.am @@ -7,12 +7,12 @@ libomclient_la_CXXFLAGS = -I$(top_srcdir)/src/common -DPKGDATADIR=\"$(pkgdatadir libomclient_la_SOURCES = \ ClientInterface.h \ - OSCEngineInterface.h \ - OSCEngineInterface.cpp \ + OSCEngineSender.h \ + OSCEngineSender.cpp \ OSCModelEngineInterface.h \ OSCModelEngineInterface.cpp \ - OSCListener.h \ - OSCListener.cpp \ + OSCClientReceiver.h \ + OSCClientReceiver.cpp \ SigClientInterface.h \ DirectSigClientInterface.h \ ThreadedSigClientInterface.h \ diff --git a/src/libs/client/ModelEngineInterface.h b/src/libs/client/ModelEngineInterface.h index 763cdc69..2bc5db14 100644 --- a/src/libs/client/ModelEngineInterface.h +++ b/src/libs/client/ModelEngineInterface.h @@ -31,7 +31,6 @@ namespace Client { class NodeModel; class PresetModel; class PatchModel; -class OSCListener; class ModelClientInterface; diff --git a/src/libs/client/OSCClientReceiver.cpp b/src/libs/client/OSCClientReceiver.cpp new file mode 100644 index 00000000..56d8ea14 --- /dev/null +++ b/src/libs/client/OSCClientReceiver.cpp @@ -0,0 +1,415 @@ +/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. + * + * 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 "OSCClientReceiver.h" +//#include "NodeModel.h" +//#include "PluginModel.h" +#include +#include +#include +#include +using std::cerr; using std::cout; using std::endl; + +namespace Ingen { +namespace Client { + + +/** Construct a OSCClientReceiver with a user-provided ModelClientInterface object for notification + * of engine events. + */ +OSCClientReceiver::OSCClientReceiver(int listen_port) +: _listen_port(listen_port), + _st(NULL)//, +// _receiving_node(false), +// _receiving_node_model(NULL), +// _receiving_node_num_ports(0), +// _num_received_ports(0) +{ + start(); +} + + +OSCClientReceiver::~OSCClientReceiver() +{ + stop(); +} + + +void +OSCClientReceiver::start() +{ + if (_st != NULL) + return; + + if (_listen_port == 0) { + _st = lo_server_thread_new(NULL, error_cb); + _listen_port = lo_server_thread_get_port(_st); + } else { + char port_str[8]; + snprintf(port_str, 8, "%d", _listen_port); + _st = lo_server_thread_new(port_str, error_cb); + } + + if (_st == NULL) { + cerr << "[OSCClientReceiver] Could not start OSC listener. Aborting." << endl; + exit(EXIT_FAILURE); + } else { + cout << "[OSCClientReceiver] Started OSC listener on port " << lo_server_thread_get_port(_st) << endl; + } + + // FIXME + lo_server_thread_add_method(_st, NULL, NULL, generic_cb, NULL); + + //lo_server_thread_add_method(_st, "/om/response/ok", "i", om_response_ok_cb, this); + //lo_server_thread_add_method(_st, "/om/response/error", "is", om_responseerror_cb, this); + + 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("[OSCMsg] %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"); + + /*for (int i=0; i < argc; ++i) { + printf(" '%c' ", types[i]); + lo_arg_pp(lo_type(types[i]), argv[i]); + printf("\n"); + } + printf("\n");*/ + + return 1; // not handled +} + + +void +OSCClientReceiver::error_cb(int num, const char* msg, const char* path) +{ + cerr << "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) +{ + string msg = "Received unknown OSC message: "; + msg += path; + + cerr << msg << endl; + + return 0; +} + + +void +OSCClientReceiver::setup_callbacks() +{ + lo_server_thread_add_method(_st, "/om/num_plugins", "i", num_plugins_cb, this); + lo_server_thread_add_method(_st, "/om/plugin", "sss", plugin_cb, this); + lo_server_thread_add_method(_st, "/om/new_patch", "si", new_patch_cb, this); + lo_server_thread_add_method(_st, "/om/destroyed", "s", destroyed_cb, this); + lo_server_thread_add_method(_st, "/om/patch_enabled", "s", patch_enabled_cb, this); + lo_server_thread_add_method(_st, "/om/patch_disabled", "s", patch_disabled_cb, this); + lo_server_thread_add_method(_st, "/om/patch_cleared", "s", patch_cleared_cb, this); + lo_server_thread_add_method(_st, "/om/object_renamed", "ss", object_renamed_cb, this); + lo_server_thread_add_method(_st, "/om/new_connection", "ss", connection_cb, this); + lo_server_thread_add_method(_st, "/om/disconnection", "ss", disconnection_cb, this); + lo_server_thread_add_method(_st, "/om/new_node", "sssii", new_node_cb, this); + lo_server_thread_add_method(_st, "/om/new_port", "ssi", new_port_cb, this); + lo_server_thread_add_method(_st, "/om/metadata/update", "sss", metadata_update_cb, this); + lo_server_thread_add_method(_st, "/om/control_change", "sf", control_change_cb, this); + lo_server_thread_add_method(_st, "/om/program_add", "siis", program_add_cb, this); + lo_server_thread_add_method(_st, "/om/program_remove", "sii", program_remove_cb, this); +} + + +/** Catches errors that aren't a direct result of a client request. + */ +int +OSCClientReceiver::m_error_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + cerr << "ERROR: " << argv[0]->s << endl; + // FIXME + //error((char*)argv[0]); + return 0; +} + + +int +OSCClientReceiver::m_new_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + new_patch(&argv[0]->s, argv[1]->i); // path, poly + return 0; +} + + +int +OSCClientReceiver::m_destroyed_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + object_destroyed((const char*)&argv[0]->s); + return 0; +} + + +int +OSCClientReceiver::m_patch_enabled_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + patch_enabled((const char*)&argv[0]->s); + return 0; +} + + +int +OSCClientReceiver::m_patch_disabled_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + patch_disabled((const char*)&argv[0]->s); + return 0; +} + + +int +OSCClientReceiver::m_patch_cleared_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + patch_cleared((const char*)&argv[0]->s); + return 0; +} + + +int +OSCClientReceiver::m_object_renamed_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + object_renamed((const char*)&argv[0]->s, (const char*)&argv[1]->s); + return 0; +} + + +int +OSCClientReceiver::m_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; + + connection(src_port_path, dst_port_path); + + return 0; +} + + +int +OSCClientReceiver::m_disconnection_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* src_port_path = &argv[0]->s; + const char* dst_port_path = &argv[1]->s; + + disconnection(src_port_path, dst_port_path); + + return 0; +} + + +/** Notification of a new node creation. + */ +int +OSCClientReceiver::m_new_node_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* type = &argv[0]->s; + const char* uri = &argv[1]->s; + const char* node_path = &argv[2]->s; + const int32_t poly = argv[3]->i; + const int32_t num_ports = argv[4]->i; + + new_node(type, uri, node_path, poly, num_ports); + + /*_receiving_node_model = new NodeModel(node_path); + _receiving_node_model->polyphonic((poly == 1)); + _receiving_node_num_ports = num_ports; + + PluginModel* pi = new PluginModel(type, uri); + _receiving_node_model->plugin(pi); + + _receiving_node = true; + _num_received_ports = 0; + */ + return 0; +} + + +/** Notification of a new port creation. + */ +int +OSCClientReceiver::m_new_port_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* port_path = &argv[0]->s; + const char* type = &argv[1]->s; + bool is_output = (argv[2]->i == 1); + /*const char* direction = &argv[2]->s; + const char* hint = &argv[3]->s; + float default_val = argv[4]->f; + float min_val = argv[5]->f; + float max_val = argv[6]->f;*/ + + new_port(port_path, type, is_output); +#if 0 + PortModel::Type ptype = PortModel::CONTROL; + if (!strcmp(type, "AUDIO")) ptype = PortModel::AUDIO; + else if (!strcmp(type, "CONTROL")) ptype = PortModel::CONTROL; + else if (!strcmp(type, "MIDI")) ptype = PortModel::MIDI; + else cerr << "[OSCClientReceiver] WARNING: Unknown port type received (" << type << ")" << endl; + +#if 0 + PortModel::Direction pdir = PortModel::INPUT; + if (!strcmp(direction, "INPUT")) pdir = PortModel::INPUT; + else if (!strcmp(direction, "OUTPUT")) pdir = PortModel::OUTPUT; + else cerr << "[OSCClientReceiver] WARNING: Unknown port direction received (" << direction << ")" << endl; +#endif + PortModel::Direction pdir = is_output ? PortModel::OUTPUT : PortModel::INPUT; + +/* + PortModel::Hint phint = PortModel::NONE; + if (!strcmp(hint, "LOGARITHMIC")) phint = PortModel::LOGARITHMIC; + else if (!strcmp(hint, "INTEGER")) phint = PortModel::INTEGER; + else if (!strcmp(hint, "TOGGLE")) phint = PortModel::TOGGLE; + + PortModel* port_model = new PortModel(port_path, ptype, pdir, phint, default_val, min_val, max_val); +*/ + PortModel* port_model = new PortModel(port_path, ptype, pdir); + if (m_receiving_node) { + assert(m_receiving_node_model); + m_receiving_node_model->add_port(port_model); + ++m_num_received_ports; + + // If transmission is done, send new node to client + if (m_num_received_ports == m_receiving_node_num_ports) { + new_node_model(m_receiving_node_model); + m_receiving_node = false; + m_receiving_node_model = NULL; + m_num_received_ports = 0; + } + } else { + new_port_model(port_model); + } + +#endif + return 0; +} + + +/** Notification of a new or updated piece of metadata. + */ +int +OSCClientReceiver::m_metadata_update_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* obj_path = &argv[0]->s; + const char* key = &argv[1]->s; + const char* value = &argv[2]->s; + + metadata_update(obj_path, key, value); + + return 0; +} + + +int +OSCClientReceiver::m_control_change_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* const port_path = &argv[0]->s; + const float value = argv[1]->f; + + control_change(port_path, value); + + return 0; +} + + +/** Number of plugins in engine, should precede /om/plugin messages in response + * to a /om/send_plugins + */ +int +OSCClientReceiver::m_num_plugins_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + /** Not worth it implementing a ModelClientInterface callback for this (?) + * Or I'm just lazy. FIXME? */ + num_plugins(argv[0]->i); + + return 0; +} + + +/** A plugin info response from the server, in response to a /send_plugins + */ +int +OSCClientReceiver::m_plugin_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + assert(argc == 3 && !strcmp(types, "sss")); + new_plugin(&argv[0]->s, &argv[1]->s, &argv[2]->s); // type, uri + + return 0; +} + + +int +OSCClientReceiver::m_program_add_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* node_path = &argv[0]->s; + int32_t bank = argv[1]->i; + int32_t program = argv[2]->i; + const char* name = &argv[3]->s; + + program_add(node_path, bank, program, name); + + return 0; +} + + +int +OSCClientReceiver::m_program_remove_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* node_path = &argv[0]->s; + int32_t bank = argv[1]->i; + int32_t program = argv[2]->i; + + program_remove(node_path, bank, program); + + return 0; +} + + +} // namespace Client +} // namespace Ingen diff --git a/src/libs/client/OSCClientReceiver.h b/src/libs/client/OSCClientReceiver.h new file mode 100644 index 00000000..60fa9495 --- /dev/null +++ b/src/libs/client/OSCClientReceiver.h @@ -0,0 +1,120 @@ +/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. + * + * 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 OSCCLIENTRECEIVER_H +#define OSCCLIENTRECEIVER_H + +#include +#include +#include "interface/ClientInterface.h" + +namespace Ingen { + +/** Client library */ +namespace Client { + +//class NodeModel; +//class PresetModel; + +/* 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 m_##name##_cb (LO_HANDLER_ARGS);\ +inline static int name##_cb(LO_HANDLER_ARGS, void* osc_listener)\ +{ return ((OSCClientReceiver*)osc_listener)->m_##name##_cb(path, types, argv, argc, msg); } + + +/** Callbacks for "notification band" OSC messages. + * + * Receives all notification of engine state, but not replies on the "control + * band". See OSC namespace documentation for details. + * + * Right now this class and Comm share the same lo_server_thread and the barrier + * between them is a bit odd, but eventually this class will be able to listen + * on a completely different port (ie have it's own lo_server_thread) to allow + * things like listening to the notification band over TCP while sending commands + * on the control band over UDP. + * + * \ingroup IngenClient + */ +class OSCClientReceiver : virtual public Ingen::Shared::ClientInterface +{ +public: + OSCClientReceiver(int listen_port); + ~OSCClientReceiver(); + + void start(); + void stop(); + + int listen_port() { return _listen_port; } + string listen_url() { return lo_server_thread_get_url(_st); } + +private: + // Prevent copies + OSCClientReceiver(const OSCClientReceiver& copy); + OSCClientReceiver& operator=(const OSCClientReceiver& copy); + + void setup_callbacks(); + + static void 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); + /* + inline static int om_response_ok_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* comm); + int m_om_response_ok_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data); + inline static int om_response_error_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* comm); + int m_om_response_error_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data); +*/ + int _listen_port; + lo_server_thread _st; + + // Used for receiving nodes - multiple messages are received before + // sending an event to the client (via ModelClientInterface) + //bool _receiving_node; + //NodeModel* _receiving_node_model; + //int32_t _receiving_node_num_ports; + //int32_t _num_received_ports; + + LO_HANDLER(error); + LO_HANDLER(num_plugins); + LO_HANDLER(plugin); + LO_HANDLER(plugin_list_end); + LO_HANDLER(new_patch); + LO_HANDLER(destroyed); + LO_HANDLER(patch_enabled); + LO_HANDLER(patch_disabled); + LO_HANDLER(patch_cleared); + LO_HANDLER(object_renamed); + LO_HANDLER(connection); + LO_HANDLER(disconnection); + LO_HANDLER(new_node); + LO_HANDLER(new_port); + LO_HANDLER(metadata_update); + LO_HANDLER(control_change); + LO_HANDLER(program_add); + LO_HANDLER(program_remove); +}; + + +} // namespace Client + +} // namespace Ingen + +#endif // OSCCLIENTRECEIVER_H diff --git a/src/libs/client/OSCEngineInterface.cpp b/src/libs/client/OSCEngineInterface.cpp deleted file mode 100644 index b747e2e4..00000000 --- a/src/libs/client/OSCEngineInterface.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. - * - * 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 "OSCEngineInterface.h" -#include "interface/ClientKey.h" - -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 :/ - */ -OSCEngineInterface::OSCEngineInterface(const string& engine_url) -: _engine_url(engine_url) -, _engine_addr(lo_address_new_from_url(engine_url.c_str())) -, _id(0) -{ -} - - -OSCEngineInterface::~OSCEngineInterface() -{ - lo_address_free(_engine_addr); -} - - -/* *** EngineInterface implementation below here *** */ - - -/** Register with the engine via OSC. - * - * Note that this does not actually use 'key', 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 EngineInterface. - */ -void -OSCEngineInterface::register_client(ClientKey key, CountedPtr client) -{ - // FIXME: use parameters.. er, somehow. - assert(_engine_addr); - lo_send(_engine_addr, "/om/engine/register_client", "i", next_id()); -} - - -void -OSCEngineInterface::unregister_client(ClientKey key) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/engine/unregister_client", "i", next_id()); -} - - - -// Engine commands -void -OSCEngineInterface::load_plugins() -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/engine/load_plugins", "i", next_id()); -} - - -void -OSCEngineInterface::activate() -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/engine/activate", "i", next_id()); -} - - -void -OSCEngineInterface::deactivate() -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/engine/deactivate", "i", next_id()); -} - - -void -OSCEngineInterface::quit() -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/engine/quit", "i", next_id()); -} - - - -// Object commands - -void -OSCEngineInterface::create_patch(const string& path, - uint32_t poly) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/create_patch", "isi", - next_id(), - path.c_str(), - poly); -} - - -void -OSCEngineInterface::create_port(const string& path, - const string& data_type, - bool is_output) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/create_port", "issi", - next_id(), - path.c_str(), - data_type.c_str(), - (is_output ? 1 : 0)); -} - - -void -OSCEngineInterface::create_node(const string& path, - const string& plugin_type, - const string& plugin_uri, - bool polyphonic) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/create_node", "isssi", - next_id(), - path.c_str(), - plugin_type.c_str(), - plugin_uri.c_str(), - (polyphonic ? 1 : 0)); -} - - -void -OSCEngineInterface::rename(const string& old_path, - const string& new_name) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/rename", "iss", - next_id(), - old_path.c_str(), - new_name.c_str()); -} - - -void -OSCEngineInterface::destroy(const string& path) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/destroy", "is", - next_id(), - path.c_str()); -} - - -void -OSCEngineInterface::clear_patch(const string& patch_path) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/clear_patch", "is", - next_id(), - patch_path.c_str()); -} - - -void -OSCEngineInterface::enable_patch(const string& patch_path) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/enable_patch", "is", - next_id(), - patch_path.c_str()); -} - - -void -OSCEngineInterface::disable_patch(const string& patch_path) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/disable_patch", "is", - next_id(), - patch_path.c_str()); -} - - -void -OSCEngineInterface::connect(const string& src_port_path, - const string& dst_port_path) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/connect", "iss", - next_id(), - src_port_path.c_str(), - dst_port_path.c_str()); -} - - -void -OSCEngineInterface::disconnect(const string& src_port_path, - const string& dst_port_path) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/disconnect", "iss", - next_id(), - src_port_path.c_str(), - dst_port_path.c_str()); -} - - -void -OSCEngineInterface::disconnect_all(const string& node_path) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/disconnect_all", "is", - next_id(), - node_path.c_str()); -} - - -void -OSCEngineInterface::set_port_value(const string& port_path, - float val) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/set_port_value", "isf", - next_id(), - port_path.c_str(), - val); -} - - -void -OSCEngineInterface::set_port_value(const string& port_path, - uint32_t voice, - float val) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/set_port_value", "isif", - next_id(), - port_path.c_str(), - voice, - val); -} - - -void -OSCEngineInterface::set_port_value_queued(const string& port_path, - float val) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/set_port_value_queued", "isf", - next_id(), - port_path.c_str(), - val); -} - - -void -OSCEngineInterface::set_program(const string& node_path, - uint32_t bank, - uint32_t program) -{ - assert(_engine_addr); - lo_send(_engine_addr, - (string("/dssi") + node_path + "/program").c_str(), - "ii", - bank, - program); -} - - -void -OSCEngineInterface::midi_learn(const string& node_path) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/synth/midi_learn", "is", - next_id(), - node_path.c_str()); -} - - -void -OSCEngineInterface::set_metadata(const string& obj_path, - const string& predicate, - const string& value) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/metadata/set", "isss", - next_id(), - obj_path.c_str(), - predicate.c_str(), - value.c_str()); -} - - -// Requests // - -void -OSCEngineInterface::ping() -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/ping", "i", next_id()); -} - - -void -OSCEngineInterface::request_port_value(const string& port_path) -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/request/port_value", "is", - next_id(), - port_path.c_str()); -} - - -void -OSCEngineInterface::request_plugins() -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/request/plugins", "i", next_id()); -} - - -void -OSCEngineInterface::request_all_objects() -{ - assert(_engine_addr); - lo_send(_engine_addr, "/om/request/all_objects", "i", next_id()); -} - - - -} // namespace Client -} // namespace Ingen - - diff --git a/src/libs/client/OSCEngineInterface.h b/src/libs/client/OSCEngineInterface.h deleted file mode 100644 index 4405438c..00000000 --- a/src/libs/client/OSCEngineInterface.h +++ /dev/null @@ -1,145 +0,0 @@ -/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. - * - * 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 OSCENGINEINTERFACE_H -#define OSCENGINEINTERFACE_H - -#include -#include -#include -#include "interface/EngineInterface.h" -using std::string; -using Ingen::Shared::EngineInterface; -using Ingen::Shared::ClientInterface; -using Ingen::Shared::ClientKey; - - -namespace Ingen { -namespace Client { - -/* OSC (via liblo) interface to the engine. - * - * Clients can use this opaquely as an EngineInterface* to control the engine - * over OSC (whether over a network or not, etc). - * - * \ingroup IngenClient - */ -class OSCEngineInterface : public EngineInterface -{ -public: - OSCEngineInterface(const string& engine_url); - - ~OSCEngineInterface(); - - string engine_url() { return _engine_url; } - - inline size_t next_id() - { if (_id != -1) { _id = (_id == -2) ? 0 : _id+1; } return _id; } - - void enable_responses() { _id = 0; } - void disable_responses() { _id = -1; } - - - /* *** EngineInterface implementation below here *** */ - - // Client registration - void register_client(ClientKey key, CountedPtr client); - void unregister_client(ClientKey key); - - - // Engine commands - void load_plugins(); - void activate(); - void deactivate(); - void quit(); - - - // Object commands - - void create_patch(const string& path, - uint32_t poly); - - void create_port(const string& path, - const string& data_type, - bool is_output); - - void create_node(const string& path, - const string& plugin_type, - const string& plugin_uri, - bool polyphonic); - - void rename(const string& old_path, - const string& new_name); - - void destroy(const string& path); - - void clear_patch(const string& patch_path); - - void enable_patch(const string& patch_path); - - void disable_patch(const string& patch_path); - - void connect(const string& src_port_path, - const string& dst_port_path); - - void disconnect(const string& src_port_path, - const string& dst_port_path); - - void disconnect_all(const string& node_path); - - void set_port_value(const string& port_path, - float val); - - void set_port_value(const string& port_path, - uint32_t voice, - float val); - - void set_port_value_queued(const string& port_path, - float val); - - void set_program(const string& node_path, - uint32_t bank, - uint32_t program); - - void midi_learn(const string& node_path); - - void set_metadata(const string& obj_path, - const string& predicate, - const string& value); - - // Requests // - - void ping(); - - void request_port_value(const string& port_path); - - void request_plugins(); - - void request_all_objects(); - -protected: - string _engine_url; - lo_address _engine_addr; - int _client_port; - int32_t _id; -}; - - -} // namespace Client -} // namespace Ingen - -#endif // OSCENGINEINTERFACE_H - diff --git a/src/libs/client/OSCEngineSender.cpp b/src/libs/client/OSCEngineSender.cpp new file mode 100644 index 00000000..cc70dc8e --- /dev/null +++ b/src/libs/client/OSCEngineSender.cpp @@ -0,0 +1,348 @@ +/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. + * + * 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 "OSCEngineSender.h" +#include "interface/ClientKey.h" + +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 string& engine_url) +: _engine_url(engine_url) +, _engine_addr(lo_address_new_from_url(engine_url.c_str())) +, _id(0) +{ +} + + +OSCEngineSender::~OSCEngineSender() +{ + lo_address_free(_engine_addr); +} + + +/* *** EngineInterface implementation below here *** */ + + +/** Register with the engine via OSC. + * + * Note that this does not actually use 'key', 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 EngineInterface. + */ +void +OSCEngineSender::register_client(ClientKey key, CountedPtr client) +{ + // FIXME: use parameters.. er, somehow. + assert(_engine_addr); + lo_send(_engine_addr, "/om/engine/register_client", "i", next_id()); +} + + +void +OSCEngineSender::unregister_client(ClientKey key) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/engine/unregister_client", "i", next_id()); +} + + + +// Engine commands +void +OSCEngineSender::load_plugins() +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/engine/load_plugins", "i", next_id()); +} + + +void +OSCEngineSender::activate() +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/engine/activate", "i", next_id()); +} + + +void +OSCEngineSender::deactivate() +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/engine/deactivate", "i", next_id()); +} + + +void +OSCEngineSender::quit() +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/engine/quit", "i", next_id()); +} + + + +// Object commands + +void +OSCEngineSender::create_patch(const string& path, + uint32_t poly) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/create_patch", "isi", + next_id(), + path.c_str(), + poly); +} + + +void +OSCEngineSender::create_port(const string& path, + const string& data_type, + bool is_output) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/create_port", "issi", + next_id(), + path.c_str(), + data_type.c_str(), + (is_output ? 1 : 0)); +} + + +void +OSCEngineSender::create_node(const string& path, + const string& plugin_type, + const string& plugin_uri, + bool polyphonic) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/create_node", "isssi", + next_id(), + path.c_str(), + plugin_type.c_str(), + plugin_uri.c_str(), + (polyphonic ? 1 : 0)); +} + + +void +OSCEngineSender::rename(const string& old_path, + const string& new_name) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/rename", "iss", + next_id(), + old_path.c_str(), + new_name.c_str()); +} + + +void +OSCEngineSender::destroy(const string& path) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/destroy", "is", + next_id(), + path.c_str()); +} + + +void +OSCEngineSender::clear_patch(const string& patch_path) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/clear_patch", "is", + next_id(), + patch_path.c_str()); +} + + +void +OSCEngineSender::enable_patch(const string& patch_path) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/enable_patch", "is", + next_id(), + patch_path.c_str()); +} + + +void +OSCEngineSender::disable_patch(const string& patch_path) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/disable_patch", "is", + next_id(), + patch_path.c_str()); +} + + +void +OSCEngineSender::connect(const string& src_port_path, + const string& dst_port_path) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/connect", "iss", + next_id(), + src_port_path.c_str(), + dst_port_path.c_str()); +} + + +void +OSCEngineSender::disconnect(const string& src_port_path, + const string& dst_port_path) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/disconnect", "iss", + next_id(), + src_port_path.c_str(), + dst_port_path.c_str()); +} + + +void +OSCEngineSender::disconnect_all(const string& node_path) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/disconnect_all", "is", + next_id(), + node_path.c_str()); +} + + +void +OSCEngineSender::set_port_value(const string& port_path, + float val) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/set_port_value", "isf", + next_id(), + port_path.c_str(), + val); +} + + +void +OSCEngineSender::set_port_value(const string& port_path, + uint32_t voice, + float val) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/set_port_value", "isif", + next_id(), + port_path.c_str(), + voice, + val); +} + + +void +OSCEngineSender::set_port_value_queued(const string& port_path, + float val) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/set_port_value_queued", "isf", + next_id(), + port_path.c_str(), + val); +} + + +void +OSCEngineSender::set_program(const string& node_path, + uint32_t bank, + uint32_t program) +{ + assert(_engine_addr); + lo_send(_engine_addr, + (string("/dssi") + node_path + "/program").c_str(), + "ii", + bank, + program); +} + + +void +OSCEngineSender::midi_learn(const string& node_path) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/synth/midi_learn", "is", + next_id(), + node_path.c_str()); +} + + +void +OSCEngineSender::set_metadata(const string& obj_path, + const string& predicate, + const string& value) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/metadata/set", "isss", + next_id(), + obj_path.c_str(), + predicate.c_str(), + value.c_str()); +} + + +// Requests // + +void +OSCEngineSender::ping() +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/ping", "i", next_id()); +} + + +void +OSCEngineSender::request_port_value(const string& port_path) +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/request/port_value", "is", + next_id(), + port_path.c_str()); +} + + +void +OSCEngineSender::request_plugins() +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/request/plugins", "i", next_id()); +} + + +void +OSCEngineSender::request_all_objects() +{ + assert(_engine_addr); + lo_send(_engine_addr, "/om/request/all_objects", "i", next_id()); +} + + + +} // namespace Client +} // namespace Ingen + + diff --git a/src/libs/client/OSCEngineSender.h b/src/libs/client/OSCEngineSender.h new file mode 100644 index 00000000..184c0569 --- /dev/null +++ b/src/libs/client/OSCEngineSender.h @@ -0,0 +1,145 @@ +/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. + * + * 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 OSCENGINESENDER_H +#define OSCENGINESENDER_H + +#include +#include +#include +#include "interface/EngineInterface.h" +using std::string; +using Ingen::Shared::EngineInterface; +using Ingen::Shared::ClientInterface; +using Ingen::Shared::ClientKey; + + +namespace Ingen { +namespace Client { + +/* OSC (via liblo) interface to the engine. + * + * Clients can use this opaquely as an EngineInterface* to control the engine + * over OSC (whether over a network or not, etc). + * + * \ingroup IngenClient + */ +class OSCEngineSender : public EngineInterface +{ +public: + OSCEngineSender(const string& engine_url); + + ~OSCEngineSender(); + + string engine_url() { return _engine_url; } + + inline size_t next_id() + { if (_id != -1) { _id = (_id == -2) ? 0 : _id+1; } return _id; } + + void enable_responses() { _id = 0; } + void disable_responses() { _id = -1; } + + + /* *** EngineInterface implementation below here *** */ + + // Client registration + void register_client(ClientKey key, CountedPtr client); + void unregister_client(ClientKey key); + + + // Engine commands + void load_plugins(); + void activate(); + void deactivate(); + void quit(); + + + // Object commands + + void create_patch(const string& path, + uint32_t poly); + + void create_port(const string& path, + const string& data_type, + bool is_output); + + void create_node(const string& path, + const string& plugin_type, + const string& plugin_uri, + bool polyphonic); + + void rename(const string& old_path, + const string& new_name); + + void destroy(const string& path); + + void clear_patch(const string& patch_path); + + void enable_patch(const string& patch_path); + + void disable_patch(const string& patch_path); + + void connect(const string& src_port_path, + const string& dst_port_path); + + void disconnect(const string& src_port_path, + const string& dst_port_path); + + void disconnect_all(const string& node_path); + + void set_port_value(const string& port_path, + float val); + + void set_port_value(const string& port_path, + uint32_t voice, + float val); + + void set_port_value_queued(const string& port_path, + float val); + + void set_program(const string& node_path, + uint32_t bank, + uint32_t program); + + void midi_learn(const string& node_path); + + void set_metadata(const string& obj_path, + const string& predicate, + const string& value); + + // Requests // + + void ping(); + + void request_port_value(const string& port_path); + + void request_plugins(); + + void request_all_objects(); + +protected: + string _engine_url; + lo_address _engine_addr; + int _client_port; + int32_t _id; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // OSCENGINESENDER_H + diff --git a/src/libs/client/OSCListener.cpp b/src/libs/client/OSCListener.cpp deleted file mode 100644 index c63ffd85..00000000 --- a/src/libs/client/OSCListener.cpp +++ /dev/null @@ -1,415 +0,0 @@ -/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. - * - * 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 "OSCListener.h" -//#include "NodeModel.h" -//#include "PluginModel.h" -#include -#include -#include -#include -using std::cerr; using std::cout; using std::endl; - -namespace Ingen { -namespace Client { - - -/** Construct a OSCListener with a user-provided ModelClientInterface object for notification - * of engine events. - */ -OSCListener::OSCListener(int listen_port) -: _listen_port(listen_port), - _st(NULL)//, -// _receiving_node(false), -// _receiving_node_model(NULL), -// _receiving_node_num_ports(0), -// _num_received_ports(0) -{ - start(); -} - - -OSCListener::~OSCListener() -{ - stop(); -} - - -void -OSCListener::start() -{ - if (_st != NULL) - return; - - if (_listen_port == 0) { - _st = lo_server_thread_new(NULL, error_cb); - _listen_port = lo_server_thread_get_port(_st); - } else { - char port_str[8]; - snprintf(port_str, 8, "%d", _listen_port); - _st = lo_server_thread_new(port_str, error_cb); - } - - if (_st == NULL) { - cerr << "[OSCListener] Could not start OSC listener. Aborting." << endl; - exit(EXIT_FAILURE); - } else { - cout << "[OSCListener] Started OSC listener on port " << lo_server_thread_get_port(_st) << endl; - } - - // FIXME - lo_server_thread_add_method(_st, NULL, NULL, generic_cb, NULL); - - //lo_server_thread_add_method(_st, "/om/response/ok", "i", om_response_ok_cb, this); - //lo_server_thread_add_method(_st, "/om/response/error", "is", om_responseerror_cb, this); - - 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 -OSCListener::stop() -{ - if (_st != NULL) { - //unregister_client(); - lo_server_thread_free(_st); - _st = NULL; - } -} - - -int -OSCListener::generic_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data) -{ - printf("[OSCMsg] %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"); - - /*for (int i=0; i < argc; ++i) { - printf(" '%c' ", types[i]); - lo_arg_pp(lo_type(types[i]), argv[i]); - printf("\n"); - } - printf("\n");*/ - - return 1; // not handled -} - - -void -OSCListener::error_cb(int num, const char* msg, const char* path) -{ - cerr << "Got error from server: " << msg << endl; -} - - - -int -OSCListener::unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data) -{ - string msg = "Received unknown OSC message: "; - msg += path; - - cerr << msg << endl; - - return 0; -} - - -void -OSCListener::setup_callbacks() -{ - lo_server_thread_add_method(_st, "/om/num_plugins", "i", num_plugins_cb, this); - lo_server_thread_add_method(_st, "/om/plugin", "sss", plugin_cb, this); - lo_server_thread_add_method(_st, "/om/new_patch", "si", new_patch_cb, this); - lo_server_thread_add_method(_st, "/om/destroyed", "s", destroyed_cb, this); - lo_server_thread_add_method(_st, "/om/patch_enabled", "s", patch_enabled_cb, this); - lo_server_thread_add_method(_st, "/om/patch_disabled", "s", patch_disabled_cb, this); - lo_server_thread_add_method(_st, "/om/patch_cleared", "s", patch_cleared_cb, this); - lo_server_thread_add_method(_st, "/om/object_renamed", "ss", object_renamed_cb, this); - lo_server_thread_add_method(_st, "/om/new_connection", "ss", connection_cb, this); - lo_server_thread_add_method(_st, "/om/disconnection", "ss", disconnection_cb, this); - lo_server_thread_add_method(_st, "/om/new_node", "sssii", new_node_cb, this); - lo_server_thread_add_method(_st, "/om/new_port", "ssi", new_port_cb, this); - lo_server_thread_add_method(_st, "/om/metadata/update", "sss", metadata_update_cb, this); - lo_server_thread_add_method(_st, "/om/control_change", "sf", control_change_cb, this); - lo_server_thread_add_method(_st, "/om/program_add", "siis", program_add_cb, this); - lo_server_thread_add_method(_st, "/om/program_remove", "sii", program_remove_cb, this); -} - - -/** Catches errors that aren't a direct result of a client request. - */ -int -OSCListener::m_error_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - cerr << "ERROR: " << argv[0]->s << endl; - // FIXME - //error((char*)argv[0]); - return 0; -} - - -int -OSCListener::m_new_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - new_patch(&argv[0]->s, argv[1]->i); // path, poly - return 0; -} - - -int -OSCListener::m_destroyed_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - object_destroyed((const char*)&argv[0]->s); - return 0; -} - - -int -OSCListener::m_patch_enabled_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - patch_enabled((const char*)&argv[0]->s); - return 0; -} - - -int -OSCListener::m_patch_disabled_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - patch_disabled((const char*)&argv[0]->s); - return 0; -} - - -int -OSCListener::m_patch_cleared_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - patch_cleared((const char*)&argv[0]->s); - return 0; -} - - -int -OSCListener::m_object_renamed_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - object_renamed((const char*)&argv[0]->s, (const char*)&argv[1]->s); - return 0; -} - - -int -OSCListener::m_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; - - connection(src_port_path, dst_port_path); - - return 0; -} - - -int -OSCListener::m_disconnection_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* src_port_path = &argv[0]->s; - const char* dst_port_path = &argv[1]->s; - - disconnection(src_port_path, dst_port_path); - - return 0; -} - - -/** Notification of a new node creation. - */ -int -OSCListener::m_new_node_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* type = &argv[0]->s; - const char* uri = &argv[1]->s; - const char* node_path = &argv[2]->s; - const int32_t poly = argv[3]->i; - const int32_t num_ports = argv[4]->i; - - new_node(type, uri, node_path, poly, num_ports); - - /*_receiving_node_model = new NodeModel(node_path); - _receiving_node_model->polyphonic((poly == 1)); - _receiving_node_num_ports = num_ports; - - PluginModel* pi = new PluginModel(type, uri); - _receiving_node_model->plugin(pi); - - _receiving_node = true; - _num_received_ports = 0; - */ - return 0; -} - - -/** Notification of a new port creation. - */ -int -OSCListener::m_new_port_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* port_path = &argv[0]->s; - const char* type = &argv[1]->s; - bool is_output = (argv[2]->i == 1); - /*const char* direction = &argv[2]->s; - const char* hint = &argv[3]->s; - float default_val = argv[4]->f; - float min_val = argv[5]->f; - float max_val = argv[6]->f;*/ - - new_port(port_path, type, is_output); -#if 0 - PortModel::Type ptype = PortModel::CONTROL; - if (!strcmp(type, "AUDIO")) ptype = PortModel::AUDIO; - else if (!strcmp(type, "CONTROL")) ptype = PortModel::CONTROL; - else if (!strcmp(type, "MIDI")) ptype = PortModel::MIDI; - else cerr << "[OSCListener] WARNING: Unknown port type received (" << type << ")" << endl; - -#if 0 - PortModel::Direction pdir = PortModel::INPUT; - if (!strcmp(direction, "INPUT")) pdir = PortModel::INPUT; - else if (!strcmp(direction, "OUTPUT")) pdir = PortModel::OUTPUT; - else cerr << "[OSCListener] WARNING: Unknown port direction received (" << direction << ")" << endl; -#endif - PortModel::Direction pdir = is_output ? PortModel::OUTPUT : PortModel::INPUT; - -/* - PortModel::Hint phint = PortModel::NONE; - if (!strcmp(hint, "LOGARITHMIC")) phint = PortModel::LOGARITHMIC; - else if (!strcmp(hint, "INTEGER")) phint = PortModel::INTEGER; - else if (!strcmp(hint, "TOGGLE")) phint = PortModel::TOGGLE; - - PortModel* port_model = new PortModel(port_path, ptype, pdir, phint, default_val, min_val, max_val); -*/ - PortModel* port_model = new PortModel(port_path, ptype, pdir); - if (m_receiving_node) { - assert(m_receiving_node_model); - m_receiving_node_model->add_port(port_model); - ++m_num_received_ports; - - // If transmission is done, send new node to client - if (m_num_received_ports == m_receiving_node_num_ports) { - new_node_model(m_receiving_node_model); - m_receiving_node = false; - m_receiving_node_model = NULL; - m_num_received_ports = 0; - } - } else { - new_port_model(port_model); - } - -#endif - return 0; -} - - -/** Notification of a new or updated piece of metadata. - */ -int -OSCListener::m_metadata_update_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* obj_path = &argv[0]->s; - const char* key = &argv[1]->s; - const char* value = &argv[2]->s; - - metadata_update(obj_path, key, value); - - return 0; -} - - -int -OSCListener::m_control_change_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* const port_path = &argv[0]->s; - const float value = argv[1]->f; - - control_change(port_path, value); - - return 0; -} - - -/** Number of plugins in engine, should precede /om/plugin messages in response - * to a /om/send_plugins - */ -int -OSCListener::m_num_plugins_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - /** Not worth it implementing a ModelClientInterface callback for this (?) - * Or I'm just lazy. FIXME? */ - num_plugins(argv[0]->i); - - return 0; -} - - -/** A plugin info response from the server, in response to a /send_plugins - */ -int -OSCListener::m_plugin_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - assert(argc == 3 && !strcmp(types, "sss")); - new_plugin(&argv[0]->s, &argv[1]->s, &argv[2]->s); // type, uri - - return 0; -} - - -int -OSCListener::m_program_add_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* node_path = &argv[0]->s; - int32_t bank = argv[1]->i; - int32_t program = argv[2]->i; - const char* name = &argv[3]->s; - - program_add(node_path, bank, program, name); - - return 0; -} - - -int -OSCListener::m_program_remove_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* node_path = &argv[0]->s; - int32_t bank = argv[1]->i; - int32_t program = argv[2]->i; - - program_remove(node_path, bank, program); - - return 0; -} - - -} // namespace Client -} // namespace Ingen diff --git a/src/libs/client/OSCListener.h b/src/libs/client/OSCListener.h deleted file mode 100644 index 3e9d165d..00000000 --- a/src/libs/client/OSCListener.h +++ /dev/null @@ -1,121 +0,0 @@ -/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. - * - * 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 OSCLISTENER_H -#define OSCLISTENER_H - -#include -#include -#include "interface/ClientInterface.h" - -namespace Ingen { - -/** Client library */ -namespace Client { - -//class NodeModel; -//class PresetModel; - -/* 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 m_##name##_cb (LO_HANDLER_ARGS);\ -inline static int name##_cb(LO_HANDLER_ARGS, void* osc_listener)\ -{ return ((OSCListener*)osc_listener)->m_##name##_cb(path, types, argv, argc, msg); } - - -/** Callbacks for "notification band" OSC messages. - * - * Receives all notification of engine state, but not replies on the "control - * band". See OSC namespace documentation for details. - * - * Right now this class and Comm share the same lo_server_thread and the barrier - * between them is a bit odd, but eventually this class will be able to listen - * on a completely different port (ie have it's own lo_server_thread) to allow - * things like listening to the notification band over TCP while sending commands - * on the control band over UDP. - * - * \ingroup IngenClient - */ -class OSCListener : virtual public Ingen::Shared::ClientInterface -{ -public: - OSCListener(int listen_port); - ~OSCListener(); - - void start(); - void stop(); - - int listen_port() { return _listen_port; } - string listen_url() { return lo_server_thread_get_url(_st); } - -private: - // Prevent copies - OSCListener(const OSCListener& copy); - OSCListener& operator=(const OSCListener& copy); - - void setup_callbacks(); - - static void 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); - /* - inline static int om_response_ok_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* comm); - int m_om_response_ok_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data); - inline static int om_response_error_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* comm); - int m_om_response_error_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data); -*/ - int _listen_port; - lo_server_thread _st; - - // Used for receiving nodes - multiple messages are received before - // sending an event to the client (via ModelClientInterface) - //bool _receiving_node; - //NodeModel* _receiving_node_model; - //int32_t _receiving_node_num_ports; - //int32_t _num_received_ports; - - LO_HANDLER(error); - LO_HANDLER(num_plugins); - LO_HANDLER(plugin); - LO_HANDLER(plugin_list_end); - LO_HANDLER(new_patch); - LO_HANDLER(destroyed); - LO_HANDLER(patch_enabled); - LO_HANDLER(patch_disabled); - LO_HANDLER(patch_cleared); - LO_HANDLER(object_renamed); - LO_HANDLER(connection); - LO_HANDLER(disconnection); - LO_HANDLER(new_node); - LO_HANDLER(new_port); - LO_HANDLER(metadata_update); - LO_HANDLER(control_change); - LO_HANDLER(program_add); - LO_HANDLER(program_remove); -}; - - -} // namespace Client - -} // namespace Ingen - -#endif // OSCLISTENER_H diff --git a/src/libs/client/OSCModelEngineInterface.cpp b/src/libs/client/OSCModelEngineInterface.cpp index 2e831705..14f2fe3a 100644 --- a/src/libs/client/OSCModelEngineInterface.cpp +++ b/src/libs/client/OSCModelEngineInterface.cpp @@ -21,7 +21,7 @@ #include #include #include -#include "OSCListener.h" +#include "OSCClientReceiver.h" #include "PatchModel.h" #include "ConnectionModel.h" #include "PresetModel.h" @@ -39,7 +39,7 @@ namespace Client { * of engine events. */ OSCModelEngineInterface::OSCModelEngineInterface(const string& engine_url) -: OSCEngineInterface(engine_url), +: OSCEngineSender(engine_url), m_request_id(0), m_is_attached(false), m_is_registered(false) diff --git a/src/libs/client/OSCModelEngineInterface.h b/src/libs/client/OSCModelEngineInterface.h index a2172e23..f600fb8f 100644 --- a/src/libs/client/OSCModelEngineInterface.h +++ b/src/libs/client/OSCModelEngineInterface.h @@ -21,7 +21,7 @@ #include #include "util/Semaphore.h" #include "interface/EngineInterface.h" -#include "OSCEngineInterface.h" +#include "OSCEngineSender.h" #include "ModelEngineInterface.h" using std::string; @@ -34,29 +34,22 @@ namespace Client { class NodeModel; class PresetModel; class PatchModel; -class OSCListener; class ModelClientInterface; /** Old model-based OSC engine command interface. * * This is an old class from before when the well-defined interfaces between - * engine and client were defined. I've wrapped it around OSCEngineInterface + * engine and client were defined. I've wrapped it around OSCEngineSender * so all the common functions are implemented there, and implemented the * remaining functions using those, for compatibility. Hopefully something * better gets figured out and this can go away completely, but for now this * gets the existing clients working through EngineInterface in the easiest * way possible. This class needs to die. * - * Old comment: - * Handles all OSC communication on the "control band". For the time being, - * manages the OSCListener which handles the "notification band", but this - * will change in the future (for complete separation). See OSC namespace - * documentation for more details. - * * \ingroup IngenClient */ -class OSCModelEngineInterface : public OSCEngineInterface, public ModelEngineInterface +class OSCModelEngineInterface : public OSCEngineSender, public ModelEngineInterface { public: //OSCModelEngineInterface(ModelClientInterface* const client_hooks, const string& engine_url); diff --git a/src/libs/client/Store.cpp b/src/libs/client/Store.cpp index 403264f2..219ea13f 100644 --- a/src/libs/client/Store.cpp +++ b/src/libs/client/Store.cpp @@ -297,7 +297,7 @@ Store::new_port_event(const string& path, const string& type, bool is_output) if (type == "AUDIO") ptype = PortModel::AUDIO; else if (type == "CONTROL") ptype = PortModel::CONTROL; else if (type== "MIDI") ptype = PortModel::MIDI; - else cerr << "[OSCListener] WARNING: Unknown port type received (" << type << ")" << endl; + else cerr << "[Store] WARNING: Unknown port type received (" << type << ")" << endl; PortModel::Direction pdir = is_output ? PortModel::OUTPUT : PortModel::INPUT; diff --git a/src/libs/engine/ClientBroadcaster.cpp b/src/libs/engine/ClientBroadcaster.cpp index 1fa64d9a..355d4232 100644 --- a/src/libs/engine/ClientBroadcaster.cpp +++ b/src/libs/engine/ClientBroadcaster.cpp @@ -30,7 +30,7 @@ #include "ObjectSender.h" #include "interface/ClientKey.h" #include "interface/ClientInterface.h" -#include "OSCClient.h" +#include "OSCClientSender.h" using std::cout; using std::cerr; using std::endl; using Ingen::Shared::ClientInterface; @@ -137,6 +137,10 @@ ClientBroadcaster::send_plugins_to(ClientInterface* client, const list& const Plugin* plugin; + // FIXME FIXME FIXME + OSCClientSender* osc_client = dynamic_cast(client); + assert(osc_client); + lo_timetag tt; lo_timetag_now(&tt); lo_bundle b = lo_bundle_new(tt); @@ -157,16 +161,14 @@ ClientBroadcaster::send_plugins_to(ClientInterface* client, const list& lo_bundle_add_message(b, "/om/plugin", m); msgs.push_back(m); if (lo_bundle_length(b) > 1024) { - // FIXME FIXME FIXME dirty, dirty cast - lo_send_bundle(((OSCClient*)client)->address(), b); + lo_send_bundle(osc_client->address(), b); lo_bundle_free(b); b = lo_bundle_new(tt); } } if (lo_bundle_length(b) > 0) { - // FIXME FIXME FIXME dirty, dirty cast - lo_send_bundle(((OSCClient*)client)->address(), b); + lo_send_bundle(osc_client->address(), b); lo_bundle_free(b); } else { lo_bundle_free(b); diff --git a/src/libs/engine/Engine.cpp b/src/libs/engine/Engine.cpp index a98f4fbf..ebe8e4f7 100644 --- a/src/libs/engine/Engine.cpp +++ b/src/libs/engine/Engine.cpp @@ -24,7 +24,6 @@ #include "util/Queue.h" #include "JackAudioDriver.h" #include "NodeFactory.h" -#include "OSCReceiver.h" #include "ClientBroadcaster.h" #include "Patch.h" #include "ObjectStore.h" @@ -49,9 +48,9 @@ using std::cout; using std::cerr; using std::endl; namespace Ingen { -Engine::Engine(const char* listen_port, AudioDriver* audio_driver) -: m_audio_driver( (audio_driver) ? audio_driver : new JackAudioDriver(*this) ), - m_osc_receiver(new OSCReceiver(*this, pre_processor_queue_size, listen_port)), +Engine::Engine(AudioDriver* audio_driver) +: m_event_source(NULL), + m_audio_driver( (audio_driver) ? audio_driver : new JackAudioDriver(*this) ), #ifdef HAVE_JACK_MIDI m_midi_driver(new JackMidiDriver(((JackAudioDriver*)m_audio_driver)->jack_client())), #elif HAVE_ALSA_MIDI @@ -72,7 +71,6 @@ Engine::Engine(const char* listen_port, AudioDriver* audio_driver) m_quit_flag(false), m_activated(false) { - m_osc_receiver->activate(); } @@ -88,7 +86,6 @@ Engine::~Engine() delete m_object_store; delete m_client_broadcaster; - delete m_osc_receiver; delete m_node_factory; delete m_midi_driver; delete m_audio_driver; @@ -183,7 +180,6 @@ Engine::deactivate() if (m_midi_driver != NULL) m_midi_driver->deactivate(); - m_osc_receiver->deactivate(); m_audio_driver->deactivate(); // Finalize any lingering events (unlikely) diff --git a/src/libs/engine/Engine.h b/src/libs/engine/Engine.h index fbad649f..35192bb5 100644 --- a/src/libs/engine/Engine.h +++ b/src/libs/engine/Engine.h @@ -27,7 +27,6 @@ namespace Ingen { class AudioDriver; class MidiDriver; class NodeFactory; -class OSCReceiver; class ClientBroadcaster; class Patch; class ObjectStore; @@ -51,7 +50,7 @@ template class Driver; class Engine { public: - Engine(const char* listen_port, AudioDriver* audio_driver = 0); + Engine(AudioDriver* audio_driver = 0); ~Engine(); int main(); @@ -63,8 +62,10 @@ public: void activate(); void deactivate(); + void set_event_source(EventSource* es) { m_event_source = es; } + + EventSource* event_source() const { return m_event_source; } AudioDriver* audio_driver() const { return m_audio_driver; } - OSCReceiver* osc_receiver() const { return m_osc_receiver; } MidiDriver* midi_driver() const { return m_midi_driver; } Maid* maid() const { return m_maid; } PostProcessor* post_processor() const { return m_post_processor; } @@ -81,8 +82,8 @@ private: Engine(const Engine&); Engine& operator=(const Engine&); + EventSource* m_event_source; AudioDriver* m_audio_driver; - OSCReceiver* m_osc_receiver; MidiDriver* m_midi_driver; Maid* m_maid; PostProcessor* m_post_processor; diff --git a/src/libs/engine/EventSource.h b/src/libs/engine/EventSource.h index 212249c6..d251cac5 100644 --- a/src/libs/engine/EventSource.h +++ b/src/libs/engine/EventSource.h @@ -17,10 +17,13 @@ #ifndef EVENTSOURCE_H #define EVENTSOURCE_H +#include "types.h" + namespace Ingen { class Event; class QueuedEvent; +class PostProcessor; /** Source for events to run in the audio thread. @@ -41,9 +44,7 @@ public: virtual ~EventSource() {} - virtual Event* pop_earliest_queued_before(const SampleCount time) = 0; - - virtual Event* pop_earliest_stamped_before(const SampleCount time) = 0; + virtual void process(PostProcessor& dest, SampleCount nframes, FrameTime cycle_start, FrameTime cycle_end) = 0; protected: EventSource() {} diff --git a/src/libs/engine/JackAudioDriver.cpp b/src/libs/engine/JackAudioDriver.cpp index 2c0d4a70..66a16d51 100644 --- a/src/libs/engine/JackAudioDriver.cpp +++ b/src/libs/engine/JackAudioDriver.cpp @@ -24,7 +24,6 @@ #include "Event.h" #include "QueuedEvent.h" #include "EventSource.h" -#include "OSCReceiver.h" #include "PostProcessor.h" #include "util/Queue.h" #include "Node.h" @@ -33,6 +32,7 @@ #include "MidiDriver.h" #include "List.h" #include "DuplexPort.h" +#include "EventSource.h" #ifdef HAVE_LASH #include "LashDriver.h" #endif @@ -181,8 +181,6 @@ void JackAudioDriver::deactivate() { if (_is_activated) { - _engine.osc_receiver()->deactivate(); - jack_deactivate(_client); _is_activated = false; @@ -242,48 +240,6 @@ JackAudioDriver::create_port(DuplexPort* patch_port) } -/** Process all the pending events for this cycle. - * - * Called from the realtime thread once every process cycle. - */ -void -JackAudioDriver::process_events(SampleCount nframes, FrameTime cycle_start, FrameTime cycle_end) -{ - Event* ev = NULL; - - /* Limit the maximum number of queued events to process per cycle. This - * makes the process callback (more) realtime-safe by preventing being - * choked by events coming in faster than they can be processed. - * FIXME: run the math on this and figure out a good value */ - const unsigned int MAX_QUEUED_EVENTS = _buffer_size / 100; - - unsigned int num_events_processed = 0; - - // Process the "slow" events first, because it's possible some of the - // RT events depend on them - - /* FIXME: Merge these next two loops into one */ - - // FIXME - while ((ev = _engine.osc_receiver()->pop_earliest_queued_before(cycle_end))) { - ev->execute(nframes, cycle_start, cycle_end); - _engine.post_processor()->push(ev); - if (++num_events_processed > MAX_QUEUED_EVENTS) - break; - } - - while ((ev = _engine.osc_receiver()->pop_earliest_stamped_before(cycle_end))) { - ev->execute(nframes, cycle_start, cycle_end); - _engine.post_processor()->push(ev); - ++num_events_processed; - } - - if (num_events_processed > 0) - _engine.post_processor()->whip(); -} - - - /**** Jack Callbacks ****/ @@ -313,7 +269,9 @@ JackAudioDriver::_process_cb(jack_nframes_t nframes) _transport_state = jack_transport_query(_client, &_position); - process_events(nframes, start_of_last_cycle, start_of_current_cycle); + if (_engine.event_source()) + _engine.event_source()->process(*_engine.post_processor(), nframes, start_of_last_cycle, start_of_current_cycle); + _engine.midi_driver()->prepare_block(start_of_last_cycle, start_of_current_cycle); // Set buffers of patch ports to Jack port buffers (zero-copy processing) diff --git a/src/libs/engine/JackAudioDriver.h b/src/libs/engine/JackAudioDriver.h index aa0cebbc..072434b9 100644 --- a/src/libs/engine/JackAudioDriver.h +++ b/src/libs/engine/JackAudioDriver.h @@ -84,8 +84,6 @@ public: void enable(); void disable(); - void process_events(SampleCount nframes, FrameTime cycle_start, FrameTime cycle_end); - DriverPort* create_port(DuplexPort* patch_port); Patch* root_patch() { return _root_patch; } diff --git a/src/libs/engine/Makefile.am b/src/libs/engine/Makefile.am index bc2560eb..1f3b4d45 100644 --- a/src/libs/engine/Makefile.am +++ b/src/libs/engine/Makefile.am @@ -23,8 +23,8 @@ libingen_la_SOURCES = \ Engine.cpp \ JackAudioDriver.h \ JackAudioDriver.cpp \ - OSCReceiver.h \ - OSCReceiver.cpp \ + OSCEngineReceiver.h \ + OSCEngineReceiver.cpp \ Responder.h \ OSCResponder.h \ OSCResponder.cpp \ @@ -33,8 +33,8 @@ libingen_la_SOURCES = \ ClientBroadcaster.cpp \ ObjectSender.h \ ObjectSender.cpp \ - OSCClient.h \ - OSCClient.cpp \ + OSCClientSender.h \ + OSCClientSender.cpp \ Buffer.h \ Buffer.cpp \ Port.h \ diff --git a/src/libs/engine/OSCClient.cpp b/src/libs/engine/OSCClient.cpp deleted file mode 100644 index acc56cdf..00000000 --- a/src/libs/engine/OSCClient.cpp +++ /dev/null @@ -1,500 +0,0 @@ -/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. - * - * 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 "OSCClient.h" -#include -#include -#include -#include "ObjectStore.h" -#include "NodeFactory.h" -#include "util.h" -#include "Patch.h" -#include "Node.h" -#include "Plugin.h" -#include "TypedPort.h" -#include "Connection.h" -#include "AudioDriver.h" -#include "interface/ClientInterface.h" -#include "Responder.h" - -using std::cout; using std::cerr; using std::endl; - -namespace Ingen { - - -/*! \page client_osc_namespace Client OSC Namespace Documentation - * - *

These are all the messages sent from the engine to the client. - * Communication takes place over two distinct bands: control band and - * notification band.

- *

The control band is where clients send commands, and receive a simple - * response, either OK or an error.

- *

All notifications of engine state (ie new nodes) are sent over the - * notification band which is seperate from the control band. The - * reasoning behind this is that many clients may be connected at the same - * time - a client may receive notifications that are not a direct consequence - * of some message it sent.

- *

The notification band can be thought of as a stream of events representing - * the changing engine state. For example, It is possible for a client to send - * commands and receive aknowledgements, and not listen to the notification band - * at all; or (in the near future anyway) for a client to use UDP for the control - * band (for speed), and TCP for the notification band (for reliability and - * order guarantees).

- * \n\n - */ - - -/* Documentation for namespace portion implemented in Responder.cpp */ - -/** \page client_osc_namespace - * \n - *

Notification Band

- */ - -/** \page client_osc_namespace - *

\b /om/response/ok - Respond successfully to a user command - * \arg \b responder-id (int) - Responder ID this is a response to - *

\n \n - */ - -/** \page client_osc_namespace - *

\b /om/response/error - Respond negatively to a user command - * \arg \b responder-id (int) - Request ID this is a response to - * \arg \b message (string) - Error message (natural language text) - *

\n \n - */ - - - -/** \page client_osc_namespace - * \n - *

Notification Band

- */ - -/** \page client_osc_namespace - *

\b /om/error - Notification that an error has occurred - * \arg \b message (string) - Error message (natural language text) - * - * \li This is for notification of errors that aren't a direct response to a - * user command, ie "unexpected" errors.

\n \n - */ -void -OSCClient::error(const string& msg) -{ - lo_send(_address, "/om/error", "s", msg.c_str()); -} - - -/** \page client_osc_namespace - *

\b /om/num_plugins - * \arg \b num (int) - Number of plugins engine has loaded - * \li This is sent before sending the list of plugins, so the client is aware - * of how many plugins (/om/plugin messages) to expect.

\n \n - */ - - -/** \page client_osc_namespace - *

\b /om/num_plugins - * \arg \b num (int) - Number of plugins engine has loaded - * \li This is sent before sending the list of plugins, so the client is aware - * of how many plugins (/om/plugin messages) to expect.

\n \n - */ -void -OSCClient::num_plugins(uint32_t num) -{ - lo_message m = lo_message_new(); - lo_message_add_int32(m, num); - lo_send_message(_address, "/om/num_plugins", m); -} - - -/** \page client_osc_namespace - *

\b /om/plugin - Notification of the existance of a plugin - * \arg \b type (string) - Type if plugin ("LADSPA", "DSSI", or "Internal") - * \arg \b uri (string) - URI of the plugin (see engine namespace documentation) \n - * \arg \b lib-name (string) - Name of shared library plugin resides in (ie "cmt.so") - * \arg \b plug-label (string) - Label of the plugin (ie "dahdsr_iaoa") - * \arg \b name (string) - Descriptive human-readable name of plugin (ie "ADSR Envelope") - *

\n \n - */ -/* -void -OSCClient::plugins() -{ - Engine::instance().node_factory()->lock_plugin_list(); - - const list& plugs = Engine::instance().node_factory()->plugins(); - const Plugin* plugin; - - lo_timetag tt; - lo_timetag_now(&tt); - lo_bundle b = lo_bundle_new(tt); - lo_message m = lo_message_new(); - list msgs; - - lo_message_add_int32(m, plugs.size()); - lo_bundle_add_message(b, "/om/num_plugins", m); - msgs.push_back(m); - - for (list::const_iterator j = plugs.begin(); j != plugs.end(); ++j) { - plugin = (*j); - m = lo_message_new(); - - lo_message_add_string(m, plugin->type_string()); - lo_message_add_string(m, plugin->uri().c_str()); - lo_message_add_string(m, plugin->plug_label().c_str()); - lo_message_add_string(m, plugin->name().c_str()); - lo_bundle_add_message(b, "/om/plugin", m); - msgs.push_back(m); - if (lo_bundle_length(b) > 1024) { - lo_send_bundle(_address, b); - lo_bundle_free(b); - b = lo_bundle_new(tt); - } - } - - if (lo_bundle_length(b) > 0) { - lo_send_bundle(_address, b); - lo_bundle_free(b); - } else { - lo_bundle_free(b); - } - for (list::const_iterator i = msgs.begin(); i != msgs.end(); ++i) - lo_message_free(*i); - - Engine::instance().node_factory()->unlock_plugin_list(); -} -*/ - -/** \page client_osc_namespace - *

\b /om/new_node - Notification of a new node's creation. - * \arg \b plug-uri (string) - URI of the plugin new node is an instance of - * \arg \b path (string) - Path of the new node - * \arg \b polyphonic (integer-boolean) - Node is polyphonic (1 = yes, 0 = no) - * \arg \b num-ports (integer) - Number of ports (number of new_port messages to expect) - * \li New nodes are sent as a bundle. The first message in the bundle will be - * this one (/om/new_node), followed by a series of /om/new_port commands, - * followed by /om/new_node_end.

\n \n - */ -void OSCClient::new_node(const string& plugin_type, - const string& plugin_uri, - const string& node_path, - bool is_polyphonic, - uint32_t num_ports) -{ - lo_send(_address, "/om/new_node", "sssii", plugin_type.c_str(), plugin_uri.c_str(), - node_path.c_str(), is_polyphonic ? 1 : 0, num_ports); -#if 0 - /* - lo_timetag tt; - lo_timetag_now(&tt); - lo_bundle b = lo_bundle_new(tt); - lo_message m = lo_message_new(); - list msgs; - - lo_message_add_string(m, plugin_type.c_str()); - lo_message_add_string(m, plugin_uri.c_str()); - lo_message_add_string(m, node_path.c_str()); - lo_message_add_int32(m, is_polyphonic ? 1 : 0); - lo_message_add_int32(m, num_ports); - - lo_bundle_add_message(b, "/om/new_node", m); - msgs.push_back(m); -*/ - - - /* - const Array& ports = node->ports(); - Port* port; - PortInfo* info; - for (size_t j=0; j < ports.size(); ++j) { - port = ports.at(j); - info = port->port_info(); - - assert(port != NULL); - assert(info != NULL); - - m = lo_message_new(); - lo_message_add_string(m, port->path().c_str()); - lo_message_add_string(m, info->type_string().c_str()); - lo_message_add_string(m, info->direction_string().c_str()); - lo_message_add_string(m, info->hint_string().c_str()); - lo_message_add_float(m, info->default_val()); - lo_message_add_float(m, info->min_val()); - lo_message_add_float(m, info->max_val()); - lo_bundle_add_message(b, "/om/new_port", m); - msgs.push_back(m); - - // If the bundle is getting very large, send it and start - // a new one - if (lo_bundle_length(b) > 1024) { - lo_send_bundle(_address, b); - lo_bundle_free(b); - b = lo_bundle_new(tt); - } - } -*/ - /*m = lo_message_new(); - //lo_bundle_add_message(b, "/om/new_node_end", m); - //msgs.push_back(m); - - lo_send_bundle(_address, b); - lo_bundle_free(b); - - for (list::const_iterator i = msgs.begin(); i != msgs.end(); ++i) - lo_message_free(*i); - - usleep(100); -*/ - /* - const map& data = node->metadata(); - // Send node metadata - for (map::const_iterator i = data.begin(); i != data.end(); ++i) - metadata_update(node->path(), (*i).first, (*i).second); - - - // Send port metadata - for (size_t j=0; j < ports.size(); ++j) { - port = ports.at(j); - const map& data = port->metadata(); - for (map::const_iterator i = data.begin(); i != data.end(); ++i) - metadata_update(port->path(), (*i).first, (*i).second); - } - - // Send control values - for (size_t i=0; i < node->ports().size(); ++i) { - TypedPort* port = (TypedPort*)node->ports().at(i); - if (port->port_info()->is_input() && port->port_info()->is_control()) - control_change(port->path(), port->buffer(0)->value_at(0)); - } - */ -#endif -} - - - -/** \page client_osc_namespace - *

\b /om/new_port - Notification of a new port's creation. - * \arg \b path (string) - Path of new port - * \arg \b data-type (string) - Type of port (CONTROL or AUDIO) - * \arg \b direction ("is-output") (integer) - Direction of data flow (Input = 0, Output = 1) - * - * \li Note that in the event of loading a patch, this message could be - * followed immediately by a control change, meaning the default-value is - * not actually the current value of the port. - * \li The minimum and maximum values are suggestions only, they are not - * enforced in any way, and going outside them is perfectly fine. Also note - * that the port ranges in om_gtk are not these ones! Those ranges are set - * as metadata.

\n \n - */ -void -OSCClient::new_port(const string& path, - const string& data_type, - bool is_output) -{ - //PortInfo* info = port->port_info(); - - lo_send(_address, "/om/new_port", "ssi", path.c_str(), data_type.c_str(), is_output); - - // Send metadata - /*const map& data = port->metadata(); - for (map::const_iterator i = data.begin(); i != data.end(); ++i) - metadata_update(port->path(), (*i).first, (*i).second);*/ -} - - -/** \page client_osc_namespace - *

\b /om/destroyed - Notification an object has been destroyed - * \arg \b path (string) - Path of object (which no longer exists)

\n \n - */ -void -OSCClient::object_destroyed(const string& path) -{ - assert(path != "/"); - - lo_send(_address, "/om/destroyed", "s", path.c_str()); -} - - -/** \page client_osc_namespace - *

\b /om/patch_cleared - Notification a patch has been cleared (all children destroyed) - * \arg \b path (string) - Path of patch (which is now empty)

\n \n - */ -void -OSCClient::patch_cleared(const string& patch_path) -{ - lo_send(_address, "/om/patch_cleared", "s", patch_path.c_str()); -} - - -/** \page client_osc_namespace - *

\b /om/patch_enabled - Notification a patch's DSP processing has been enabled. - * \arg \b path (string) - Path of enabled patch

\n \n - */ -void -OSCClient::patch_enabled(const string& patch_path) -{ - lo_send(_address, "/om/patch_enabled", "s", patch_path.c_str()); -} - - -/** \page client_osc_namespace - *

\b /om/patch_disabled - Notification a patch's DSP processing has been disabled. - * \arg \b path (string) - Path of disabled patch

\n \n - */ -void -OSCClient::patch_disabled(const string& patch_path) -{ - lo_send(_address, "/om/patch_disabled", "s", patch_path.c_str()); -} - - -/** \page client_osc_namespace - *

\b /om/new_connection - Notification a new connection has been made. - * \arg \b src-path (string) - Path of the source port - * \arg \b dst-path (string) - Path of the destination port

\n \n - */ -void -OSCClient::connection(const string& src_port_path, const string& dst_port_path) -{ - lo_send(_address, "/om/new_connection", "ss", src_port_path.c_str(), dst_port_path.c_str()); -} - - -/** \page client_osc_namespace - *

\b /om/disconnection - Notification a connection has been unmade. - * \arg \b src-path (string) - Path of the source port - * \arg \b dst-path (string) - Path of the destination port

\n \n - */ -void -OSCClient::disconnection(const string& src_port_path, const string& dst_port_path) -{ - lo_send(_address, "/om/disconnection", "ss", src_port_path.c_str(), dst_port_path.c_str()); -} - - -/** \page client_osc_namespace - *

\b /om/metadata/update - Notification of a piece of metadata. - * \arg \b path (string) - Path of the object associated with metadata (can be a node, patch, or port) - * \arg \b key (string) - * \arg \b value (string)

\n \n - */ -void -OSCClient::metadata_update(const string& path, const string& key, const string& value) -{ - lo_send(_address, "/om/metadata/update", "sss", path.c_str(), key.c_str(), value.c_str()); -} - - -/** \page client_osc_namespace - *

\b /om/control_change - Notification the value of a port has changed - * \arg \b path (string) - Path of port - * \arg \b value (float) - New value of port - * - * \li This will only send updates for values set by clients of course - not values - * changing because of connections to other ports!

\n \n - */ -void -OSCClient::control_change(const string& port_path, float value) -{ - lo_send(_address, "/om/control_change", "sf", port_path.c_str(), value); -} - - -/** \page client_osc_namespace - *

\b /om/plugin - Notification of the existance of a plugin - * \arg \b type (string) - Type if plugin ("LADSPA", "DSSI", or "Internal")

\n \n - * \arg \b uri (string) - Type if plugin ("LADSPA", "DSSI", or "Internal")

\n \n - * \arg \b name (string) - Descriptive human-readable name of plugin (ie "ADSR Envelope") - */ -void -OSCClient::new_plugin(const string& type, const string& uri, const string& name) -{ - lo_message m = lo_message_new(); - lo_message_add_string(m, type.c_str()); - lo_message_add_string(m, uri.c_str()); - lo_message_add_string(m, name.c_str()); - lo_send_message(_address, "/om/plugin", m); -} - - -/** \page client_osc_namespace - *

\b /om/new_patch - Notification of a new patch - * \arg \b path (string) - Path of new patch - * \arg \b poly (int) - Polyphony of new patch (\em not a boolean like new_node)

\n \n - */ -void -OSCClient::new_patch(const string& path, uint32_t poly) -{ - lo_send(_address, "/om/new_patch", "si", path.c_str(), poly); - - /* - if (p->process()) - patch_enabled(p->path()); - - // Send metadata - const map& data = p->metadata(); - for (map::const_iterator i = data.begin(); i != data.end(); ++i) { - metadata_update(p->path(), (*i).first, (*i).second); - } - */ -} - - -/** \page client_osc_namespace - *

\b /om/object_renamed - Notification of an object's renaming - * \arg \b old-path (string) - Old path of object - * \arg \b new-path (string) - New path of object

\n \n - */ -void -OSCClient::object_renamed(const string& old_path, const string& new_path) -{ - lo_send(_address, "/om/object_renamed", "ss", old_path.c_str(), new_path.c_str()); -} - - -/** Sends all GraphObjects known to the engine. - */ -/* -void -OSCClient::all_objects() -{ - for (Tree::iterator i = Engine::instance().object_store()->objects().begin(); - i != Engine::instance().object_store()->objects().end(); ++i) - if ((*i)->as_node() != NULL && (*i)->parent() == NULL) - (*i)->as_node()->send_creation_messages(this); -} -*/ - -/** Sends information about a program associated with a DSSI plugin node. - */ -void -OSCClient::program_add(const string& node_path, uint32_t bank, uint32_t program, const string& name) -{ - lo_send(_address, "/om/program_add", "siis", - node_path.c_str(), bank, program, name.c_str()); -} - - -void -OSCClient::program_remove(const string& node_path, uint32_t bank, uint32_t program) -{ - lo_send(_address, "/om/program_remove", "sii", - node_path.c_str(), bank, program); -} - - -} // namespace Ingen diff --git a/src/libs/engine/OSCClient.h b/src/libs/engine/OSCClient.h deleted file mode 100644 index 423bb32f..00000000 --- a/src/libs/engine/OSCClient.h +++ /dev/null @@ -1,131 +0,0 @@ -/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. - * - * 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 OSCCLIENT_H -#define OSCCLIENT_H - -#include -#include -#include -#include -#include -#include "types.h" -#include "interface/ClientInterface.h" - -using std::list; using std::string; -using std::cerr; - -namespace Ingen { - - -/** Implements ClientInterface for OSC clients (sends OSC messages). - * - * \ingroup engine - */ -class OSCClient : public Shared::ClientInterface -{ -public: - OSCClient(const string& url) - : _url(url), - _address(lo_address_new_from_url(url.c_str())) - {} - - virtual ~OSCClient() - { lo_address_free(_address); } - - const string& url() const { return _url; } - const lo_address address() const { return _address; } - - //void plugins(); // FIXME remove - - - - /* *** ClientInterface Implementation Below *** */ - - - //void client_registration(const string& url, int client_id); - - // need a liblo feature to make this possible :/ - void bundle_begin() {} - void bundle_end() {} - - void num_plugins(uint32_t num); - - void error(const string& msg); - - virtual void new_plugin(const string& type, - const string& uri, - const string& name); - - virtual void new_patch(const string& path, uint32_t poly); - - virtual void new_node(const string& plugin_type, - const string& plugin_uri, - const string& node_path, - bool is_polyphonic, - uint32_t num_ports); - - virtual void new_port(const string& path, - const string& data_type, - bool is_output); - - virtual void patch_enabled(const string& path); - - virtual void patch_disabled(const string& path); - - virtual void patch_cleared(const string& path); - - virtual void object_destroyed(const string& path); - - virtual void object_renamed(const string& old_path, - const string& new_path); - - virtual void connection(const string& src_port_path, - const string& dst_port_path); - - virtual void disconnection(const string& src_port_path, - const string& dst_port_path); - - virtual void metadata_update(const string& subject_path, - const string& predicate, - const string& value); - - virtual void control_change(const string& port_path, - float value); - - virtual void program_add(const string& node_path, - uint32_t bank, - uint32_t program, - const string& program_name); - - virtual void program_remove(const string& node_path, - uint32_t bank, - uint32_t program); - -private: - // Prevent copies (undefined) - OSCClient(const OSCClient&); - OSCClient& operator=(const OSCClient&); - - string _url; - lo_address _address; -}; - - -} // namespace Ingen - -#endif // OSCCLIENT_H - diff --git a/src/libs/engine/OSCClientSender.cpp b/src/libs/engine/OSCClientSender.cpp new file mode 100644 index 00000000..d6f56e74 --- /dev/null +++ b/src/libs/engine/OSCClientSender.cpp @@ -0,0 +1,487 @@ +/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. + * + * 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 "OSCClientSender.h" +#include +#include +#include +#include "ObjectStore.h" +#include "NodeFactory.h" +#include "util.h" +#include "Patch.h" +#include "Node.h" +#include "Plugin.h" +#include "TypedPort.h" +#include "Connection.h" +#include "AudioDriver.h" +#include "interface/ClientInterface.h" +#include "Responder.h" + +using std::cout; using std::cerr; using std::endl; + +namespace Ingen { + + +/*! \page client_osc_namespace Client OSC Namespace Documentation + * + *

These are all the messages sent from the engine to the client. + * Communication takes place over two distinct bands: control band and + * notification band.

+ *

The control band is where clients send commands, and receive a simple + * response, either OK or an error.

+ *

All notifications of engine state (ie new nodes) are sent over the + * notification band which is seperate from the control band. The + * reasoning behind this is that many clients may be connected at the same + * time - a client may receive notifications that are not a direct consequence + * of some message it sent.

+ *

The notification band can be thought of as a stream of events representing + * the changing engine state. For example, It is possible for a client to send + * commands and receive aknowledgements, and not listen to the notification band + * at all; or (in the near future anyway) for a client to use UDP for the control + * band (for speed), and TCP for the notification band (for reliability and + * order guarantees).

+ * \n\n + */ + + +/* Documentation for namespace portion implemented in Responder.cpp */ + +/** \page client_osc_namespace + * \n + *

Notification Band

+ */ + +/** \page client_osc_namespace + *

\b /om/response/ok - Respond successfully to a user command + * \arg \b responder-id (int) - Responder ID this is a response to + *

\n \n + */ + +/** \page client_osc_namespace + *

\b /om/response/error - Respond negatively to a user command + * \arg \b responder-id (int) - Request ID this is a response to + * \arg \b message (string) - Error message (natural language text) + *

\n \n + */ + + + +/** \page client_osc_namespace + * \n + *

Notification Band

+ */ + +/** \page client_osc_namespace + *

\b /om/error - Notification that an error has occurred + * \arg \b message (string) - Error message (natural language text) + * + * \li This is for notification of errors that aren't a direct response to a + * user command, ie "unexpected" errors.

\n \n + */ +void +OSCClientSender::error(const string& msg) +{ + lo_send(_address, "/om/error", "s", msg.c_str()); +} + + +/** \page client_osc_namespace + *

\b /om/num_plugins + * \arg \b num (int) - Number of plugins engine has loaded + * \li This is sent before sending the list of plugins, so the client is aware + * of how many plugins (/om/plugin messages) to expect.

\n \n + */ + + +/** \page client_osc_namespace + *

\b /om/num_plugins + * \arg \b num (int) - Number of plugins engine has loaded + * \li This is sent before sending the list of plugins, so the client is aware + * of how many plugins (/om/plugin messages) to expect.

\n \n + */ +void +OSCClientSender::num_plugins(uint32_t num) +{ + lo_message m = lo_message_new(); + lo_message_add_int32(m, num); + lo_send_message(_address, "/om/num_plugins", m); +} + + +/** \page client_osc_namespace + *

\b /om/plugin - Notification of the existance of a plugin + * \arg \b type (string) - Type if plugin ("LADSPA", "DSSI", or "Internal") + * \arg \b uri (string) - URI of the plugin (see engine namespace documentation) \n + * \arg \b lib-name (string) - Name of shared library plugin resides in (ie "cmt.so") + * \arg \b plug-label (string) - Label of the plugin (ie "dahdsr_iaoa") + * \arg \b name (string) - Descriptive human-readable name of plugin (ie "ADSR Envelope") + *

\n \n + */ +/* +void +OSCClientSender::plugins() +{ + Engine::instance().node_factory()->lock_plugin_list(); + + const list& plugs = Engine::instance().node_factory()->plugins(); + const Plugin* plugin; + + lo_timetag tt; + lo_timetag_now(&tt); + lo_bundle b = lo_bundle_new(tt); + lo_message m = lo_message_new(); + list msgs; + + lo_message_add_int32(m, plugs.size()); + lo_bundle_add_message(b, "/om/num_plugins", m); + msgs.push_back(m); + + for (list::const_iterator j = plugs.begin(); j != plugs.end(); ++j) { + plugin = (*j); + m = lo_message_new(); + + lo_message_add_string(m, plugin->type_string()); + lo_message_add_string(m, plugin->uri().c_str()); + lo_message_add_string(m, plugin->plug_label().c_str()); + lo_message_add_string(m, plugin->name().c_str()); + lo_bundle_add_message(b, "/om/plugin", m); + msgs.push_back(m); + if (lo_bundle_length(b) > 1024) { + lo_send_bundle(_address, b); + lo_bundle_free(b); + b = lo_bundle_new(tt); + } + } + + if (lo_bundle_length(b) > 0) { + lo_send_bundle(_address, b); + lo_bundle_free(b); + } else { + lo_bundle_free(b); + } + for (list::const_iterator i = msgs.begin(); i != msgs.end(); ++i) + lo_message_free(*i); + + Engine::instance().node_factory()->unlock_plugin_list(); +} +*/ + +/** \page client_osc_namespace + *

\b /om/new_node - Notification of a new node's creation. + * \arg \b plug-uri (string) - URI of the plugin new node is an instance of + * \arg \b path (string) - Path of the new node + * \arg \b polyphonic (integer-boolean) - Node is polyphonic (1 = yes, 0 = no) + * \arg \b num-ports (integer) - Number of ports (number of new_port messages to expect) + * \li New nodes are sent as a bundle. The first message in the bundle will be + * this one (/om/new_node), followed by a series of /om/new_port commands, + * followed by /om/new_node_end.

\n \n + */ +void OSCClientSender::new_node(const string& plugin_type, + const string& plugin_uri, + const string& node_path, + bool is_polyphonic, + uint32_t num_ports) +{ + lo_send(_address, "/om/new_node", "sssii", plugin_type.c_str(), plugin_uri.c_str(), + node_path.c_str(), is_polyphonic ? 1 : 0, num_ports); +#if 0 + /* + lo_timetag tt; + lo_timetag_now(&tt); + lo_bundle b = lo_bundle_new(tt); + lo_message m = lo_message_new(); + list msgs; + + lo_message_add_string(m, plugin_type.c_str()); + lo_message_add_string(m, plugin_uri.c_str()); + lo_message_add_string(m, node_path.c_str()); + lo_message_add_int32(m, is_polyphonic ? 1 : 0); + lo_message_add_int32(m, num_ports); + + lo_bundle_add_message(b, "/om/new_node", m); + msgs.push_back(m); +*/ + + + /* + const Array& ports = node->ports(); + Port* port; + PortInfo* info; + for (size_t j=0; j < ports.size(); ++j) { + port = ports.at(j); + info = port->port_info(); + + assert(port != NULL); + assert(info != NULL); + + m = lo_message_new(); + lo_message_add_string(m, port->path().c_str()); + lo_message_add_string(m, info->type_string().c_str()); + lo_message_add_string(m, info->direction_string().c_str()); + lo_message_add_string(m, info->hint_string().c_str()); + lo_message_add_float(m, info->default_val()); + lo_message_add_float(m, info->min_val()); + lo_message_add_float(m, info->max_val()); + lo_bundle_add_message(b, "/om/new_port", m); + msgs.push_back(m); + + // If the bundle is getting very large, send it and start + // a new one + if (lo_bundle_length(b) > 1024) { + lo_send_bundle(_address, b); + lo_bundle_free(b); + b = lo_bundle_new(tt); + } + } +*/ + /*m = lo_message_new(); + //lo_bundle_add_message(b, "/om/new_node_end", m); + //msgs.push_back(m); + + lo_send_bundle(_address, b); + lo_bundle_free(b); + + for (list::const_iterator i = msgs.begin(); i != msgs.end(); ++i) + lo_message_free(*i); + + usleep(100); +*/ + /* + const map& data = node->metadata(); + // Send node metadata + for (map::const_iterator i = data.begin(); i != data.end(); ++i) + metadata_update(node->path(), (*i).first, (*i).second); + + + // Send port metadata + for (size_t j=0; j < ports.size(); ++j) { + port = ports.at(j); + const map& data = port->metadata(); + for (map::const_iterator i = data.begin(); i != data.end(); ++i) + metadata_update(port->path(), (*i).first, (*i).second); + } + + // Send control values + for (size_t i=0; i < node->ports().size(); ++i) { + TypedPort* port = (TypedPort*)node->ports().at(i); + if (port->port_info()->is_input() && port->port_info()->is_control()) + control_change(port->path(), port->buffer(0)->value_at(0)); + } + */ +#endif +} + + + +/** \page client_osc_namespace + *

\b /om/new_port - Notification of a new port's creation. + * \arg \b path (string) - Path of new port + * \arg \b data-type (string) - Type of port (CONTROL or AUDIO) + * \arg \b direction ("is-output") (integer) - Direction of data flow (Input = 0, Output = 1) + * + * \li Note that in the event of loading a patch, this message could be + * followed immediately by a control change, meaning the default-value is + * not actually the current value of the port. + * \li The minimum and maximum values are suggestions only, they are not + * enforced in any way, and going outside them is perfectly fine. Also note + * that the port ranges in om_gtk are not these ones! Those ranges are set + * as metadata.

\n \n + */ +void +OSCClientSender::new_port(const string& path, + const string& data_type, + bool is_output) +{ + //PortInfo* info = port->port_info(); + + lo_send(_address, "/om/new_port", "ssi", path.c_str(), data_type.c_str(), is_output); + + // Send metadata + /*const map& data = port->metadata(); + for (map::const_iterator i = data.begin(); i != data.end(); ++i) + metadata_update(port->path(), (*i).first, (*i).second);*/ +} + + +/** \page client_osc_namespace + *

\b /om/destroyed - Notification an object has been destroyed + * \arg \b path (string) - Path of object (which no longer exists)

\n \n + */ +void +OSCClientSender::object_destroyed(const string& path) +{ + assert(path != "/"); + + lo_send(_address, "/om/destroyed", "s", path.c_str()); +} + + +/** \page client_osc_namespace + *

\b /om/patch_cleared - Notification a patch has been cleared (all children destroyed) + * \arg \b path (string) - Path of patch (which is now empty)

\n \n + */ +void +OSCClientSender::patch_cleared(const string& patch_path) +{ + lo_send(_address, "/om/patch_cleared", "s", patch_path.c_str()); +} + + +/** \page client_osc_namespace + *

\b /om/patch_enabled - Notification a patch's DSP processing has been enabled. + * \arg \b path (string) - Path of enabled patch

\n \n + */ +void +OSCClientSender::patch_enabled(const string& patch_path) +{ + lo_send(_address, "/om/patch_enabled", "s", patch_path.c_str()); +} + + +/** \page client_osc_namespace + *

\b /om/patch_disabled - Notification a patch's DSP processing has been disabled. + * \arg \b path (string) - Path of disabled patch

\n \n + */ +void +OSCClientSender::patch_disabled(const string& patch_path) +{ + lo_send(_address, "/om/patch_disabled", "s", patch_path.c_str()); +} + + +/** \page client_osc_namespace + *

\b /om/new_connection - Notification a new connection has been made. + * \arg \b src-path (string) - Path of the source port + * \arg \b dst-path (string) - Path of the destination port

\n \n + */ +void +OSCClientSender::connection(const string& src_port_path, const string& dst_port_path) +{ + lo_send(_address, "/om/new_connection", "ss", src_port_path.c_str(), dst_port_path.c_str()); +} + + +/** \page client_osc_namespace + *

\b /om/disconnection - Notification a connection has been unmade. + * \arg \b src-path (string) - Path of the source port + * \arg \b dst-path (string) - Path of the destination port

\n \n + */ +void +OSCClientSender::disconnection(const string& src_port_path, const string& dst_port_path) +{ + lo_send(_address, "/om/disconnection", "ss", src_port_path.c_str(), dst_port_path.c_str()); +} + + +/** \page client_osc_namespace + *

\b /om/metadata/update - Notification of a piece of metadata. + * \arg \b path (string) - Path of the object associated with metadata (can be a node, patch, or port) + * \arg \b key (string) + * \arg \b value (string)

\n \n + */ +void +OSCClientSender::metadata_update(const string& path, const string& key, const string& value) +{ + lo_send(_address, "/om/metadata/update", "sss", path.c_str(), key.c_str(), value.c_str()); +} + + +/** \page client_osc_namespace + *

\b /om/control_change - Notification the value of a port has changed + * \arg \b path (string) - Path of port + * \arg \b value (float) - New value of port + * + * \li This will only send updates for values set by clients of course - not values + * changing because of connections to other ports!

\n \n + */ +void +OSCClientSender::control_change(const string& port_path, float value) +{ + lo_send(_address, "/om/control_change", "sf", port_path.c_str(), value); +} + + +/** \page client_osc_namespace + *

\b /om/plugin - Notification of the existance of a plugin + * \arg \b type (string) - Type if plugin ("LADSPA", "DSSI", or "Internal")

\n \n + * \arg \b uri (string) - Type if plugin ("LADSPA", "DSSI", or "Internal")

\n \n + * \arg \b name (string) - Descriptive human-readable name of plugin (ie "ADSR Envelope") + */ +void +OSCClientSender::new_plugin(const string& type, const string& uri, const string& name) +{ + lo_message m = lo_message_new(); + lo_message_add_string(m, type.c_str()); + lo_message_add_string(m, uri.c_str()); + lo_message_add_string(m, name.c_str()); + lo_send_message(_address, "/om/plugin", m); +} + + +/** \page client_osc_namespace + *

\b /om/new_patch - Notification of a new patch + * \arg \b path (string) - Path of new patch + * \arg \b poly (int) - Polyphony of new patch (\em not a boolean like new_node)

\n \n + */ +void +OSCClientSender::new_patch(const string& path, uint32_t poly) +{ + lo_send(_address, "/om/new_patch", "si", path.c_str(), poly); + + /* + if (p->process()) + patch_enabled(p->path()); + + // Send metadata + const map& data = p->metadata(); + for (map::const_iterator i = data.begin(); i != data.end(); ++i) { + metadata_update(p->path(), (*i).first, (*i).second); + } + */ +} + + +/** \page client_osc_namespace + *

\b /om/object_renamed - Notification of an object's renaming + * \arg \b old-path (string) - Old path of object + * \arg \b new-path (string) - New path of object

\n \n + */ +void +OSCClientSender::object_renamed(const string& old_path, const string& new_path) +{ + lo_send(_address, "/om/object_renamed", "ss", old_path.c_str(), new_path.c_str()); +} + + +/** Sends information about a program associated with a DSSI plugin node. + */ +void +OSCClientSender::program_add(const string& node_path, uint32_t bank, uint32_t program, const string& name) +{ + lo_send(_address, "/om/program_add", "siis", + node_path.c_str(), bank, program, name.c_str()); +} + + +void +OSCClientSender::program_remove(const string& node_path, uint32_t bank, uint32_t program) +{ + lo_send(_address, "/om/program_remove", "sii", + node_path.c_str(), bank, program); +} + + +} // namespace Ingen diff --git a/src/libs/engine/OSCClientSender.h b/src/libs/engine/OSCClientSender.h new file mode 100644 index 00000000..1826a36d --- /dev/null +++ b/src/libs/engine/OSCClientSender.h @@ -0,0 +1,131 @@ +/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. + * + * 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 OSCCLIENTSENDER_H +#define OSCCLIENTSENDER_H + +#include +#include +#include +#include +#include +#include "types.h" +#include "interface/ClientInterface.h" + +using std::list; using std::string; +using std::cerr; + +namespace Ingen { + + +/** Implements ClientInterface for OSC clients (sends OSC messages). + * + * \ingroup engine + */ +class OSCClientSender : public Shared::ClientInterface +{ +public: + OSCClientSender(const string& url) + : _url(url), + _address(lo_address_new_from_url(url.c_str())) + {} + + virtual ~OSCClientSender() + { lo_address_free(_address); } + + const string& url() const { return _url; } + const lo_address address() const { return _address; } + + //void plugins(); // FIXME remove + + + + /* *** ClientInterface Implementation Below *** */ + + + //void client_registration(const string& url, int client_id); + + // need a liblo feature to make this possible :/ + void bundle_begin() {} + void bundle_end() {} + + void num_plugins(uint32_t num); + + void error(const string& msg); + + virtual void new_plugin(const string& type, + const string& uri, + const string& name); + + virtual void new_patch(const string& path, uint32_t poly); + + virtual void new_node(const string& plugin_type, + const string& plugin_uri, + const string& node_path, + bool is_polyphonic, + uint32_t num_ports); + + virtual void new_port(const string& path, + const string& data_type, + bool is_output); + + virtual void patch_enabled(const string& path); + + virtual void patch_disabled(const string& path); + + virtual void patch_cleared(const string& path); + + virtual void object_destroyed(const string& path); + + virtual void object_renamed(const string& old_path, + const string& new_path); + + virtual void connection(const string& src_port_path, + const string& dst_port_path); + + virtual void disconnection(const string& src_port_path, + const string& dst_port_path); + + virtual void metadata_update(const string& subject_path, + const string& predicate, + const string& value); + + virtual void control_change(const string& port_path, + float value); + + virtual void program_add(const string& node_path, + uint32_t bank, + uint32_t program, + const string& program_name); + + virtual void program_remove(const string& node_path, + uint32_t bank, + uint32_t program); + +private: + // Prevent copies (undefined) + OSCClientSender(const OSCClientSender&); + OSCClientSender& operator=(const OSCClientSender&); + + string _url; + lo_address _address; +}; + + +} // namespace Ingen + +#endif // OSCCLIENTSENDER_H + diff --git a/src/libs/engine/OSCEngineReceiver.cpp b/src/libs/engine/OSCEngineReceiver.cpp new file mode 100644 index 00000000..4b1ae496 --- /dev/null +++ b/src/libs/engine/OSCEngineReceiver.cpp @@ -0,0 +1,909 @@ +/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. + * + * 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 "OSCEngineReceiver.h" +#include +#include +#include +#include +#include "types.h" +#include "util/Queue.h" +#include "util/CountedPtr.h" +#include "QueuedEventSource.h" +#include "interface/ClientKey.h" +#include "interface/ClientInterface.h" +#include "OSCClientSender.h" +#include "OSCResponder.h" +#include "ClientBroadcaster.h" + +using std::cerr; using std::cout; using std::endl; + +namespace Ingen { + +using Shared::ClientKey; + + +/*! \page engine_osc_namespace Engine OSC Namespace Documentation + * + *

These are the commands the engine recognizes. A client can control every + * aspect of the engine entirely with these commands.

+ * + *

All commands on this page are in the "control band". If a client needs to + * know about the state of the engine, it must listen to the "notification band". + * See the "Client OSC Namespace Documentation" for details. + */ + + +OSCEngineReceiver::OSCEngineReceiver(Engine& engine, size_t queue_size, const char* const port) +: QueuedEngineInterface(engine, queue_size, queue_size), // FIXME + _port(port), + _server(NULL), + _osc_responder(NULL) +{ + _server = lo_server_new(port, error_cb); + + if (_server == NULL) { + cerr << "[OSC] Could not start OSC server. Aborting." << endl; + exit(EXIT_FAILURE); + } else { + char* lo_url = lo_server_get_url(_server); + cout << "[OSC] Started OSC server at " << lo_url << endl; + free(lo_url); + } + + // For debugging, print all incoming OSC messages + lo_server_add_method(_server, NULL, NULL, generic_cb, NULL); + + // Set response address for this message. + // It's important this is first and returns nonzero. + lo_server_add_method(_server, NULL, NULL, set_response_address_cb, this); + + // Commands + lo_server_add_method(_server, "/om/ping", "i", ping_cb, this); + lo_server_add_method(_server, "/om/ping_slow", "i", ping_slow_cb, this); + lo_server_add_method(_server, "/om/engine/quit", "i", quit_cb, this); + //lo_server_add_method(_server, "/om/engine/register_client", "is", register_client_cb, this); + lo_server_add_method(_server, "/om/engine/register_client", "i", register_client_cb, this); + lo_server_add_method(_server, "/om/engine/unregister_client", "i", unregister_client_cb, this); + lo_server_add_method(_server, "/om/engine/load_plugins", "i", load_plugins_cb, this); + lo_server_add_method(_server, "/om/engine/activate", "i", engine_activate_cb, this); + lo_server_add_method(_server, "/om/engine/deactivate", "i", engine_deactivate_cb, this); + lo_server_add_method(_server, "/om/synth/create_patch", "isi", create_patch_cb, this); + lo_server_add_method(_server, "/om/synth/enable_patch", "is", enable_patch_cb, this); + lo_server_add_method(_server, "/om/synth/disable_patch", "is", disable_patch_cb, this); + lo_server_add_method(_server, "/om/synth/clear_patch", "is", clear_patch_cb, this); + lo_server_add_method(_server, "/om/synth/create_port", "issi", create_port_cb, this); + lo_server_add_method(_server, "/om/synth/create_node", "issssi", create_node_cb, this); + lo_server_add_method(_server, "/om/synth/create_node", "isssi", create_node_by_uri_cb, this); + lo_server_add_method(_server, "/om/synth/destroy", "is", destroy_cb, this); + lo_server_add_method(_server, "/om/synth/rename", "iss", rename_cb, this); + lo_server_add_method(_server, "/om/synth/connect", "iss", connect_cb, this); + lo_server_add_method(_server, "/om/synth/disconnect", "iss", disconnect_cb, this); + lo_server_add_method(_server, "/om/synth/disconnect_all", "is", disconnect_all_cb, this); + lo_server_add_method(_server, "/om/synth/set_port_value", "isf", set_port_value_cb, this); + lo_server_add_method(_server, "/om/synth/set_port_value", "isif", set_port_value_voice_cb, this); + lo_server_add_method(_server, "/om/synth/set_port_value_slow", "isf", set_port_value_slow_cb, this); + lo_server_add_method(_server, "/om/synth/note_on", "isii", note_on_cb, this); + lo_server_add_method(_server, "/om/synth/note_off", "isi", note_off_cb, this); + lo_server_add_method(_server, "/om/synth/all_notes_off", "isi", all_notes_off_cb, this); + lo_server_add_method(_server, "/om/synth/midi_learn", "is", midi_learn_cb, this); +#ifdef HAVE_LASH + lo_server_add_method(_server, "/om/lash/restore_finished", "i", lash_restore_done_cb, this); +#endif + + lo_server_add_method(_server, "/om/metadata/request", "isss", metadata_get_cb, this); + lo_server_add_method(_server, "/om/metadata/set", "isss", metadata_set_cb, this); + + // Queries + lo_server_add_method(_server, "/om/request/plugins", "i", request_plugins_cb, this); + lo_server_add_method(_server, "/om/request/all_objects", "i", request_all_objects_cb, this); + lo_server_add_method(_server, "/om/request/port_value", "is", request_port_value_cb, this); + + // DSSI support +#ifdef HAVE_DSSI + // XXX WARNING: notice this is a catch-all + lo_server_add_method(_server, NULL, NULL, dssi_cb, this); +#endif + + lo_server_add_method(_server, NULL, NULL, unknown_cb, NULL); +} + + +OSCEngineReceiver::~OSCEngineReceiver() +{ + deactivate(); + + if (_server != NULL) { + lo_server_free(_server); + _server = NULL; + } +} + + +void +OSCEngineReceiver::activate() +{ + set_name("OSCEngineReceiver"); + QueuedEventSource::activate(); + set_scheduling(SCHED_FIFO, 10); +} + + +void +OSCEngineReceiver::deactivate() +{ + cout << "[OSCEngineReceiver] Stopped OSC listening thread" << endl; + QueuedEventSource::deactivate(); +} + + +/** Override the semaphore driven _run method of QueuedEngineInterface + * to wait on OSC messages and prepare them right away in the same thread. + */ +void +OSCEngineReceiver::_run() +{ + /* get a timestamp here and stamp all the events with the same time so + * they all get executed in the same cycle */ + + while (true) { + assert( ! unprepared_events()); + + // Wait on a message and enqueue it + lo_server_recv(_server); + + // Enqueue every other message that is here "now" + // (would this provide truly atomic bundles?) + while (lo_server_recv_noblock(_server, 0) > 0) ; + + // Process them all + while (unprepared_events()) + _whipped(); // Whip our slave self + + // No more unprepared events + } +} + + +/** Create a new responder for this message, if necessary. + * + * This is based on the fact that the current responder is stored in a ref + * counted pointer, and events just take a reference to that. Thus, events + * may delete their responder if we've since switched to a new one, or the + * same one can stay around and serve a series of events. Reference counting + * is pretty sweet, eh? + * + * If this message came from the same source as the last message, no allocation + * of responders or lo_addresses or any of it needs to be done. Unfortunately + * the only way to check is by comparing URLs, because liblo addresses suck. + * + * Really, this entire thing is a basically just a crafty way of partially + * working around the fact that liblo addresses really suck. Oh well. + */ +int +OSCEngineReceiver::set_response_address_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) +{ + OSCEngineReceiver* const me = reinterpret_cast(user_data); + + //cerr << "SET RESPONSE\n"; + + if (argc < 1 || types[0] != 'i') // Not a valid Ingen message + return 0; // Save liblo the trouble + + //cerr << "** valid msg\n"; + + const int id = argv[0]->i; + + // Need to respond + if (id != -1) { + const lo_address addr = lo_message_get_source(msg); + char* const url = lo_address_get_url(addr); + + //cerr << "** need to respond\n"; + + // Currently have an OSC responder, check if it's still okay + if (me->_responder == me->_osc_responder) { + //cerr << "** osc responder\n"; + + if (!strcmp(url, me->_osc_responder->url())) { + // Nice one, same address + //cerr << "** Using cached response address, hooray" << endl; + } else { + // Shitty deal, make a new one + //cerr << "** Setting response address to " << url << "(2)" << endl; + me->_osc_responder = CountedPtr(new OSCResponder(id, url)); + me->set_responder(me->_osc_responder); + // (responder takes ownership of url, no leak) + } + + // Otherwise we have a NULL responder, definitely need to set a new one + } else { + //cerr << "** null responder\n"; + me->_osc_responder = CountedPtr(new OSCResponder(id, url)); + me->set_responder(me->_osc_responder); + //cerr << "** Setting response address to " << url << "(2)" << endl; + } + + // Don't respond + } else { + me->disable_responses(); + //cerr << "** Not responding." << endl; + } + + // If this returns 0 no OSC commands will work + return 1; +} + + +void +OSCEngineReceiver::error_cb(int num, const char* msg, const char* path) +{ + cerr << "liblo server error " << num << " in path \"" << "\" - " << msg << endl; +} + + +/** \page engine_osc_namespace + *

\b /om/ping - Immediately sends a successful response to the given response id. + * \arg \b response-id (integer)

\n \n + */ +int +OSCEngineReceiver::m_ping_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _responder->respond_ok(); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/ping_slow - Sends response after going through the ("slow") event queue. + * \arg \b response-id (integer) + * + * \li See the documentation for /om/synth/set_port_value_slow for an explanation of how + * this differs from /om/ping. This is useful to send after sending a large cluster of + * events as a sentinel and wait on it's response, to know when the events are all + * finished processing.

\n \n + */ +int +OSCEngineReceiver::m_ping_slow_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + ping(); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/engine/quit - Terminates the engine. + * \arg \b response-id (integer) + * + * \li Note that there is NO order guarantees with this command at all. You could + * send 10 messages then quit, and the quit reply could come immediately and the + * 10 messages would never get executed.

\n \n + */ +int +OSCEngineReceiver::m_quit_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + + quit(); + return 0; +} + +/** \page engine_osc_namespace + *

\b /om/engine/register_client - Registers a new client with the engine + * \arg \b response-id (integer) + * + * The incoming address will be used for the new registered client. If you + * want to register a different specific address, use the URL version. + */ +int +OSCEngineReceiver::m_register_client_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + lo_address addr = lo_message_get_source(msg); + + char* const url = lo_address_get_url(addr); + CountedPtr client(new OSCClientSender((const char*)url)); + register_client(ClientKey(ClientKey::OSC_URL, (const char*)url), client); + free(url); + + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/engine/unregister_client - Unregisters a client + * \arg \b response-id (integer)

\n \n + */ +int +OSCEngineReceiver::m_unregister_client_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + lo_address addr = lo_message_get_source(msg); + + char* url = lo_address_get_url(addr); + unregister_client(ClientKey(ClientKey::OSC_URL, url)); + free(url); + + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/engine/load_plugins - Locates all available plugins, making them available for use. + * \arg \b response-id (integer)

\n \n + */ +int +OSCEngineReceiver::m_load_plugins_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + load_plugins(); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/engine/activate - Activate the engine (MIDI, audio, everything) + * \arg \b response-id (integer)

+ * + * \li Note that you must send this message first if you want the engine to do + * anything at all - including respond to your messages! \n \n + */ +int +OSCEngineReceiver::m_engine_activate_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + QueuedEngineInterface::activate(); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/engine/deactivate - Deactivate the engine completely. + * \arg \b response-id (integer)

\n \n + */ +int +OSCEngineReceiver::m_engine_deactivate_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + QueuedEngineInterface::deactivate(); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/create_patch - Creates a new, empty, toplevel patch. + * \arg \b response-id (integer) + * \arg \b patch-path (string) - Patch path (complete, ie /master/parent/new_patch) + * \arg \b poly (integer) - Patch's (internal) polyphony

\n \n + */ +int +OSCEngineReceiver::m_create_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* patch_path = &argv[1]->s; + const int poly = argv[2]->i; + + create_patch(patch_path, poly); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/rename - Rename an Object (only Nodes, for now) + * \arg \b response-id (integer) + * \arg \b path - Object's path + * \arg \b name - New name for object

\n \n + */ +int +OSCEngineReceiver::m_rename_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* object_path = &argv[1]->s; + const char* name = &argv[2]->s; + + rename(object_path, name); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/enable_patch - Enable DSP processing of a patch + * \arg \b response-id (integer) + * \arg \b patch-path - Patch's path + */ +int +OSCEngineReceiver::m_enable_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* patch_path = &argv[1]->s; + + enable_patch(patch_path); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/disable_patch - Disable DSP processing of a patch + * \arg \b response-id (integer) + * \arg \b patch-path - Patch's path + */ +int +OSCEngineReceiver::m_disable_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* patch_path = &argv[1]->s; + + disable_patch(patch_path); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/clear_patch - Remove all nodes from a patch + * \arg \b response-id (integer) + * \arg \b patch-path - Patch's path + */ +int +OSCEngineReceiver::m_clear_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* patch_path = &argv[1]->s; + + clear_patch(patch_path); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/create_port - Add a port into a given patch (load a plugin by URI) + * \arg \b response-id (integer) + * \arg \b path (string) - Full path of the new port (ie. /patch2/subpatch/newport) + * \arg \b data-type (string) - Data type for port to contain ("AUDIO", "CONTROL", or "MIDI") + * \arg \b direction ("is-output") (integer) - Direction of data flow (Input = 0, Output = 1)

\n \n + */ +int +OSCEngineReceiver::m_create_port_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* port_path = &argv[1]->s; + const char* data_type = &argv[2]->s; + const int direction = argv[3]->i; + + create_port(port_path, data_type, (direction == 1)); + return 0; +} + +/** \page engine_osc_namespace + *

\b /om/synth/create_node - Add a node into a given patch (load a plugin by URI) + * \arg \b response-id (integer) + * \arg \b node-path (string) - Full path of the new node (ie. /patch2/subpatch/newnode) + * \arg \b type (string) - Plugin type ("Internal", "LV2", "DSSI", "LADSPA") + * \arg \b plug-uri (string) - URI of the plugin to load + * \arg \b poly (integer-boolean) - Whether node is polyphonic (0 = false, 1 = true)

\n \n + */ +int +OSCEngineReceiver::m_create_node_by_uri_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* node_path = &argv[1]->s; + const char* type = &argv[2]->s; + const char* plug_uri = &argv[3]->s; + const int poly = argv[4]->i; + + // FIXME: make sure poly is valid + + create_node(node_path, type, plug_uri, (poly == 1)); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/create_node - Add a node into a given patch (load a plugin by libname, label) \b DEPRECATED + * \arg \b response-id (integer) + * \arg \b node-path (string) - Full path of the new node (ie. /patch2/subpatch/newnode) + * \arg \b type (string) - Plugin type ("LADSPA" or "Internal") + * \arg \b lib-name (string) - Name of library where plugin resides (eg "cmt.so") + * \arg \b plug-label (string) - Label (ID) of plugin (eg "sine_fcaa") + * \arg \b poly (integer-boolean) - Whether node is polyphonic (0 = false, 1 = true) + * + * \li This is only here to provide backwards compatibility for old patches that store LADSPA plugin + * references as libname, label. It is to be removed ASAP, don't use it. + *

\n \n + */ +int +OSCEngineReceiver::m_create_node_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* node_path = &argv[1]->s; + const char* type = &argv[2]->s; + const char* lib_name = &argv[3]->s; + const char* plug_label = &argv[4]->s; + const int poly = argv[5]->i; + + create_node(node_path, type, lib_name, plug_label, (poly == 1)); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/destroy - Removes (destroys) a Patch or a Node + * \arg \b response-id (integer) + * \arg \b node-path (string) - Full path of the object

\n \n + */ +int +OSCEngineReceiver::m_destroy_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* node_path = &argv[1]->s; + + destroy(node_path); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/connect - Connects two ports (must be in the same patch) + * \arg \b response-id (integer) + * \arg \b src-port-path (string) - Full path of source port + * \arg \b dst-port-path (string) - Full path of destination port

\n \n + */ +int +OSCEngineReceiver::m_connect_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* src_port_path = &argv[1]->s; + const char* dst_port_path = &argv[2]->s; + + connect(src_port_path, dst_port_path); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/disconnect - Disconnects two ports. + * \arg \b response-id (integer) + * \arg \b src-port-path (string) - Full path of source port + * \arg \b dst-port-path (string) - Full path of destination port

\n \n + */ +int +OSCEngineReceiver::m_disconnect_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* src_port_path = &argv[1]->s; + const char* dst_port_path = &argv[2]->s; + + disconnect(src_port_path, dst_port_path); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/disconnect_all - Disconnect all connections to/from a node. + * \arg \b response-id (integer) + * \arg \b node-path (string) - Full path of node.

\n \n + */ +int +OSCEngineReceiver::m_disconnect_all_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* node_path = &argv[1]->s; + + disconnect_all(node_path); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/set_port_value - Sets the value of a port for all voices (both AR and CR) + * \arg \b response-id (integer) + * \arg \b port-path (string) - Name of port + * \arg \b value (float) - Value to set port to + */ +int +OSCEngineReceiver::m_set_port_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* port_path = &argv[1]->s; + const float value = argv[2]->f; + + set_port_value(port_path, value); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/set_port_value - Sets the value of a port for a specific voice (both AR and CR) + * \arg \b response-id (integer) + * \arg \b port-path (string) - Name of port + * \arg \b voice (integer) - Voice to set port value for + * \arg \b value (float) - Value to set port to + */ +int +OSCEngineReceiver::m_set_port_value_voice_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* port_path = &argv[1]->s; + const int voice = argv[2]->i; + const float value = argv[3]->f; + + set_port_value(port_path, voice, value); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/set_port_value_slow - Sets the value of a port for all voices (as a QueuedEvent) + * \arg \b response-id (integer) + * \arg \b port-path (string) - Name of port + * \arg \b value (float) - Value to set port to + * + * \li This version exists so you can send it immediately after a QueuedEvent it may depend on (ie a + * node creation) and be sure it happens after the event (a normal set_port_value could beat the + * slow event and arrive out of order).

\n \n + */ +int +OSCEngineReceiver::m_set_port_value_slow_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* port_path = &argv[1]->s; + const float value = argv[2]->f; + + set_port_value_queued(port_path, value); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/note_on - Triggers a note-on, just as if it came from MIDI + * \arg \b response-id (integer) + * \arg \b node-path (string) - Patch of Node to trigger (must be a trigger or note node) + * \arg \b note-num (int) - MIDI style note number (0-127) + * \arg \b velocity (int) - MIDI style velocity (0-127)

\n \n + */ +int +OSCEngineReceiver::m_note_on_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + /* + + const char* node_path = &argv[1]->s; + const uchar note_num = argv[2]->i; + const uchar velocity = argv[3]->i; + */ + cerr << "FIXME: OSC note on\n"; + //note_on(node_path, note_num, velocity); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/note_off - Triggers a note-off, just as if it came from MIDI + * \arg \b response-id (integer) + * \arg \b node-path (string) - Patch of Node to trigger (must be a trigger or note node) + * \arg \b note-num (int) - MIDI style note number (0-127)

\n \n + */ +int +OSCEngineReceiver::m_note_off_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + /* + + const char* patch_path = &argv[1]->s; + const uchar note_num = argv[2]->i; + */ + cerr << "FIXME: OSC note off\n"; + //note_off(patch_path, note_num); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/all_notes_off - Triggers a note-off for all voices, just as if it came from MIDI + * \arg \b response-id (integer) + * \arg \b patch-path (string) - Patch of patch to send event to

\n \n + */ +int +OSCEngineReceiver::m_all_notes_off_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + /* + + const char* patch_path = &argv[1]->s; + */ + cerr << "FIXME: OSC all notes off\n"; + //all_notes_off(patch_path); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/synth/midi_learn - Initiate MIDI learn for a given (MIDI Control) Node + * \arg \b response-id (integer) + * \arg \b node-path (string) - Patch of the Node that should learn the next MIDI event. + * + * \li This of course will only do anything for MIDI control nodes. The node will learn the next MIDI + * event that arrives at it's MIDI input port - no behind the scenes voodoo happens here. It is planned + * that a plugin specification supporting arbitrary OSC commands for plugins will exist one day, and this + * method will go away completely.

\n \n + */ +int +OSCEngineReceiver::m_midi_learn_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* patch_path = &argv[1]->s; + + midi_learn(patch_path); + + return 0; +} + + +#ifdef HAVE_LASH +/** \page engine_osc_namespace + *

\b /om/lash/restore_done - Notify LASH restoring is finished and connections can be made. + * \arg \b response-id (integer) + */ +int +OSCEngineReceiver::m_lash_restore_done_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + lash_restore_done(); + return 0; +} +#endif // HAVE_LASH + + +/** \page engine_osc_namespace + *

\b /om/metadata/set - Sets a piece of metadata, associated with a synth-space object (node, etc) + * \arg \b response-id (integer) + * \arg \b object-path (string) - Full path of object to associate metadata with + * \arg \b key (string) - Key (index) for new piece of metadata + * \arg \b value (string) - Value of new piece of metadata + */ +int +OSCEngineReceiver::m_metadata_set_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* node_path = &argv[1]->s; + const char* key = &argv[2]->s; + const char* value = &argv[3]->s; + + set_metadata(node_path, key, value); + + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/metadata/responder - Requests the engine send a piece of metadata, associated with a synth-space object (node, etc) + * \arg \b response-id (integer) + * \arg \b object-path (string) - Full path of object metadata is associated with + * \arg \b key (string) - Key (index) for piece of metadata + * + * \li Reply will be sent to client registered with the source address of this message.

\n \n + */ +int +OSCEngineReceiver::m_metadata_get_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + /* + const char* node_path = &argv[1]->s; + const char* key = &argv[2]->s; + */ + cerr << "FIXME: OSC metadata request\n"; + // FIXME: Equivalent? + /* + RequestMetadataEvent* ev = new RequestMetadataEvent( + new OSCResponder(ClientKey(addr)), + node_path, key); + + */ + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/responder/plugins - Requests the engine send a list of all known plugins. + * \arg \b response-id (integer) + * + * \li Reply will be sent to client registered with the source address of this message.

\n \n + */ +int +OSCEngineReceiver::m_request_plugins_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + request_plugins(); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/responder/all_objects - Requests the engine send information about \em all objects (patches, nodes, etc) + * \arg \b response-id (integer) + * + * \li Reply will be sent to client registered with the source address of this message.

\n \n + */ +int +OSCEngineReceiver::m_request_all_objects_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + request_all_objects(); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /om/responder/port_value - Requests the engine send the value of a port. + * \arg \b response-id (integer) + * \arg \b port-path (string) - Full path of port to send the value of

\n \n + * + * \li Reply will be sent to client registered with the source address of this message.

\n \n + */ +int +OSCEngineReceiver::m_request_port_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* port_path = &argv[1]->s; + + request_port_value(port_path); + return 0; +} + + +#ifdef HAVE_DSSI +int +OSCEngineReceiver::m_dssi_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ +#if 0 + string node_path(path); + + if (node_path.substr(0, 5) != "/dssi") + return 1; + + string command = node_path.substr(node_path.find_last_of("/")+1); + node_path = node_path.substr(5); // chop off leading "/dssi/" + node_path = node_path.substr(0, node_path.find_last_of("/")); // chop off command at end + + //cout << "DSSI: Got message " << command << " for node " << node_path << endl; + + QueuedEvent* ev = NULL; + + if (command == "update" && !strcmp(types, "s")) + ev = new DSSIUpdateEvent(NULL, node_path, &argv[0]->s); + else if (command == "control" && !strcmp(types, "if")) + ev = new DSSIControlEvent(NULL, node_path, argv[0]->i, argv[1]->f); + else if (command == "configure" && ~strcmp(types, "ss")) + ev = new DSSIConfigureEvent(NULL, node_path, &argv[0]->s, &argv[1]->s); + else if (command == "program" && ~strcmp(types, "ii")) + ev = new DSSIProgramEvent(NULL, node_path, argv[0]->i, argv[1]->i); + + if (ev != NULL) + push(ev); + else + cerr << "[OSCEngineReceiver] Unknown DSSI command received: " << path << endl; +#endif + return 0; +} +#endif + + +// Static Callbacks // + + +// Display incoming OSC messages (for debugging purposes) +int +OSCEngineReceiver::generic_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) +{ + printf("[OSCMsg] %s\n", path); + + for (int i=0; i < argc; ++i) { + printf(" '%c' ", types[i]); + lo_arg_pp(lo_type(types[i]), argv[i]); + printf("\n"); + } + printf("\n"); + + return 1; // not handled +} + + +int +OSCEngineReceiver::unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) +{ + const lo_address addr = lo_message_get_source(msg); + char* const url = lo_address_get_url(addr); + + cerr << "Unknown command " << path << " (" << types << "), sending error.\n"; + + string error_msg = "Unknown command: "; + error_msg.append(path).append(" ").append(types); + + OSCResponder(0, url).respond_error(error_msg); + + return 0; +} + + +} // namespace Ingen diff --git a/src/libs/engine/OSCEngineReceiver.h b/src/libs/engine/OSCEngineReceiver.h new file mode 100644 index 00000000..cc2d7350 --- /dev/null +++ b/src/libs/engine/OSCEngineReceiver.h @@ -0,0 +1,129 @@ +/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. + * + * 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 OSCENGINERECEIVER_H +#define OSCENGINERECEIVER_H + +#include "config.h" +#include +#include +#include "QueuedEngineInterface.h" +#include "OSCResponder.h" +using std::string; + +namespace Ingen { + +class JackDriver; +class NodeFactory; +class Patch; + + +/* 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 m_##name##_cb (LO_HANDLER_ARGS);\ +inline static int name##_cb(LO_HANDLER_ARGS, void* myself)\ +{ return ((OSCEngineReceiver*)myself)->m_##name##_cb(path, types, argv, argc, msg); } + + +/* FIXME: Make this receive and preprocess in the same thread? */ + + +/** Receives OSC messages from liblo. + * + * This inherits from QueuedEngineInterface and calls it's own functions + * via OSC. It's not actually a directly callable EngineInterface (it's + * callable via OSC...) so it should be implemented-as-a (privately inherit) + * QueuedEngineInterface, but it needs to be public so it's an EventSource + * the Driver can use. This probably should be fixed somehow.. + * + * \ingroup engine + */ +class OSCEngineReceiver : public QueuedEngineInterface +{ +public: + OSCEngineReceiver(Engine& engine, size_t queue_size, const char* const port); + ~OSCEngineReceiver(); + + void activate(); + void deactivate(); + +private: + // Prevent copies (undefined) + OSCEngineReceiver(const OSCEngineReceiver&); + OSCEngineReceiver& operator=(const OSCEngineReceiver&); + + virtual void _run(); + + static void error_cb(int num, const char* msg, const char* path); + static int set_response_address_cb(LO_HANDLER_ARGS, void* myself); + 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(load_plugins); + LO_HANDLER(engine_activate); + LO_HANDLER(engine_deactivate); + LO_HANDLER(create_patch); + LO_HANDLER(rename); + LO_HANDLER(create_port); + LO_HANDLER(create_node); + LO_HANDLER(create_node_by_uri); + LO_HANDLER(enable_patch); + LO_HANDLER(disable_patch); + LO_HANDLER(clear_patch); + LO_HANDLER(destroy); + LO_HANDLER(connect); + LO_HANDLER(disconnect); + LO_HANDLER(disconnect_all); + LO_HANDLER(set_port_value); + LO_HANDLER(set_port_value_voice); + LO_HANDLER(set_port_value_slow); + LO_HANDLER(note_on); + LO_HANDLER(note_off); + LO_HANDLER(all_notes_off); + LO_HANDLER(midi_learn); + LO_HANDLER(metadata_get); + LO_HANDLER(metadata_set); + LO_HANDLER(request_plugins); + LO_HANDLER(request_all_objects); + LO_HANDLER(request_port_value); +#ifdef HAVE_DSSI + LO_HANDLER(dssi); +#endif +#ifdef HAVE_LASH + LO_HANDLER(lash_restore_done); +#endif + + const char* const _port; + lo_server _server; + + /** Cached OSC responder (for most recent incoming message) */ + CountedPtr _osc_responder; +}; + + +} // namespace Ingen + +#endif // OSCENGINERECEIVER_H diff --git a/src/libs/engine/OSCReceiver.cpp b/src/libs/engine/OSCReceiver.cpp deleted file mode 100644 index 5c08c17a..00000000 --- a/src/libs/engine/OSCReceiver.cpp +++ /dev/null @@ -1,909 +0,0 @@ -/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. - * - * 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 "OSCReceiver.h" -#include -#include -#include -#include -#include "types.h" -#include "util/Queue.h" -#include "util/CountedPtr.h" -#include "QueuedEventSource.h" -#include "interface/ClientKey.h" -#include "interface/ClientInterface.h" -#include "OSCClient.h" -#include "OSCResponder.h" -#include "ClientBroadcaster.h" - -using std::cerr; using std::cout; using std::endl; - -namespace Ingen { - -using Shared::ClientKey; - - -/*! \page engine_osc_namespace Engine OSC Namespace Documentation - * - *

These are the commands the engine recognizes. A client can control every - * aspect of the engine entirely with these commands.

- * - *

All commands on this page are in the "control band". If a client needs to - * know about the state of the engine, it must listen to the "notification band". - * See the "Client OSC Namespace Documentation" for details. - */ - - -OSCReceiver::OSCReceiver(Engine& engine, size_t queue_size, const char* const port) -: QueuedEngineInterface(engine, queue_size, queue_size), // FIXME - _port(port), - _server(NULL), - _osc_responder(NULL) -{ - _server = lo_server_new(port, error_cb); - - if (_server == NULL) { - cerr << "[OSC] Could not start OSC server. Aborting." << endl; - exit(EXIT_FAILURE); - } else { - char* lo_url = lo_server_get_url(_server); - cout << "[OSC] Started OSC server at " << lo_url << endl; - free(lo_url); - } - - // For debugging, print all incoming OSC messages - lo_server_add_method(_server, NULL, NULL, generic_cb, NULL); - - // Set response address for this message. - // It's important this is first and returns nonzero. - lo_server_add_method(_server, NULL, NULL, set_response_address_cb, this); - - // Commands - lo_server_add_method(_server, "/om/ping", "i", ping_cb, this); - lo_server_add_method(_server, "/om/ping_slow", "i", ping_slow_cb, this); - lo_server_add_method(_server, "/om/engine/quit", "i", quit_cb, this); - //lo_server_add_method(_server, "/om/engine/register_client", "is", register_client_cb, this); - lo_server_add_method(_server, "/om/engine/register_client", "i", register_client_cb, this); - lo_server_add_method(_server, "/om/engine/unregister_client", "i", unregister_client_cb, this); - lo_server_add_method(_server, "/om/engine/load_plugins", "i", load_plugins_cb, this); - lo_server_add_method(_server, "/om/engine/activate", "i", engine_activate_cb, this); - lo_server_add_method(_server, "/om/engine/deactivate", "i", engine_deactivate_cb, this); - lo_server_add_method(_server, "/om/synth/create_patch", "isi", create_patch_cb, this); - lo_server_add_method(_server, "/om/synth/enable_patch", "is", enable_patch_cb, this); - lo_server_add_method(_server, "/om/synth/disable_patch", "is", disable_patch_cb, this); - lo_server_add_method(_server, "/om/synth/clear_patch", "is", clear_patch_cb, this); - lo_server_add_method(_server, "/om/synth/create_port", "issi", create_port_cb, this); - lo_server_add_method(_server, "/om/synth/create_node", "issssi", create_node_cb, this); - lo_server_add_method(_server, "/om/synth/create_node", "isssi", create_node_by_uri_cb, this); - lo_server_add_method(_server, "/om/synth/destroy", "is", destroy_cb, this); - lo_server_add_method(_server, "/om/synth/rename", "iss", rename_cb, this); - lo_server_add_method(_server, "/om/synth/connect", "iss", connect_cb, this); - lo_server_add_method(_server, "/om/synth/disconnect", "iss", disconnect_cb, this); - lo_server_add_method(_server, "/om/synth/disconnect_all", "is", disconnect_all_cb, this); - lo_server_add_method(_server, "/om/synth/set_port_value", "isf", set_port_value_cb, this); - lo_server_add_method(_server, "/om/synth/set_port_value", "isif", set_port_value_voice_cb, this); - lo_server_add_method(_server, "/om/synth/set_port_value_slow", "isf", set_port_value_slow_cb, this); - lo_server_add_method(_server, "/om/synth/note_on", "isii", note_on_cb, this); - lo_server_add_method(_server, "/om/synth/note_off", "isi", note_off_cb, this); - lo_server_add_method(_server, "/om/synth/all_notes_off", "isi", all_notes_off_cb, this); - lo_server_add_method(_server, "/om/synth/midi_learn", "is", midi_learn_cb, this); -#ifdef HAVE_LASH - lo_server_add_method(_server, "/om/lash/restore_finished", "i", lash_restore_done_cb, this); -#endif - - lo_server_add_method(_server, "/om/metadata/request", "isss", metadata_get_cb, this); - lo_server_add_method(_server, "/om/metadata/set", "isss", metadata_set_cb, this); - - // Queries - lo_server_add_method(_server, "/om/request/plugins", "i", request_plugins_cb, this); - lo_server_add_method(_server, "/om/request/all_objects", "i", request_all_objects_cb, this); - lo_server_add_method(_server, "/om/request/port_value", "is", request_port_value_cb, this); - - // DSSI support -#ifdef HAVE_DSSI - // XXX WARNING: notice this is a catch-all - lo_server_add_method(_server, NULL, NULL, dssi_cb, this); -#endif - - lo_server_add_method(_server, NULL, NULL, unknown_cb, NULL); -} - - -OSCReceiver::~OSCReceiver() -{ - deactivate(); - - if (_server != NULL) { - lo_server_free(_server); - _server = NULL; - } -} - - -void -OSCReceiver::activate() -{ - set_name("OSCReceiver"); - QueuedEventSource::activate(); - set_scheduling(SCHED_FIFO, 10); -} - - -void -OSCReceiver::deactivate() -{ - cout << "[OSCReceiver] Stopped OSC listening thread" << endl; - QueuedEventSource::deactivate(); -} - - -/** Override the semaphore driven _run method of QueuedEngineInterface - * to wait on OSC messages and prepare them right away in the same thread. - */ -void -OSCReceiver::_run() -{ - /* get a timestamp here and stamp all the events with the same time so - * they all get executed in the same cycle */ - - while (true) { - assert( ! unprepared_events()); - - // Wait on a message and enqueue it - lo_server_recv(_server); - - // Enqueue every other message that is here "now" - // (would this provide truly atomic bundles?) - while (lo_server_recv_noblock(_server, 0) > 0) ; - - // Process them all - while (unprepared_events()) - _whipped(); // Whip our slave self - - // No more unprepared events - } -} - - -/** Create a new responder for this message, if necessary. - * - * This is based on the fact that the current responder is stored in a ref - * counted pointer, and events just take a reference to that. Thus, events - * may delete their responder if we've since switched to a new one, or the - * same one can stay around and serve a series of events. Reference counting - * is pretty sweet, eh? - * - * If this message came from the same source as the last message, no allocation - * of responders or lo_addresses or any of it needs to be done. Unfortunately - * the only way to check is by comparing URLs, because liblo addresses suck. - * - * Really, this entire thing is a basically just a crafty way of partially - * working around the fact that liblo addresses really suck. Oh well. - */ -int -OSCReceiver::set_response_address_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) -{ - OSCReceiver* const me = reinterpret_cast(user_data); - - //cerr << "SET RESPONSE\n"; - - if (argc < 1 || types[0] != 'i') // Not a valid Ingen message - return 0; // Save liblo the trouble - - //cerr << "** valid msg\n"; - - const int id = argv[0]->i; - - // Need to respond - if (id != -1) { - const lo_address addr = lo_message_get_source(msg); - char* const url = lo_address_get_url(addr); - - //cerr << "** need to respond\n"; - - // Currently have an OSC responder, check if it's still okay - if (me->_responder == me->_osc_responder) { - //cerr << "** osc responder\n"; - - if (!strcmp(url, me->_osc_responder->url())) { - // Nice one, same address - //cerr << "** Using cached response address, hooray" << endl; - } else { - // Shitty deal, make a new one - //cerr << "** Setting response address to " << url << "(2)" << endl; - me->_osc_responder = CountedPtr(new OSCResponder(id, url)); - me->set_responder(me->_osc_responder); - // (responder takes ownership of url, no leak) - } - - // Otherwise we have a NULL responder, definitely need to set a new one - } else { - //cerr << "** null responder\n"; - me->_osc_responder = CountedPtr(new OSCResponder(id, url)); - me->set_responder(me->_osc_responder); - //cerr << "** Setting response address to " << url << "(2)" << endl; - } - - // Don't respond - } else { - me->disable_responses(); - //cerr << "** Not responding." << endl; - } - - // If this returns 0 no OSC commands will work - return 1; -} - - -void -OSCReceiver::error_cb(int num, const char* msg, const char* path) -{ - cerr << "liblo server error " << num << " in path \"" << "\" - " << msg << endl; -} - - -/** \page engine_osc_namespace - *

\b /om/ping - Immediately sends a successful response to the given response id. - * \arg \b response-id (integer)

\n \n - */ -int -OSCReceiver::m_ping_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - _responder->respond_ok(); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/ping_slow - Sends response after going through the ("slow") event queue. - * \arg \b response-id (integer) - * - * \li See the documentation for /om/synth/set_port_value_slow for an explanation of how - * this differs from /om/ping. This is useful to send after sending a large cluster of - * events as a sentinel and wait on it's response, to know when the events are all - * finished processing.

\n \n - */ -int -OSCReceiver::m_ping_slow_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - ping(); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/engine/quit - Terminates the engine. - * \arg \b response-id (integer) - * - * \li Note that there is NO order guarantees with this command at all. You could - * send 10 messages then quit, and the quit reply could come immediately and the - * 10 messages would never get executed.

\n \n - */ -int -OSCReceiver::m_quit_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - - quit(); - return 0; -} - -/** \page engine_osc_namespace - *

\b /om/engine/register_client - Registers a new client with the engine - * \arg \b response-id (integer) - * - * The incoming address will be used for the new registered client. If you - * want to register a different specific address, use the URL version. - */ -int -OSCReceiver::m_register_client_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - lo_address addr = lo_message_get_source(msg); - - char* const url = lo_address_get_url(addr); - CountedPtr client(new OSCClient((const char*)url)); - register_client(ClientKey(ClientKey::OSC_URL, (const char*)url), client); - free(url); - - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/engine/unregister_client - Unregisters a client - * \arg \b response-id (integer)

\n \n - */ -int -OSCReceiver::m_unregister_client_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - lo_address addr = lo_message_get_source(msg); - - char* url = lo_address_get_url(addr); - unregister_client(ClientKey(ClientKey::OSC_URL, url)); - free(url); - - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/engine/load_plugins - Locates all available plugins, making them available for use. - * \arg \b response-id (integer)

\n \n - */ -int -OSCReceiver::m_load_plugins_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - load_plugins(); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/engine/activate - Activate the engine (MIDI, audio, everything) - * \arg \b response-id (integer)

- * - * \li Note that you must send this message first if you want the engine to do - * anything at all - including respond to your messages! \n \n - */ -int -OSCReceiver::m_engine_activate_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - QueuedEngineInterface::activate(); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/engine/deactivate - Deactivate the engine completely. - * \arg \b response-id (integer)

\n \n - */ -int -OSCReceiver::m_engine_deactivate_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - QueuedEngineInterface::deactivate(); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/create_patch - Creates a new, empty, toplevel patch. - * \arg \b response-id (integer) - * \arg \b patch-path (string) - Patch path (complete, ie /master/parent/new_patch) - * \arg \b poly (integer) - Patch's (internal) polyphony

\n \n - */ -int -OSCReceiver::m_create_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* patch_path = &argv[1]->s; - const int poly = argv[2]->i; - - create_patch(patch_path, poly); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/rename - Rename an Object (only Nodes, for now) - * \arg \b response-id (integer) - * \arg \b path - Object's path - * \arg \b name - New name for object

\n \n - */ -int -OSCReceiver::m_rename_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* object_path = &argv[1]->s; - const char* name = &argv[2]->s; - - rename(object_path, name); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/enable_patch - Enable DSP processing of a patch - * \arg \b response-id (integer) - * \arg \b patch-path - Patch's path - */ -int -OSCReceiver::m_enable_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* patch_path = &argv[1]->s; - - enable_patch(patch_path); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/disable_patch - Disable DSP processing of a patch - * \arg \b response-id (integer) - * \arg \b patch-path - Patch's path - */ -int -OSCReceiver::m_disable_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* patch_path = &argv[1]->s; - - disable_patch(patch_path); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/clear_patch - Remove all nodes from a patch - * \arg \b response-id (integer) - * \arg \b patch-path - Patch's path - */ -int -OSCReceiver::m_clear_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* patch_path = &argv[1]->s; - - clear_patch(patch_path); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/create_port - Add a port into a given patch (load a plugin by URI) - * \arg \b response-id (integer) - * \arg \b path (string) - Full path of the new port (ie. /patch2/subpatch/newport) - * \arg \b data-type (string) - Data type for port to contain ("AUDIO", "CONTROL", or "MIDI") - * \arg \b direction ("is-output") (integer) - Direction of data flow (Input = 0, Output = 1)

\n \n - */ -int -OSCReceiver::m_create_port_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* port_path = &argv[1]->s; - const char* data_type = &argv[2]->s; - const int direction = argv[3]->i; - - create_port(port_path, data_type, (direction == 1)); - return 0; -} - -/** \page engine_osc_namespace - *

\b /om/synth/create_node - Add a node into a given patch (load a plugin by URI) - * \arg \b response-id (integer) - * \arg \b node-path (string) - Full path of the new node (ie. /patch2/subpatch/newnode) - * \arg \b type (string) - Plugin type ("Internal", "LV2", "DSSI", "LADSPA") - * \arg \b plug-uri (string) - URI of the plugin to load - * \arg \b poly (integer-boolean) - Whether node is polyphonic (0 = false, 1 = true)

\n \n - */ -int -OSCReceiver::m_create_node_by_uri_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* node_path = &argv[1]->s; - const char* type = &argv[2]->s; - const char* plug_uri = &argv[3]->s; - const int poly = argv[4]->i; - - // FIXME: make sure poly is valid - - create_node(node_path, type, plug_uri, (poly == 1)); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/create_node - Add a node into a given patch (load a plugin by libname, label) \b DEPRECATED - * \arg \b response-id (integer) - * \arg \b node-path (string) - Full path of the new node (ie. /patch2/subpatch/newnode) - * \arg \b type (string) - Plugin type ("LADSPA" or "Internal") - * \arg \b lib-name (string) - Name of library where plugin resides (eg "cmt.so") - * \arg \b plug-label (string) - Label (ID) of plugin (eg "sine_fcaa") - * \arg \b poly (integer-boolean) - Whether node is polyphonic (0 = false, 1 = true) - * - * \li This is only here to provide backwards compatibility for old patches that store LADSPA plugin - * references as libname, label. It is to be removed ASAP, don't use it. - *

\n \n - */ -int -OSCReceiver::m_create_node_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* node_path = &argv[1]->s; - const char* type = &argv[2]->s; - const char* lib_name = &argv[3]->s; - const char* plug_label = &argv[4]->s; - const int poly = argv[5]->i; - - create_node(node_path, type, lib_name, plug_label, (poly == 1)); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/destroy - Removes (destroys) a Patch or a Node - * \arg \b response-id (integer) - * \arg \b node-path (string) - Full path of the object

\n \n - */ -int -OSCReceiver::m_destroy_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* node_path = &argv[1]->s; - - destroy(node_path); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/connect - Connects two ports (must be in the same patch) - * \arg \b response-id (integer) - * \arg \b src-port-path (string) - Full path of source port - * \arg \b dst-port-path (string) - Full path of destination port

\n \n - */ -int -OSCReceiver::m_connect_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* src_port_path = &argv[1]->s; - const char* dst_port_path = &argv[2]->s; - - connect(src_port_path, dst_port_path); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/disconnect - Disconnects two ports. - * \arg \b response-id (integer) - * \arg \b src-port-path (string) - Full path of source port - * \arg \b dst-port-path (string) - Full path of destination port

\n \n - */ -int -OSCReceiver::m_disconnect_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* src_port_path = &argv[1]->s; - const char* dst_port_path = &argv[2]->s; - - disconnect(src_port_path, dst_port_path); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/disconnect_all - Disconnect all connections to/from a node. - * \arg \b response-id (integer) - * \arg \b node-path (string) - Full path of node.

\n \n - */ -int -OSCReceiver::m_disconnect_all_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* node_path = &argv[1]->s; - - disconnect_all(node_path); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/set_port_value - Sets the value of a port for all voices (both AR and CR) - * \arg \b response-id (integer) - * \arg \b port-path (string) - Name of port - * \arg \b value (float) - Value to set port to - */ -int -OSCReceiver::m_set_port_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* port_path = &argv[1]->s; - const float value = argv[2]->f; - - set_port_value(port_path, value); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/set_port_value - Sets the value of a port for a specific voice (both AR and CR) - * \arg \b response-id (integer) - * \arg \b port-path (string) - Name of port - * \arg \b voice (integer) - Voice to set port value for - * \arg \b value (float) - Value to set port to - */ -int -OSCReceiver::m_set_port_value_voice_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* port_path = &argv[1]->s; - const int voice = argv[2]->i; - const float value = argv[3]->f; - - set_port_value(port_path, voice, value); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/set_port_value_slow - Sets the value of a port for all voices (as a QueuedEvent) - * \arg \b response-id (integer) - * \arg \b port-path (string) - Name of port - * \arg \b value (float) - Value to set port to - * - * \li This version exists so you can send it immediately after a QueuedEvent it may depend on (ie a - * node creation) and be sure it happens after the event (a normal set_port_value could beat the - * slow event and arrive out of order).

\n \n - */ -int -OSCReceiver::m_set_port_value_slow_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* port_path = &argv[1]->s; - const float value = argv[2]->f; - - set_port_value_queued(port_path, value); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/note_on - Triggers a note-on, just as if it came from MIDI - * \arg \b response-id (integer) - * \arg \b node-path (string) - Patch of Node to trigger (must be a trigger or note node) - * \arg \b note-num (int) - MIDI style note number (0-127) - * \arg \b velocity (int) - MIDI style velocity (0-127)

\n \n - */ -int -OSCReceiver::m_note_on_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - /* - - const char* node_path = &argv[1]->s; - const uchar note_num = argv[2]->i; - const uchar velocity = argv[3]->i; - */ - cerr << "FIXME: OSC note on\n"; - //note_on(node_path, note_num, velocity); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/note_off - Triggers a note-off, just as if it came from MIDI - * \arg \b response-id (integer) - * \arg \b node-path (string) - Patch of Node to trigger (must be a trigger or note node) - * \arg \b note-num (int) - MIDI style note number (0-127)

\n \n - */ -int -OSCReceiver::m_note_off_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - /* - - const char* patch_path = &argv[1]->s; - const uchar note_num = argv[2]->i; - */ - cerr << "FIXME: OSC note off\n"; - //note_off(patch_path, note_num); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/all_notes_off - Triggers a note-off for all voices, just as if it came from MIDI - * \arg \b response-id (integer) - * \arg \b patch-path (string) - Patch of patch to send event to

\n \n - */ -int -OSCReceiver::m_all_notes_off_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - /* - - const char* patch_path = &argv[1]->s; - */ - cerr << "FIXME: OSC all notes off\n"; - //all_notes_off(patch_path); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/synth/midi_learn - Initiate MIDI learn for a given (MIDI Control) Node - * \arg \b response-id (integer) - * \arg \b node-path (string) - Patch of the Node that should learn the next MIDI event. - * - * \li This of course will only do anything for MIDI control nodes. The node will learn the next MIDI - * event that arrives at it's MIDI input port - no behind the scenes voodoo happens here. It is planned - * that a plugin specification supporting arbitrary OSC commands for plugins will exist one day, and this - * method will go away completely.

\n \n - */ -int -OSCReceiver::m_midi_learn_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* patch_path = &argv[1]->s; - - midi_learn(patch_path); - - return 0; -} - - -#ifdef HAVE_LASH -/** \page engine_osc_namespace - *

\b /om/lash/restore_done - Notify LASH restoring is finished and connections can be made. - * \arg \b response-id (integer) - */ -int -OSCReceiver::m_lash_restore_done_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - lash_restore_done(); - return 0; -} -#endif // HAVE_LASH - - -/** \page engine_osc_namespace - *

\b /om/metadata/set - Sets a piece of metadata, associated with a synth-space object (node, etc) - * \arg \b response-id (integer) - * \arg \b object-path (string) - Full path of object to associate metadata with - * \arg \b key (string) - Key (index) for new piece of metadata - * \arg \b value (string) - Value of new piece of metadata - */ -int -OSCReceiver::m_metadata_set_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* node_path = &argv[1]->s; - const char* key = &argv[2]->s; - const char* value = &argv[3]->s; - - set_metadata(node_path, key, value); - - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/metadata/responder - Requests the engine send a piece of metadata, associated with a synth-space object (node, etc) - * \arg \b response-id (integer) - * \arg \b object-path (string) - Full path of object metadata is associated with - * \arg \b key (string) - Key (index) for piece of metadata - * - * \li Reply will be sent to client registered with the source address of this message.

\n \n - */ -int -OSCReceiver::m_metadata_get_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - /* - const char* node_path = &argv[1]->s; - const char* key = &argv[2]->s; - */ - cerr << "FIXME: OSC metadata request\n"; - // FIXME: Equivalent? - /* - RequestMetadataEvent* ev = new RequestMetadataEvent( - new OSCResponder(ClientKey(addr)), - node_path, key); - - */ - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/responder/plugins - Requests the engine send a list of all known plugins. - * \arg \b response-id (integer) - * - * \li Reply will be sent to client registered with the source address of this message.

\n \n - */ -int -OSCReceiver::m_request_plugins_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - request_plugins(); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/responder/all_objects - Requests the engine send information about \em all objects (patches, nodes, etc) - * \arg \b response-id (integer) - * - * \li Reply will be sent to client registered with the source address of this message.

\n \n - */ -int -OSCReceiver::m_request_all_objects_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - request_all_objects(); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /om/responder/port_value - Requests the engine send the value of a port. - * \arg \b response-id (integer) - * \arg \b port-path (string) - Full path of port to send the value of

\n \n - * - * \li Reply will be sent to client registered with the source address of this message.

\n \n - */ -int -OSCReceiver::m_request_port_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* port_path = &argv[1]->s; - - request_port_value(port_path); - return 0; -} - - -#ifdef HAVE_DSSI -int -OSCReceiver::m_dssi_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ -#if 0 - string node_path(path); - - if (node_path.substr(0, 5) != "/dssi") - return 1; - - string command = node_path.substr(node_path.find_last_of("/")+1); - node_path = node_path.substr(5); // chop off leading "/dssi/" - node_path = node_path.substr(0, node_path.find_last_of("/")); // chop off command at end - - //cout << "DSSI: Got message " << command << " for node " << node_path << endl; - - QueuedEvent* ev = NULL; - - if (command == "update" && !strcmp(types, "s")) - ev = new DSSIUpdateEvent(NULL, node_path, &argv[0]->s); - else if (command == "control" && !strcmp(types, "if")) - ev = new DSSIControlEvent(NULL, node_path, argv[0]->i, argv[1]->f); - else if (command == "configure" && ~strcmp(types, "ss")) - ev = new DSSIConfigureEvent(NULL, node_path, &argv[0]->s, &argv[1]->s); - else if (command == "program" && ~strcmp(types, "ii")) - ev = new DSSIProgramEvent(NULL, node_path, argv[0]->i, argv[1]->i); - - if (ev != NULL) - push(ev); - else - cerr << "[OSCReceiver] Unknown DSSI command received: " << path << endl; -#endif - return 0; -} -#endif - - -// Static Callbacks // - - -// Display incoming OSC messages (for debugging purposes) -int -OSCReceiver::generic_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) -{ - printf("[OSCMsg] %s\n", path); - - for (int i=0; i < argc; ++i) { - printf(" '%c' ", types[i]); - lo_arg_pp(lo_type(types[i]), argv[i]); - printf("\n"); - } - printf("\n"); - - return 1; // not handled -} - - -int -OSCReceiver::unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) -{ - const lo_address addr = lo_message_get_source(msg); - char* const url = lo_address_get_url(addr); - - cerr << "Unknown command " << path << " (" << types << "), sending error.\n"; - - string error_msg = "Unknown command: "; - error_msg.append(path).append(" ").append(types); - - OSCResponder(0, url).respond_error(error_msg); - - return 0; -} - - -} // namespace Ingen diff --git a/src/libs/engine/OSCReceiver.h b/src/libs/engine/OSCReceiver.h deleted file mode 100644 index 671944fd..00000000 --- a/src/libs/engine/OSCReceiver.h +++ /dev/null @@ -1,129 +0,0 @@ -/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. - * - * 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 OSCRECEIVER_H -#define OSCRECEIVER_H - -#include "config.h" -#include -#include -#include "QueuedEngineInterface.h" -#include "OSCResponder.h" -using std::string; - -namespace Ingen { - -class JackDriver; -class NodeFactory; -class Patch; - - -/* 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 m_##name##_cb (LO_HANDLER_ARGS);\ -inline static int name##_cb(LO_HANDLER_ARGS, void* osc_receiver)\ -{ return ((OSCReceiver*)osc_receiver)->m_##name##_cb(path, types, argv, argc, msg); } - - -/* FIXME: Make this receive and preprocess in the same thread? */ - - -/** Receives OSC messages from liblo. - * - * This inherits from QueuedEngineInterface and calls it's own functions - * via OSC. It's not actually a directly callable EngineInterface (it's - * callable via OSC...) so it should be implemented-as-a (privately inherit) - * QueuedEngineInterface, but it needs to be public so it's an EventSource - * the Driver can use. This probably should be fixed somehow.. - * - * \ingroup engine - */ -class OSCReceiver : public QueuedEngineInterface -{ -public: - OSCReceiver(Engine& engine, size_t queue_size, const char* const port); - ~OSCReceiver(); - - void activate(); - void deactivate(); - -private: - // Prevent copies (undefined) - OSCReceiver(const OSCReceiver&); - OSCReceiver& operator=(const OSCReceiver&); - - virtual void _run(); - - static void error_cb(int num, const char* msg, const char* path); - static int set_response_address_cb(LO_HANDLER_ARGS, void* osc_receiver); - static int generic_cb(LO_HANDLER_ARGS, void* osc_receiver); - static int unknown_cb(LO_HANDLER_ARGS, void* osc_receiver); - - LO_HANDLER(quit); - LO_HANDLER(ping); - LO_HANDLER(ping_slow); - LO_HANDLER(register_client); - LO_HANDLER(unregister_client); - LO_HANDLER(load_plugins); - LO_HANDLER(engine_activate); - LO_HANDLER(engine_deactivate); - LO_HANDLER(create_patch); - LO_HANDLER(rename); - LO_HANDLER(create_port); - LO_HANDLER(create_node); - LO_HANDLER(create_node_by_uri); - LO_HANDLER(enable_patch); - LO_HANDLER(disable_patch); - LO_HANDLER(clear_patch); - LO_HANDLER(destroy); - LO_HANDLER(connect); - LO_HANDLER(disconnect); - LO_HANDLER(disconnect_all); - LO_HANDLER(set_port_value); - LO_HANDLER(set_port_value_voice); - LO_HANDLER(set_port_value_slow); - LO_HANDLER(note_on); - LO_HANDLER(note_off); - LO_HANDLER(all_notes_off); - LO_HANDLER(midi_learn); - LO_HANDLER(metadata_get); - LO_HANDLER(metadata_set); - LO_HANDLER(request_plugins); - LO_HANDLER(request_all_objects); - LO_HANDLER(request_port_value); -#ifdef HAVE_DSSI - LO_HANDLER(dssi); -#endif -#ifdef HAVE_LASH - LO_HANDLER(lash_restore_done); -#endif - - const char* const _port; - lo_server _server; - - /** Cached OSC responder (for most recent incoming message) */ - CountedPtr _osc_responder; -}; - - -} // namespace Ingen - -#endif // OSCRECEIVER_H diff --git a/src/libs/engine/QueuedEventSource.cpp b/src/libs/engine/QueuedEventSource.cpp index 0ef11ac1..915b7811 100644 --- a/src/libs/engine/QueuedEventSource.cpp +++ b/src/libs/engine/QueuedEventSource.cpp @@ -16,6 +16,7 @@ #include "QueuedEventSource.h" #include "QueuedEvent.h" +#include "PostProcessor.h" #include #include using std::cout; using std::cerr; using std::endl; @@ -64,12 +65,43 @@ QueuedEventSource::push_queued(QueuedEvent* const ev) } +/** Process all events for a cycle. + * + * Executed events will be pushed to @a dest. + */ void -QueuedEventSource::push_stamped(Event* const ev) +QueuedEventSource::process(PostProcessor& dest, SampleCount nframes, FrameTime cycle_start, FrameTime cycle_end) { - _stamped_queue.push(ev); + Event* ev = NULL; + + /* Limit the maximum number of queued events to process per cycle. This + * makes the process callback (more) realtime-safe by preventing being + * choked by events coming in faster than they can be processed. + * FIXME: test this and figure out a good value */ + const unsigned int MAX_QUEUED_EVENTS = nframes / 100; + + unsigned int num_events_processed = 0; + + /* FIXME: Merge these next two loops into one */ + + while ((ev = pop_earliest_queued_before(cycle_end))) { + ev->execute(nframes, cycle_start, cycle_end); + dest.push(ev); + if (++num_events_processed > MAX_QUEUED_EVENTS) + break; + } + + while ((ev = pop_earliest_stamped_before(cycle_end))) { + ev->execute(nframes, cycle_start, cycle_end); + dest.push(ev); + ++num_events_processed; + } + + if (num_events_processed > 0) + dest.whip(); } + /** Pops the prepared event at the front of the prepare queue, if it exists. * * This method will only pop events that have been prepared, and are diff --git a/src/libs/engine/QueuedEventSource.h b/src/libs/engine/QueuedEventSource.h index abdea293..f6be92d7 100644 --- a/src/libs/engine/QueuedEventSource.h +++ b/src/libs/engine/QueuedEventSource.h @@ -29,6 +29,7 @@ namespace Ingen { class QueuedEvent; +class PostProcessor; /** Queue of events that need processing before reaching the audio thread. @@ -50,14 +51,16 @@ public: void activate() { Slave::start(); } void deactivate() { Slave::stop(); } - Event* pop_earliest_queued_before(const SampleCount time); - inline Event* pop_earliest_stamped_before(const SampleCount time); + void process(PostProcessor& dest, SampleCount nframes, FrameTime cycle_start, FrameTime cycle_end); void unblock(); protected: void push_queued(QueuedEvent* const ev); - void push_stamped(Event* const ev); + inline void push_stamped(Event* const ev) { _stamped_queue.push(ev); } + + Event* pop_earliest_queued_before(const SampleCount time); + inline Event* pop_earliest_stamped_before(const SampleCount time); bool unprepared_events() { return (_prepared_back != _back); } -- cgit v1.2.1