From c7b05953949dbc80eee22348270166c2a6704a95 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 21 Oct 2011 23:44:02 +0000 Subject: Move engine side OSC and HTTP stuff to separate modules. git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@3576 a436a847-0d15-0410-975c-d299462d15a1 --- src/http/HTTPClientSender.cpp | 160 ++++++++++ src/http/HTTPClientSender.hpp | 109 +++++++ src/http/HTTPEngineReceiver.cpp | 228 +++++++++++++++ src/http/HTTPEngineReceiver.hpp | 76 +++++ src/http/ingen_http.cpp | 54 ++++ src/osc/OSCClientSender.cpp | 269 +++++++++++++++++ src/osc/OSCClientSender.hpp | 110 +++++++ src/osc/OSCEngineReceiver.cpp | 588 +++++++++++++++++++++++++++++++++++++ src/osc/OSCEngineReceiver.hpp | 130 +++++++++ src/osc/ingen_osc.cpp | 53 ++++ src/server/HTTPClientSender.cpp | 156 ---------- src/server/HTTPClientSender.hpp | 109 ------- src/server/HTTPEngineReceiver.cpp | 227 --------------- src/server/HTTPEngineReceiver.hpp | 76 ----- src/server/OSCClientSender.cpp | 276 ------------------ src/server/OSCClientSender.hpp | 110 ------- src/server/OSCEngineReceiver.cpp | 593 -------------------------------------- src/server/OSCEngineReceiver.hpp | 130 --------- src/server/ingen_http.cpp | 52 ---- src/server/ingen_osc.cpp | 51 ---- src/server/wscript | 24 -- wscript | 6 +- 22 files changed, 1781 insertions(+), 1806 deletions(-) create mode 100644 src/http/HTTPClientSender.cpp create mode 100644 src/http/HTTPClientSender.hpp create mode 100644 src/http/HTTPEngineReceiver.cpp create mode 100644 src/http/HTTPEngineReceiver.hpp create mode 100644 src/http/ingen_http.cpp create mode 100644 src/osc/OSCClientSender.cpp create mode 100644 src/osc/OSCClientSender.hpp create mode 100644 src/osc/OSCEngineReceiver.cpp create mode 100644 src/osc/OSCEngineReceiver.hpp create mode 100644 src/osc/ingen_osc.cpp delete mode 100644 src/server/HTTPClientSender.cpp delete mode 100644 src/server/HTTPClientSender.hpp delete mode 100644 src/server/HTTPEngineReceiver.cpp delete mode 100644 src/server/HTTPEngineReceiver.hpp delete mode 100644 src/server/OSCClientSender.cpp delete mode 100644 src/server/OSCClientSender.hpp delete mode 100644 src/server/OSCEngineReceiver.cpp delete mode 100644 src/server/OSCEngineReceiver.hpp delete mode 100644 src/server/ingen_http.cpp delete mode 100644 src/server/ingen_osc.cpp diff --git a/src/http/HTTPClientSender.cpp b/src/http/HTTPClientSender.cpp new file mode 100644 index 00000000..564954ae --- /dev/null +++ b/src/http/HTTPClientSender.cpp @@ -0,0 +1,160 @@ +/* This file is part of Ingen. + * Copyright 2008-2011 David 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 + +#include + +#include "ingen/serialisation/Serialiser.hpp" +#include "ingen/shared/World.hpp" +#include "raul/Atom.hpp" +#include "raul/AtomRDF.hpp" +#include "raul/log.hpp" + +#include "../server/Engine.hpp" + +#include "HTTPClientSender.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +void +HTTPClientSender::response_ok(int32_t id) +{ +} + +void +HTTPClientSender::response_error(int32_t id, const std::string& msg) +{ + warn << "HTTP Error " << id << " (" << msg << ")" << endl; +} + +void +HTTPClientSender::error(const std::string& msg) +{ + warn << "HTTP send error " << msg << endl; +} + +void +HTTPClientSender::put(const URI& uri, + const Resource::Properties& properties, + Resource::Graph ctx) +{ + const std::string request_uri = (Raul::Path::is_path(uri)) + ? _url + "/patch" + uri.substr(uri.find("/")) + : uri.str(); + + + Sord::Model model(*_engine.world()->rdf_world()); + for (Resource::Properties::const_iterator i = properties.begin(); + i != properties.end(); ++i) + model.add_statement( + Sord::URI(*_engine.world()->rdf_world(), request_uri), + AtomRDF::atom_to_node(model, i->first.str()), + AtomRDF::atom_to_node(model, i->second)); + + const string str = model.write_to_string("", SERD_TURTLE); + send_chunk(str); +} + +void +HTTPClientSender::delta(const URI& uri, + const Resource::Properties& remove, + const Resource::Properties& add) +{ +} + +void +HTTPClientSender::del(const URI& uri) +{ + send_chunk(string("<").append(uri.str()).append("> a .")); +} + +void +HTTPClientSender::connect(const Path& src_path, const Path& dst_path) +{ + const string msg = string( + "@prefix rdf: .\n" + "@prefix ingen: .\n").append( + "<> ingen:connection [\n" + "\tingen:destination <").append(dst_path.str()).append("> ;\n" + "\tingen:source <").append(src_path.str()).append(">\n] .\n"); + send_chunk(msg); +} + +void +HTTPClientSender::disconnect(const URI& src, + const URI& dst) +{ +} + +void +HTTPClientSender::disconnect_all(const Raul::Path& parent_patch_path, + const Raul::Path& path) +{ +} + +void +HTTPClientSender::set_property(const URI& subject, const URI& key, const Atom& value) +{ +#if 0 + Sord::Node node = AtomRDF::atom_to_node(*_engine.world()->rdf_world(), value); + const string msg = string( + "@prefix rdf: .\n" + "@prefix ingen: .\n" + "@prefix ingenui: .\n").append( + subject.str()).append("> ingen:property [\n" + "rdf:predicate ").append(key.str()).append(" ;\n" + "rdf:value ").append(node.to_string()).append("\n] .\n"); + send_chunk(msg); +#endif +} + +void +HTTPClientSender::activity(const Path& path, const Raul::Atom& value) +{ + if (value.type() == Atom::BOOL) { + const string msg = string( + "@prefix ingen: .\n\n<").append( + path.str()).append("> ingen:activity true .\n"); + send_chunk(msg); + } else if (value.type() == Atom::FLOAT) { + const string msg = string( + "@prefix ingen: .\n\n<").append( + path.str()).append("> ingen:activity ").append( + value.get_bool() ? "true" : "false").append(" .\n"); + send_chunk(msg); + } else { + warn << "Unknown activity type at " << path << endl; + } +} + +void +HTTPClientSender::move(const Path& old_path, const Path& new_path) +{ + string msg = string( + "@prefix rdf: .\n" + "@prefix ingen: .\n\n<").append( + old_path.str()).append("> rdf:subject <").append(new_path.str()).append("> .\n"); + send_chunk(msg); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/http/HTTPClientSender.hpp b/src/http/HTTPClientSender.hpp new file mode 100644 index 00000000..116f0601 --- /dev/null +++ b/src/http/HTTPClientSender.hpp @@ -0,0 +1,109 @@ +/* This file is part of Ingen. + * Copyright 2008-2011 David 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 INGEN_ENGINE_HTTPCLIENTSENDER_HPP +#define INGEN_ENGINE_HTTPCLIENTSENDER_HPP + +#include +#include + +#include "raul/Thread.hpp" + +#include "ingen/ClientInterface.hpp" +#include "../shared/HTTPSender.hpp" + +namespace Ingen { + +class ServerInterface; + +namespace Server { + +class Engine; + +/** Implements ClientInterface for HTTP clients. + * Sends changes as RDF deltas over an HTTP stream + * (a single message with chunked encoding response). + * + * \ingroup engine + */ +class HTTPClientSender + : public ClientInterface + , public Ingen::Shared::HTTPSender +{ +public: + explicit HTTPClientSender(Engine& engine) + : _engine(engine) + , _enabled(true) + {} + + bool enabled() const { return _enabled; } + + void enable() { _enabled = true; } + void disable() { _enabled = false; } + + void bundle_begin() { HTTPSender::bundle_begin(); } + void bundle_end() { HTTPSender::bundle_end(); } + + Raul::URI uri() const { return "http://example.org/"; } + + /* *** ClientInterface Implementation Below *** */ + + void response_ok(int32_t id); + void response_error(int32_t id, const std::string& msg); + + void error(const std::string& msg); + + virtual void put(const Raul::URI& path, + const Resource::Properties& properties, + Resource::Graph ctx); + + virtual void delta(const Raul::URI& path, + const Resource::Properties& remove, + const Resource::Properties& add); + + virtual void del(const Raul::URI& uri); + + virtual void move(const Raul::Path& old_path, + const Raul::Path& new_path); + + virtual void connect(const Raul::Path& src_port_path, + const Raul::Path& dst_port_path); + + virtual void disconnect(const Raul::URI& src, + const Raul::URI& dst); + + virtual void disconnect_all(const Raul::Path& parent_patch_path, + const Raul::Path& path); + + virtual void set_property(const Raul::URI& subject_path, + const Raul::URI& predicate, + const Raul::Atom& value); + + virtual void activity(const Raul::Path& path, + const Raul::Atom& value); + +private: + Engine& _engine; + std::string _url; + bool _enabled; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_HTTPCLIENTSENDER_HPP + diff --git a/src/http/HTTPEngineReceiver.cpp b/src/http/HTTPEngineReceiver.cpp new file mode 100644 index 00000000..2f39ee9c --- /dev/null +++ b/src/http/HTTPEngineReceiver.cpp @@ -0,0 +1,228 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David 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 +#include +#include + +#include + +#include + +#include "raul/SharedPtr.hpp" +#include "raul/log.hpp" + +#include "ingen/ClientInterface.hpp" +#include "ingen/ServerInterface.hpp" +#include "ingen/serialisation/Parser.hpp" +#include "ingen/serialisation/Serialiser.hpp" +#include "ingen/shared/Module.hpp" +#include "ingen/shared/Store.hpp" + +#include "../server/ClientBroadcaster.hpp" +#include "../server/Engine.hpp" + +#include "HTTPClientSender.hpp" +#include "HTTPEngineReceiver.hpp" + +#define LOG(s) s << "[HTTPEngineReceiver] " + +using namespace std; +using namespace Raul; + +namespace Ingen { + +using namespace Serialisation; + +namespace Server { + +HTTPEngineReceiver::HTTPEngineReceiver(Engine& engine, + SharedPtr interface, + uint16_t port) + : _engine(engine) + , _interface(interface) + , _server(soup_server_new(SOUP_SERVER_PORT, port, NULL)) +{ + _receive_thread = new ReceiveThread(*this); + + soup_server_add_handler(_server, NULL, message_callback, this, NULL); + + LOG(info) << "Started HTTP server on port " << soup_server_get_port(_server) << endl; + + if (!engine.world()->parser() || !engine.world()->serialiser()) + engine.world()->load_module("serialisation"); + + _receive_thread->set_name("HTTPEngineReceiver Listener"); + _receive_thread->start(); +} + +HTTPEngineReceiver::~HTTPEngineReceiver() +{ + _receive_thread->stop(); + delete _receive_thread; + + if (_server) { + soup_server_quit(_server); + _server = NULL; + } +} + +void +HTTPEngineReceiver::message_callback(SoupServer* server, + SoupMessage* msg, + const char* path_str, + GHashTable* query, + SoupClientContext* client, + void* data) +{ + HTTPEngineReceiver* me = (HTTPEngineReceiver*)data; + ServerInterface* interface = me->_interface.get(); + + using namespace Ingen::Shared; + + SharedPtr store = me->_engine.world()->store(); + if (!store) { + soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + return; + } + + string path = path_str; + if (path[path.length() - 1] == '/') { + path = path.substr(0, path.length()-1); + } + + SharedPtr serialiser = me->_engine.world()->serialiser(); + + const string base_uri = "path:/"; + const char* mime_type = "text/plain"; + + // Special GET paths + if (msg->method == SOUP_METHOD_GET) { + if (path == Path::root().str() || path.empty()) { + const string r = string("@prefix rdfs: .\n") + .append("\n<> rdfs:seeAlso ;") + .append("\n rdfs:seeAlso ;") + .append("\n rdfs:seeAlso ."); + soup_message_set_status(msg, SOUP_STATUS_OK); + soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, r.c_str(), r.length()); + return; + + } else if (msg->method == SOUP_METHOD_GET && path.substr(0, 8) == "/plugins") { + // FIXME: kludge + #if 0 + interface->get("ingen:plugins"); + me->_receive_thread->whip(); + + serialiser->start_to_string("/", base_uri); + for (NodeFactory::Plugins::const_iterator p = me->_engine.node_factory()->plugins().begin(); + p != me->_engine.node_factory()->plugins().end(); ++p) + serialiser->serialise_plugin(*(Shared::Plugin*)p->second); + const string r = serialiser->finish(); + soup_message_set_status(msg, SOUP_STATUS_OK); + soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, r.c_str(), r.length()); + #endif + return; + + } else if (path.substr(0, 6) == "/patch") { + path = '/' + path.substr(6); + if (path.substr(0, 2) == "//") + path = path.substr(1); + + } else if (path.substr(0, 7) == "/stream") { + HTTPClientSender* client = new HTTPClientSender(me->_engine); + interface->register_client(client); + + // Respond with port number of stream for client + const int port = client->listen_port(); + char buf[32]; + snprintf(buf, sizeof(buf), "%d", port); + soup_message_set_status(msg, SOUP_STATUS_OK); + soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, buf, strlen(buf)); + return; + } + } + + if (!Path::is_valid(path)) { + LOG(error) << "Bad HTTP path: " << path << endl; + soup_message_set_status(msg, SOUP_STATUS_BAD_REQUEST); + const string& err = (boost::format("Bad path: %1%") % path).str(); + soup_message_set_response(msg, "text/plain", SOUP_MEMORY_COPY, + err.c_str(), err.length()); + return; + } + + if (msg->method == SOUP_METHOD_GET) { + Glib::RWLock::ReaderLock lock(store->lock()); + + // Find object + Store::const_iterator start = store->find(path); + if (start == store->end()) { + soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND); + const string& err = (boost::format("No such object: %1%") % path).str(); + soup_message_set_response(msg, "text/plain", SOUP_MEMORY_COPY, + err.c_str(), err.length()); + return; + } + + // Get serialiser + SharedPtr serialiser = me->_engine.world()->serialiser(); + if (!serialiser) { + soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + soup_message_set_response(msg, "text/plain", SOUP_MEMORY_STATIC, + "No serialiser available\n", 24); + return; + } + + // Serialise object + const string response = serialiser->to_string(start->second, + "http://localhost:16180/patch", GraphObject::Properties()); + + soup_message_set_status(msg, SOUP_STATUS_OK); + soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, + response.c_str(), response.length()); + + } else if (msg->method == SOUP_METHOD_PUT) { + Glib::RWLock::WriterLock lock(store->lock()); + + // Get parser + SharedPtr parser = me->_engine.world()->parser(); + if (!parser) { + soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + return; + } + + parser->parse_string(me->_engine.world(), interface, msg->request_body->data, base_uri); + soup_message_set_status(msg, SOUP_STATUS_OK); + + } else if (msg->method == SOUP_METHOD_DELETE) { + interface->del(path); + soup_message_set_status(msg, SOUP_STATUS_OK); + + } else { + soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED); + } +} + +void +HTTPEngineReceiver::ReceiveThread::_run() +{ + soup_server_run(_receiver._server); +} + +} // namespace Server +} // namespace Ingen + diff --git a/src/http/HTTPEngineReceiver.hpp b/src/http/HTTPEngineReceiver.hpp new file mode 100644 index 00000000..e17efe05 --- /dev/null +++ b/src/http/HTTPEngineReceiver.hpp @@ -0,0 +1,76 @@ +/* This file is part of Ingen. + * Copyright 2008-2011 David 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 INGEN_ENGINE_HTTPENGINERECEIVER_HPP +#define INGEN_ENGINE_HTTPENGINERECEIVER_HPP + +#include + +#include + +#include "raul/Thread.hpp" + +typedef struct _SoupServer SoupServer; +typedef struct _SoupMessage SoupMessage; +typedef struct SoupClientContext SoupClientContext; + +namespace Ingen { + +class ServerInterface; + +namespace Server { + +class Engine; + +class HTTPEngineReceiver +{ +public: + HTTPEngineReceiver(Engine& engine, + SharedPtr interface, + uint16_t port); + + ~HTTPEngineReceiver(); + +private: + struct ReceiveThread : public Raul::Thread { + explicit ReceiveThread(HTTPEngineReceiver& receiver) + : _receiver(receiver) + {} + virtual void _run(); + private: + HTTPEngineReceiver& _receiver; + }; + + friend class ReceiveThread; + + static void message_callback(SoupServer* server, + SoupMessage* msg, + const char* path, + GHashTable *query, + SoupClientContext* client, + void* data); + + Engine& _engine; + SharedPtr _interface; + ReceiveThread* _receive_thread; + SoupServer* _server; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_HTTPENGINERECEIVER_HPP diff --git a/src/http/ingen_http.cpp b/src/http/ingen_http.cpp new file mode 100644 index 00000000..0a08f59b --- /dev/null +++ b/src/http/ingen_http.cpp @@ -0,0 +1,54 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David 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 "ingen/shared/Module.hpp" +#include "ingen/shared/World.hpp" + +#include "../server/Engine.hpp" +#include "../server/ServerInterfaceImpl.hpp" + +#include "HTTPEngineReceiver.hpp" + +using namespace std; +using namespace Ingen; + +struct IngenHTTPModule : public Ingen::Shared::Module { + void load(Ingen::Shared::World* world) { + Server::Engine* engine = (Server::Engine*)world->local_engine().get(); + SharedPtr interface( + new Server::ServerInterfaceImpl(*engine)); + + receiver = SharedPtr( + new Server::HTTPEngineReceiver( + *engine, + interface, + world->conf()->option("engine-port").get_int32())); + engine->add_event_source(interface); + } + + SharedPtr receiver; +}; + +extern "C" { + +Ingen::Shared::Module* +ingen_module_load() +{ + return new IngenHTTPModule(); +} + +} // extern "C" diff --git a/src/osc/OSCClientSender.cpp b/src/osc/OSCClientSender.cpp new file mode 100644 index 00000000..ba098a61 --- /dev/null +++ b/src/osc/OSCClientSender.cpp @@ -0,0 +1,269 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David 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 + +#include +#include + +#include "raul/log.hpp" +#include "raul/AtomLiblo.hpp" +#include "raul/Path.hpp" + +#include "ingen/ClientInterface.hpp" + +#include "OSCClientSender.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +/** @page client_osc_namespace Client OSC Namespace Documentation + * + *

These are the commands the client recognizes. All monitoring of + * changes in the engine happens via these commands.

+ */ + +/** @page client_osc_namespace + *

/ok

+ * @arg @p response-id :: Integer + * + * @par + * Successful response to some command. + */ +void +OSCClientSender::response_ok(int32_t id) +{ + if (!_enabled) + return; + + if (lo_send(_address, "/ok", "i", id, LO_ARGS_END) < 0) { + Raul::error << "Unable to send OK " << id << "! (" + << lo_address_errstr(_address) << ")" << endl; + } +} + +/** @page client_osc_namespace + *

/error

+ * @arg @p response-id :: Integer + * @arg @p message :: String + * + * @par + * Unsuccessful response to some command. + */ +void +OSCClientSender::response_error(int32_t id, const std::string& msg) +{ + if (!_enabled) + return; + + if (lo_send(_address, "/error", "is", id, msg.c_str(), LO_ARGS_END) < 0) { + Raul::error << "Unable to send error " << id << "! (" + << lo_address_errstr(_address) << ")" << endl; + } +} + +/** @page client_osc_namespace + *

/error

+ * @arg @p message :: String + * + * @par + * Notification that an error has occurred. This is for notification of errors + * that aren't a direct response to a user command, i.e. "unexpected" errors. + */ +void +OSCClientSender::error(const std::string& msg) +{ + send("/error", "s", msg.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/put

+ * @arg @p path :: String + * @arg @p predicate :: URI String + * @arg @p value + * @arg @p ... + * + * @par + * PUT a set of properties to a path. + */ +void +OSCClientSender::put(const Raul::URI& path, + const Resource::Properties& properties, + Resource::Graph ctx) +{ + typedef Resource::Properties::const_iterator iterator; + lo_message m = lo_message_new(); + lo_message_add_string(m, path.c_str()); + lo_message_add_string(m, Resource::graph_to_uri(ctx).c_str()); + for (iterator i = properties.begin(); i != properties.end(); ++i) { + lo_message_add_string(m, i->first.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, i->second); + } + send_message("/put", m); +} + +void +OSCClientSender::delta(const Raul::URI& path, + const Resource::Properties& remove, + const Resource::Properties& add) +{ + typedef Resource::Properties::const_iterator iterator; + + const bool bundle = !_bundle; + if (bundle) + bundle_begin(); + + send("/delta_begin", "s", path.c_str(), LO_ARGS_END); + + for (iterator i = remove.begin(); i != remove.end(); ++i) { + lo_message m = lo_message_new(); + lo_message_add_string(m, i->first.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, i->second); + send_message("/delta_remove", m); + } + + for (iterator i = add.begin(); i != add.end(); ++i) { + lo_message m = lo_message_new(); + lo_message_add_string(m, i->first.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, i->second); + send_message("/delta_add", m); + } + + send("/delta_end", "", LO_ARGS_END); + + if (bundle) + bundle_end(); +} + +/** @page client_osc_namespace + *

/move

+ * @arg @p old-path :: String + * @arg @p new-path :: String + * + * @par + * MOVE an object to a new path. + * The new path will have the same parent as the old path. + */ +void +OSCClientSender::move(const Path& old_path, const Path& new_path) +{ + send("/move", "ss", old_path.c_str(), new_path.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/delete

+ * @arg @p path :: String + * + * @par + * DELETE an object. + */ +void +OSCClientSender::del(const URI& uri) +{ + send("/delete", "s", uri.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/connect

+ * @arg @p src-path :: String + * @arg @p dst-path :: String + * + * @par + * Notification a new connection has been made. + */ +void +OSCClientSender::connect(const Path& src_port_path, + const Path& dst_port_path) +{ + send("/connect", "ss", src_port_path.c_str(), dst_port_path.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/disconnect

+ * @arg @p src-path :: String + * @arg @p dst-path :: String + * + * @par + * Notification a connection has been unmade. + */ +void +OSCClientSender::disconnect(const URI& src, + const URI& dst) +{ + send("/disconnect", "ss", src.c_str(), dst.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/disconnect_all

+ * @arg @p parent-patch-path :: String + * @arg @p path :: String + * + * @par + * Notification all connections to an object have been disconnected. + */ +void +OSCClientSender::disconnect_all(const Raul::Path& parent_patch_path, + const Raul::Path& path) +{ + send("/disconnect_all", "ss", parent_patch_path.c_str(), path.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/set_property

+ * @arg @p path :: String + * @arg @p key :: URI String + * @arg @p value + * + * @par + * Notification of a property. + */ +void +OSCClientSender::set_property(const URI& path, + const URI& key, + const Atom& value) +{ + lo_message m = lo_message_new(); + lo_message_add_string(m, path.c_str()); + lo_message_add_string(m, key.c_str()); + AtomLiblo::lo_message_add_atom(m, value); + send_message("/set_property", m); +} + +/** @page client_osc_namespace + *

/activity

+ * @arg @p path :: String + * + * @par + * Notification of "activity" (e.g. port message blinkenlights). + */ +void +OSCClientSender::activity(const Path& path, const Raul::Atom& value) +{ + if (!_enabled) + return; + + lo_message m = lo_message_new(); + lo_message_add_string(m, path.c_str()); + AtomLiblo::lo_message_add_atom(m, value); + send_message("/activity", m); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/osc/OSCClientSender.hpp b/src/osc/OSCClientSender.hpp new file mode 100644 index 00000000..ffc3b202 --- /dev/null +++ b/src/osc/OSCClientSender.hpp @@ -0,0 +1,110 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David 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 INGEN_ENGINE_OSCCLIENTSENDER_HPP +#define INGEN_ENGINE_OSCCLIENTSENDER_HPP + +#include +#include + +#include + +#include "ingen/ClientInterface.hpp" +#include "ingen/GraphObject.hpp" +#include "../shared/OSCSender.hpp" + +namespace Ingen { + +class ServerInterface; + +namespace Server { + + +/** Implements ClientInterface for OSC clients (sends OSC messages). + * + * \ingroup engine + */ +class OSCClientSender : public ClientInterface, + public Ingen::Shared::OSCSender +{ +public: + explicit OSCClientSender(const Raul::URI& url, + size_t max_packet_size) + : Shared::OSCSender(max_packet_size) + , _url(url) + { + _address = lo_address_new_from_url(url.c_str()); + } + + virtual ~OSCClientSender() + { lo_address_free(_address); } + + bool enabled() const { return _enabled; } + + void enable() { _enabled = true; } + void disable() { _enabled = false; } + + void bundle_begin() { OSCSender::bundle_begin(); } + void bundle_end() { OSCSender::bundle_end(); } + + Raul::URI uri() const { return _url; } + + /* *** ClientInterface Implementation Below *** */ + + void response_ok(int32_t id); + void response_error(int32_t id, const std::string& msg); + + void error(const std::string& msg); + + virtual void put(const Raul::URI& path, + const Resource::Properties& properties, + Resource::Graph ctx=Resource::DEFAULT); + + virtual void delta(const Raul::URI& path, + const Resource::Properties& remove, + const Resource::Properties& add); + + virtual void del(const Raul::URI& uri); + + virtual void move(const Raul::Path& old_path, + const Raul::Path& new_path); + + virtual void connect(const Raul::Path& src_port_path, + const Raul::Path& dst_port_path); + + virtual void disconnect(const Raul::URI& src, + const Raul::URI& dst); + + virtual void disconnect_all(const Raul::Path& parent_patch_path, + const Raul::Path& path); + + virtual void set_property(const Raul::URI& subject, + const Raul::URI& predicate, + const Raul::Atom& value); + + virtual void activity(const Raul::Path& path, + const Raul::Atom& value); + +private: + Raul::URI _url; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_OSCCLIENTSENDER_HPP + diff --git a/src/osc/OSCEngineReceiver.cpp b/src/osc/OSCEngineReceiver.cpp new file mode 100644 index 00000000..254cc850 --- /dev/null +++ b/src/osc/OSCEngineReceiver.cpp @@ -0,0 +1,588 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David 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 +#include + +#include + +#include + +#include "raul/AtomLiblo.hpp" +#include "raul/Path.hpp" +#include "raul/SharedPtr.hpp" +#include "raul/log.hpp" + +#include "ingen-config.h" +#include "ingen/ClientInterface.hpp" +#include "ingen/ServerInterface.hpp" + +#include "../server/ClientBroadcaster.hpp" +#include "../server/Engine.hpp" + +#include "OSCClientSender.hpp" +#include "OSCEngineReceiver.hpp" + +#define LOG(s) s << "[OSCEngineReceiver] " + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +/** @page engine_osc_namespace Server 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, + SharedPtr interface, + uint16_t port) + : _engine(engine) + , _interface(interface) + , _server(NULL) +{ + _receive_thread = new ReceiveThread(*this); + + char port_str[6]; + snprintf(port_str, sizeof(port_str), "%u", port); + + _server = lo_server_new(port_str, error_cb); + + if (_server == NULL) { + LOG(error) << "Could not start OSC server. Aborting." << endl; + exit(EXIT_FAILURE); + } else { + char* lo_url = lo_server_get_url(_server); + LOG(info) << "Started OSC server at " << lo_url << endl; + free(lo_url); + } + +#ifdef RAUL_LOG_DEBUG + lo_server_add_method(_server, NULL, NULL, generic_cb, NULL); +#endif + + // 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); + +#ifdef LIBLO_BUNDLES + lo_server_add_bundle_handlers(_server, bundle_start_cb, bundle_end_cb, this); +#endif + + // Commands + lo_server_add_method(_server, "/ping", "i", ping_cb, this); + lo_server_add_method(_server, "/ping_queued", "i", ping_slow_cb, this); + lo_server_add_method(_server, "/register_client", "i", register_client_cb, this); + lo_server_add_method(_server, "/unregister_client", "i", unregister_client_cb, this); + lo_server_add_method(_server, "/put", NULL, put_cb, this); + lo_server_add_method(_server, "/delta_begin", NULL, delta_begin_cb, this); + lo_server_add_method(_server, "/delta_remove", NULL, delta_remove_cb, this); + lo_server_add_method(_server, "/delta_add", NULL, delta_add_cb, this); + lo_server_add_method(_server, "/delta_end", NULL, delta_end_cb, this); + lo_server_add_method(_server, "/move", "iss", move_cb, this); + lo_server_add_method(_server, "/delete", "is", del_cb, this); + lo_server_add_method(_server, "/connect", "iss", connect_cb, this); + lo_server_add_method(_server, "/disconnect", "iss", disconnect_cb, this); + lo_server_add_method(_server, "/disconnect_all", "iss", disconnect_all_cb, this); + lo_server_add_method(_server, "/note_on", "isii", note_on_cb, this); + lo_server_add_method(_server, "/note_off", "isi", note_off_cb, this); + lo_server_add_method(_server, "/all_notes_off", "isi", all_notes_off_cb, this); + lo_server_add_method(_server, "/learn", "is", learn_cb, this); + lo_server_add_method(_server, "/set_property", NULL, set_property_cb, this); + + // Queries + lo_server_add_method(_server, "/get", "is", get_cb, this); + + lo_server_add_method(_server, NULL, NULL, unknown_cb, NULL); + + _receive_thread->set_name("OSCEngineReceiver Listener"); + _receive_thread->start(); + _receive_thread->set_scheduling(SCHED_FIFO, 5); +} + +OSCEngineReceiver::~OSCEngineReceiver() +{ + _receive_thread->stop(); + delete _receive_thread; + + if (_server != NULL) { + lo_server_free(_server); + _server = NULL; + } +} + +/** Override the semaphore driven _run method of ServerInterfaceImpl + * to wait on OSC messages and prepare them right away in the same thread. + */ +void +OSCEngineReceiver::ReceiveThread::_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) { + // Wait on a message and enqueue it + lo_server_recv(_receiver._server); + + // Enqueue every other message that is here "now" + // (would this provide truly atomic bundles?) + while (lo_server_recv_noblock(_receiver._server, 0) > 0) {} + } +} + +/** Create a new request for this message, if necessary. + * + * This is based on the fact that the current request is stored in a ref + * counted pointer, and events just take a reference to that. Thus, events + * may delete their request if we've since switched to a new one, or the + * same one can stay around and serve a series of events. + * Hooray for reference counting. + * + * If this message came from the same source as the last message, no allocation + * of requests 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. + * Lack of a fast liblo address comparison really sucks here, in any case. + */ +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); + + if (argc < 1 || types[0] != 'i') // Not a valid Ingen message + return 0; // Save liblo the trouble + + const int32_t id = argv[0]->i; + + const lo_address addr = lo_message_get_source(msg); + char* const url = lo_address_get_url(addr); + + if (id != -1) { + // TODO: Cache client + ClientInterface* client = me->_engine.broadcaster()->client(url); + me->_interface->respond_to(client, id); + } else { + me->_interface->disable_responses(); + } + + free(url); + + // If this returns 0 no OSC commands will work + return 1; +} + +#ifdef LIBLO_BUNDLES +int +OSCEngineReceiver::_bundle_start_cb(lo_timetag time) +{ + info << "BUNDLE START" << endl; + return 0; +} + +int +OSCEngineReceiver::_bundle_end_cb() +{ + info << "BUNDLE END" << endl; + return 0; +} +#endif + +void +OSCEngineReceiver::error_cb(int num, const char* msg, const char* path) +{ + error << "liblo server error" << num; + if (path) { + error << " for path `" << path << "'"; + } + error << " (" << msg << ")" << endl; +} + +/** @page engine_osc_namespace + *

/ping

+ * @arg @p response-id :: Integer + */ +int +OSCEngineReceiver::_ping_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const lo_address addr = lo_message_get_source(msg); + if (lo_send(addr, "/ok", "i", argv[0]->i) < 0) + warn << "Unable to send response (" << lo_address_errstr(addr) << ")" << endl; + return 0; +} + +/** @page engine_osc_namespace + *

/ping_queued

+ * @arg @p response-id :: Integer + * + * @par + * Reply to sender with a successful response after going through the + * event queue. This is useful for checking if the engine is actually active, + * or for sending after several events as a sentinel and wait on it's response, + * to know when all previous events have finished processing. + */ +int +OSCEngineReceiver::_ping_slow_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _interface->ping(); + return 0; +} + +/** @page engine_osc_namespace + *

/register_client

+ * @arg @p response-id :: Integer + * + * @par + * Register a new client with the engine. The incoming address will be + * used for the new registered client. + */ +int +OSCEngineReceiver::_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); + ClientInterface* client = new OSCClientSender( + (const char*)url, + _engine.world()->conf()->option("packet-size").get_int32()); + _interface->register_client(client); + free(url); + + return 0; +} + +/** @page engine_osc_namespace + *

/unregister_client

+ * @arg @p response-id :: Integer + * + * @par + * Unregister a client. + */ +int +OSCEngineReceiver::_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); + _interface->unregister_client(url); + free(url); + + return 0; +} + +/** @page engine_osc_namespace + *

/get

+ * @arg @p response-id :: Integer + * @arg @p uri :: URI String + * + * @par + * Request all properties of an object. + */ +int +OSCEngineReceiver::_get_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _interface->get(&argv[1]->s); + return 0; +} + +/** @page engine_osc_namespace + *

/put

+ * @arg @p response-id :: Integer + * @arg @p path :: String + * @arg @p context :: URI String + * @arg @p predicate :: URI String + * @arg @p value + * @arg @p ... + * + * @par + * PUT a set of properties to a path. + */ +int +OSCEngineReceiver::_put_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* obj_path = &argv[1]->s; + const char* ctx = &argv[2]->s; + Resource::Properties prop; + for (int i = 3; i < argc-1; i += 2) + prop.insert(make_pair(&argv[i]->s, + AtomLiblo::lo_arg_to_atom(types[i+1], argv[i+1]))); + _interface->put(obj_path, prop, Resource::uri_to_graph(ctx)); + return 0; +} + +int +OSCEngineReceiver::_delta_begin_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* obj_path = &argv[1]->s; + assert(_delta_remove.empty()); + assert(_delta_add.empty()); + _delta_uri = obj_path; + return 0; +} + +int +OSCEngineReceiver::_delta_remove_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _delta_remove.insert(make_pair(&argv[1]->s, + AtomLiblo::lo_arg_to_atom(types[2], argv[2]))); + return 0; +} + +int +OSCEngineReceiver::_delta_add_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _delta_add.insert(make_pair(&argv[1]->s, + AtomLiblo::lo_arg_to_atom(types[2], argv[2]))); + return 0; +} + +int +OSCEngineReceiver::_delta_end_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _interface->delta(_delta_uri, _delta_remove, _delta_add); + _delta_uri = Raul::URI(); + _delta_remove.clear(); + _delta_add.clear(); + return 0; +} + +/** @page engine_osc_namespace + *

/move

+ * @arg @p response-id :: Integer + * @arg @p old-path :: String + * @arg @p new-path :: String + * + * @par + * MOVE an object to a new path. + */ +int +OSCEngineReceiver::_move_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* old_path = &argv[1]->s; + const char* new_path = &argv[2]->s; + + _interface->move(old_path, new_path); + return 0; +} + +/** @page engine_osc_namespace + *

/delete

+ * @arg @p response-id :: Integer + * @arg @p path :: String + * + * @par + * DELETE an object. + */ +int +OSCEngineReceiver::_del_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* uri = &argv[1]->s; + + _interface->del(uri); + return 0; +} + +/** @page engine_osc_namespace + *

/connect

+ * @arg @p response-id :: Integer + * @arg @p src-port-path :: String + * @arg @p dst-port-path :: String + * + * @par + * Connect two ports (which must be in the same patch). + */ +int +OSCEngineReceiver::_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; + + _interface->connect(src_port_path, dst_port_path); + return 0; +} + +/** @page engine_osc_namespace + *

/disconnect

+ * @arg @p response-id :: Integer + * @arg @p src-port-path :: String + * @arg @p dst-port-path :: String + * + * @par + * Disconnect two ports. + */ +int +OSCEngineReceiver::_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; + + _interface->disconnect(src_port_path, dst_port_path); + return 0; +} + +/** @page engine_osc_namespace + *

/disconnect_all

+ * @arg @p response-id :: Integer + * @arg @p patch-path :: String + * @arg @p object-path :: String + * + * @par + * Disconnect all connections to/from a node/port. + */ +int +OSCEngineReceiver::_disconnect_all_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* patch_path = &argv[1]->s; + const char* object_path = &argv[2]->s; + + _interface->disconnect_all(patch_path, object_path); + return 0; +} + +/** @page engine_osc_namespace + *

/note_on

+ * @arg @p response-id :: Integer + * @arg @p node-path :: String + * @arg @p note-num (int) + * @arg @p velocity (int) + * + * @par + * Trigger a note-on, just as if it came from MIDI. + */ +int +OSCEngineReceiver::_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 uint8_t note_num = argv[2]->i; + const uint8_t velocity = argv[3]->i; + */ + warn << "TODO: OSC note on" << endl; + //note_on(node_path, note_num, velocity); + return 0; +} + +/** @page engine_osc_namespace + *

/note_off

+ * @arg @p response-id :: Integer + * @arg @p node-path :: String + * @arg @p note-num :: Integer + * + * @par + * Trigger a note-off, just as if it came from MIDI. + */ +int +OSCEngineReceiver::_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 uint8_t note_num = argv[2]->i; + */ + warn << "TODO: OSC note off" << endl; + //note_off(patch_path, note_num); + return 0; +} + +/** @page engine_osc_namespace + *

/all_notes_off

+ * @arg @p response-id :: Integer + * @arg @p patch-path :: String + * + * @par + * Trigger a note-off for all voices, just as if it came from MIDI. + */ +int +OSCEngineReceiver::_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; + */ + warn << "TODO: OSC all notes off" << endl; + //all_notes_off(patch_path); + return 0; +} + +/** @page engine_osc_namespace + *

/set_property

+ * @arg @p response-id :: Integer + * @arg @p uri :: URI String + * @arg @p key :: URI String + * @arg @p value :: String + * + * @par + * Set a property on a graph object. + */ +int +OSCEngineReceiver::_set_property_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + if (argc != 4 || types[0] != 'i' || types[1] != 's' || types[2] != 's') + return 1; + + const char* object_path = &argv[1]->s; + const char* key = &argv[2]->s; + + Raul::Atom value = Raul::AtomLiblo::lo_arg_to_atom(types[3], argv[3]); + + _interface->set_property(object_path, key, value); + return 0; +} + + +// 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("[OSCEngineReceiver] %s (%s)\t", path, types); + + for (int i=0; i < argc; ++i) { + lo_arg_pp(lo_type(types[i]), argv[i]); + printf("\t"); + } + printf("\n"); + + return 1; // not handled +} + +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); + + warn << "Unknown OSC command " << path << " (" << types << ") " + << "received from " << url << endl; + + string error_msg = "Unknown command: "; + error_msg.append(path).append(" ").append(types); + + lo_send(addr, "/error", "s", error_msg.c_str(), LO_ARGS_END); + free(url); + + return 0; +} + +} // namespace Server +} // namespace Ingen diff --git a/src/osc/OSCEngineReceiver.hpp b/src/osc/OSCEngineReceiver.hpp new file mode 100644 index 00000000..fe16ac1a --- /dev/null +++ b/src/osc/OSCEngineReceiver.hpp @@ -0,0 +1,130 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David 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 INGEN_ENGINE_OSCENGINERECEIVER_HPP +#define INGEN_ENGINE_OSCENGINERECEIVER_HPP + +#include + +#include + +#include "raul/Thread.hpp" +#include "raul/URI.hpp" + +#include "ingen/Resource.hpp" + +#include "ingen-config.h" + +namespace Ingen { + +class ServerInterface; + +namespace Server { + +class Engine; + +/* Some boilerplate killing macros... */ +#define LO_HANDLER_ARGS const char* path, const char* types, lo_arg** argv, int argc, lo_message msg + +/* Defines a static handler to be passed to lo_add_method, which is a trivial + * wrapper around a non-static method that does the real work. Makes a whoole + * lot of ugly boiler plate go away */ +#define LO_HANDLER(name) \ +int _##name##_cb (LO_HANDLER_ARGS);\ +inline static int name##_cb(LO_HANDLER_ARGS, void* myself)\ +{ return ((OSCEngineReceiver*)myself)->_##name##_cb(path, types, argv, argc, msg); } + +/* FIXME: Make this receive and preprocess in the same thread? */ + +/** Receive OSC messages and call interface functions. + * + * \ingroup engine + */ +class OSCEngineReceiver +{ +public: + OSCEngineReceiver(Engine& engine, + SharedPtr interface, + uint16_t port); + + ~OSCEngineReceiver(); + +private: + struct ReceiveThread : public Raul::Thread { + explicit ReceiveThread(OSCEngineReceiver& receiver) : _receiver(receiver) {} + virtual void _run(); + private: + OSCEngineReceiver& _receiver; + }; + + friend struct ReceiveThread; + + ReceiveThread* _receive_thread; + + Raul::URI _delta_uri; + Resource::Properties _delta_remove; + Resource::Properties _delta_add; + +#ifdef LIBLO_BUNDLES + static int bundle_start_cb(lo_timetag time, void* myself) { + return ((OSCEngineReceiver*)myself)->_bundle_start_cb(time); + } + static int bundle_end_cb(void* myself) { + return ((OSCEngineReceiver*)myself)->_bundle_end_cb(); + } + + int _bundle_start_cb(lo_timetag time); + int _bundle_end_cb(); +#endif + + 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(get); + LO_HANDLER(put); + LO_HANDLER(delta_begin); + LO_HANDLER(delta_remove); + LO_HANDLER(delta_add); + LO_HANDLER(delta_end); + LO_HANDLER(move); + LO_HANDLER(del); + LO_HANDLER(connect); + LO_HANDLER(disconnect); + LO_HANDLER(disconnect_all); + LO_HANDLER(note_on); + LO_HANDLER(note_off); + LO_HANDLER(all_notes_off); + LO_HANDLER(learn); + LO_HANDLER(set_property); + LO_HANDLER(property_set); + + Engine& _engine; + SharedPtr _interface; + lo_server _server; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_OSCENGINERECEIVER_HPP diff --git a/src/osc/ingen_osc.cpp b/src/osc/ingen_osc.cpp new file mode 100644 index 00000000..4a8946c0 --- /dev/null +++ b/src/osc/ingen_osc.cpp @@ -0,0 +1,53 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David 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 "ingen/shared/Module.hpp" +#include "ingen/shared/World.hpp" + +#include "../server/Engine.hpp" +#include "../server/ServerInterfaceImpl.hpp" + +#include "OSCEngineReceiver.hpp" + +using namespace std; +using namespace Ingen; + +struct IngenOSCModule : public Ingen::Shared::Module { + void load(Ingen::Shared::World* world) { + Server::Engine* engine = (Server::Engine*)world->local_engine().get(); + SharedPtr interface( + new Server::ServerInterfaceImpl(*engine)); + receiver = SharedPtr( + new Server::OSCEngineReceiver( + *engine, + interface, + world->conf()->option("engine-port").get_int32())); + engine->add_event_source(interface); + } + + SharedPtr receiver; +}; + +extern "C" { + +Ingen::Shared::Module* +ingen_module_load() +{ + return new IngenOSCModule(); +} + +} // extern "C" diff --git a/src/server/HTTPClientSender.cpp b/src/server/HTTPClientSender.cpp deleted file mode 100644 index 85a291a5..00000000 --- a/src/server/HTTPClientSender.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2008-2011 David 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 -#include -#include "raul/log.hpp" -#include "raul/Atom.hpp" -#include "raul/AtomRDF.hpp" -#include "ingen/serialisation/Serialiser.hpp" -#include "ingen/shared/World.hpp" -#include "HTTPClientSender.hpp" -#include "Engine.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Server { - -void -HTTPClientSender::response_ok(int32_t id) -{ -} - -void -HTTPClientSender::response_error(int32_t id, const std::string& msg) -{ - warn << "HTTP Error " << id << " (" << msg << ")" << endl; -} - -void -HTTPClientSender::error(const std::string& msg) -{ - warn << "HTTP send error " << msg << endl; -} - -void -HTTPClientSender::put(const URI& uri, - const Resource::Properties& properties, - Resource::Graph ctx) -{ - const std::string request_uri = (Raul::Path::is_path(uri)) - ? _url + "/patch" + uri.substr(uri.find("/")) - : uri.str(); - - - Sord::Model model(*_engine.world()->rdf_world()); - for (Resource::Properties::const_iterator i = properties.begin(); - i != properties.end(); ++i) - model.add_statement( - Sord::URI(*_engine.world()->rdf_world(), request_uri), - AtomRDF::atom_to_node(model, i->first.str()), - AtomRDF::atom_to_node(model, i->second)); - - const string str = model.write_to_string("", SERD_TURTLE); - send_chunk(str); -} - -void -HTTPClientSender::delta(const URI& uri, - const Resource::Properties& remove, - const Resource::Properties& add) -{ -} - -void -HTTPClientSender::del(const URI& uri) -{ - send_chunk(string("<").append(uri.str()).append("> a .")); -} - -void -HTTPClientSender::connect(const Path& src_path, const Path& dst_path) -{ - const string msg = string( - "@prefix rdf: .\n" - "@prefix ingen: .\n").append( - "<> ingen:connection [\n" - "\tingen:destination <").append(dst_path.str()).append("> ;\n" - "\tingen:source <").append(src_path.str()).append(">\n] .\n"); - send_chunk(msg); -} - -void -HTTPClientSender::disconnect(const URI& src, - const URI& dst) -{ -} - -void -HTTPClientSender::disconnect_all(const Raul::Path& parent_patch_path, - const Raul::Path& path) -{ -} - -void -HTTPClientSender::set_property(const URI& subject, const URI& key, const Atom& value) -{ -#if 0 - Sord::Node node = AtomRDF::atom_to_node(*_engine.world()->rdf_world(), value); - const string msg = string( - "@prefix rdf: .\n" - "@prefix ingen: .\n" - "@prefix ingenui: .\n").append( - subject.str()).append("> ingen:property [\n" - "rdf:predicate ").append(key.str()).append(" ;\n" - "rdf:value ").append(node.to_string()).append("\n] .\n"); - send_chunk(msg); -#endif -} - -void -HTTPClientSender::activity(const Path& path, const Raul::Atom& value) -{ - if (value.type() == Atom::BOOL) { - const string msg = string( - "@prefix ingen: .\n\n<").append( - path.str()).append("> ingen:activity true .\n"); - send_chunk(msg); - } else if (value.type() == Atom::FLOAT) { - const string msg = string( - "@prefix ingen: .\n\n<").append( - path.str()).append("> ingen:activity ").append( - value.get_bool() ? "true" : "false").append(" .\n"); - send_chunk(msg); - } else { - warn << "Unknown activity type at " << path << endl; - } -} - -void -HTTPClientSender::move(const Path& old_path, const Path& new_path) -{ - string msg = string( - "@prefix rdf: .\n" - "@prefix ingen: .\n\n<").append( - old_path.str()).append("> rdf:subject <").append(new_path.str()).append("> .\n"); - send_chunk(msg); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/HTTPClientSender.hpp b/src/server/HTTPClientSender.hpp deleted file mode 100644 index 116f0601..00000000 --- a/src/server/HTTPClientSender.hpp +++ /dev/null @@ -1,109 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2008-2011 David 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 INGEN_ENGINE_HTTPCLIENTSENDER_HPP -#define INGEN_ENGINE_HTTPCLIENTSENDER_HPP - -#include -#include - -#include "raul/Thread.hpp" - -#include "ingen/ClientInterface.hpp" -#include "../shared/HTTPSender.hpp" - -namespace Ingen { - -class ServerInterface; - -namespace Server { - -class Engine; - -/** Implements ClientInterface for HTTP clients. - * Sends changes as RDF deltas over an HTTP stream - * (a single message with chunked encoding response). - * - * \ingroup engine - */ -class HTTPClientSender - : public ClientInterface - , public Ingen::Shared::HTTPSender -{ -public: - explicit HTTPClientSender(Engine& engine) - : _engine(engine) - , _enabled(true) - {} - - bool enabled() const { return _enabled; } - - void enable() { _enabled = true; } - void disable() { _enabled = false; } - - void bundle_begin() { HTTPSender::bundle_begin(); } - void bundle_end() { HTTPSender::bundle_end(); } - - Raul::URI uri() const { return "http://example.org/"; } - - /* *** ClientInterface Implementation Below *** */ - - void response_ok(int32_t id); - void response_error(int32_t id, const std::string& msg); - - void error(const std::string& msg); - - virtual void put(const Raul::URI& path, - const Resource::Properties& properties, - Resource::Graph ctx); - - virtual void delta(const Raul::URI& path, - const Resource::Properties& remove, - const Resource::Properties& add); - - virtual void del(const Raul::URI& uri); - - virtual void move(const Raul::Path& old_path, - const Raul::Path& new_path); - - virtual void connect(const Raul::Path& src_port_path, - const Raul::Path& dst_port_path); - - virtual void disconnect(const Raul::URI& src, - const Raul::URI& dst); - - virtual void disconnect_all(const Raul::Path& parent_patch_path, - const Raul::Path& path); - - virtual void set_property(const Raul::URI& subject_path, - const Raul::URI& predicate, - const Raul::Atom& value); - - virtual void activity(const Raul::Path& path, - const Raul::Atom& value); - -private: - Engine& _engine; - std::string _url; - bool _enabled; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_HTTPCLIENTSENDER_HPP - diff --git a/src/server/HTTPEngineReceiver.cpp b/src/server/HTTPEngineReceiver.cpp deleted file mode 100644 index a7b4ee1f..00000000 --- a/src/server/HTTPEngineReceiver.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David 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 -#include -#include - -#include - -#include - -#include "raul/SharedPtr.hpp" -#include "raul/log.hpp" - -#include "ingen/ClientInterface.hpp" -#include "ingen/ServerInterface.hpp" -#include "ingen/serialisation/Parser.hpp" -#include "ingen/serialisation/Serialiser.hpp" -#include "ingen/shared/Module.hpp" -#include "ingen/shared/Store.hpp" - -#include "ClientBroadcaster.hpp" -#include "Engine.hpp" -#include "HTTPClientSender.hpp" -#include "HTTPEngineReceiver.hpp" - -#define LOG(s) s << "[HTTPEngineReceiver] " - -using namespace std; -using namespace Raul; - -namespace Ingen { - -using namespace Serialisation; - -namespace Server { - -HTTPEngineReceiver::HTTPEngineReceiver(Engine& engine, - SharedPtr interface, - uint16_t port) - : _engine(engine) - , _interface(interface) - , _server(soup_server_new(SOUP_SERVER_PORT, port, NULL)) -{ - _receive_thread = new ReceiveThread(*this); - - soup_server_add_handler(_server, NULL, message_callback, this, NULL); - - LOG(info) << "Started HTTP server on port " << soup_server_get_port(_server) << endl; - - if (!engine.world()->parser() || !engine.world()->serialiser()) - engine.world()->load_module("serialisation"); - - _receive_thread->set_name("HTTPEngineReceiver Listener"); - _receive_thread->start(); -} - -HTTPEngineReceiver::~HTTPEngineReceiver() -{ - _receive_thread->stop(); - delete _receive_thread; - - if (_server) { - soup_server_quit(_server); - _server = NULL; - } -} - -void -HTTPEngineReceiver::message_callback(SoupServer* server, - SoupMessage* msg, - const char* path_str, - GHashTable* query, - SoupClientContext* client, - void* data) -{ - HTTPEngineReceiver* me = (HTTPEngineReceiver*)data; - ServerInterface* interface = me->_interface.get(); - - using namespace Ingen::Shared; - - SharedPtr store = me->_engine.world()->store(); - if (!store) { - soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - return; - } - - string path = path_str; - if (path[path.length() - 1] == '/') { - path = path.substr(0, path.length()-1); - } - - SharedPtr serialiser = me->_engine.world()->serialiser(); - - const string base_uri = "path:/"; - const char* mime_type = "text/plain"; - - // Special GET paths - if (msg->method == SOUP_METHOD_GET) { - if (path == Path::root().str() || path.empty()) { - const string r = string("@prefix rdfs: .\n") - .append("\n<> rdfs:seeAlso ;") - .append("\n rdfs:seeAlso ;") - .append("\n rdfs:seeAlso ."); - soup_message_set_status(msg, SOUP_STATUS_OK); - soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, r.c_str(), r.length()); - return; - - } else if (msg->method == SOUP_METHOD_GET && path.substr(0, 8) == "/plugins") { - // FIXME: kludge - #if 0 - interface->get("ingen:plugins"); - me->_receive_thread->whip(); - - serialiser->start_to_string("/", base_uri); - for (NodeFactory::Plugins::const_iterator p = me->_engine.node_factory()->plugins().begin(); - p != me->_engine.node_factory()->plugins().end(); ++p) - serialiser->serialise_plugin(*(Shared::Plugin*)p->second); - const string r = serialiser->finish(); - soup_message_set_status(msg, SOUP_STATUS_OK); - soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, r.c_str(), r.length()); - #endif - return; - - } else if (path.substr(0, 6) == "/patch") { - path = '/' + path.substr(6); - if (path.substr(0, 2) == "//") - path = path.substr(1); - - } else if (path.substr(0, 7) == "/stream") { - HTTPClientSender* client = new HTTPClientSender(me->_engine); - interface->register_client(client); - - // Respond with port number of stream for client - const int port = client->listen_port(); - char buf[32]; - snprintf(buf, sizeof(buf), "%d", port); - soup_message_set_status(msg, SOUP_STATUS_OK); - soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, buf, strlen(buf)); - return; - } - } - - if (!Path::is_valid(path)) { - LOG(error) << "Bad HTTP path: " << path << endl; - soup_message_set_status(msg, SOUP_STATUS_BAD_REQUEST); - const string& err = (boost::format("Bad path: %1%") % path).str(); - soup_message_set_response(msg, "text/plain", SOUP_MEMORY_COPY, - err.c_str(), err.length()); - return; - } - - if (msg->method == SOUP_METHOD_GET) { - Glib::RWLock::ReaderLock lock(store->lock()); - - // Find object - Store::const_iterator start = store->find(path); - if (start == store->end()) { - soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND); - const string& err = (boost::format("No such object: %1%") % path).str(); - soup_message_set_response(msg, "text/plain", SOUP_MEMORY_COPY, - err.c_str(), err.length()); - return; - } - - // Get serialiser - SharedPtr serialiser = me->_engine.world()->serialiser(); - if (!serialiser) { - soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - soup_message_set_response(msg, "text/plain", SOUP_MEMORY_STATIC, - "No serialiser available\n", 24); - return; - } - - // Serialise object - const string response = serialiser->to_string(start->second, - "http://localhost:16180/patch", GraphObject::Properties()); - - soup_message_set_status(msg, SOUP_STATUS_OK); - soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, - response.c_str(), response.length()); - - } else if (msg->method == SOUP_METHOD_PUT) { - Glib::RWLock::WriterLock lock(store->lock()); - - // Get parser - SharedPtr parser = me->_engine.world()->parser(); - if (!parser) { - soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - return; - } - - parser->parse_string(me->_engine.world(), interface, msg->request_body->data, base_uri); - soup_message_set_status(msg, SOUP_STATUS_OK); - - } else if (msg->method == SOUP_METHOD_DELETE) { - interface->del(path); - soup_message_set_status(msg, SOUP_STATUS_OK); - - } else { - soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED); - } -} - -void -HTTPEngineReceiver::ReceiveThread::_run() -{ - soup_server_run(_receiver._server); -} - -} // namespace Server -} // namespace Ingen - diff --git a/src/server/HTTPEngineReceiver.hpp b/src/server/HTTPEngineReceiver.hpp deleted file mode 100644 index e17efe05..00000000 --- a/src/server/HTTPEngineReceiver.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2008-2011 David 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 INGEN_ENGINE_HTTPENGINERECEIVER_HPP -#define INGEN_ENGINE_HTTPENGINERECEIVER_HPP - -#include - -#include - -#include "raul/Thread.hpp" - -typedef struct _SoupServer SoupServer; -typedef struct _SoupMessage SoupMessage; -typedef struct SoupClientContext SoupClientContext; - -namespace Ingen { - -class ServerInterface; - -namespace Server { - -class Engine; - -class HTTPEngineReceiver -{ -public: - HTTPEngineReceiver(Engine& engine, - SharedPtr interface, - uint16_t port); - - ~HTTPEngineReceiver(); - -private: - struct ReceiveThread : public Raul::Thread { - explicit ReceiveThread(HTTPEngineReceiver& receiver) - : _receiver(receiver) - {} - virtual void _run(); - private: - HTTPEngineReceiver& _receiver; - }; - - friend class ReceiveThread; - - static void message_callback(SoupServer* server, - SoupMessage* msg, - const char* path, - GHashTable *query, - SoupClientContext* client, - void* data); - - Engine& _engine; - SharedPtr _interface; - ReceiveThread* _receive_thread; - SoupServer* _server; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_HTTPENGINERECEIVER_HPP diff --git a/src/server/OSCClientSender.cpp b/src/server/OSCClientSender.cpp deleted file mode 100644 index 5e9c01f0..00000000 --- a/src/server/OSCClientSender.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David 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 - -#include -#include - -#include "raul/log.hpp" -#include "raul/AtomLiblo.hpp" - -#include "EngineStore.hpp" -#include "NodeImpl.hpp" -#include "OSCClientSender.hpp" -#include "PatchImpl.hpp" - -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "ingen/ClientInterface.hpp" -#include "util.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Server { - -/** @page client_osc_namespace Client OSC Namespace Documentation - * - *

These are the commands the client recognizes. All monitoring of - * changes in the engine happens via these commands.

- */ - -/** @page client_osc_namespace - *

/ok

- * @arg @p response-id :: Integer - * - * @par - * Successful response to some command. - */ -void -OSCClientSender::response_ok(int32_t id) -{ - if (!_enabled) - return; - - - - if (lo_send(_address, "/ok", "i", id, LO_ARGS_END) < 0) { - Raul::error << "Unable to send OK " << id << "! (" - << lo_address_errstr(_address) << ")" << endl; - } -} - -/** @page client_osc_namespace - *

/error

- * @arg @p response-id :: Integer - * @arg @p message :: String - * - * @par - * Unsuccessful response to some command. - */ -void -OSCClientSender::response_error(int32_t id, const std::string& msg) -{ - if (!_enabled) - return; - - if (lo_send(_address, "/error", "is", id, msg.c_str(), LO_ARGS_END) < 0) { - Raul::error << "Unable to send error " << id << "! (" - << lo_address_errstr(_address) << ")" << endl; - } -} - -/** @page client_osc_namespace - *

/error

- * @arg @p message :: String - * - * @par - * Notification that an error has occurred. This is for notification of errors - * that aren't a direct response to a user command, i.e. "unexpected" errors. - */ -void -OSCClientSender::error(const std::string& msg) -{ - send("/error", "s", msg.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/put

- * @arg @p path :: String - * @arg @p predicate :: URI String - * @arg @p value - * @arg @p ... - * - * @par - * PUT a set of properties to a path. - */ -void -OSCClientSender::put(const Raul::URI& path, - const Resource::Properties& properties, - Resource::Graph ctx) -{ - typedef Resource::Properties::const_iterator iterator; - lo_message m = lo_message_new(); - lo_message_add_string(m, path.c_str()); - lo_message_add_string(m, Resource::graph_to_uri(ctx).c_str()); - for (iterator i = properties.begin(); i != properties.end(); ++i) { - lo_message_add_string(m, i->first.c_str()); - Raul::AtomLiblo::lo_message_add_atom(m, i->second); - } - send_message("/put", m); -} - -void -OSCClientSender::delta(const Raul::URI& path, - const Resource::Properties& remove, - const Resource::Properties& add) -{ - typedef Resource::Properties::const_iterator iterator; - - const bool bundle = !_bundle; - if (bundle) - bundle_begin(); - - send("/delta_begin", "s", path.c_str(), LO_ARGS_END); - - for (iterator i = remove.begin(); i != remove.end(); ++i) { - lo_message m = lo_message_new(); - lo_message_add_string(m, i->first.c_str()); - Raul::AtomLiblo::lo_message_add_atom(m, i->second); - send_message("/delta_remove", m); - } - - for (iterator i = add.begin(); i != add.end(); ++i) { - lo_message m = lo_message_new(); - lo_message_add_string(m, i->first.c_str()); - Raul::AtomLiblo::lo_message_add_atom(m, i->second); - send_message("/delta_add", m); - } - - send("/delta_end", "", LO_ARGS_END); - - if (bundle) - bundle_end(); -} - -/** @page client_osc_namespace - *

/move

- * @arg @p old-path :: String - * @arg @p new-path :: String - * - * @par - * MOVE an object to a new path. - * The new path will have the same parent as the old path. - */ -void -OSCClientSender::move(const Path& old_path, const Path& new_path) -{ - send("/move", "ss", old_path.c_str(), new_path.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/delete

- * @arg @p path :: String - * - * @par - * DELETE an object. - */ -void -OSCClientSender::del(const URI& uri) -{ - send("/delete", "s", uri.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/connect

- * @arg @p src-path :: String - * @arg @p dst-path :: String - * - * @par - * Notification a new connection has been made. - */ -void -OSCClientSender::connect(const Path& src_port_path, - const Path& dst_port_path) -{ - send("/connect", "ss", src_port_path.c_str(), dst_port_path.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/disconnect

- * @arg @p src-path :: String - * @arg @p dst-path :: String - * - * @par - * Notification a connection has been unmade. - */ -void -OSCClientSender::disconnect(const URI& src, - const URI& dst) -{ - send("/disconnect", "ss", src.c_str(), dst.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/disconnect_all

- * @arg @p parent-patch-path :: String - * @arg @p path :: String - * - * @par - * Notification all connections to an object have been disconnected. - */ -void -OSCClientSender::disconnect_all(const Raul::Path& parent_patch_path, - const Raul::Path& path) -{ - send("/disconnect_all", "ss", parent_patch_path.c_str(), path.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/set_property

- * @arg @p path :: String - * @arg @p key :: URI String - * @arg @p value - * - * @par - * Notification of a property. - */ -void -OSCClientSender::set_property(const URI& path, - const URI& key, - const Atom& value) -{ - lo_message m = lo_message_new(); - lo_message_add_string(m, path.c_str()); - lo_message_add_string(m, key.c_str()); - AtomLiblo::lo_message_add_atom(m, value); - send_message("/set_property", m); -} - -/** @page client_osc_namespace - *

/activity

- * @arg @p path :: String - * - * @par - * Notification of "activity" (e.g. port message blinkenlights). - */ -void -OSCClientSender::activity(const Path& path, const Raul::Atom& value) -{ - if (!_enabled) - return; - - lo_message m = lo_message_new(); - lo_message_add_string(m, path.c_str()); - AtomLiblo::lo_message_add_atom(m, value); - send_message("/activity", m); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/OSCClientSender.hpp b/src/server/OSCClientSender.hpp deleted file mode 100644 index ffc3b202..00000000 --- a/src/server/OSCClientSender.hpp +++ /dev/null @@ -1,110 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David 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 INGEN_ENGINE_OSCCLIENTSENDER_HPP -#define INGEN_ENGINE_OSCCLIENTSENDER_HPP - -#include -#include - -#include - -#include "ingen/ClientInterface.hpp" -#include "ingen/GraphObject.hpp" -#include "../shared/OSCSender.hpp" - -namespace Ingen { - -class ServerInterface; - -namespace Server { - - -/** Implements ClientInterface for OSC clients (sends OSC messages). - * - * \ingroup engine - */ -class OSCClientSender : public ClientInterface, - public Ingen::Shared::OSCSender -{ -public: - explicit OSCClientSender(const Raul::URI& url, - size_t max_packet_size) - : Shared::OSCSender(max_packet_size) - , _url(url) - { - _address = lo_address_new_from_url(url.c_str()); - } - - virtual ~OSCClientSender() - { lo_address_free(_address); } - - bool enabled() const { return _enabled; } - - void enable() { _enabled = true; } - void disable() { _enabled = false; } - - void bundle_begin() { OSCSender::bundle_begin(); } - void bundle_end() { OSCSender::bundle_end(); } - - Raul::URI uri() const { return _url; } - - /* *** ClientInterface Implementation Below *** */ - - void response_ok(int32_t id); - void response_error(int32_t id, const std::string& msg); - - void error(const std::string& msg); - - virtual void put(const Raul::URI& path, - const Resource::Properties& properties, - Resource::Graph ctx=Resource::DEFAULT); - - virtual void delta(const Raul::URI& path, - const Resource::Properties& remove, - const Resource::Properties& add); - - virtual void del(const Raul::URI& uri); - - virtual void move(const Raul::Path& old_path, - const Raul::Path& new_path); - - virtual void connect(const Raul::Path& src_port_path, - const Raul::Path& dst_port_path); - - virtual void disconnect(const Raul::URI& src, - const Raul::URI& dst); - - virtual void disconnect_all(const Raul::Path& parent_patch_path, - const Raul::Path& path); - - virtual void set_property(const Raul::URI& subject, - const Raul::URI& predicate, - const Raul::Atom& value); - - virtual void activity(const Raul::Path& path, - const Raul::Atom& value); - -private: - Raul::URI _url; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_OSCCLIENTSENDER_HPP - diff --git a/src/server/OSCEngineReceiver.cpp b/src/server/OSCEngineReceiver.cpp deleted file mode 100644 index 8451f4ee..00000000 --- a/src/server/OSCEngineReceiver.cpp +++ /dev/null @@ -1,593 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David 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 -#include - -#include - -#include - -#include "raul/AtomLiblo.hpp" -#include "raul/Path.hpp" -#include "raul/SharedPtr.hpp" -#include "raul/log.hpp" - -#include "ingen-config.h" -#include "ingen/ClientInterface.hpp" - -#include "ClientBroadcaster.hpp" -#include "Engine.hpp" -#include "OSCClientSender.hpp" -#include "OSCEngineReceiver.hpp" -#include "ServerInterfaceImpl.hpp" -#include "ThreadManager.hpp" - -#define LOG(s) s << "[OSCEngineReceiver] " - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Server { - -/** @page engine_osc_namespace Server 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, - SharedPtr interface, - uint16_t port) - : _engine(engine) - , _interface(interface) - , _server(NULL) -{ - _receive_thread = new ReceiveThread(*this); - - char port_str[6]; - snprintf(port_str, sizeof(port_str), "%u", port); - - _server = lo_server_new(port_str, error_cb); - - if (_server == NULL) { - LOG(error) << "Could not start OSC server. Aborting." << endl; - exit(EXIT_FAILURE); - } else { - char* lo_url = lo_server_get_url(_server); - LOG(info) << "Started OSC server at " << lo_url << endl; - free(lo_url); - } - -#ifdef RAUL_LOG_DEBUG - lo_server_add_method(_server, NULL, NULL, generic_cb, NULL); -#endif - - // 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); - -#ifdef LIBLO_BUNDLES - lo_server_add_bundle_handlers(_server, bundle_start_cb, bundle_end_cb, this); -#endif - - // Commands - lo_server_add_method(_server, "/ping", "i", ping_cb, this); - lo_server_add_method(_server, "/ping_queued", "i", ping_slow_cb, this); - lo_server_add_method(_server, "/register_client", "i", register_client_cb, this); - lo_server_add_method(_server, "/unregister_client", "i", unregister_client_cb, this); - lo_server_add_method(_server, "/put", NULL, put_cb, this); - lo_server_add_method(_server, "/delta_begin", NULL, delta_begin_cb, this); - lo_server_add_method(_server, "/delta_remove", NULL, delta_remove_cb, this); - lo_server_add_method(_server, "/delta_add", NULL, delta_add_cb, this); - lo_server_add_method(_server, "/delta_end", NULL, delta_end_cb, this); - lo_server_add_method(_server, "/move", "iss", move_cb, this); - lo_server_add_method(_server, "/delete", "is", del_cb, this); - lo_server_add_method(_server, "/connect", "iss", connect_cb, this); - lo_server_add_method(_server, "/disconnect", "iss", disconnect_cb, this); - lo_server_add_method(_server, "/disconnect_all", "iss", disconnect_all_cb, this); - lo_server_add_method(_server, "/note_on", "isii", note_on_cb, this); - lo_server_add_method(_server, "/note_off", "isi", note_off_cb, this); - lo_server_add_method(_server, "/all_notes_off", "isi", all_notes_off_cb, this); - lo_server_add_method(_server, "/learn", "is", learn_cb, this); - lo_server_add_method(_server, "/set_property", NULL, set_property_cb, this); - - // Queries - lo_server_add_method(_server, "/get", "is", get_cb, this); - - lo_server_add_method(_server, NULL, NULL, unknown_cb, NULL); - - //_interface->set_name("OSCEngineReceiver"); - //_interface->start(); - _receive_thread->set_name("OSCEngineReceiver Listener"); - _receive_thread->start(); - _receive_thread->set_scheduling(SCHED_FIFO, 5); -} - -OSCEngineReceiver::~OSCEngineReceiver() -{ - _receive_thread->stop(); - //_interface->stop(); - delete _receive_thread; - - if (_server != NULL) { - lo_server_free(_server); - _server = NULL; - } -} - -/** Override the semaphore driven _run method of ServerInterfaceImpl - * to wait on OSC messages and prepare them right away in the same thread. - */ -void -OSCEngineReceiver::ReceiveThread::_run() -{ - Thread::get().set_context(THREAD_PRE_PROCESS); - - /* get a timestamp here and stamp all the events with the same time so - * they all get executed in the same cycle */ - - while (true) { - // Wait on a message and enqueue it - lo_server_recv(_receiver._server); - - // Enqueue every other message that is here "now" - // (would this provide truly atomic bundles?) - while (lo_server_recv_noblock(_receiver._server, 0) > 0) {} - } -} - -/** Create a new request for this message, if necessary. - * - * This is based on the fact that the current request is stored in a ref - * counted pointer, and events just take a reference to that. Thus, events - * may delete their request if we've since switched to a new one, or the - * same one can stay around and serve a series of events. - * Hooray for reference counting. - * - * If this message came from the same source as the last message, no allocation - * of requests 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. - * Lack of a fast liblo address comparison really sucks here, in any case. - */ -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); - - if (argc < 1 || types[0] != 'i') // Not a valid Ingen message - return 0; // Save liblo the trouble - - const int32_t id = argv[0]->i; - - const lo_address addr = lo_message_get_source(msg); - char* const url = lo_address_get_url(addr); - - if (id != -1) { - // TODO: Cache client - ClientInterface* client = me->_engine.broadcaster()->client(url); - me->_interface->respond_to(client, id); - } else { - me->_interface->disable_responses(); - } - - free(url); - - // If this returns 0 no OSC commands will work - return 1; -} - -#ifdef LIBLO_BUNDLES -int -OSCEngineReceiver::_bundle_start_cb(lo_timetag time) -{ - info << "BUNDLE START" << endl; - return 0; -} - -int -OSCEngineReceiver::_bundle_end_cb() -{ - info << "BUNDLE END" << endl; - return 0; -} -#endif - -void -OSCEngineReceiver::error_cb(int num, const char* msg, const char* path) -{ - error << "liblo server error" << num; - if (path) { - error << " for path `" << path << "'"; - } - error << " (" << msg << ")" << endl; -} - -/** @page engine_osc_namespace - *

/ping

- * @arg @p response-id :: Integer - */ -int -OSCEngineReceiver::_ping_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const lo_address addr = lo_message_get_source(msg); - if (lo_send(addr, "/ok", "i", argv[0]->i) < 0) - warn << "Unable to send response (" << lo_address_errstr(addr) << ")" << endl; - return 0; -} - -/** @page engine_osc_namespace - *

/ping_queued

- * @arg @p response-id :: Integer - * - * @par - * Reply to sender with a successful response after going through the - * event queue. This is useful for checking if the engine is actually active, - * or for sending after several events as a sentinel and wait on it's response, - * to know when all previous events have finished processing. - */ -int -OSCEngineReceiver::_ping_slow_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - _interface->ping(); - return 0; -} - -/** @page engine_osc_namespace - *

/register_client

- * @arg @p response-id :: Integer - * - * @par - * Register a new client with the engine. The incoming address will be - * used for the new registered client. - */ -int -OSCEngineReceiver::_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); - ClientInterface* client = new OSCClientSender( - (const char*)url, - _engine.world()->conf()->option("packet-size").get_int32()); - _interface->register_client(client); - free(url); - - return 0; -} - -/** @page engine_osc_namespace - *

/unregister_client

- * @arg @p response-id :: Integer - * - * @par - * Unregister a client. - */ -int -OSCEngineReceiver::_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); - _interface->unregister_client(url); - free(url); - - return 0; -} - -/** @page engine_osc_namespace - *

/get

- * @arg @p response-id :: Integer - * @arg @p uri :: URI String - * - * @par - * Request all properties of an object. - */ -int -OSCEngineReceiver::_get_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - _interface->get(&argv[1]->s); - return 0; -} - -/** @page engine_osc_namespace - *

/put

- * @arg @p response-id :: Integer - * @arg @p path :: String - * @arg @p context :: URI String - * @arg @p predicate :: URI String - * @arg @p value - * @arg @p ... - * - * @par - * PUT a set of properties to a path. - */ -int -OSCEngineReceiver::_put_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* obj_path = &argv[1]->s; - const char* ctx = &argv[2]->s; - Resource::Properties prop; - for (int i = 3; i < argc-1; i += 2) - prop.insert(make_pair(&argv[i]->s, - AtomLiblo::lo_arg_to_atom(types[i+1], argv[i+1]))); - _interface->put(obj_path, prop, Resource::uri_to_graph(ctx)); - return 0; -} - -int -OSCEngineReceiver::_delta_begin_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* obj_path = &argv[1]->s; - assert(_delta_remove.empty()); - assert(_delta_add.empty()); - _delta_uri = obj_path; - return 0; -} - -int -OSCEngineReceiver::_delta_remove_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - _delta_remove.insert(make_pair(&argv[1]->s, - AtomLiblo::lo_arg_to_atom(types[2], argv[2]))); - return 0; -} - -int -OSCEngineReceiver::_delta_add_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - _delta_add.insert(make_pair(&argv[1]->s, - AtomLiblo::lo_arg_to_atom(types[2], argv[2]))); - return 0; -} - -int -OSCEngineReceiver::_delta_end_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - _interface->delta(_delta_uri, _delta_remove, _delta_add); - _delta_uri = Raul::URI(); - _delta_remove.clear(); - _delta_add.clear(); - return 0; -} - -/** @page engine_osc_namespace - *

/move

- * @arg @p response-id :: Integer - * @arg @p old-path :: String - * @arg @p new-path :: String - * - * @par - * MOVE an object to a new path. - */ -int -OSCEngineReceiver::_move_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* old_path = &argv[1]->s; - const char* new_path = &argv[2]->s; - - _interface->move(old_path, new_path); - return 0; -} - -/** @page engine_osc_namespace - *

/delete

- * @arg @p response-id :: Integer - * @arg @p path :: String - * - * @par - * DELETE an object. - */ -int -OSCEngineReceiver::_del_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* uri = &argv[1]->s; - - _interface->del(uri); - return 0; -} - -/** @page engine_osc_namespace - *

/connect

- * @arg @p response-id :: Integer - * @arg @p src-port-path :: String - * @arg @p dst-port-path :: String - * - * @par - * Connect two ports (which must be in the same patch). - */ -int -OSCEngineReceiver::_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; - - _interface->connect(src_port_path, dst_port_path); - return 0; -} - -/** @page engine_osc_namespace - *

/disconnect

- * @arg @p response-id :: Integer - * @arg @p src-port-path :: String - * @arg @p dst-port-path :: String - * - * @par - * Disconnect two ports. - */ -int -OSCEngineReceiver::_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; - - _interface->disconnect(src_port_path, dst_port_path); - return 0; -} - -/** @page engine_osc_namespace - *

/disconnect_all

- * @arg @p response-id :: Integer - * @arg @p patch-path :: String - * @arg @p object-path :: String - * - * @par - * Disconnect all connections to/from a node/port. - */ -int -OSCEngineReceiver::_disconnect_all_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* patch_path = &argv[1]->s; - const char* object_path = &argv[2]->s; - - _interface->disconnect_all(patch_path, object_path); - return 0; -} - -/** @page engine_osc_namespace - *

/note_on

- * @arg @p response-id :: Integer - * @arg @p node-path :: String - * @arg @p note-num (int) - * @arg @p velocity (int) - * - * @par - * Trigger a note-on, just as if it came from MIDI. - */ -int -OSCEngineReceiver::_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 uint8_t note_num = argv[2]->i; - const uint8_t velocity = argv[3]->i; - */ - warn << "TODO: OSC note on" << endl; - //note_on(node_path, note_num, velocity); - return 0; -} - -/** @page engine_osc_namespace - *

/note_off

- * @arg @p response-id :: Integer - * @arg @p node-path :: String - * @arg @p note-num :: Integer - * - * @par - * Trigger a note-off, just as if it came from MIDI. - */ -int -OSCEngineReceiver::_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 uint8_t note_num = argv[2]->i; - */ - warn << "TODO: OSC note off" << endl; - //note_off(patch_path, note_num); - return 0; -} - -/** @page engine_osc_namespace - *

/all_notes_off

- * @arg @p response-id :: Integer - * @arg @p patch-path :: String - * - * @par - * Trigger a note-off for all voices, just as if it came from MIDI. - */ -int -OSCEngineReceiver::_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; - */ - warn << "TODO: OSC all notes off" << endl; - //all_notes_off(patch_path); - return 0; -} - -/** @page engine_osc_namespace - *

/set_property

- * @arg @p response-id :: Integer - * @arg @p uri :: URI String - * @arg @p key :: URI String - * @arg @p value :: String - * - * @par - * Set a property on a graph object. - */ -int -OSCEngineReceiver::_set_property_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - if (argc != 4 || types[0] != 'i' || types[1] != 's' || types[2] != 's') - return 1; - - const char* object_path = &argv[1]->s; - const char* key = &argv[2]->s; - - Raul::Atom value = Raul::AtomLiblo::lo_arg_to_atom(types[3], argv[3]); - - _interface->set_property(object_path, key, value); - return 0; -} - - -// 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("[OSCEngineReceiver] %s (%s)\t", path, types); - - for (int i=0; i < argc; ++i) { - lo_arg_pp(lo_type(types[i]), argv[i]); - printf("\t"); - } - printf("\n"); - - return 1; // not handled -} - -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); - - warn << "Unknown OSC command " << path << " (" << types << ") " - << "received from " << url << endl; - - string error_msg = "Unknown command: "; - error_msg.append(path).append(" ").append(types); - - lo_send(addr, "/error", "s", error_msg.c_str(), LO_ARGS_END); - free(url); - - return 0; -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/OSCEngineReceiver.hpp b/src/server/OSCEngineReceiver.hpp deleted file mode 100644 index fe16ac1a..00000000 --- a/src/server/OSCEngineReceiver.hpp +++ /dev/null @@ -1,130 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David 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 INGEN_ENGINE_OSCENGINERECEIVER_HPP -#define INGEN_ENGINE_OSCENGINERECEIVER_HPP - -#include - -#include - -#include "raul/Thread.hpp" -#include "raul/URI.hpp" - -#include "ingen/Resource.hpp" - -#include "ingen-config.h" - -namespace Ingen { - -class ServerInterface; - -namespace Server { - -class Engine; - -/* Some boilerplate killing macros... */ -#define LO_HANDLER_ARGS const char* path, const char* types, lo_arg** argv, int argc, lo_message msg - -/* Defines a static handler to be passed to lo_add_method, which is a trivial - * wrapper around a non-static method that does the real work. Makes a whoole - * lot of ugly boiler plate go away */ -#define LO_HANDLER(name) \ -int _##name##_cb (LO_HANDLER_ARGS);\ -inline static int name##_cb(LO_HANDLER_ARGS, void* myself)\ -{ return ((OSCEngineReceiver*)myself)->_##name##_cb(path, types, argv, argc, msg); } - -/* FIXME: Make this receive and preprocess in the same thread? */ - -/** Receive OSC messages and call interface functions. - * - * \ingroup engine - */ -class OSCEngineReceiver -{ -public: - OSCEngineReceiver(Engine& engine, - SharedPtr interface, - uint16_t port); - - ~OSCEngineReceiver(); - -private: - struct ReceiveThread : public Raul::Thread { - explicit ReceiveThread(OSCEngineReceiver& receiver) : _receiver(receiver) {} - virtual void _run(); - private: - OSCEngineReceiver& _receiver; - }; - - friend struct ReceiveThread; - - ReceiveThread* _receive_thread; - - Raul::URI _delta_uri; - Resource::Properties _delta_remove; - Resource::Properties _delta_add; - -#ifdef LIBLO_BUNDLES - static int bundle_start_cb(lo_timetag time, void* myself) { - return ((OSCEngineReceiver*)myself)->_bundle_start_cb(time); - } - static int bundle_end_cb(void* myself) { - return ((OSCEngineReceiver*)myself)->_bundle_end_cb(); - } - - int _bundle_start_cb(lo_timetag time); - int _bundle_end_cb(); -#endif - - 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(get); - LO_HANDLER(put); - LO_HANDLER(delta_begin); - LO_HANDLER(delta_remove); - LO_HANDLER(delta_add); - LO_HANDLER(delta_end); - LO_HANDLER(move); - LO_HANDLER(del); - LO_HANDLER(connect); - LO_HANDLER(disconnect); - LO_HANDLER(disconnect_all); - LO_HANDLER(note_on); - LO_HANDLER(note_off); - LO_HANDLER(all_notes_off); - LO_HANDLER(learn); - LO_HANDLER(set_property); - LO_HANDLER(property_set); - - Engine& _engine; - SharedPtr _interface; - lo_server _server; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_OSCENGINERECEIVER_HPP diff --git a/src/server/ingen_http.cpp b/src/server/ingen_http.cpp deleted file mode 100644 index c0bc0ce4..00000000 --- a/src/server/ingen_http.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David 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 "ingen/shared/Module.hpp" -#include "ingen/shared/World.hpp" -#include "HTTPEngineReceiver.hpp" -#include "ServerInterfaceImpl.hpp" -#include "Engine.hpp" - -using namespace std; -using namespace Ingen; - -struct IngenHTTPModule : public Ingen::Shared::Module { - void load(Ingen::Shared::World* world) { - Server::Engine* engine = (Server::Engine*)world->local_engine().get(); - SharedPtr interface( - new Server::ServerInterfaceImpl(*engine)); - - receiver = SharedPtr( - new Server::HTTPEngineReceiver( - *engine, - interface, - world->conf()->option("engine-port").get_int32())); - engine->add_event_source(interface); - } - - SharedPtr receiver; -}; - -extern "C" { - -Ingen::Shared::Module* -ingen_module_load() -{ - return new IngenHTTPModule(); -} - -} // extern "C" diff --git a/src/server/ingen_osc.cpp b/src/server/ingen_osc.cpp deleted file mode 100644 index 024a93ce..00000000 --- a/src/server/ingen_osc.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David 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 "ingen/shared/Module.hpp" -#include "ingen/shared/World.hpp" -#include "OSCEngineReceiver.hpp" -#include "ServerInterfaceImpl.hpp" -#include "Engine.hpp" - -using namespace std; -using namespace Ingen; - -struct IngenOSCModule : public Ingen::Shared::Module { - void load(Ingen::Shared::World* world) { - Server::Engine* engine = (Server::Engine*)world->local_engine().get(); - SharedPtr interface( - new Server::ServerInterfaceImpl(*engine)); - receiver = SharedPtr( - new Server::OSCEngineReceiver( - *engine, - interface, - world->conf()->option("engine-port").get_int32())); - engine->add_event_source(interface); - } - - SharedPtr receiver; -}; - -extern "C" { - -Ingen::Shared::Module* -ingen_module_load() -{ - return new IngenOSCModule(); -} - -} // extern "C" diff --git a/src/server/wscript b/src/server/wscript index 4172b23e..04731313 100644 --- a/src/server/wscript +++ b/src/server/wscript @@ -65,30 +65,6 @@ def build(bld): core_libs = 'GLIBMM GTHREAD LV2CORE LILV RAUL SORD' autowaf.use_lib(bld, obj, core_libs) - if bld.is_defined('HAVE_SOUP'): - obj = bld(features = 'cxx cxxshlib', - source = '''HTTPClientSender.cpp - HTTPEngineReceiver.cpp - ingen_http.cpp''', - includes = ['.', '../..', '../../include'], - name = 'libingen_http', - target = 'ingen_http', - install_path = '${LIBDIR}', - use = 'libingen_server') - autowaf.use_lib(bld, obj, core_libs + ' SOUP') - - if bld.is_defined('HAVE_LIBLO'): - obj = bld(features = 'cxx cxxshlib', - source = '''OSCClientSender.cpp - OSCEngineReceiver.cpp - ingen_osc.cpp''', - includes = ['.', '../..', '../../include'], - name = 'libingen_osc', - target = 'ingen_osc', - install_path = '${LIBDIR}', - use = 'libingen_server') - autowaf.use_lib(bld, obj, core_libs + ' LIBLO') - if bld.is_defined('HAVE_JACK'): obj = bld(features = 'cxx cxxshlib', source = 'JackDriver.cpp ingen_jack.cpp', diff --git a/wscript b/wscript index b9d61244..026c3db3 100644 --- a/wscript +++ b/wscript @@ -136,10 +136,12 @@ def build(bld): bld.path.ant_glob('include/ingen/%s/*' % i)) # Modules - bld.recurse('src/client') + bld.recurse('src/shared') bld.recurse('src/serialisation') bld.recurse('src/server') - bld.recurse('src/shared') + bld.recurse('src/client') + bld.recurse('src/http') + bld.recurse('src/osc') if bld.is_defined('INGEN_BUILD_GUI'): bld.recurse('src/gui') -- cgit v1.2.1