OSC becomes a control protocol object; make ardev_common.sh(.in) look for surfaces...
authorPaul Davis <paul@linuxaudiosystems.com>
Thu, 15 Jan 2009 22:09:23 +0000 (22:09 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Thu, 15 Jan 2009 22:09:23 +0000 (22:09 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@4411 d708f5d6-7413-0410-9779-e7cbd77b26cf

14 files changed:
SConstruct
gtk2_ardour/ardev_common.sh.in
libs/ardour/SConscript
libs/ardour/ardour/osc.h [deleted file]
libs/ardour/globals.cc
libs/ardour/osc.cc [deleted file]
libs/ardour/session.cc
libs/surfaces/generic_midi/SConscript
libs/surfaces/osc/SConscript [new file with mode: 0644]
libs/surfaces/osc/interface.cc [new file with mode: 0644]
libs/surfaces/osc/osc.cc [new file with mode: 0644]
libs/surfaces/osc/osc.h [new file with mode: 0644]
libs/surfaces/osc/osc_controllable.cc [new file with mode: 0644]
libs/surfaces/osc/osc_controllable.h [new file with mode: 0644]

index e603a0e305f892168d241b6127db874cd1923a81..1744090bd4277c127bdd66c79133a720e4bbff14 100644 (file)
@@ -45,7 +45,6 @@ opts.AddOptions(
     BoolOption('EXTRA_WARN', 'Compile with -Wextra, -ansi, and -pedantic.  Might break compilation.  For pedants', 0),
     BoolOption('FREESOUND', 'Include Freesound database lookup', 0),
     BoolOption('FPU_OPTIMIZATION', 'Build runtime checked assembler code', 1),
-    BoolOption('LIBLO', 'Compile with support for liblo library', 1),
     BoolOption('NLS', 'Set to turn on i18n support', 1),
     PathOption('PREFIX', 'Set the install "prefix"', '/usr/local'),
     BoolOption('SURFACES', 'Build support for control surfaces', 1),
@@ -867,10 +866,6 @@ if env['EXTRA_WARN']:
     env.Append(CXXFLAGS="-ansi")
 #    env.Append(CFLAGS="-iso")
 
-if env['LIBLO']:
-    env.Append(CCFLAGS="-DHAVE_LIBLO")
-
-
 #
 # fix scons nitpickiness on APPLE
 #
@@ -998,16 +993,17 @@ libraries['boost'] = conf.Finish ()
 #
 # Check for liblo
 
-if env['LIBLO']:
-    libraries['lo'] = LibraryInfo ()
-    prep_libcheck(env, libraries['lo'])
+libraries['lo'] = LibraryInfo ()
+prep_libcheck(env, libraries['lo'])
 
-    conf = Configure (libraries['lo'])
-    if conf.CheckLib ('lo', 'lo_server_new') == False:
-        print "liblo does not appear to be installed."
-        sys.exit (1)
-    
-    libraries['lo'] = conf.Finish ()
+conf = Configure (libraries['lo'])
+if conf.CheckLib ('lo', 'lo_server_new') == False:
+    print "liblo does not appear to be installed."
+    env['HAVE_LIBLO'] = False
+else:
+    env['HAVE_LIBLO'] = True
+
+libraries['lo'] = conf.Finish ()
 
 #
 # Check for dmalloc
@@ -1270,7 +1266,7 @@ else:
 
 #
 # * always build the LGPL control protocol lib, since we link against it from libardour
-# * ditto for generic MIDI
+# * ditto for generic MIDI and OSC
 # * tranzport & wiimote check whether they should build internally, but we need them here
 #   so that they are included in the tarball
 #
@@ -1280,7 +1276,8 @@ surface_subdirs = [ 'libs/surfaces/control_protocol',
                     'libs/surfaces/tranzport',
                     'libs/surfaces/mackie',
                     'libs/surfaces/powermate',
-                   'libs/surfaces/wiimote'
+                   'libs/surfaces/wiimote',
+                   'libs/surfaces/osc'
                     ]
 
 if env['SURFACES']:
index 28eaad39c7419be4f679ace66e67f45b17e2cf3a..1af6c77062260d743729b99e5b3e5734c33f2b81 100644 (file)
@@ -3,6 +3,7 @@ cd `dirname "$0"`/..
 #export G_DEBUG=fatal_criticals
 
 export ARDOUR_PATH=gtk2_ardour/icons:gtk2_ardour/pixmaps:gtk2_ardour:.
+export ARDOUR_SURFACES_PATH=libs/surfaces/osc:libs/surfaces/generic_midi:libs/surfaces/tranzport:libs/surfaces/powermate
 export ARDOUR_DATA_PATH=gtk2_ardour:.
 if test -d $HOME/gtk/inst ; then
     echo USING NEW CLEARLOOKS
index 58db70c6a6ead15378de3213cd43fbddedaa4225..f21b623ed29bf28a5af4fcfc3f7a25e0cb50304e 100644 (file)
@@ -178,7 +178,6 @@ version.cc
 
 arch_specific_objects = [ ]
 
-osc_files = [ 'osc.cc' ]
 vst_files = [ 'vst_plugin.cc', 'session_vst.cc' ]
 lv2_files = [ 'lv2_plugin.cc' ]
 audiounit_files = [ 'audio_unit.cc' ]
@@ -194,9 +193,6 @@ if ardour['LV2']:
        extra_sources += lv2_files
        ardour.Append(CCFLAGS="-DHAVE_SLV2")
 
-if ardour['LIBLO']:
-    extra_sources += osc_files
-
 ardour.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
 ardour.Append(CXXFLAGS="-DDATA_DIR=\\\"" + os.path.join (final_prefix, 'share') + "\\\"")
 ardour.Append(CXXFLAGS="-DMODULE_DIR=\\\"" + os.path.join (final_prefix, env['LIBDIR']) + "\\\"")
@@ -362,9 +358,6 @@ else:
 if ardour['LV2']:
        ardour.Merge ([ libraries['slv2'] ])
 
-if ardour['LIBLO']:
-       ardour.Merge ([ libraries['lo'] ])
-
 if ardour['COREAUDIO'] or ardour['AUDIOUNITS']:
        ardour.Merge ([ libraries['appleutility'] ])
 
@@ -418,7 +411,6 @@ env.Alias('tarball', env.Distribute (env['DISTTREE'],
                                     [ 'sse_functions_xmm.cc', 'sse_functions.s', 'sse_functions_64bit.s' ] +
                                     [ 'rb_effect.cc', 'st_stretch.cc', 'st_pitch.cc' ] +
                                     ardour_files + 
-                                    osc_files + 
                                     vst_files + 
                                     coreaudio_files + 
                                     audiounit_files +
diff --git a/libs/ardour/ardour/osc.h b/libs/ardour/ardour/osc.h
deleted file mode 100644 (file)
index 33759fe..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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 <control_protocol/basic_ui.h>
-
-namespace ARDOUR {
-       class Session;
-       class Route;
-
-class OSC : public BasicUI, public sigc::trackable
-{
-  public:
-       OSC (uint32_t port);
-       virtual ~OSC();
-       
-       void set_session (ARDOUR::Session&);
-       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;
-       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 session_going_away ();
-
-       // Handlers for "Application Hook" signals
-       void session_loaded( ARDOUR::Session& );
-       void session_exported( std::string, std::string );
-       // end "Application Hook" handles
-
-       std::string get_server_url ();
-       std::string get_unix_server_url ();
-
-       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);
-};
-
-}
-
-#endif // ardour_osc_h
index 8be2e3b8b643ef8a1e0a23d4229666ff4bcf8846..c6c56f99a400939c51b85d94a030ba57eaa20bf2 100644 (file)
 #include <ardour/audioengine.h>
 #include <ardour/filesystem_paths.h>
 
-#ifdef HAVE_LIBLO
-#include <ardour/osc.h>
-#endif
-
 #include <ardour/mix.h>
 #include <ardour/runtime_functions.h>
 
@@ -84,10 +80,6 @@ ARDOUR::Configuration* ARDOUR::Config = 0;
 ARDOUR::RuntimeProfile* ARDOUR::Profile = 0;
 ARDOUR::AudioLibrary* ARDOUR::Library = 0;
 
-#ifdef HAVE_LIBLO
-ARDOUR::OSC* ARDOUR::osc = 0;
-#endif
-
 using namespace ARDOUR;
 using namespace std;
 using namespace PBD;
@@ -111,26 +103,6 @@ mix_buffers_no_gain_t   ARDOUR::mix_buffers_no_gain = 0;
 
 sigc::signal<void,std::string> ARDOUR::BootMessage;
 
-#ifdef HAVE_LIBLO
-static int
-setup_osc ()
-{
-       /* no real cost to creating this object, and it avoids
-          conditionals anywhere that uses it
-       */
-
-       osc = new OSC (Config->get_osc_port());
-
-       if (Config->get_use_osc ()) {
-               BootMessage (_("Starting OSC"));
-               return osc->start ();
-       } else {
-               return 0;
-       }
-}
-
-#endif
-
 int
 ARDOUR::setup_midi ()
 {
@@ -341,11 +313,6 @@ ARDOUR::init (bool use_vst, bool try_optimization)
 
        Profile = new RuntimeProfile;
 
-#ifdef HAVE_LIBLO
-       if (setup_osc ()) {
-               return -1;
-       }
-#endif
 
 #ifdef VST_SUPPORT
        if (Config->get_use_vst() && fst_init ()) {
diff --git a/libs/ardour/osc.cc b/libs/ardour/osc.cc
deleted file mode 100644 (file)
index ffd2df6..0000000
+++ /dev/null
@@ -1,577 +0,0 @@
-/*
- * 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 <ardour/osc.h>
-#include <ardour/session.h>
-#include <ardour/route.h>
-#include <ardour/audio_track.h>
-#include <ardour/dB.h>
-#include <ardour/filesystem_paths.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 (uint32_t port)
-       : _port(port)
-{
-       _shutdown = false;
-       _osc_server = 0;
-       _osc_unix_server = 0;
-       _osc_thread = 0;
-}
-
-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;
-}
-
-OSC::~OSC()
-{
-       stop ();
-}
-
-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];
-
-#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::set_session (Session& s)
-{
-       session = &s;
-       session->GoingAway.connect (mem_fun (*this, &OSC::session_going_away));
-
-       // "Application Hooks"
-       session_loaded( s );
-       session->Exported.connect( mem_fun( *this, &OSC::session_exported ) );
-}
-
-void
-OSC::session_going_away ()
-{
-       session = 0;
-}
-
-// "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() );
-}
-
-// 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")) {
-
-               if (session) {
-                       lo_send (addr, retpath, "i", session->transport_frame());
-               }
-
-       } else if (strcmp (argv[0]->s, "transport_speed")) {
-
-               if (session) {
-                       lo_send (addr, retpath, "i", session->transport_frame());
-               }
-
-       } else if (strcmp (argv[0]->s, "transport_locked")) {
-
-               if (session) {
-                       lo_send (addr, retpath, "i", session->transport_frame());
-               }
-
-       } else if (strcmp (argv[0]->s, "punch_in") {
-
-               if (session) {
-                       lo_send (addr, retpath, "i", session->transport_frame());
-               }
-
-       } else if (strcmp (argv[0]->s, "punch_out") {
-
-               if (session) {
-                       lo_send (addr, retpath, "i", session->transport_frame());
-               }
-
-       } else if (strcmp (argv[0]->s, "rec_enable") {
-
-               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;
-}
index 67ea01b8bfdf0402de53afaa68f5dd51b9028a1a..c020509924a3e87ea8d0addbb08e5912a8b60616 100644 (file)
 #include <ardour/analyser.h>
 #include <ardour/bundle.h>
 
-#ifdef HAVE_LIBLO
-#include <ardour/osc.h>
-#endif
-
 #include "i18n.h"
 
 using namespace std;
@@ -728,15 +724,6 @@ Session::when_engine_running ()
        BootMessage (_("Connect to engine"));
 
        _engine.set_session (this);
-
-#ifdef HAVE_LIBLO
-       /* and to OSC */
-
-       BootMessage (_("OSC startup"));
-
-       osc->set_session (*this);
-#endif
-
 }
 
 void
index d4c9e92214adb1b8f6cb486ea204bd98e50ce0e5..86d879cdcd5eeb85ff09bb0c0b29f64af55c8c13 100644 (file)
@@ -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 (file)
index 0000000..2e1b0c7
--- /dev/null
@@ -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 (file)
index 0000000..042dac1
--- /dev/null
@@ -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 (file)
index 0000000..4503993
--- /dev/null
@@ -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 (file)
index 0000000..b241747
--- /dev/null
@@ -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 (file)
index 0000000..1295965
--- /dev/null
@@ -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 (file)
index 0000000..4818313
--- /dev/null
@@ -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__ */