summaryrefslogtreecommitdiffstats
path: root/src/engine/OSCEngineReceiver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/OSCEngineReceiver.cpp')
-rw-r--r--src/engine/OSCEngineReceiver.cpp586
1 files changed, 0 insertions, 586 deletions
diff --git a/src/engine/OSCEngineReceiver.cpp b/src/engine/OSCEngineReceiver.cpp
deleted file mode 100644
index fdb0bcc4..00000000
--- a/src/engine/OSCEngineReceiver.cpp
+++ /dev/null
@@ -1,586 +0,0 @@
-/* This file is part of Ingen.
- * Copyright 2007-2011 David Robillard <http://drobilla.net>
- *
- * Ingen is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 2 of the License, or (at your option) any later
- * version.
- *
- * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-//#define ENABLE_AVAHI 1
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <string>
-
-#include <lo/lo.h>
-
-#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 "EventSource.hpp"
-#include "OSCClientSender.hpp"
-#include "OSCEngineReceiver.hpp"
-#include "ThreadManager.hpp"
-
-#define LOG(s) s << "[OSCEngineReceiver] "
-
-using namespace std;
-using namespace Raul;
-
-namespace Ingen {
-namespace Engine {
-
-/** @page engine_osc_namespace Engine OSC Namespace Documentation
- *
- * <p>These are the commands the engine recognizes. A client can control every
- * aspect of the engine entirely with these commands.</p>
- *
- * <p>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.</p>
- */
-
-OSCEngineReceiver::OSCEngineReceiver(Engine& engine, size_t queue_size, uint16_t port)
- : QueuedEngineInterface(engine, queue_size) // FIXME
- , _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);
-#ifdef ENABLE_AVAHI
- lo_server_avahi_init(_server, "ingen");
-#endif
-
- 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, "/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, "/request_property", "iss", request_property_cb, this);
- lo_server_add_method(_server, "/get", "is", get_cb, this);
-
- lo_server_add_method(_server, NULL, NULL, unknown_cb, NULL);
-
- Thread::set_name("OSCEngineReceiver");
- start();
- _receive_thread->set_name("OSCEngineReceiver Listener");
- _receive_thread->start();
- _receive_thread->set_scheduling(SCHED_FIFO, 5);
-}
-
-OSCEngineReceiver::~OSCEngineReceiver()
-{
- _receive_thread->stop();
- stop();
- delete _receive_thread;
-
- if (_server != NULL) {
-#ifdef ENABLE_AVAHI
- lo_server_avahi_free(_server);
-#endif
- lo_server_free(_server);
- _server = NULL;
- }
-}
-
-/** Override the semaphore driven _run method of QueuedEngineInterface
- * 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)
- if (_receiver.unprepared_events())
- _receiver.whip();
-
- // No more unprepared events
- }
-}
-
-/** 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<OSCEngineReceiver*>(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);
-
- const SharedPtr<Request> r = me->_request;
-
- /* Different address than last time, have to do a lookup */
- if (!r || !r->client() || strcmp(url, r->client()->uri().c_str())) {
- ClientInterface* client = me->_engine.broadcaster()->client(url);
- if (client)
- me->_request = SharedPtr<Request>(new Request(me, client, id));
- else
- me->_request = SharedPtr<Request>(new Request(me));
- }
-
- if (id != -1) {
- me->set_next_response_id(id);
- } else {
- me->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
- * <h2>/ping</h2>
- * @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
- * <h2>/ping_queued</h2>
- * @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)
-{
- ping();
- return 0;
-}
-
-/** @page engine_osc_namespace
- * <h2>/register_client</h2>
- * @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());
- register_client(client);
- free(url);
-
- return 0;
-}
-
-/** @page engine_osc_namespace
- * <h2>/unregister_client</h2>
- * @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);
- unregister_client(url);
- free(url);
-
- return 0;
-}
-
-/** @page engine_osc_namespace
- * <h2>/get</h2>
- * @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)
-{
- get(&argv[1]->s);
- return 0;
-}
-
-/** @page engine_osc_namespace
- * <h2>/put</h2>
- * @arg @p response-id :: Integer
- * @arg @p path :: 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;
- Resource::Properties prop;
- for (int i = 2; i < argc-1; i += 2)
- prop.insert(make_pair(&argv[i]->s, AtomLiblo::lo_arg_to_atom(types[i+1], argv[i+1])));
- put(obj_path, prop);
- return 0;
-}
-
-/** @page engine_osc_namespace
- * <h2>/move</h2>
- * @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;
-
- move(old_path, new_path);
- return 0;
-}
-
-/** @page engine_osc_namespace
- * <h2>/delete</h2>
- * @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;
-
- del(uri);
- return 0;
-}
-
-/** @page engine_osc_namespace
- * <h2>/connect</h2>
- * @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;
-
- connect(src_port_path, dst_port_path);
- return 0;
-}
-
-/** @page engine_osc_namespace
- * <h2>/disconnect</h2>
- * @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;
-
- disconnect(src_port_path, dst_port_path);
- return 0;
-}
-
-/** @page engine_osc_namespace
- * <h2>/disconnect_all</h2>
- * @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;
-
- disconnect_all(patch_path, object_path);
- return 0;
-}
-
-/** @page engine_osc_namespace
- * <h2>/note_on</h2>
- * @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
- * <h2>/note_off</h2>
- * @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
- * <h2>/all_notes_off</h2>
- * @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
- * <h2>/set_property</h2>
- * @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]);
-
- set_property(object_path, key, value);
- return 0;
-}
-
-/** @page engine_osc_namespace
- * <h2>/request_property</h2>
- * @arg @p response-id :: Integer
- * @arg @p uri :: URI String
- * @arg @p key :: URI String
- *
- * Request the value of a property on an object.
- */
-int
-OSCEngineReceiver::_request_property_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
-{
- const char* object_path = &argv[1]->s;
- const char* key = &argv[2]->s;
-
- request_property(object_path, key);
- 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 Engine
-} // namespace Ingen