View Issue Details

IDProjectCategoryView StatusLast Update
0002239ardourfeaturespublic2020-04-19 20:13
Reportermtaht Assigned Toovenwerks  
PrioritynormalSeverityfeatureReproducibilityalways
Status closedResolutionfixed 
Product VersionSVN/2.0-ongoing 
Summary0002239: Make OSC send useful information to OSC controllers
DescriptionI've been trying to work out a good way for ardour's OSC to talk to controllers like the tranzport, alphatrack, monome, etc.

The principal problem is that ardour's view of time is continuous, while any given controller I've played with can only respond to events on a 10ms interval, at best.

Adopting a "subscribe" paradigm for events a given controller is interested in (a la sooperlooper) is probably the best way to go. The first crack at it is in a nonworking (yet compiling) patch here, so we can discuss how to move forward.

The only thing really worth looking at is the broadcast_state and broadcast_track routines here. It looks like, even for large projects, that packet size will stay well within udp's limits.

Sending 100-200 packets per second shouldn't stress any modern hardware in the slightest. We could even multicast...

Ideally I'd just be able to loop through the tracks and pull out the current information (gain,pan,name,etc) for each, but it's looking more like I have to attach callbacks from hell using routes, on everything, and I'm hoping I'm just dull, rather than misunderstanding things. (I'd rather be working on getting a client to understand the sent messages than delving deep into ardour to convince it to send them, hint, hint)

Additional InformationThere's also a small bugfix in here. If you disable OSC then re-enable it, it fails, currently, due to not resetting _shutdown.

Need to move to select with a timeout rather than poll as poll uses millsec, and everywhere else I have microsec resolution. select can do nanosecond resolution (theoretically)
TagsNo tags attached.

Activities

mtaht

2008-05-16 06:55

developer   ~0004937

Last edited: 2008-05-16 07:01

The attached patch is a snapshot of my progress this week towards a more complete OSC implementation in ardour

It implements:

* A quick and dirty linux 2.6.24 and later "timerfd" mechanism to implement timeouts and a reliable clock. This is much cleaner than the equivalent poll and pipe mechanism used elsewhere in ardour. (The abstraction can be retrofitted to use the older mechanism for older kernels and other os's, but I haven't got around to it)

* OSC multicast (via the current svn version of liblo)

* Deletion of the old unix_server code which never worked anyway

* A start at broadcasting useful information (global state and track data).

The proof of concept here was to see if could send 110 BIG packets a second containing OSC data without affecting the rest of ardour (xruns, etc).

At least on my hardware (quad core 64 bit), running OSC doesn't even tweak the 'top' utility's process load meter, so the network usage appears to have trivial impact on ardour, even with 32 tracks enabled. We'll see what happens when I start extracting peak gain and the like, but my feeling is that the existing automation code for extrapolating pan and gain points is going to be much harder on ardour than the OSC code.

Actually, I'm sending 220 BIG packets a second. (one multicast, one unicast) It really hurts my wireless access point to do this (dhcp has trouble getting through), but the wired network shrugs it off.

Needed:

* Standard address/port combination for multicast

* A little help in probing the depths of ardour for right magic for the commented out function calls in broadcast_track and broadcast_state would be helpful

* It would be good to know if this causes xruns on weaker hardware.

* Comments on additional useful information for use by surfaces are welcomed.

HOW TO USE

Run Linux 2.6.24 or later. RT is not required. (but helpful for all the reasons RT is helpful)

Pull down liblo from svn.

Compile!

On a multicast enabled system, add a multicast route:

route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0

And make sure iptables isn't blocking multicast.

and you can then see the multicast packets go by via a sniffer.

I have preliminary python and C clients in the works, as well as some progress on the transport and alphatrack. This is far from a final protocol definition, or truly useful code. I have a start at that too (not in the patch), for a mostly stateless command set.

2008-05-16 06:55

 

OSC.patch (40,989 bytes)   
diff --git a/gtk2_ardour/engine_dialog.cc b/gtk2_ardour/engine_dialog.cc
index 0c00237..df3c5b2 100644
--- a/gtk2_ardour/engine_dialog.cc
+++ b/gtk2_ardour/engine_dialog.cc
@@ -44,7 +44,7 @@ EngineControl::EngineControl ()
 	  periods_spinner (periods_adjustment),
 	  priority_adjustment (60, 10, 90, 1, 10),
 	  priority_spinner (priority_adjustment),
-	  ports_adjustment (128, 8, 1024, 1, 16),
+	  ports_adjustment (128, 8, 8192, 1, 16),
 	  ports_spinner (ports_adjustment),
 	  realtime_button (_("Realtime")),
 	  no_memory_lock_button (_("Do not lock memory")),
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index 261ed4c..da87c4d 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -101,6 +101,7 @@ source.cc
 source_factory.cc
 tempo.cc
 track.cc
+timerfd.cc
 transient_detector.cc
 utils.cc
 version.cc
diff --git a/libs/ardour/ardour/osc.h b/libs/ardour/ardour/osc.h
index d7c2f4b..b65c768 100644
--- a/libs/ardour/ardour/osc.h
+++ b/libs/ardour/ardour/osc.h
@@ -19,6 +19,7 @@
 
 #ifndef ardour_osc_h
 #define ardour_osc_h
+#define OSC_MULTICAST_ADDR ("239.39.1.99")
 
 #include <string>
 
@@ -49,17 +50,38 @@ class OSC : public BasicUI, public sigc::trackable
 
   private:
 	uint32_t _port;
+
 	volatile bool _ok;
 	volatile bool _shutdown;
 	lo_server _osc_server;
-	lo_server _osc_unix_server;
+	lo_server _osc_multicast_server;
+	lo_address _osc_multicast_server_addr;
+	lo_address clients;
 	std::string _osc_unix_socket_path;
 	std::string _osc_url_file;
 	pthread_t _osc_thread;
 	int _request_pipe[2];
+	int _listeners;
+
+	std::map<std::string, lo_address> _retaddr_map;
+
+	//	CommandMap * _cmd_map;
+	
+	typedef std::pair<int, std::string> InstancePair;
+	typedef std::pair<lo_address, std::string> UrlPair;
+	typedef std::list<UrlPair> UrlList;
+	typedef std::map<InstancePair, UrlList > ControlRegistrationMap;
+	typedef std::map<InstancePair, float> LastValueMap;
+
+	// Arbitrarily added
+	int _set;
+	void * _current_value; 
 
 	static void * _osc_receiver(void * arg);
+	static void * _timer_receiver(void * arg);
+
 	void osc_receiver();
+	void timer_receiver();
 	void send(); // This should accept an OSC payload
 
 	bool init_osc_thread ();
@@ -67,8 +89,8 @@ class OSC : public BasicUI, public sigc::trackable
 	void poke_osc_thread ();
 
 	void register_callbacks ();
-
 	void session_going_away ();
+	lo_address find_or_cache_addr(std::string returl);
 
 	// Handlers for "Application Hook" signals
 	void session_loaded( ARDOUR::Session& );
@@ -76,8 +98,27 @@ class OSC : public BasicUI, public sigc::trackable
 	// end "Application Hook" handles
 
 	std::string get_server_url ();
-	std::string get_unix_server_url ();
-
+	std::string get_multicast_server_url ();
+
+	void update_buffer_load ();
+	void update_cpu_load ();
+
+	int every_twenty_seconds();
+	int every_second ();
+	int every_point_one_seconds ();
+	int every_point_zero_one_seconds ();
+	int every_point_zero_zero_one_seconds ();
+
+	sigc::connection second_connection;
+	sigc::connection point_one_second_connection;
+	sigc::connection point_oh_five_second_connection;
+	sigc::connection point_zero_one_second_connection;
+	sigc::connection point_zero_zero_one_second_connection;
+
+	int subscribe(char *path);
+	int unsubscribe(char *path);
+	int broadcast_state(); 
+	int broadcast_track_state();
 	int current_value (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data);
 
 #define PATH_CALLBACK(name) \
@@ -120,6 +161,12 @@ class OSC : public BasicUI, public sigc::trackable
 
 	PATH_CALLBACK1(set_transport_speed,f,);
 	PATH_CALLBACK1(access_action,s,&);
+	PATH_CALLBACK1(subscribe,s,(char*));
+	PATH_CALLBACK1(unsubscribe,s,(char*));
+	//PATH_CALLBACK1(rec_enable_all,i,&);
+	//PATH_CALLBACK1(punch_in,i,&);
+	//PATH_CALLBACK1(punch_out,i,&);
+	//PATH_CALLBACK1(add_marker_named,s,&);
 };
 
 }
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index e5750e8..ea89ab8 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -889,7 +889,7 @@ class Session : public PBD::StatefulDestructible
 	void reset_playback_load_min ();
 	void reset_capture_load_min ();
 	
-	float read_data_rate () const; // in usec
+	float read_data_rate () const;
 	float write_data_rate () const;
 
 	/* ranges */
diff --git a/libs/ardour/ardour/timerfd.h b/libs/ardour/ardour/timerfd.h
new file mode 100644
index 0000000..c61083b
--- /dev/null
+++ b/libs/ardour/ardour/timerfd.h
@@ -0,0 +1,123 @@
+/*
+
+Copyright (C) 2008 David Taht
+
+This program is in the public domain.
+
+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.  
+
+*/
+
+/* Present a timerfd-like interface, even on systems that don't have timerfd 
+   This allows timers to be used in complex things like poll/select/epoll   
+*/
+
+#ifndef timerfd_h
+#define timerfd_h
+
+/**
+	nanosecond timer on a fd class.
+*/
+
+// For now, pure timerfds is supported
+
+#define PURE_TIMERFD 1
+
+class Timerfd
+{
+private:
+  uint64_t start_time;
+  uint64_t end_time;
+  uint64_t pause_time;
+  uint64_t interval;
+  static int timerfd_mode;
+  clock_t timer;
+  int _timer_fd;
+  bool running;
+#ifndef PURE_TIMERFD
+  sigevent evp;
+  timer_t t;
+  int _control_fd;
+  pthread_t timer_thread;
+#endif
+
+public:
+  Timerfd();
+  ~Timerfd();
+
+  uint64_t get_res();       // What the system reports as possible
+  uint64_t get_real_res();  // What it is really capable of
+
+  int get_fd() { return(_timer_fd); }
+
+  bool start();
+  bool stop();
+
+  int clear(); // Clear pending events from the queue
+
+  bool pause();  // Stop the timer from firing, but keep the start time
+  bool resume(); // Resume the timer
+
+  bool enable();
+  bool disable();
+
+  uint64_t set(uint64_t s);
+  uint64_t get();
+  uint64_t get_elapsed();
+  uint64_t get_remaining();
+  int fired();
+
+  //  operator=()
+  //  operator+=()
+  //  operator-=()
+
+  // A common source of errors is to screw up your timebase
+  // So these convienence functions help
+
+  uint64_t set_nanoseconds(uint64_t s) { return(set(s)); }
+  uint64_t set_microseconds(uint64_t s){ return(set(s * 1000L)); }
+  uint64_t set_milliseconds(uint64_t s){ return(set(s * 1000000L)); }
+  uint64_t set_seconds(uint64_t s)     { return(set(s * 1000000000L)); }
+
+  uint64_t set_ns(uint64_t s){ return(set(s)); }
+  uint64_t set_us(uint64_t s){ return(set(s * 1000L)); }
+  uint64_t set_ms(uint64_t s){ return(set(s * 1000000L)); }
+
+  uint64_t get_nanoseconds()  { return(get()); }
+  uint64_t get_microseconds() { return(get() / 1000L); }
+  uint64_t get_milliseconds() { return(get() / 1000000L); }
+  uint64_t get_seconds()      { return(get() / 1000000000L); }
+
+  uint64_t get_ns() { return(get()); }
+  uint64_t get_us() { return(get() / 1000L); }
+  uint64_t get_ms() { return(get() / 1000000L); }
+
+  uint64_t get_remaining_nanoseconds()  { return(get_remaining()); }
+  uint64_t get_remaining_microseconds() { return(get_remaining() / 1000L); }
+  uint64_t get_remaining_milliseconds() { return(get_remaining() / 1000000L); }
+  uint64_t get_remaining_seconds()      { return(get_remaining() / 1000000000L); }
+
+  uint64_t get_remaining_ns()  { return(get_remaining()); }
+  uint64_t get_remaining_us() { return(get_remaining() / 1000L); }
+  uint64_t get_remaining_ms() { return(get_remaining() / 1000000L); }
+
+  uint64_t get_elapsed_nanoseconds()  { return(get_elapsed()); }
+  uint64_t get_elapsed_microseconds() { return(get_elapsed() / 1000L); }
+  uint64_t get_elapsed_milliseconds() { return(get_elapsed() / 1000000L); }
+  uint64_t get_elapsed_seconds()      { return(get_elapsed() / 1000000000L); }
+
+  uint64_t get_elapsed_ns()  { return(get_elapsed()); }
+  uint64_t get_elapsed_us() { return(get_elapsed() / 1000L); }
+  uint64_t get_elapsed_ms() { return(get_elapsed() / 1000000L); }
+
+  uint64_t restart()
+  {
+		uint64_t retval = stop();
+		start();
+		return retval;
+  }
+};
+
+#endif
diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc
index 419efe8..6041a0e 100644
--- a/libs/ardour/audio_diskstream.cc
+++ b/libs/ardour/audio_diskstream.cc
@@ -2461,7 +2461,7 @@ AudioDiskstream::ChannelInfo::ChannelInfo (nframes_t bufsize, nframes_t speed_si
 
 	playback_buf = new RingBufferNPT<Sample> (bufsize);
 	capture_buf = new RingBufferNPT<Sample> (bufsize);
-	capture_transition_buf = new RingBufferNPT<CaptureTransition> (256);
+	capture_transition_buf = new RingBufferNPT<CaptureTransition> (1024);
 	
 	/* touch the ringbuffer buffers, which will cause
 	   them to be mapped into locked physical RAM if
diff --git a/libs/ardour/osc.cc b/libs/ardour/osc.cc
index 101e4cb..ac8ae44 100644
--- a/libs/ardour/osc.cc
+++ b/libs/ardour/osc.cc
@@ -18,11 +18,14 @@
  */
 
 #include <iostream>
+#include <algorithm>
+#include <cmath>
 #include <fstream>
+#include <sstream>
 #include <cstdio>
 #include <cstdlib>
 #include <cerrno>
-#include <algorithm>
+#include <iomanip>
 
 #include <sys/poll.h>
 #include <unistd.h>
@@ -33,32 +36,86 @@
 #include <pbd/pthread_utils.h>
 
 #include <ardour/osc.h>
-#include <ardour/session.h>
 #include <ardour/route.h>
+#include <ardour/session.h>
+#include <ardour/location.h>
+#include <ardour/dB.h>
 #include <ardour/audio_track.h>
-
+#include <ardour/timerfd.h>
+#include <ardour/panner.h>
+#include <ardour/tempo.h>
 #include "i18n.h"
 
 using namespace ARDOUR;
 using namespace sigc;
 using namespace std;
+using namespace PBD;
+using boost::shared_ptr;
 
 static void error_callback(int num, const char *m, const char *path)
 {
-#ifdef DEBUG
+  // #ifdef DEBUG
 	fprintf(stderr, "liblo server error %d in path %s: %s\n", num, path, m);
-#endif
+	// #endif
 }
 
+/* epoll is better
+ 
+ void mainloop(int queue)
+ {
+   struct epoll_event list[10];
+   int                events;
+   int                i;
+   struct foo         data;
+ 
+   while(1)
+   {
+     events = epoll_wait(queue,list,10,TIMEOUT);
+     if (events < 0)
+       continue;	// error but ignore for now
+     for (i = 0 ; i < events ; i++)
+     {
+       data = list[i].data.ptr;
+       (*data->fn)(&list[i]);	// 
+     }
+   }
+ }
+ */
+ 
+
 OSC::OSC (uint32_t port)
 	: _port(port)
 {
 	_shutdown = false;
 	_osc_server = 0;
-	_osc_unix_server = 0;
+	_osc_multicast_server = 0;
 	_osc_thread = 0;
+	_listeners = 1;
+	clients = lo_address_new( NULL, "2222" );
+
 }
 
+lo_address 
+ OSC::find_or_cache_addr(string returl)
+ {
+ 	lo_address addr = 0;
+ 
+ 	if (returl.empty()) return 0;
+ 	
+ 	if (_retaddr_map.find(returl) == _retaddr_map.end()) {
+ 		addr = lo_address_new_from_url (returl.c_str());
+ 		if (lo_address_errno (addr) < 0) {
+ 			fprintf(stderr, "addr error %d: %s\n", lo_address_errno(addr), lo_address_errstr(addr));
+ 		}
+ 		_retaddr_map[returl] = addr;
+ 	}
+ 	else {
+ 		addr = _retaddr_map[returl];
+ 	}
+ 	
+ 	return addr;
+  }
+
 int
 OSC::start ()
 {
@@ -82,29 +139,32 @@ OSC::start ()
 		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;
 
+ 	/*  This opens up ardour on (hopefully) the same port as the 
+	    ptp server
+	    This allows for multiple ardour instances
+	    I fear for what can happen on a network with multiple 
+	    versions of ardour running */
+  
+ 	for (int j=0; j < 20; ++j) {
+ 		snprintf(tmpstr, sizeof(tmpstr), "%d", _port);
+  		
+ 		if ((_osc_multicast_server = lo_server_new_multicast (OSC_MULTICAST_ADDR,tmpstr, error_callback))) {
+ 		  _osc_multicast_server_addr = lo_address_new(OSC_MULTICAST_ADDR, tmpstr);
+ 		  // lo_server_set_ttl(_osc_multicast_server,1);
+ 			break;
+  		}
+#ifdef DEBUG		
+ 		cerr << "can't get osc multicast at port: " << _port << endl;
+#endif
+ 		_port++;
+		continue;
+ 	}
+  
+ 	cerr << "OSC @ " << get_multicast_server_url () << endl;
+
+
 	_osc_url_file = Glib::build_filename (get_user_ardour_path (), "osc_url");
 
 	ofstream urlfile;
@@ -120,7 +180,7 @@ OSC::start ()
 	register_callbacks();
 	
 	// lo_server_thread_add_method(_sthread, NULL, NULL, OSC::_dummy_handler, this);
-		
+	_shutdown = false;	
 	if (!init_osc_thread()) {
 		return -1;
 	}
@@ -140,21 +200,20 @@ OSC::stop ()
 
 	lo_server_free (_osc_server);
 	_osc_server = 0;
+
+	lo_server_free (_osc_multicast_server);
+	_osc_multicast_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() );
 	}
-	
-   if (!  _osc_url_file.empty() ) {
-      unlink(_osc_url_file.c_str() );
-   }
 	return 0;
 }
 
 OSC::~OSC()
 {
 	stop ();
+	lo_address_free(clients);
 }
 
 void
@@ -164,7 +223,7 @@ OSC::register_callbacks()
 	lo_server serv;
 
 	srvs[0] = _osc_server;
-	srvs[1] = _osc_unix_server;
+	srvs[1] = _osc_multicast_server;
 	
 	for (size_t i = 0; i < 2; ++i) {
 
@@ -196,6 +255,11 @@ OSC::register_callbacks()
 		REGISTER_CALLBACK (serv, "/ardour/rec_enable_toggle", "", rec_enable_toggle);
 		REGISTER_CALLBACK (serv, "/ardour/toggle_all_rec_enables", "", toggle_all_rec_enables);
 
+		// New stuff
+
+		REGISTER_CALLBACK (serv, "/a3/subscribe", "s", subscribe);
+		REGISTER_CALLBACK (serv, "/a3/unsubscribe", "s", unsubscribe);
+
 #if 0
 		REGISTER_CALLBACK (serv, "/ardour/*/#current_value", "", current_value);
 		REGISTER_CALLBACK (serv, "/ardour/set", "", set);
@@ -211,6 +275,67 @@ OSC::register_callbacks()
 	}
 }
 
+void
+OSC::update_cpu_load ()
+{
+	char buf[32];
+	//	snprintf (buf, sizeof (buf), _("DSP: %5.1f%%"), engine->get_cpu_load());
+	// cpu_load_label.set_text (buf);
+}
+
+void
+OSC::update_buffer_load ()
+{
+	char buf[64];
+
+	if (session) {
+		snprintf (buf, sizeof (buf), _("Buffers p:%" PRIu32 "%% c:%" PRIu32 "%%"), 
+			  session->playback_load(), session->capture_load());
+	  // buffer_load_label.set_text (buf);
+	} else {
+	  // buffer_load_label.set_text ("");
+	}
+}
+
+
+int
+OSC::every_twenty_seconds ()
+{
+  // Ping OSC devices to make sure they are alive
+	return TRUE;
+}
+
+
+int
+OSC::every_second ()
+{
+  //	update_cpu_load ();
+  //	update_buffer_load ();
+  //	update_disk_space ();
+	return TRUE;
+}
+
+int
+OSC::every_point_one_seconds ()
+{
+  broadcast_state(); /* EMIT_SIGNAL */
+  return TRUE;
+}
+
+int
+OSC::every_point_zero_one_seconds ()
+{
+  broadcast_track_state(); /* EMIT_SIGNAL */
+  return TRUE;
+}
+
+int
+OSC::every_point_zero_zero_one_seconds ()
+{
+  return TRUE; // dunno
+}
+
+
 bool
 OSC::init_osc_thread ()
 {
@@ -244,6 +369,15 @@ OSC::init_osc_thread ()
 	return true;
 }
 
+int OSC::subscribe(char *path) {
+// lo_address lo_message_get_source  	(   	lo_message   	 m  	 ) 
+  return 0;
+}
+int OSC::unsubscribe(char *path) {
+// lo_address lo_message_get_source  	(   	lo_message   	 m  	 ) 
+  return 0;
+}
+
 void
 OSC::terminate_osc_thread ()
 {
@@ -282,13 +416,14 @@ OSC::get_server_url()
 }
 
 std::string
-OSC::get_unix_server_url()
+OSC::get_multicast_server_url()
 {
 	string url;
 	char * urlstr;
 
-	if (_osc_unix_server) {
-		urlstr = lo_server_get_url (_osc_unix_server);
+	if (_osc_multicast_server) {
+		// FIXME broken in current liblo svn
+		urlstr = lo_server_get_url (_osc_multicast_server);
 		url = urlstr;
 		free (urlstr);
 	}
@@ -296,6 +431,186 @@ OSC::get_unix_server_url()
 	return url;
 }
 
+int
+OSC::broadcast_track_state() {
+  if(session && _listeners) {
+    // construct a state struct which we use a couple times
+    int x = 0;
+    nframes_t frame;
+    int settings = 0;
+    int id = 0;
+    int channels = 2;
+    float gain = .5;
+    int ntracks = session->ntracks();
+
+    int panners = 0;
+    vector<float> pan(8);
+    vector<float> volume(8);
+    boost::shared_ptr<Session::RouteList> routes = session->get_routes();
+
+    pan[0] = .4;
+    pan[1] = .6;
+    volume[0] = .7;
+    volume[1] = .9;
+
+
+    // name string 
+    // volume
+    // audio channels
+    // gain float
+    // panners
+    /* 
+       Automation state
+
+	ARDOUR::AutoState gain_state = rs.route().gain_automation_state();
+	if ( gain_state == Touch || gain_state == Play )
+	{
+		notify_gain_changed( &rs, false );
+	}
+	
+	ARDOUR::AutoState panner_state = rs.route().panner().automation_state();
+	if ( panner_state == Touch || panner_state == Play )
+	{
+     */
+
+    /* solo, active, muted, recording */
+
+    // convert it to OSC
+    char msg[32];
+    lo_bundle b = lo_bundle_new(LO_TT_IMMEDIATE);
+    vector<lo_message> m(60); // number of tracks
+
+    x = 0;
+    for ( Session::RouteList::iterator i = routes->begin(); i != routes->end(); ++i ) {
+      Panner & panner = (*i)->panner();
+      gain = (*i)->gain();
+      x = (*i)->remote_control_id();
+      //(*i)->record_enabled() << 1 |
+      settings = (*i)->active() | 
+	(*i)->muted() << 2  | (*i)->soloed() << 3; 
+      // FIXME get more settings, channels, pan... 
+      m[x] = lo_message_new();
+      // My python didn't like this format
+      // sprintf(msg,"/a3/%d/trk",x); 
+      if(!(*i)->master()) {
+	  sprintf(msg,"/a3/trk"); // no, needs to be remote control id
+	} else {
+	  sprintf(msg,"/a3/mst"); // no, needs to be remote control id
+	}
+      lo_message_add_int32(m[x],x);
+      lo_message_add_int32(m[x],settings);
+      lo_message_add_float(m[x],gain);
+      lo_message_add_int32(m[x],channels); // perhaps part of the string
+      lo_message_add_int32(m[x],panners); // perhaps part of the string
+      lo_message_add_string(m[x],(*i)->name().c_str());
+      //      for (int j = 0; j < panners; j++) {
+      //  panner().[j]->get_effective_position(pan[j]);
+      //lo_message_add_float(m[x],pan[j]);
+      // }
+      //      for (int j = 0; j < channels; j++) {
+      //	lo_message_add_float(m[x],volume[j]);
+      // }
+      lo_bundle_add_message(b,msg,m[x]); // FIXME track id as part of the message makes sense
+      // x++;
+    }
+
+    //    lo_bundle_add_message(b,"/a3/gs",m[x]);
+    // temporarily just use one address for testing
+    if(_osc_server) {
+      lo_send_bundle_from(clients, _osc_server, b);
+    }
+    if(_osc_multicast_server) {
+      lo_send_bundle_from(_osc_multicast_server_addr,_osc_multicast_server,b);
+    }
+    // Send it to all listeners that are interested
+    //    u = foreach(url) {
+    //  lo_bundle_send(u,b);
+    // }
+    // Clean up the mess
+
+    lo_bundle_free_messages(b);
+  }
+  
+  return 0;
+}
+
+int
+OSC::broadcast_state() {
+    struct global_state {
+      nframes64_t current_frame;
+      float transport_speed;
+      BBT_Time bbt;
+      SMPTE::Time smpte;
+      // bpm
+      Sample master_volume;
+      Sample master_gain;
+      int settings; // make 64 bits?
+  } g;
+  
+
+  if(session && _listeners) {
+    // construct a state struct which we use a couple times
+    int x = 0;
+    nframes_t frame;
+    //if (session->get_play_range ()) {
+    //	session->request_play_range (false);
+    //}
+	
+    g.current_frame = frame = session->transport_frame();
+    session->bbt_time(frame,g.bbt);
+    smpte_time(frame,g.smpte);
+    g.transport_speed = session->transport_speed();
+    g.settings =  
+      Config->get_clicking() << 14 |
+      session->synced_to_jack() << 13    |
+      session->transport_locked() << 12  |
+      session->get_play_loop()==1 << 11     |
+      session->actively_recording() << 10  |
+      session->get_record_enabled() << 9 |
+      session->is_auditioning() << 8     |
+      session->transport_stopped() << 7  |
+      session->soloing() << 5            |
+      Config->get_punch_in() << 1           |
+      Config->get_punch_out();
+      //      session->timemaster << 6           |
+      // session->running() << 4            ; 
+      //      session->muted() << 3              |
+      //      session->soloing() << 2              |
+
+    // convert it to OSC
+    lo_bundle b = lo_bundle_new(LO_TT_IMMEDIATE);
+    lo_message m;
+    m = lo_message_new();
+    lo_message_add_int32(m,frame);
+    // lo_message_add_int64(m[x],g.current_frame); pyosc didn't like 64 bits
+    lo_message_add_int32(m,g.settings);
+    lo_message_add_float(m,g.transport_speed);
+    lo_message_add_int32(m,g.bbt.bars);
+    lo_message_add_int32(m,g.bbt.beats);
+    lo_message_add_int32(m,g.bbt.ticks);
+    lo_message_add_int32(m,g.smpte.hours);
+    lo_message_add_int32(m,g.smpte.minutes);
+    lo_message_add_int32(m,g.smpte.seconds);
+    lo_message_add_int32(m,g.smpte.frames);
+    
+    lo_bundle_add_message(b,"/a3/gs",m);
+    // Send it to all listeners that are interested
+    //    u = foreach(url) {
+    //  lo_bundle_send(u,b);
+    // }
+  
+    if(_osc_server) {
+      lo_send_bundle_from(clients, _osc_server, b);
+    }
+
+    if(_osc_multicast_server) {
+      lo_send_bundle_from(_osc_multicast_server_addr,_osc_multicast_server,b);
+    }
+    lo_bundle_free_messages(b);
+  }
+  return 0;
+}
+
 
 /* server thread */
 
@@ -307,76 +622,148 @@ OSC::_osc_receiver(void * arg)
 	return 0;
 }
 
+/* Just some notes on how the route API works:
+
+	if (route_table[0]) {
+		boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack> (route_table[0]);
+		lights_pending[LightTrackrec]  = at && at->record_enabled();
+		lights_pending[LightTrackmute] = route_get_muted(0); 
+		lights_pending[LightTracksolo] = route_get_soloed(0);
+}
+
+	route_set_gain (0, slider_position_to_gain (gain_fraction));
+
+	float level_l = route_get_peak_input_power (0, 0);
+show.cc:160:	float level_r = route_get_peak_input_power (0, 1);
+route_get_gain
+
+	set_route_table_size (1);
+
+	sigc::signal<void,std::string,void*> gui_changed;
+	sigc::signal<void,void*> SelectedChanged;
+
+	for (list<Route *>::iterator i = routes.begin(); i != routes.end(); i++) {
+		g = (*i)->gain();
+		
+uint32_t
+Route::remote_control_id() const
+{
+	return _remote_control_id;
+}
+*/
+
 void
 OSC::osc_receiver()
 {
-	struct pollfd pfd[3];
-	int fds[3];
-	lo_server srvs[3];
+	struct pollfd pfd[16];
+	int fds[16];
+	lo_server srvs[16];
 	int nfds = 0;
+	int osc_start = 0;
 	int timeout = -1;
 	int ret;
+	Timerfd timers[4];
+
+	_listeners = 1; // FIXME THIS IS JUST A TEST
+
+	fds[nfds++] = _request_pipe[0];
 	
-	fds[0] = _request_pipe[0];
-	nfds++;
+	timers[0].set_milliseconds(10);  // Every 10 ms
+	timers[1].set_milliseconds(100); // Every 100 ms
+	timers[2].set_seconds(20); // Every 20 seconds
+	timers[3].set_seconds(60); // end the test
 	
-	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++;
+	for(int i = 0; i<4; i++) {
+	  // Yes you have fds before you enable the timer
+	  fds[nfds++] = timers[i].get_fd(); 
+	  timers[i].enable(); 
 	}
 
-	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++;
+	osc_start = 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++;
 	}
-	
-	
-	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;
-		}
+	if (_osc_multicast_server && lo_server_get_socket_fd(_osc_multicast_server) >= 0) {
+	  fds[nfds] = lo_server_get_socket_fd(_osc_multicast_server);
+	  srvs[nfds] = _osc_multicast_server;
+	  nfds++;
+	}	
 
-		//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;
-		}
+	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 */
+	      if(!_shutdown) {
+		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;
 		
-		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]);
-			}
-		}
-
+	  if (_shutdown) {
+	    break;
+	  }
+	  
+	  if ((pfd[0].revents & ~POLLIN)) {
+	    cerr << "OSC: error polling extra port" << endl;
+	    break;
+	  }
+	  
+	  if(pfd[1].revents & POLLIN) {
+	    timers[0].clear();
+	    if(_listeners) {
+	      // std::cout << "Broadcasting Track packet" << std::endl; 
+	      broadcast_track_state();
+	    }
+	  }
+	  
+	  if(pfd[2].revents & POLLIN) {
+	    timers[1].clear();
+	    // std::cout << "Broadcasting Control packet" << std::endl; 
+	    broadcast_state();
+	  }
+	  
+	  if(pfd[3].revents & POLLIN) {
+	    timers[2].clear();
+	    //	    if(_listeners) {
+	      std::cout << "Sending Ping packet" << std::endl;
+	      //	    }
+	  }
+	  
+	  if(pfd[4].revents & POLLIN) {
+	    timers[3].clear();
+	    std::cerr << "Reaping non-pong" << std::endl;
+	  }
+	  
+	   for (int i=osc_start; 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;
+	
+	cerr << "OSC engine shutdown" << endl;
 	
 	if (_osc_server) {
 		int fd = lo_server_get_socket_fd(_osc_server);
@@ -388,16 +775,19 @@ OSC::osc_receiver()
 		_osc_server = 0;
 	}
 
-	if (_osc_unix_server) {
-		cerr << "freeing unix server" << endl;
-		lo_server_free (_osc_unix_server);
-		_osc_unix_server = 0;
+	if (_osc_multicast_server) {
+		// FIXME shutdown right
+		cerr << "freeing multicast server" << endl;
+		lo_server_free (_osc_multicast_server);
+		_osc_multicast_server = 0;
 	}
 	
 	close(_request_pipe[0]);
 	close(_request_pipe[1]);
 }
 
+/* server thread */
+
 void
 OSC::set_session (Session& s)
 {
@@ -435,50 +825,51 @@ OSC::session_exported( std::string path, std::string name ) {
 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') {
 
-	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;
+	const char *messagestr = (const char *) argv[0]->s;
+	const char *returl = (const char *) argv[1]->s;
+
 	lo_address addr = find_or_cache_addr (returl);
 
-	const char *retpath = argv[2]->s;
+	const char *retpath = (const char *) argv[2]->s;
+
 
 	
-	if (strcmp (argv[0]->s, "transport_frame")) {
+	if (strcmp (messagestr, "transport_frame")) {
 
 		if (session) {
 			lo_send (addr, retpath, "i", session->transport_frame());
 		}
 
-	} else if (strcmp (argv[0]->s, "transport_speed")) {
+	} else if (strcmp (messagestr, "transport_speed")) {
 
 		if (session) {
-			lo_send (addr, retpath, "i", session->transport_frame());
+			lo_send (addr, retpath, "f", session->transport_speed());
 		}
 
-	} else if (strcmp (argv[0]->s, "transport_locked")) {
+	} else if (strcmp (messagestr, "transport_locked")) {
 
 		if (session) {
-			lo_send (addr, retpath, "i", session->transport_frame());
+			lo_send (addr, retpath, "i", session->transport_locked());
 		}
 
-	} else if (strcmp (argv[0]->s, "punch_in") {
+	} else if (strcmp (messagestr, "punch_in")) { // FIXME
 
 		if (session) {
 			lo_send (addr, retpath, "i", session->transport_frame());
 		}
 
-	} else if (strcmp (argv[0]->s, "punch_out") {
+	} else if (strcmp (messagestr, "punch_out")) { // FIXME
 
 		if (session) {
 			lo_send (addr, retpath, "i", session->transport_frame());
 		}
 
-	} else if (strcmp (argv[0]->s, "rec_enable") {
+	} else if (strcmp (messagestr, "rec_enable")) { // FIXME
 
 		if (session) {
 			lo_send (addr, retpath, "i", session->transport_frame());
@@ -488,6 +879,5 @@ OSC::current_value (const char *path, const char *types, lo_arg **argv, int argc
 
 		/* error */
 	}
-#endif
 	return 0;
 }
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index b11ed56..43b0753 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -1275,6 +1275,8 @@ void
 Session::enable_record ()
 {
 	/* XXX really atomic compare+swap here */
+        // int new_status = Recording;
+	// g_atomic_int_compare_and_exchange(&_record_status,0,Recording);
 	if (g_atomic_int_get (&_record_status) != Recording) {
 		g_atomic_int_set (&_record_status, Recording);
 		_last_record_location = _transport_frame;
diff --git a/libs/ardour/timerfd.cc b/libs/ardour/timerfd.cc
new file mode 100644
index 0000000..55fc860
--- /dev/null
+++ b/libs/ardour/timerfd.cc
@@ -0,0 +1,388 @@
+// #define _GNU_SOURCE
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <time.h>
+#include <errno.h>
+
+#include <iostream>
+#include <boost/cstdint.hpp>
+#include <ardour/timerfd.h>
+
+/*
+ ** This were good at the time of 2.6.23-rc7 ...
+ **/
+
+#ifdef __linux__
+#ifndef __NR_timerfd
+#if defined(__x86_64__)
+#define __NR_timerfd_create 283
+#define __NR_timerfd_settime 286
+#define __NR_timerfd_gettime 287
+#elif defined(__i386__)
+#define __NR_timerfd_create 322
+#define __NR_timerfd_settime 325
+#define __NR_timerfd_gettime 326
+#else
+#error Cannot detect your architecture!
+#endif
+#else
+#warning Using predefined values for timerfd in linux 2.6.24 and later
+#undef __NR_timerfd_create 
+#undef __NR_timerfd_settime 
+#undef __NR_timerfd_gettime 
+
+#if defined(__x86_64__)
+#define __NR_timerfd_create 283
+#define __NR_timerfd_settime 286
+#define __NR_timerfd_gettime 287
+#elif defined(__i386__)
+#define __NR_timerfd_create 322
+#define __NR_timerfd_settime 325
+#define __NR_timerfd_gettime 326
+#endif
+#endif
+#endif
+
+/* Definitions from include/linux/timerfd.h */
+#define TFD_TIMER_ABSTIME (1 << 0)
+
+static int timerfd_create(int clockid, int flags) {
+
+	return syscall(__NR_timerfd_create, clockid, flags);
+}
+
+static int timerfd_settime(int ufc, int flags, const struct itimerspec *utmr,
+		    struct itimerspec *otmr) {
+
+	return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr);
+}
+
+static int timerfd_gettime(int ufc, struct itimerspec *otmr) {
+
+	return syscall(__NR_timerfd_gettime, ufc, otmr);
+}
+
+Timerfd::~Timerfd() {
+  running = false;
+  ::close(_timer_fd);
+}
+
+Timerfd::Timerfd() {
+  running = false;
+  _timer_fd = 0;
+  if(timerfd_mode != 2) {
+    _timer_fd = timerfd_create(CLOCK_MONOTONIC,0);
+    if(_timer_fd < 1) {
+      timerfd_mode = 2;
+    std::cerr << "Can't create timerfd " << errno << std::endl;
+    } else {
+      timerfd_mode = 1;
+    }
+  } else {
+    std::cerr << "Falling back to pthreads" << std::endl;
+  }
+}
+
+// What the system reports as possible
+uint64_t 
+Timerfd::get_res() {
+}
+
+  // What it is really capable of
+uint64_t 
+Timerfd::get_real_res() {
+}
+
+bool Timerfd::start() {
+  bool old = running;
+  running = true; // atomic?
+  enable();
+  return(old);
+}
+ 
+bool 
+Timerfd::stop() {
+  bool old = running;
+  running = false; // atomic
+  disable();
+  return(old);
+}
+
+int 
+Timerfd::clear() {
+  uint64_t t1;
+  read(_timer_fd,&t1,sizeof(t1));
+  // Do a read on the fd
+}
+
+// Stop the timer from firing, but keep the start time
+bool 
+Timerfd::pause() {
+  struct itimerspec now;
+  struct itimerspec then;
+  now.it_interval.tv_sec  = 0;
+  now.it_interval.tv_nsec = 0;
+  timerfd_settime(_timer_fd, 0, &now, &then);
+  // pull the start time
+  // 
+  return(true);
+}
+
+// Resume the timer
+bool 
+Timerfd::resume() {
+  uint64_t s = interval;
+  struct itimerspec now;
+  now.it_interval.tv_sec  = s / 1000000000L;
+  now.it_interval.tv_nsec = s % 1000000000L;
+  timerfd_settime(_timer_fd, 0, &now, NULL);
+  // pull the start time
+  // 
+  return(s);
+}
+
+bool Timerfd::enable() {
+  struct itimerspec now;
+  struct itimerspec then;
+  int ret;
+  now.it_interval.tv_sec  = interval / 1000000000L;
+  now.it_interval.tv_nsec = interval % 1000000000L;
+  now.it_value.tv_sec = now.it_interval.tv_sec;
+  now.it_value.tv_nsec = now.it_interval.tv_nsec;
+
+  ret = timerfd_settime(_timer_fd, 0, &now, &then);
+  // pull the start time
+  // 
+  if(ret != 0) {
+    std::cerr << "enable returned: " << ret << " errno: " << strerror(errno) << std::endl;
+  }
+  return(true);
+}
+
+bool 
+Timerfd::disable() {
+  struct itimerspec now;
+  struct itimerspec then;
+  now.it_interval.tv_sec  = 0;
+  now.it_interval.tv_nsec = 0;
+  timerfd_settime(_timer_fd, 0, &now, &then);
+  // pull the start time
+  // 
+  return(true);
+}
+
+int 
+Timerfd::fired() {
+}
+
+uint64_t 
+Timerfd::set(uint64_t s) {
+  interval = s;
+  return(s);
+}
+
+uint64_t 
+Timerfd::get() {
+  struct itimerspec now;
+  timerfd_gettime(_timer_fd, &now);
+  uint64_t total = (uint64_t) now.it_interval.tv_sec * 1000000000L;
+  return (now.it_interval.tv_nsec + total);
+}
+
+uint64_t 
+Timerfd::get_elapsed() {
+}
+
+uint64_t 
+Timerfd::get_remaining() {
+}
+
+int Timerfd::timerfd_mode = 0;
+
+#ifdef _TIMER_TEST
+
+struct tmr_type {
+	int id;
+	char const *name;
+};
+
+
+static unsigned long long getustime(int clockid) {
+	struct timespec tp;
+
+	if (clock_gettime((clockid_t) clockid, &tp)) {
+		perror("clock_gettime");
+		return 0;
+	}
+
+	return 1000000ULL * tp.tv_sec + tp.tv_nsec / 1000;
+}
+
+static void set_timespec(struct timespec *tmr, unsigned long long ustime) {
+
+	tmr->tv_sec = (time_t) (ustime / 1000000ULL);
+	tmr->tv_nsec = (long) (1000ULL * (ustime % 1000000ULL));
+}
+
+static long waittmr(int tfd, int timeo) {
+	u_int64_t ticks;
+	struct pollfd pfd;
+
+	pfd.fd = tfd;
+	pfd.events = POLLIN;
+	pfd.revents = 0;
+	if (poll(&pfd, 1, timeo) < 0) {
+		perror("poll");
+		return -1;
+	}
+	if ((pfd.revents & POLLIN) == 0) {
+		fprintf(stdout, "no ticks happened\n");
+		return -1;
+	}
+	if (read(tfd, &ticks, sizeof(ticks)) != sizeof(ticks)) {
+		perror("timerfd read");
+		return -1;
+	}
+
+	return ticks;
+}
+
+
+int main(int ac, char **av) {
+	int i, tfd, tfd2;
+	long ticks;
+	unsigned long long tnow, ttmr;
+	u_int64_t uticks;
+	struct itimerspec tmr;
+	struct tmr_type clks[] = {
+		{ CLOCK_MONOTONIC, "CLOCK MONOTONIC" },
+		{ CLOCK_REALTIME, "CLOCK REALTIME" },
+	};
+
+	for (i = 0; i < sizeof(clks) / sizeof(clks[0]); i++) {
+		fprintf(stdout, "\n\n---------------------------------------\n");
+		fprintf(stdout, "| testing %s\n", clks[i].name);
+		fprintf(stdout, "---------------------------------------\n\n");
+
+		fprintf(stdout, "relative timer test (at 500 ms) ...\n");
+		set_timespec(&tmr.it_value, 500 * 1000);
+		set_timespec(&tmr.it_interval, 0);
+		tnow = getustime(clks[i].id);
+		if ((tfd = timerfd_create(clks[i].id, 0)) == -1) {
+			perror("timerfd");
+			return 1;
+		}
+		fprintf(stdout, "timerfd = %d\n", tfd);
+
+		if (timerfd_settime(tfd, 0, &tmr, NULL)) {
+			perror("timerfd_settime");
+			return 1;
+		}
+
+		fprintf(stdout, "waiting timer ...\n");
+		ticks = waittmr(tfd, -1);
+		ttmr = getustime(clks[i].id);
+		if (ticks <= 0)
+			fprintf(stdout, "whooops! no timer showed up!\n");
+		else
+			fprintf(stdout, "got timer ticks (%ld) after %llu ms\n",
+				ticks, (ttmr - tnow) / 1000);
+
+
+		fprintf(stdout, "absolute timer test (at 500 ms) ...\n");
+		tnow = getustime(clks[i].id);
+		set_timespec(&tmr.it_value, tnow + 500 * 1000);
+		set_timespec(&tmr.it_interval, 0);
+		if (timerfd_settime(tfd, TFD_TIMER_ABSTIME, &tmr, NULL)) {
+			perror("timerfd_settime");
+			return 1;
+		}
+
+		fprintf(stdout, "wating timer ...\n");
+		ticks = waittmr(tfd, -1);
+		ttmr = getustime(clks[i].id);
+		if (ticks <= 0)
+			fprintf(stdout, "whooops! no timer showed up!\n");
+		else
+			fprintf(stdout, "got timer ticks (%ld) after %llu ms\n",
+				ticks, (ttmr - tnow) / 1000);
+
+		fprintf(stdout, "sequential timer test (100 ms clock) ...\n");
+		tnow = getustime(clks[i].id);
+		set_timespec(&tmr.it_value, tnow + 100 * 1000);
+		set_timespec(&tmr.it_interval, 100 * 1000);
+		if (timerfd_settime(tfd, TFD_TIMER_ABSTIME, &tmr, NULL)) {
+			perror("timerfd_settime");
+			return 1;
+		}
+
+		fprintf(stdout, "sleeping 1 second ...\n");
+		sleep(1);
+		if (timerfd_gettime(tfd, &tmr)) {
+			perror("timerfd_gettime");
+			return 1;
+		}
+		fprintf(stdout, "timerfd_gettime returned:\n"
+			"\tit_value = { %ld, %ld } it_interval = { %ld, %ld }\n",
+			(long) tmr.it_value.tv_sec, (long) tmr.it_value.tv_nsec,
+			(long) tmr.it_interval.tv_sec, (long) tmr.it_interval.tv_nsec);
+		fprintf(stdout, "sleeping 1 second ...\n");
+		sleep(1);
+
+		fprintf(stdout, "wating timer ...\n");
+		ticks = waittmr(tfd, -1);
+		ttmr = getustime(clks[i].id);
+		if (ticks <= 0)
+			fprintf(stdout, "whooops! no timer showed up!\n");
+		else
+			fprintf(stdout, "got timer ticks (%ld) after %llu ms\n",
+				ticks, (ttmr - tnow) / 1000);
+
+
+		fprintf(stdout, "O_NONBLOCK test ...\n");
+		tnow = getustime(clks[i].id);
+		set_timespec(&tmr.it_value, 100 * 1000);
+		set_timespec(&tmr.it_interval, 0);
+		if (timerfd_settime(tfd, 0, &tmr, NULL)) {
+			perror("timerfd_settime");
+			return 1;
+		}
+		fprintf(stdout, "timerfd = %d\n", tfd);
+
+		fprintf(stdout, "wating timer (flush the single tick) ...\n");
+		ticks = waittmr(tfd, -1);
+		ttmr = getustime(clks[i].id);
+		if (ticks <= 0)
+			fprintf(stdout, "whooops! no timer showed up!\n");
+		else
+			fprintf(stdout, "got timer ticks (%ld) after %llu ms\n",
+				ticks, (ttmr - tnow) / 1000);
+
+		fcntl(tfd, F_SETFL, fcntl(tfd, F_GETFL, 0) | O_NONBLOCK);
+
+		if (read(tfd, &uticks, sizeof(uticks)) > 0)
+			fprintf(stdout, "whooops! timer ticks not zero when should have been\n");
+		else if (errno != EAGAIN)
+			fprintf(stdout, "whooops! bad errno value (%d = '%s')!\n",
+				errno, strerror(errno));
+		else
+			fprintf(stdout, "success\n");
+
+		fcntl(tfd, F_SETFL, fcntl(tfd, F_GETFL, 0) & ~O_NONBLOCK);
+
+		close(tfd);
+	}
+
+	return 0;
+}
+
+#endif
diff --git a/libs/surfaces/control_protocol/basic_ui.cc b/libs/surfaces/control_protocol/basic_ui.cc
index 7c03252..6f224d0 100644
--- a/libs/surfaces/control_protocol/basic_ui.cc
+++ b/libs/surfaces/control_protocol/basic_ui.cc
@@ -292,3 +292,29 @@ BasicUI::sample_to_smpte (nframes_t sample, SMPTE::Time& smpte, bool use_offset,
 {
 	session->sample_to_smpte (sample, *((SMPTE::Time*)&smpte), use_offset, use_subframes);
 }
+
+
+void 
+BasicUI::rec_enable_all (nframes_t sample, bool enable) const
+{
+
+}
+
+void 
+BasicUI::add_marker_named (nframes_t sample, char *s) 
+{
+
+}
+
+void 
+BasicUI::punch_in (nframes_t sample, bool enable) 
+{
+
+}
+
+void 
+BasicUI::punch_out (nframes_t sample, bool enable)
+{
+
+}
+
diff --git a/libs/surfaces/control_protocol/control_protocol/basic_ui.h b/libs/surfaces/control_protocol/control_protocol/basic_ui.h
index c8b5a2a..44fd3a2 100644
--- a/libs/surfaces/control_protocol/control_protocol/basic_ui.h
+++ b/libs/surfaces/control_protocol/control_protocol/basic_ui.h
@@ -26,6 +26,8 @@
 #include <jack/types.h>
 #include <control_protocol/smpte.h>
 
+typedef unsigned int nframes_t;
+
 namespace ARDOUR {
 	class Session;
 }
@@ -78,9 +80,15 @@ class BasicUI {
 	void smpte_to_sample (SMPTE::Time& smpte, jack_nframes_t& sample, bool use_offset, bool use_subframes) const;
 	void sample_to_smpte (jack_nframes_t sample, SMPTE::Time& smpte, bool use_offset, bool use_subframes) const;
 
+	void rec_enable_all (nframes_t sample, bool enable) const;
+	void add_marker_named(nframes_t sample, char *s);
+	void punch_in(nframes_t sample, bool i);
+	void punch_out(nframes_t sample, bool i);
+
   protected:
 	BasicUI ();
 	ARDOUR::Session* session;
 };
 
+
 #endif /* __ardour_basic_ui_h__ */
OSC.patch (40,989 bytes)   

paul

2008-05-16 12:06

administrator   ~0004938

mike, not sure what you're thinking of when you comment about using poll/pipe in relation to timeouts. all the uses of poll/pipe that i can think of are really related to inter-thread wakeups, and are not timeout based.

mtaht

2008-05-16 16:17

developer   ~0004940

Last edited: 2008-05-16 17:24

Originally I was going to implement a poll and pipe thread for this to implement the various reliable timers required. I still will, for older/other OSes, but I'll use the timerfd abstraction to embed those threads in that class, unless I come up with a clean way to support mac (kevent?).

For the stateless stuff, clocking the remote device by the outgoing stream's packets is basically how voip works. (after the raging success of the 10ms clock I'm thinking of trying 1ms!)

For stateful stuff (e.g. I've sent a command, and need a reply), there needs to be a per-command-outstanding-per-OSC-device timer to send retries...

ovenwerks

2017-08-20 23:47

reporter   ~0019987

Ardour's OSC has moved on a fair bit since this was entered. There is a whole set of controls that are fed back to the controller. At this point, feature requests that shows holes in the current feedback structure would be more helpful.

system

2020-04-19 20:13

developer   ~0021723

Issue has been closed automatically, by Trigger Close Plugin.
Feel free to re-open with additional information if you think the issue is not resolved.

Issue History

Date Modified Username Field Change
2008-05-07 13:51 mtaht New Issue
2008-05-07 13:51 mtaht File Added: osc.patch
2008-05-16 06:35 mtaht File Deleted: osc.patch
2008-05-16 06:55 mtaht Note Added: 0004937
2008-05-16 06:55 mtaht File Added: OSC.patch
2008-05-16 07:01 mtaht Note Edited: 0004937
2008-05-16 12:06 paul Note Added: 0004938
2008-05-16 16:17 mtaht Note Added: 0004940
2008-05-16 17:24 mtaht Note Edited: 0004940
2017-08-20 23:47 ovenwerks Note Added: 0019987
2017-08-20 23:47 ovenwerks Status new => resolved
2017-08-20 23:47 ovenwerks Resolution open => fixed
2017-08-20 23:47 ovenwerks Assigned To => ovenwerks
2020-04-19 20:13 system Note Added: 0021723
2020-04-19 20:13 system Status resolved => closed