summaryrefslogtreecommitdiff
path: root/libs/surfaces
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2009-01-15 22:09:23 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2009-01-15 22:09:23 +0000
commit16556bb182b19e6baae34543768668b5eeb3cd45 (patch)
treed7b584921af2ccb1b28269a5ae8aed7c521572a8 /libs/surfaces
parent13e40db5e5587499cf7a388c12b519a51f55cc81 (diff)
OSC becomes a control protocol object; make ardev_common.sh(.in) look for surfaces in the right place(s)
git-svn-id: svn://localhost/ardour2/branches/3.0@4411 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/surfaces')
-rw-r--r--libs/surfaces/generic_midi/SConscript2
-rw-r--r--libs/surfaces/osc/SConscript63
-rw-r--r--libs/surfaces/osc/interface.cc66
-rw-r--r--libs/surfaces/osc/osc.cc891
-rw-r--r--libs/surfaces/osc/osc.h196
-rw-r--r--libs/surfaces/osc/osc_controllable.cc54
-rw-r--r--libs/surfaces/osc/osc_controllable.h46
7 files changed, 1317 insertions, 1 deletions
diff --git a/libs/surfaces/generic_midi/SConscript b/libs/surfaces/generic_midi/SConscript
index d4c9e92214..86d879cdcd 100644
--- a/libs/surfaces/generic_midi/SConscript
+++ b/libs/surfaces/generic_midi/SConscript
@@ -9,7 +9,7 @@ Import('env final_prefix install_prefix final_config_prefix libraries i18n')
genericmidi = env.Clone()
#
-# this defines the version number of libardour_genericmidi
+# this defines the translation domain of libardour_genericmidi
#
domain = 'ardour_genericmidi'
diff --git a/libs/surfaces/osc/SConscript b/libs/surfaces/osc/SConscript
new file mode 100644
index 0000000000..2e1b0c7ac7
--- /dev/null
+++ b/libs/surfaces/osc/SConscript
@@ -0,0 +1,63 @@
+# -*- python -*-
+
+import os
+import os.path
+import glob
+
+Import('env final_prefix install_prefix final_config_prefix libraries i18n')
+
+osc = env.Clone()
+
+#
+# this defines the translation domain of libardour_osc
+#
+
+domain = 'ardour_osc'
+
+osc.Append(DOMAIN = domain, MAJOR = 1, MINOR = 0, MICRO = 0)
+osc.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"")
+osc.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
+osc.Append(PACKAGE = domain)
+osc.Append(POTFILE = domain + '.pot')
+
+osc_files=Split("""
+osc.cc
+osc_controllable.cc
+interface.cc
+""")
+
+osc.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
+osc.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"")
+osc.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"")
+osc.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")
+osc.Append(CPPPATH = libraries['jack'].get('CPPPATH', []))
+
+osc.Merge ([
+ libraries['lo'],
+ libraries['midi++2'],
+ libraries['evoral'],
+ libraries['ardour'],
+ libraries['ardour_cp'],
+ libraries['pbd'],
+ libraries['sigc2'],
+ libraries['xml'],
+ libraries['glib2'],
+ libraries['glibmm2']
+ ])
+
+libardour_osc = osc.SharedLibrary('ardour_osc', osc_files)
+
+if osc['HAVE_LIBLO']:
+ Default(libardour_osc)
+ if env['NLS']:
+ i18n (osc, osc_files, env)
+ env.Alias('install', env.Install(os.path.join(install_prefix,
+ env['LIBDIR'],
+ 'ardour3',
+ 'surfaces'),
+ libardour_osc))
+
+env.Alias('tarball', env.Distribute (env['DISTTREE'],
+ [ 'SConscript' ] +
+ osc_files +
+ glob.glob('po/*.po') + glob.glob('*.h')))
diff --git a/libs/surfaces/osc/interface.cc b/libs/surfaces/osc/interface.cc
new file mode 100644
index 0000000000..042dac1ded
--- /dev/null
+++ b/libs/surfaces/osc/interface.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2006 Paul Davis
+ * Copyright (C) 2007 Michael Taht
+ *
+ * This program 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.
+ *
+ * This program 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 more 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * */
+
+#include <control_protocol/control_protocol.h>
+#include "osc.h"
+
+using namespace ARDOUR;
+
+ControlProtocol*
+new_osc_protocol (ControlProtocolDescriptor* descriptor, Session* s)
+{
+ OSC* osc = new OSC (*s, Config->get_osc_port());
+
+ osc->set_active (true);
+
+ return osc;
+}
+
+void
+delete_osc_protocol (ControlProtocolDescriptor* descriptor, ControlProtocol* cp)
+{
+ delete cp;
+}
+
+bool
+probe_osc_protocol (ControlProtocolDescriptor* descriptor)
+{
+ return true; // we can always do OSC
+}
+
+static ControlProtocolDescriptor osc_descriptor = {
+ name : "Open Sound Control (OSC)",
+ id : "uri://ardour.org/surfaces/osc:0",
+ ptr : 0,
+ module : 0,
+ mandatory : 1,
+ supports_feedback : true,
+ probe : probe_osc_protocol,
+ initialize : new_osc_protocol,
+ destroy : delete_osc_protocol
+};
+
+extern "C" {
+ControlProtocolDescriptor*
+protocol_descriptor () {
+ return &osc_descriptor;
+}
+}
+
diff --git a/libs/surfaces/osc/osc.cc b/libs/surfaces/osc/osc.cc
new file mode 100644
index 0000000000..4503993360
--- /dev/null
+++ b/libs/surfaces/osc/osc.cc
@@ -0,0 +1,891 @@
+/*
+ * Copyright (C) 2006 Paul Davis
+ *
+ * This program 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.
+ *
+ * This program 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 more 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <iostream>
+#include <fstream>
+#include <cstdio>
+#include <cstdlib>
+#include <cerrno>
+#include <algorithm>
+
+#include <sys/poll.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <glibmm/miscutils.h>
+
+#include <pbd/pthread_utils.h>
+#include <pbd/file_utils.h>
+#include <pbd/filesystem.h>
+#include <pbd/failed_constructor.h>
+
+#include <ardour/session.h>
+#include <ardour/route.h>
+#include <ardour/audio_track.h>
+#include <ardour/midi_track.h>
+#include <ardour/dB.h>
+#include <ardour/filesystem_paths.h>
+
+#include "osc.h"
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace sigc;
+using namespace std;
+
+
+static void error_callback(int num, const char *m, const char *path)
+{
+#ifdef DEBUG
+ fprintf(stderr, "liblo server error %d in path %s: %s\n", num, path, m);
+#endif
+}
+
+OSC::OSC (Session& s, uint32_t port)
+ : ControlProtocol (s, "OSC")
+ , _port(port)
+{
+ _shutdown = false;
+ _osc_server = 0;
+ _osc_unix_server = 0;
+ _osc_thread = 0;
+ _namespace_root = "/ardour";
+ _send_route_changes = true;
+
+ // "Application Hooks"
+ session_loaded (s);
+ session->Exported.connect( mem_fun( *this, &OSC::session_exported ) );
+
+ /* catch up with existing routes */
+
+ boost::shared_ptr<Session::RouteList> rl = session->get_routes ();
+ route_added (*(rl.get()));
+
+ // session->RouteAdded.connect (mem_fun (*this, &OSC::route_added));
+}
+
+OSC::~OSC()
+{
+ stop ();
+}
+
+int
+OSC::set_active (bool yn)
+{
+ if (yn) {
+ return start ();
+ } else {
+ return stop ();
+ }
+}
+
+bool
+OSC::get_active () const
+{
+ return _osc_server != 0;
+}
+
+int
+OSC::set_feedback (bool yn)
+{
+ _send_route_changes = yn;
+ return 0;
+}
+
+bool
+OSC::get_feedback () const
+{
+ return _send_route_changes;
+}
+
+int
+OSC::start ()
+{
+ char tmpstr[255];
+
+ if (_osc_server) {
+ /* already started */
+ return 0;
+ }
+
+ for (int j=0; j < 20; ++j) {
+ snprintf(tmpstr, sizeof(tmpstr), "%d", _port);
+
+ if ((_osc_server = lo_server_new (tmpstr, error_callback))) {
+ break;
+ }
+#ifdef DEBUG
+ cerr << "can't get osc at port: " << _port << endl;
+#endif
+ _port++;
+ continue;
+ }
+
+#ifdef ARDOUR_OSC_UNIX_SERVER
+
+ // APPEARS sluggish for now
+
+ // attempt to create unix socket server too
+
+ snprintf(tmpstr, sizeof(tmpstr), "/tmp/sooperlooper_XXXXXX");
+ int fd = mkstemp(tmpstr);
+
+ if (fd >= 0 ) {
+ unlink (tmpstr);
+ close (fd);
+
+ _osc_unix_server = lo_server_new (tmpstr, error_callback);
+
+ if (_osc_unix_server) {
+ _osc_unix_socket_path = tmpstr;
+ }
+ }
+#endif
+
+ cerr << "OSC @ " << get_server_url () << endl;
+
+ PBD::sys::path url_file;
+
+ if (find_file_in_search_path (ardour_search_path() + system_config_search_path(),
+ "osc_url", url_file)) {
+ _osc_url_file = url_file.to_string();
+ ofstream urlfile;
+ urlfile.open(_osc_url_file.c_str(), ios::trunc);
+ if ( urlfile )
+ {
+ urlfile << get_server_url () << endl;
+ urlfile.close();
+ }
+ else
+ {
+ cerr << "Couldn't write '" << _osc_url_file << "'" <<endl;
+ }
+ }
+
+ register_callbacks();
+
+ // lo_server_thread_add_method(_sthread, NULL, NULL, OSC::_dummy_handler, this);
+
+ if (!init_osc_thread()) {
+ return -1;
+ }
+ return 0;
+}
+
+int
+OSC::stop ()
+{
+ if (_osc_server == 0) {
+ /* already stopped */
+ return 0;
+ }
+
+ // stop server thread
+ terminate_osc_thread();
+
+ lo_server_free (_osc_server);
+ _osc_server = 0;
+
+ if (!_osc_unix_socket_path.empty()) {
+ // unlink it
+ unlink(_osc_unix_socket_path.c_str());
+ }
+
+ if (! _osc_url_file.empty() ) {
+ unlink(_osc_url_file.c_str() );
+ }
+ return 0;
+}
+
+void
+OSC::register_callbacks()
+{
+ lo_server srvs[2];
+ lo_server serv;
+
+ srvs[0] = _osc_server;
+ srvs[1] = _osc_unix_server;
+
+ for (size_t i = 0; i < 2; ++i) {
+
+ if (!srvs[i]) {
+ continue;
+ }
+
+ serv = srvs[i];
+
+ /* this is a special catchall handler */
+
+ lo_server_add_method (serv, 0, 0, _catchall, this);
+
+#define REGISTER_CALLBACK(serv,path,types, function) lo_server_add_method (serv, path, types, OSC::_ ## function, this)
+
+ REGISTER_CALLBACK (serv, "/ardour/add_marker", "", add_marker);
+ REGISTER_CALLBACK (serv, "/ardour/access_action", "s", access_action);
+ REGISTER_CALLBACK (serv, "/ardour/loop_toggle", "", loop_toggle);
+ REGISTER_CALLBACK (serv, "/ardour/goto_start", "", goto_start);
+ REGISTER_CALLBACK (serv, "/ardour/goto_end", "", goto_end);
+ REGISTER_CALLBACK (serv, "/ardour/rewind", "", rewind);
+ REGISTER_CALLBACK (serv, "/ardour/ffwd", "", ffwd);
+ REGISTER_CALLBACK (serv, "/ardour/transport_stop", "", transport_stop);
+ REGISTER_CALLBACK (serv, "/ardour/transport_play", "", transport_play);
+ REGISTER_CALLBACK (serv, "/ardour/set_transport_speed", "f", set_transport_speed);
+ REGISTER_CALLBACK (serv, "/ardour/save_state", "", save_state);
+ REGISTER_CALLBACK (serv, "/ardour/prev_marker", "", prev_marker);
+ REGISTER_CALLBACK (serv, "/ardour/next_marker", "", next_marker);
+ REGISTER_CALLBACK (serv, "/ardour/undo", "", undo);
+ REGISTER_CALLBACK (serv, "/ardour/redo", "", redo);
+ REGISTER_CALLBACK (serv, "/ardour/toggle_punch_in", "", toggle_punch_in);
+ REGISTER_CALLBACK (serv, "/ardour/toggle_punch_out", "", toggle_punch_out);
+ REGISTER_CALLBACK (serv, "/ardour/rec_enable_toggle", "", rec_enable_toggle);
+ REGISTER_CALLBACK (serv, "/ardour/toggle_all_rec_enables", "", toggle_all_rec_enables);
+
+ REGISTER_CALLBACK (serv, "/ardour/routes/mute", "ii", route_mute);
+ REGISTER_CALLBACK (serv, "/ardour/routes/solo", "ii", route_solo);
+ REGISTER_CALLBACK (serv, "/ardour/routes/recenable", "ii", route_recenable);
+ REGISTER_CALLBACK (serv, "/ardour/routes/gainabs", "if", route_set_gain_abs);
+ REGISTER_CALLBACK (serv, "/ardour/routes/gaindB", "if", route_set_gain_dB);
+
+#if 0
+ REGISTER_CALLBACK (serv, "/ardour/*/#current_value", "", current_value);
+ REGISTER_CALLBACK (serv, "/ardour/set", "", set);
+#endif
+
+#if 0
+ // un/register_update args= s:ctrl s:returl s:retpath
+ lo_server_add_method(serv, "/register_update", "sss", OSC::global_register_update_handler, this);
+ lo_server_add_method(serv, "/unregister_update", "sss", OSC::global_unregister_update_handler, this);
+ lo_server_add_method(serv, "/register_auto_update", "siss", OSC::global_register_auto_update_handler, this);
+ lo_server_add_method(serv, "/unregister_auto_update", "sss", OSC::_global_unregister_auto_update_handler, this);
+#endif
+ }
+}
+
+bool
+OSC::init_osc_thread ()
+{
+ // create new thread to run server
+ if (pipe (_request_pipe)) {
+ cerr << "Cannot create osc request signal pipe" << strerror (errno) << endl;
+ return false;
+ }
+
+ if (fcntl (_request_pipe[0], F_SETFL, O_NONBLOCK)) {
+ cerr << "osc: cannot set O_NONBLOCK on signal read pipe " << strerror (errno) << endl;
+ return false;
+ }
+
+ if (fcntl (_request_pipe[1], F_SETFL, O_NONBLOCK)) {
+ cerr << "osc: cannot set O_NONBLOCK on signal write pipe " << strerror (errno) << endl;
+ return false;
+ }
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 500000);
+
+ pthread_create (&_osc_thread, &attr, &OSC::_osc_receiver, this);
+ if (!_osc_thread) {
+ return false;
+ }
+ pthread_attr_destroy(&attr);
+
+ //pthread_detach (_osc_thread);
+ return true;
+}
+
+void
+OSC::terminate_osc_thread ()
+{
+ void* status;
+
+ _shutdown = true;
+
+ poke_osc_thread ();
+
+ pthread_join (_osc_thread, &status);
+}
+
+void
+OSC::poke_osc_thread ()
+{
+ char c;
+
+ if (write (_request_pipe[1], &c, 1) != 1) {
+ cerr << "cannot send signal to osc thread! " << strerror (errno) << endl;
+ }
+}
+
+std::string
+OSC::get_server_url()
+{
+ string url;
+ char * urlstr;
+
+ if (_osc_server) {
+ urlstr = lo_server_get_url (_osc_server);
+ url = urlstr;
+ free (urlstr);
+ }
+
+ return url;
+}
+
+std::string
+OSC::get_unix_server_url()
+{
+ string url;
+ char * urlstr;
+
+ if (_osc_unix_server) {
+ urlstr = lo_server_get_url (_osc_unix_server);
+ url = urlstr;
+ free (urlstr);
+ }
+
+ return url;
+}
+
+
+/* server thread */
+
+void *
+OSC::_osc_receiver(void * arg)
+{
+ PBD::notify_gui_about_thread_creation (pthread_self(), X_("OSC"));
+
+ static_cast<OSC*> (arg)->osc_receiver();
+ return 0;
+}
+
+void
+OSC::osc_receiver()
+{
+ struct pollfd pfd[3];
+ int fds[3];
+ lo_server srvs[3];
+ int nfds = 0;
+ int timeout = -1;
+ int ret;
+
+ fds[0] = _request_pipe[0];
+ nfds++;
+
+ if (_osc_server && lo_server_get_socket_fd(_osc_server) >= 0) {
+ fds[nfds] = lo_server_get_socket_fd(_osc_server);
+ srvs[nfds] = _osc_server;
+ nfds++;
+ }
+
+ if (_osc_unix_server && lo_server_get_socket_fd(_osc_unix_server) >= 0) {
+ fds[nfds] = lo_server_get_socket_fd(_osc_unix_server);
+ srvs[nfds] = _osc_unix_server;
+ nfds++;
+ }
+
+
+ while (!_shutdown) {
+
+ for (int i=0; i < nfds; ++i) {
+ pfd[i].fd = fds[i];
+ pfd[i].events = POLLIN|POLLPRI|POLLHUP|POLLERR;
+ pfd[i].revents = 0;
+ }
+
+ again:
+ //cerr << "poll on " << nfds << " for " << timeout << endl;
+ if ((ret = poll (pfd, nfds, timeout)) < 0) {
+ if (errno == EINTR) {
+ /* gdb at work, perhaps */
+ goto again;
+ }
+
+ cerr << "OSC thread poll failed: " << strerror (errno) << endl;
+
+ break;
+ }
+
+ //cerr << "poll returned " << ret << " pfd[0].revents = " << pfd[0].revents << " pfd[1].revents = " << pfd[1].revents << endl;
+
+ if (_shutdown) {
+ break;
+ }
+
+ if ((pfd[0].revents & ~POLLIN)) {
+ cerr << "OSC: error polling extra port" << endl;
+ break;
+ }
+
+ for (int i=1; i < nfds; ++i) {
+ if (pfd[i].revents & POLLIN)
+ {
+ // this invokes callbacks
+ // cerr << "invoking recv on " << pfd[i].fd << endl;
+ lo_server_recv(srvs[i]);
+ }
+ }
+
+ }
+
+ //cerr << "SL engine shutdown" << endl;
+
+ if (_osc_server) {
+ int fd = lo_server_get_socket_fd(_osc_server);
+ if (fd >=0) {
+ // hack around
+ close(fd);
+ }
+ lo_server_free (_osc_server);
+ _osc_server = 0;
+ }
+
+ if (_osc_unix_server) {
+ cerr << "freeing unix server" << endl;
+ lo_server_free (_osc_unix_server);
+ _osc_unix_server = 0;
+ }
+
+ close(_request_pipe[0]);
+ close(_request_pipe[1]);
+}
+
+void
+OSC::current_value_query (const char* path, size_t len, lo_arg **argv, int argc, lo_message msg)
+{
+ char* subpath;
+
+ subpath = (char*) malloc (len-15+1);
+ memcpy (subpath, path, len-15);
+ subpath[len-15] = '\0';
+
+ send_current_value (subpath, argv, argc, msg);
+
+ free (subpath);
+}
+
+void
+OSC::send_current_value (const char* path, lo_arg** argv, int argc, lo_message msg)
+{
+ if (!session) {
+ return;
+ }
+
+ lo_message reply = lo_message_new ();
+ boost::shared_ptr<Route> r;
+ int id;
+
+ lo_message_add_string (reply, path);
+
+ if (argc == 0) {
+ lo_message_add_string (reply, "bad syntax");
+ } else {
+ id = argv[0]->i;
+ r = session->route_by_remote_id (id);
+
+ if (!r) {
+ lo_message_add_string (reply, "not found");
+ } else {
+
+ if (strcmp (path, "/routes/state") == 0) {
+
+ if (boost::dynamic_pointer_cast<AudioTrack>(r)) {
+ lo_message_add_string (reply, "AT");
+ } else if (boost::dynamic_pointer_cast<MidiTrack>(r)) {
+ lo_message_add_string (reply, "MT");
+ } else {
+ lo_message_add_string (reply, "B");
+ }
+
+ lo_message_add_string (reply, r->name().c_str());
+ lo_message_add_int32 (reply, r->n_inputs().n_audio());
+ lo_message_add_int32 (reply, r->n_outputs().n_audio());
+ lo_message_add_int32 (reply, r->muted());
+ lo_message_add_int32 (reply, r->soloed());
+
+ } else if (strcmp (path, "/routes/mute") == 0) {
+
+ lo_message_add_int32 (reply, (float) r->muted());
+
+ } else if (strcmp (path, "/routes/solo") == 0) {
+
+ lo_message_add_int32 (reply, r->soloed());
+ }
+ }
+ }
+
+ lo_send_message (lo_message_get_source (msg), "#reply", reply);
+ lo_message_free (reply);
+}
+
+int
+OSC::_catchall (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data)
+{
+ return ((OSC*)user_data)->catchall (path, types, argv, argc, data);
+}
+
+int
+OSC::catchall (const char *path, const char *types, lo_arg **argv, int argc, lo_message msg)
+{
+ size_t len;
+ int ret = 1; /* unhandled */
+
+ cerr << "Received a message, path = " << path << " types = \""
+ << (types ? types : "NULL") << '"' << endl;
+
+ /* 15 for /#current_value plus 2 for /<path> */
+
+ len = strlen (path);
+
+ if (len >= 17 && !strcmp (&path[len-15], "/#current_value")) {
+ current_value_query (path, len, argv, argc, msg);
+ ret = 0;
+
+ } else if (strcmp (path, "/routes/listen") == 0) {
+
+ cerr << "set up listener\n";
+
+ lo_message reply = lo_message_new ();
+
+ if (argc > 0) {
+ int id = argv[0]->i;
+ boost::shared_ptr<Route> r = session->route_by_remote_id (id);
+
+ if (!r) {
+ lo_message_add_string (reply, "not found");
+ cerr << "no such route\n";
+ } else {
+
+ ListenerPair listener;
+
+ listener.first = r.get();
+ listener.second = lo_message_get_source (msg);
+
+ cerr << "add listener\n";
+
+ listen_to_route (listener);
+
+ lo_message_add_string (reply, "0");
+ }
+
+ } else {
+ lo_message_add_string (reply, "syntax error");
+ }
+
+ lo_send_message (lo_message_get_source (msg), "#reply", reply);
+ lo_message_free (reply);
+
+ } else if (strcmp (path, "/routes/ignore") == 0) {
+
+ if (argc > 0) {
+ int id = argv[0]->i;
+ boost::shared_ptr<Route> r = session->route_by_remote_id (id);
+
+ if (r) {
+ ListenerPair listener;
+
+ listener.first = r.get();
+ listener.second = lo_message_get_source (msg);
+
+ drop_listener_pair (listener);
+ }
+ }
+ }
+
+ return ret;
+}
+
+void
+OSC::route_added (Session::RouteList& rl)
+{
+}
+
+void
+OSC::listen_to_route (const ListenerPair& lp)
+{
+ Listeners::iterator x;
+ bool route_exists = false;
+
+ cerr << "listen to route\n";
+
+ /* check existing listener pairs to avoid duplicate listens */
+
+ for (x = listeners.begin(); x != listeners.end(); ++x) {
+
+ if ((*x)->route == lp.first) {
+ route_exists = true;
+
+ if ((*x)->addr == lp.second ) {
+ return;
+ }
+ }
+ }
+
+ Listener* l = new Listener (lp.first, lp.second);
+
+ cerr << "listener binding to signals\n";
+
+ l->connections.push_back (lp.first->solo_changed.connect (bind (mem_fun (*this, &OSC::route_changed), RouteSolo, lp.first, lp.second)));
+ l->connections.push_back (lp.first->mute_changed.connect (bind (mem_fun (*this, &OSC::route_changed), RouteMute, lp.first, lp.second)));
+ l->connections.push_back (lp.first->gain_control()->Changed.connect (bind (mem_fun (*this, &OSC::route_changed_deux), RouteGain, lp.first, lp.second)));
+
+ if (!route_exists) {
+ l->route->GoingAway.connect (bind (mem_fun (*this, &OSC::drop_listeners_by_route), l->route));
+ }
+
+ listeners.push_back (l);
+}
+
+void
+OSC::drop_listeners_by_route (Route* r)
+{
+ Listeners::iterator x;
+
+ for (x = listeners.begin(); x != listeners.end();) {
+ if ((*x)->route == r) {
+ delete *x;
+ x = listeners.erase (x);
+ } else {
+ ++x;
+ }
+ }
+}
+
+void
+OSC::drop_listener_pair (const ListenerPair& lp)
+{
+ Listeners::iterator x;
+
+ for (x = listeners.begin(); x != listeners.end(); ++x) {
+ if ((*x)->route == lp.first && (*x)->addr == lp.second) {
+ listeners.erase (x);
+ return;
+ }
+ }
+}
+
+// "Application Hook" Handlers //
+void
+OSC::session_loaded( Session& s ) {
+ lo_address listener = lo_address_new( NULL, "7770" );
+ lo_send( listener, "/session/loaded", "ss", s.path().c_str(), s.name().c_str() );
+}
+
+void
+OSC::session_exported( std::string path, std::string name ) {
+ lo_address listener = lo_address_new( NULL, "7770" );
+ lo_send( listener, "/session/exported", "ss", path.c_str(), name.c_str() );
+}
+
+void
+OSC::set_send_route_changes (bool yn)
+{
+ _send_route_changes = yn;
+}
+
+void
+OSC::route_changed (void* src, RouteChangeType what, Route* r, lo_address addr)
+{
+ route_changed_deux (what, r, addr);
+}
+
+void
+OSC::route_changed_deux (RouteChangeType what, Route* r, lo_address addr)
+{
+ if (!_send_route_changes) {
+ return;
+ }
+
+ string prefix = _namespace_root;
+ int ret;
+
+ switch (what) {
+
+ case OSC::RouteSolo:
+ prefix += "/changed/route/solo";
+ ret = lo_send (addr, prefix.c_str(), "ii", r->remote_control_id(), (int) r->soloed());
+ break;
+
+ case OSC::RouteMute:
+ prefix += "/changed/route/mute";
+ ret = lo_send (addr, prefix.c_str(), "ii", r->remote_control_id(), (int) r->muted());
+ break;
+
+ case OSC::RouteGain:
+ prefix += "/changed/route/gain";
+ ret = lo_send (addr, prefix.c_str(), "if", r->remote_control_id(), r->effective_gain());
+
+ default:
+ error << "OSC: unhandled route change\n";
+ return;
+ }
+
+ if (ret < 0) {
+ ListenerPair lp;
+
+ lp.first = r;
+ lp.second = addr;
+
+ cerr << "Error sending to listener ... dropping\n";
+ drop_listener_pair (lp);
+ }
+
+}
+
+// end "Application Hook" Handlers //
+
+/* path callbacks */
+
+int
+OSC::current_value (const char *path, const char *types, lo_arg **argv, int argc, void *data, void* user_data)
+{
+#if 0
+ const char* returl;
+
+ if (argc < 3 || types == 0 || strlen (types) < 3 || types[0] != 's' || types[1] != 's' || types[2] != s) {
+ return 1;
+ }
+
+ const char *returl = argv[1]->s;
+ lo_address addr = find_or_cache_addr (returl);
+
+ const char *retpath = argv[2]->s;
+
+
+ if (strcmp (argv[0]->s, "transport_frame") == 0) {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else if (strcmp (argv[0]->s, "transport_speed") == 0) {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else if (strcmp (argv[0]->s, "transport_locked") == 0) {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else if (strcmp (argv[0]->s, "punch_in") == 0) {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else if (strcmp (argv[0]->s, "punch_out") == 0) {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else if (strcmp (argv[0]->s, "rec_enable") == 0) {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else {
+
+ /* error */
+ }
+#endif
+ return 0;
+}
+
+int
+OSC::route_mute (int rid, int yn)
+{
+ if (!session) return -1;
+
+ boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
+
+ if (r) {
+ r->set_mute (yn, this);
+ }
+ return 0;
+}
+
+int
+OSC::route_solo (int rid, int yn)
+{
+ if (!session) return -1;
+
+ boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
+
+ if (r) {
+ r->set_solo (yn, this);
+ }
+ return 0;
+}
+
+int
+OSC::route_recenable (int rid, int yn)
+{
+ if (!session) return -1;
+
+ boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
+
+ if (r) {
+ r->set_record_enable (yn, this);
+ }
+ return 0;
+}
+
+int
+OSC::route_set_gain_abs (int rid, float level)
+{
+ if (!session) return -1;
+
+ boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
+
+ if (r) {
+ r->set_gain (level, this);
+ }
+
+ return 0;
+}
+
+int
+OSC::route_set_gain_dB (int rid, float dB)
+{
+ if (!session) return -1;
+
+ boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
+
+ if (r) {
+ r->set_gain (dB_to_coefficient (dB), this);
+ }
+
+ return 0;
+}
+
+XMLNode&
+OSC::get_state ()
+{
+ return *(new XMLNode ("OSC"));
+}
+
+int
+OSC::set_state (const XMLNode&)
+{
+ return 0;
+}
diff --git a/libs/surfaces/osc/osc.h b/libs/surfaces/osc/osc.h
new file mode 100644
index 0000000000..b2417474fc
--- /dev/null
+++ b/libs/surfaces/osc/osc.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2006 Paul Davis
+ *
+ * This program 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.
+ *
+ * This program 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 more 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef ardour_osc_h
+#define ardour_osc_h
+
+#include <string>
+
+#include <sys/time.h>
+#include <pthread.h>
+
+#include <lo/lo.h>
+
+#include <sigc++/sigc++.h>
+
+#include <ardour/types.h>
+#include <ardour/session.h>
+#include <control_protocol/control_protocol.h>
+
+namespace ARDOUR {
+
+class Session;
+class Route;
+
+class OSC : public ControlProtocol
+{
+ public:
+ OSC (Session&, uint32_t port);
+ virtual ~OSC();
+
+ XMLNode& get_state ();
+ int set_state (const XMLNode&);
+
+ int set_active (bool yn);
+ bool get_active () const;
+ int set_feedback (bool yn);
+ bool get_feedback () const;
+
+ void set_namespace_root (std::string);
+ bool send_route_changes () const { return _send_route_changes; }
+ void set_send_route_changes (bool yn);
+
+ int start ();
+ int stop ();
+
+ private:
+ uint32_t _port;
+ volatile bool _ok;
+ volatile bool _shutdown;
+ lo_server _osc_server;
+ lo_server _osc_unix_server;
+ std::string _osc_unix_socket_path;
+ std::string _osc_url_file;
+ std::string _namespace_root;
+ bool _send_route_changes;
+ pthread_t _osc_thread;
+ int _request_pipe[2];
+
+ static void * _osc_receiver(void * arg);
+ void osc_receiver();
+ void send(); // This should accept an OSC payload
+
+ bool init_osc_thread ();
+ void terminate_osc_thread ();
+ void poke_osc_thread ();
+
+ void register_callbacks ();
+
+ void route_added (ARDOUR::Session::RouteList&);
+
+ // Handlers for "Application Hook" signals
+ void session_loaded (ARDOUR::Session&);
+ void session_exported (std::string, std::string);
+
+ enum RouteChangeType {
+ RouteSolo,
+ RouteMute,
+ RouteGain
+ };
+
+ void route_changed (void* ignored, RouteChangeType, ARDOUR::Route*, lo_address);
+ void route_changed_deux (RouteChangeType, ARDOUR::Route*, lo_address);
+
+ // end "Application Hook" handles
+
+ std::string get_server_url ();
+ std::string get_unix_server_url ();
+
+ void send_current_value (const char* path, lo_arg** argv, int argc, lo_message msg);
+ void current_value_query (const char* path, size_t len, lo_arg **argv, int argc, lo_message msg);
+ int catchall (const char *path, const char *types, lo_arg **argv, int argc, void *data);
+ static int _catchall (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data);
+
+ int current_value (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data);
+
+#define PATH_CALLBACK(name) \
+ static int _ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data) { \
+ return static_cast<OSC*>(user_data)->cb_ ## name (path, types, argv, argc, data); \
+ } \
+ int cb_ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data) { \
+ name (); \
+ return 0; \
+ }
+
+ PATH_CALLBACK(add_marker);
+ PATH_CALLBACK(loop_toggle);
+ PATH_CALLBACK(goto_start);
+ PATH_CALLBACK(goto_end);
+ PATH_CALLBACK(rewind);
+ PATH_CALLBACK(ffwd);
+ PATH_CALLBACK(transport_stop);
+ PATH_CALLBACK(transport_play);
+ PATH_CALLBACK(save_state);
+ PATH_CALLBACK(prev_marker);
+ PATH_CALLBACK(next_marker);
+ PATH_CALLBACK(undo);
+ PATH_CALLBACK(redo);
+ PATH_CALLBACK(toggle_punch_in);
+ PATH_CALLBACK(toggle_punch_out);
+ PATH_CALLBACK(rec_enable_toggle);
+ PATH_CALLBACK(toggle_all_rec_enables);
+
+#define PATH_CALLBACK1(name,type,optional) \
+ static int _ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data) { \
+ return static_cast<OSC*>(user_data)->cb_ ## name (path, types, argv, argc, data); \
+ } \
+ int cb_ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data) { \
+ if (argc > 0) { \
+ name (optional argv[0]->type); \
+ } \
+ return 0; \
+ }
+
+ PATH_CALLBACK1(set_transport_speed,f,);
+ PATH_CALLBACK1(access_action,s,&);
+
+#define PATH_CALLBACK2(name,arg1type,arg2type) \
+ static int _ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data) { \
+ return static_cast<OSC*>(user_data)->cb_ ## name (path, types, argv, argc, data); \
+ } \
+ int cb_ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data) { \
+ if (argc > 1) { \
+ name (argv[0]->arg1type, argv[1]->arg2type); \
+ } \
+ return 0; \
+ }
+
+ PATH_CALLBACK2(route_mute,i,i);
+ PATH_CALLBACK2(route_solo,i,i);
+ PATH_CALLBACK2(route_recenable,i,i);
+ PATH_CALLBACK2(route_set_gain_abs,i,f);
+ PATH_CALLBACK2(route_set_gain_dB,i,f);
+
+ int route_mute (int rid, int yn);
+ int route_solo (int rid, int yn);
+ int route_recenable (int rid, int yn);
+ int route_set_gain_abs (int rid, float level);
+ int route_set_gain_dB (int rid, float dB);
+
+ struct Listener {
+ Route* route;
+ lo_address addr;
+ std::vector<sigc::connection> connections;
+
+ Listener (Route* r, lo_address a) : route (r), addr (a) {}
+ };
+
+ typedef std::pair<Route*, lo_address> ListenerPair;
+ typedef std::list<Listener*> Listeners;
+
+ Listeners listeners;
+
+ void listen_to_route (const ListenerPair&);
+ void drop_listener_pair (const ListenerPair&);
+ void drop_listeners_by_route (Route*);
+};
+
+}
+
+#endif // ardour_osc_h
diff --git a/libs/surfaces/osc/osc_controllable.cc b/libs/surfaces/osc/osc_controllable.cc
new file mode 100644
index 0000000000..12959652b8
--- /dev/null
+++ b/libs/surfaces/osc/osc_controllable.cc
@@ -0,0 +1,54 @@
+/*
+ Copyright (C) 2009 Paul Davis
+
+ This program 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.
+
+ This program 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 more 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cstdio> /* for sprintf, sigh */
+#include <climits>
+#include <pbd/error.h>
+#include <pbd/xml++.h>
+
+#include "osc_controllable.h"
+
+using namespace sigc;
+using namespace PBD;
+using namespace ARDOUR;
+
+OSCControllable::OSCControllable (lo_address a, Controllable& c)
+ : controllable (c)
+ , addr (a)
+{
+}
+
+OSCControllable::~OSCControllable ()
+{
+ lo_address_free (addr);
+}
+
+XMLNode&
+OSCControllable::get_state ()
+{
+ XMLNode& root (controllable.get_state());
+ return root;
+}
+
+int
+OSCControllable::set_state (const XMLNode& node)
+{
+ return 0;
+}
+
diff --git a/libs/surfaces/osc/osc_controllable.h b/libs/surfaces/osc/osc_controllable.h
new file mode 100644
index 0000000000..48183138ec
--- /dev/null
+++ b/libs/surfaces/osc/osc_controllable.h
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 1998-2006 Paul Davis
+
+ This program 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.
+
+ This program 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 more 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __osc_osccontrollable_h__
+#define __osc_osccontrollable_h__
+
+#include <string>
+
+#include <sigc++/sigc++.h>
+#include <lo/lo.h>
+
+#include <pbd/controllable.h>
+#include <pbd/stateful.h>
+#include <ardour/types.h>
+
+class OSCControllable : public PBD::Stateful
+{
+ public:
+ OSCControllable (lo_address addr, PBD::Controllable&);
+ virtual ~OSCControllable ();
+
+ XMLNode& get_state ();
+ int set_state (const XMLNode& node);
+
+ private:
+ PBD::Controllable& controllable;
+ lo_address addr;
+};
+
+#endif /* __osc_osccontrollable_h__ */