Mike Taht's great work on the tranzport driver.
authorSampo Savolainen <v2@iki.fi>
Fri, 29 Dec 2006 19:13:18 +0000 (19:13 +0000)
committerSampo Savolainen <v2@iki.fi>
Fri, 29 Dec 2006 19:13:18 +0000 (19:13 +0000)
git-svn-id: svn://localhost/ardour2/trunk@1252 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/surfaces/tranzport/tranzport_control_protocol.cc
libs/surfaces/tranzport/tranzport_control_protocol.h

index ea85a32a778076b36b7a0afe55ef754c1b1df9e1..7a88499193b2b59d340e8b358757dc5ea60cb75d 100644 (file)
     $Id$
 */
 
+/* Design notes: The tranzport is a unique device, basically a 
+   20 lcd gui with 22 shift keys and 8 blinking lights. 
+
+   As such it has several unique constraints. The device exerts flow control
+   by having a usb write fail. It is pointless to retry madly at that point,
+   the device is busy, and it's not going to become unbusy very quickly. 
+
+   So writes need to be either "mandatory" or "unreliable", and therein 
+   lies the rub, as the kernel can also drop writes, and missing an
+   interrupt in userspace is also generally bad.
+
+   It will be good one day, to break the gui, keyboard, and blinking light
+   components into separate parts, but for now, this remains monolithic.
+
+   A more complex surface might have hundreds of lights and several displays.
+
+   mike.taht@gmail.com
+ */
+
+#define DEFAULT_USB_TIMEOUT 10
+#define MAX_RETRY 1
+#define MAX_TRANZPORT_INFLIGHT 4
+#define DEBUG_TRANZPORT 0
+#define HAVE_TRANZPORT_KERNEL_DRIVER 0
+
 #include <iostream>
 #include <algorithm>
 #include <cmath>
@@ -33,6 +58,7 @@
 #include <ardour/route.h>
 #include <ardour/audio_track.h>
 #include <ardour/session.h>
+#include <ardour/tempo.h>
 #include <ardour/location.h>
 #include <ardour/dB.h>
 
@@ -51,6 +77,12 @@ BaseUI::RequestType LEDChange = BaseUI::new_request_type ();
 BaseUI::RequestType Print = BaseUI::new_request_type ();
 BaseUI::RequestType SetCurrentTrack = BaseUI::new_request_type ();
 
+/* Base Tranzport cmd strings */
+
+static const uint8_t cmd_light_on[] =  { 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00 };
+static const uint8_t cmd_light_off[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
+static const uint8_t cmd_write_screen[] =  { 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00 };
+
 static inline double 
 gain_to_slider_position (ARDOUR::gain_t g)
 {
@@ -74,8 +106,7 @@ TranzportControlProtocol::TranzportControlProtocol (Session& s)
        /* tranzport controls one track at a time */
 
        set_route_table_size (1);
-       
-       timeout = 60000;
+       timeout = 6000; // what is this for?
        buttonmask = 0;
        _datawheel = 0;
        _device_status = STATUS_OFFLINE;
@@ -84,51 +115,212 @@ TranzportControlProtocol::TranzportControlProtocol (Session& s)
        last_where = max_frames;
        wheel_mode = WheelTimeline;
        wheel_shift_mode = WheelShiftGain;
+       wheel_increment = WheelIncrScreen;
+       bling_mode = BlingOff;
        timerclear (&last_wheel_motion);
        last_wheel_dir = 1;
        last_track_gain = FLT_MAX;
        display_mode = DisplayNormal;
        gain_fraction = 0.0;
+       invalidate();
+       screen_init();
+       lights_init();
+       print(0,0,"!!Welcome to Ardour!!");
+       print(1,0,"!Peace through Music!");
+}
 
-       memset (current_screen, 0, sizeof (current_screen));
-       memset (pending_screen, 0, sizeof (pending_screen));
+void TranzportControlProtocol::light_validate (LightID light) 
+{
+       lights_invalid[light] = 0;
+}
+
+void TranzportControlProtocol::light_invalidate (LightID light) 
+{
+       lights_invalid[light] = 1;
+}
 
-       for (uint32_t i = 0; i < sizeof(lights)/sizeof(lights[0]); ++i) {
-               lights[i] = false;
+void TranzportControlProtocol::lights_validate () 
+{
+       memset (lights_invalid, 0, sizeof (lights_invalid)); 
+}
+
+void TranzportControlProtocol::lights_invalidate () 
+{
+       memset (lights_invalid, 1, sizeof (lights_invalid)); 
+}
+
+void TranzportControlProtocol::lights_init()
+{
+       for (uint32_t i = 0; i < sizeof(lights_current)/sizeof(lights_current[0]); i++) {
+               lights_invalid[i] = lights_current[i] = 
+                       lights_pending[i] = lights_flash[i] = false;
+       }
+}
+
+
+
+int
+TranzportControlProtocol::lights_flush ()
+{
+       if ( _device_status == STATUS_OFFLINE) { return (0); }
+
+       //  Figure out iterators one day soon
+       //  for (LightID i = i.start(), i = i.end(); i++) {
+       //  if (lights_pending[i] != lights_current[i] || lights_invalid[i]) {
+       //    if (light_set(i, lights_pending[i])) { 
+       //       return i-1;
+       //    } 
+       //  }
+       //}
+       if ((lights_pending[LightRecord] != lights_current[LightRecord]) || lights_invalid[LightRecord]) {
+               if (light_set(LightRecord,lights_pending[LightRecord])) {
+                       return 1;
+               }
+       }
+       if ((lights_pending[LightTrackrec] != lights_current[LightTrackrec]) || lights_invalid[LightTrackrec]) {
+               if (light_set(LightTrackrec,lights_pending[LightTrackrec])) {
+                       return 1;
+               }
+       }
+
+       if ((lights_pending[LightTrackmute] != lights_current[LightTrackmute]) || lights_invalid[LightTrackmute]) {
+               if (light_set(LightTrackmute,lights_pending[LightTrackmute])) {
+                       return 1;
+               }
        }
 
-       for (uint32_t i = 0; i < sizeof(pending_lights)/sizeof(pending_lights[0]); ++i) {
-               pending_lights[i] = false;
+       if ((lights_pending[LightTracksolo] != lights_current[LightTracksolo]) || lights_invalid[LightTracksolo]) {
+               if (light_set(LightTracksolo,lights_pending[LightTracksolo])) {
+                       return 1;
+               }
+       }
+       if ((lights_pending[LightAnysolo] != lights_current[LightAnysolo]) || lights_invalid[LightAnysolo]) {
+               if (light_set(LightAnysolo,lights_pending[LightAnysolo])) {
+                       return 1;
+               }
+       }
+       if ((lights_pending[LightLoop] != lights_current[LightLoop]) || lights_invalid[LightLoop]) {
+               if (light_set(LightLoop,lights_pending[LightLoop])) {
+                       return 1;
+               }
        }
+       if ((lights_pending[LightPunch] != lights_current[LightPunch]) || lights_invalid[LightPunch]) {
+               if (light_set(LightPunch,lights_pending[LightPunch])) {
+                       return 1;
+               }
+       }
+
+       return 0;
 }
 
-TranzportControlProtocol::~TranzportControlProtocol ()
+// Screen specific commands
+
+void
+TranzportControlProtocol::screen_clear ()
 {
-       set_active (false);
+       const char *blank = "                    ";
+       print(0,0,blank); 
+       print(1,0,blank);
 }
 
-bool
-TranzportControlProtocol::probe ()
+void TranzportControlProtocol::screen_invalidate ()
 {
-       struct usb_bus *bus;
-       struct usb_device *dev;
+       for(int row = 0; row < 2; row++) {
+               for(int col = 0; col < 20; col++) {
+                       screen_invalid[row][col] = true;
+                       screen_current[row][col] = 0x7f;
+                       screen_pending[row][col] = ' ';
+                       // screen_flash[row][col] = ' ';
+               }
+       }
+       // memset (&screen_invalid, 1, sizeof(screen_invalid));
+       // memset (&screen_current, 0x7F, sizeof (screen_current)); // fill cache with a character we otherwise never use
+}
 
-       usb_init();
-       usb_find_busses();
-       usb_find_devices();
+void TranzportControlProtocol::screen_validate ()
+{
+}
 
-       for (bus = usb_busses; bus; bus = bus->next) {
+void TranzportControlProtocol::screen_init ()
+{
+       screen_invalidate();
+}
 
-               for(dev = bus->devices; dev; dev = dev->next) {
-                       if (dev->descriptor.idVendor == VENDORID && dev->descriptor.idProduct == PRODUCTID) {
-                               return true; 
+int
+TranzportControlProtocol::screen_flush ()
+{
+       int cell = 0, row, col_base, col, pending = 0;
+       if ( _device_status == STATUS_OFFLINE) { return (-1); }
+
+       for (row = 0; row < 2 && pending == 0; row++) {
+               for (col_base = 0, col = 0; col < 20 && pending == 0; ) {
+                       if ((screen_pending[row][col] != screen_current[row][col]) 
+                                       || screen_invalid[row][col]) {
+
+                               /* something in this cell is different, so dump the cell to the device. */
+
+                               uint8_t cmd[8]; 
+                               cmd[0] = 0x00; 
+                               cmd[1] = 0x01; 
+                               cmd[2] = cell; 
+                               cmd[3] = screen_pending[row][col_base]; 
+                               cmd[4] = screen_pending[row][col_base+1];
+                               cmd[5] = screen_pending[row][col_base+2]; 
+                               cmd[6] = screen_pending[row][col_base+3];
+                               cmd[7] = 0x00;
+
+                               if(write(cmd) != 0) {
+                                       /* try to update this cell on the next go-round */
+#if DEBUG_TRANZPORT > 4
+                                       printf("usb screen update failed for some reason... why? \ncmd and data were %02x %02x %02x %02x %02x %02x %02x %02x\n", 
+                                                       cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]); 
+#endif
+                                       pending += 1;   
+                                       // Shouldn't need to do this
+                                       // screen_invalid[row][col_base] = screen_invalid[row][col_base+1] = 
+                                       // screen_invalid[row][col_base+2] = screen_invalid[row][col_base+3] = true;
+
+                               } else {
+                                       /* successful write: copy to current cached display */
+                                       screen_invalid[row][col_base] = screen_invalid[row][col_base+1] = 
+                                               screen_invalid[row][col_base+2] = screen_invalid[row][col_base+3] = false;
+                                       memcpy (&screen_current[row][col_base], &screen_pending[row][col_base], 4);
+                               }
+
+                               /* skip the rest of the 4 character cell since we wrote+copied it already */
+
+                               col_base += 4;
+                               col = col_base;
+                               cell++;
+
+                       } else {
+
+                               col++;
+
+                               if (col && col % 4 == 0) {
+                                       cell++;
+                                       col_base += 4;
+                               }
                        }
                }
        }
+       return pending;
+}
 
-       return false;
+
+//  Tranzport specific
+
+void TranzportControlProtocol::invalidate() 
+{
+       lcd_damage(); lights_invalidate(); screen_invalidate(); // one of these days lcds can be fine but screens not
+}
+
+TranzportControlProtocol::~TranzportControlProtocol ()
+{
+       set_active (false);
 }
 
+
 int
 TranzportControlProtocol::set_active (bool yn)
 {
@@ -139,7 +331,7 @@ TranzportControlProtocol::set_active (bool yn)
                        if (open ()) {
                                return -1;
                        }
-                       
+
                        if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) {
                                _active = true;
                        } else {
@@ -148,11 +340,12 @@ TranzportControlProtocol::set_active (bool yn)
 
                } else {
                        cerr << "Begin tranzport shutdown\n";
+                       screen_clear ();
+                       lcd_damage();
+                       lights_off ();
+                       for(int x = 0; x < 10 && flush(); x++) { usleep(1000); }
                        pthread_cancel_one (thread);
-                       cerr << "Thread dead\n";
-                       // lcd_clear ();
-                       // lights_off ();
-                       // cerr << "dev reset\n";
+                       cerr << "Tranzport Thread dead\n";
                        close ();
                        _active = false;
                        cerr << "End tranzport shutdown\n";
@@ -167,8 +360,8 @@ TranzportControlProtocol::show_track_gain ()
 {
        if (route_table[0]) {
                gain_t g = route_get_gain (0);
-               if (g != last_track_gain) {
-                       char buf[16];
+               if ((g != last_track_gain) || lcd_isdamaged(0,9,8)) {
+                       char buf[16]; 
                        snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0)));
                        print (0, 9, buf); 
                        last_track_gain = g;
@@ -191,20 +384,66 @@ void
 TranzportControlProtocol::next_display_mode ()
 {
        switch (display_mode) {
-       case DisplayNormal:
-               display_mode = DisplayBigMeter;
-               break;
 
-       case DisplayBigMeter:
-               display_mode = DisplayNormal;
-               break;
+               case DisplayNormal:
+                       enter_big_meter_mode();
+                       break;
+
+               case DisplayBigMeter:
+                       enter_normal_display_mode();
+                       break;
+
+               case DisplayRecording:
+                       enter_normal_display_mode();
+                       break;
+
+               case DisplayRecordingMeter:
+                       enter_big_meter_mode();
+                       break;
+
+               case DisplayConfig: 
+               case DisplayBling:
+               case DisplayBlingMeter:
+                       enter_normal_display_mode();
+                       break;
        }
 }
 
+// FIXME, these 3 aren't done yet
+
+void
+TranzportControlProtocol::enter_recording_mode ()
+{
+       lcd_damage(); // excessive
+       screen_clear ();
+       lights_off ();
+       display_mode = DisplayRecording;
+}
+
+void
+TranzportControlProtocol::enter_bling_mode ()
+{
+       lcd_damage();
+       screen_clear ();
+       lights_off ();
+       display_mode = DisplayBling;
+}
+
+void
+TranzportControlProtocol::enter_config_mode ()
+{
+       lcd_damage();
+       screen_clear ();
+       lights_off ();
+       display_mode = DisplayConfig;
+}
+
+
 void
 TranzportControlProtocol::enter_big_meter_mode ()
 {
-       lcd_clear ();
+       screen_clear ();
+       lcd_damage();
        lights_off ();
        last_meter_fill = 0;
        display_mode = DisplayBigMeter;
@@ -213,16 +452,11 @@ TranzportControlProtocol::enter_big_meter_mode ()
 void
 TranzportControlProtocol::enter_normal_display_mode ()
 {
-       last_where += 1; /* force time redisplay */
-       last_track_gain = FLT_MAX; /* force gain redisplay */
-
-       lcd_clear ();
+       screen_clear ();
+       lcd_damage();
        lights_off ();
-       show_current_track ();
-       show_wheel_mode ();
-       show_wheel_mode ();
-       show_transport_time ();
        display_mode = DisplayNormal;
+       //  normal_update();
 }
 
 
@@ -230,10 +464,11 @@ float
 log_meter (float db)
 {
        float def = 0.0f; /* Meter deflection %age */
-       if (db < -70.0f) {
-               def = 0.0f;
-       } else if (db < -60.0f) {
+
+       if (db < -70.0f) return 0.0f;
+       if (db > 6.0f) return 1.0f;
+
+       if (db < -60.0f) {
                def = (db + 70.0f) * 0.25f;
        } else if (db < -50.0f) {
                def = (db + 60.0f) * 0.5f + 2.5f;
@@ -245,32 +480,36 @@ log_meter (float db)
                def = (db + 30.0f) * 2.0f + 30.0f;
        } else if (db < 6.0f) {
                def = (db + 20.0f) * 2.5f + 50.0f;
-       } else {
-               def = 115.0f;
        }
-       
+
        /* 115 is the deflection %age that would be 
           when db=6.0. this is an arbitrary
           endpoint for our scaling.
-       */
-       
+          */
+
        return def/115.0f;
 }
 
 void
 TranzportControlProtocol::show_meter ()
 {
+       // you only seem to get a route_table[0] on moving forward - bug elsewhere
        if (route_table[0] == 0) {
+               // Principle of least surprise
+               print (0, 0, "No audio to meter!!!");
+               print (1, 0, "Select another track"); 
                return;
        }
 
        float level = route_get_peak_input_power (0, 0);
        float fraction = log_meter (level);
 
+       /* Someday add a peak bar*/
+
        /* we draw using a choice of a sort of double colon-like character ("::") or a single, left-aligned ":".
           the screen is 20 chars wide, so we can display 40 different levels. compute the level,
           then figure out how many "::" to fill. if the answer is odd, make the last one a ":"
-       */
+          */
 
        uint32_t fill  = (uint32_t) floor (fraction * 40);
        char buf[21];
@@ -285,7 +524,7 @@ TranzportControlProtocol::show_meter ()
 
        bool add_single_level = (fill % 2 != 0);
        fill /= 2;
-       
+
        if (fraction > 0.98) {
                light_on (LightAnysolo);
        }
@@ -310,25 +549,64 @@ TranzportControlProtocol::show_meter ()
        }
 
        /* print() requires this */
-       
+
        buf[21] = '\0';
 
        print (0, 0, buf);
        print (1, 0, buf);
 }
 
+void
+TranzportControlProtocol::show_bbt (nframes_t where)
+{ 
+       if ((where != last_where) || lcd_isdamaged(1,9,8)) {
+               char buf[16];
+               BBT_Time bbt;
+               session->tempo_map().bbt_time (where, bbt);
+               sprintf (buf, "%03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
+               last_bars = bbt.bars;
+               last_beats = bbt.beats;
+               last_ticks = bbt.ticks;
+               last_where = where;
+
+               if(last_ticks < 1960) { print (1, 9, buf); } // save a write so we can do leds
+
+               // if displaymode is recordmode show beats but not yet
+               lights_pending[LightRecord] = false;
+               lights_pending[LightAnysolo] = false;
+               switch(last_beats) {
+                       case 1: if(last_ticks < 500 || last_ticks > 1960) lights_pending[LightRecord] = true; break;
+                       default: if(last_ticks < 250) lights_pending[LightAnysolo] = true;
+               }
+
+               // update lights for tempo one day
+               //        if (bbt_upper_info_label) {
+               //     TempoMap::Metric m (session->tempo_map().metric_at (when));
+               //     sprintf (buf, "%-5.2f", m.tempo().beats_per_minute());
+               //      bbt_lower_info_label->set_text (buf);
+               //      sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor());
+               //      bbt_upper_info_label->set_text (buf);
+       }
+       }
+
+
 void
 TranzportControlProtocol::show_transport_time ()
 {
        nframes_t where = session->transport_frame();
-       
-       if (where != last_where) {
+       show_bbt(where);
+}      
+
+void
+TranzportControlProtocol::show_smpte (nframes_t where)
+{
+       if ((where != last_where) || lcd_isdamaged(1,9,10)) {
 
                char buf[5];
                SMPTE::Time smpte;
 
                session->smpte_time (where, smpte);
-               
+
                if (smpte.negative) {
                        sprintf (buf, "-%02" PRIu32 ":", smpte.hours);
                } else {
@@ -343,7 +621,7 @@ TranzportControlProtocol::show_transport_time ()
                print (1, 15, buf);
 
                sprintf (buf, "%02" PRIu32, smpte.frames);
-               print (1, 18, buf);
+               print_noretry (1, 18, buf); 
 
                last_where = where;
        }
@@ -355,6 +633,33 @@ TranzportControlProtocol::_monitor_work (void* arg)
        return static_cast<TranzportControlProtocol*>(arg)->monitor_work ();
 }
 
+// I note that these usb specific open, close, probe, read routines are basically 
+// pure boilerplate and could easily be abstracted elsewhere
+
+#if !HAVE_TRANZPORT_KERNEL_DRIVER
+
+bool
+TranzportControlProtocol::probe ()
+{
+       struct usb_bus *bus;
+       struct usb_device *dev;
+
+       usb_init();
+       usb_find_busses();
+       usb_find_devices();
+
+       for (bus = usb_busses; bus; bus = bus->next) {
+
+               for(dev = bus->devices; dev; dev = dev->next) {
+                       if (dev->descriptor.idVendor == VENDORID && dev->descriptor.idProduct == PRODUCTID) {
+                               return true; 
+                       }
+               }
+       }
+
+       return false;
+}
+
 int
 TranzportControlProtocol::open ()
 {
@@ -424,393 +729,469 @@ TranzportControlProtocol::close ()
 
        return ret;
 }
+
+int TranzportControlProtocol::read(uint8_t *buf, uint32_t timeout_override) 
+{
+       int val;
+       // Get smarter about handling usb errors soon. Like disconnect
+       //  pthread_testcancel();
+       val = usb_interrupt_read (udev, READ_ENDPOINT, (char *) buf, 8, 10);
+       //  pthread_testcancel();
+       return val;
+} 
+
        
 int
-TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
+TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override)
 {
        int val;
-
+       if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
        val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
 
-       if (val < 0)
+       if (val < 0) {
+#if DEBUG_TRANZPORT
+               printf("usb_interrupt_write failed: %d\n", val);
+#endif
                return val;
-       if (val != 8)
+               }
+
+       if (val != 8) {
+#if DEBUG_TRANZPORT
+               printf("usb_interrupt_write failed: %d\n", val);
+#endif
                return -1;
+               }
+       ++inflight;
+
        return 0;
 
 }      
 
-void
-TranzportControlProtocol::lcd_clear ()
+int
+TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
 {
-       /* special case this for speed and atomicity */
-
-       uint8_t cmd[8];
+#if MAX_RETRY > 1
+       int val;
+       int retry = 0;
+       if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
        
-       cmd[0] = 0x00;
-       cmd[1] = 0x01;
-       cmd[3] = ' ';
-       cmd[4] = ' ';
-       cmd[5] = ' ';
-       cmd[6] = ' ';
-       cmd[7] = 0x00;
-
-       for (uint8_t i = 0; i < 10; ++i) {
-               cmd[2] = i;
-               usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, 1000);
+       while((val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout))!=8 && retry++ < MAX_RETRY) {
+               printf("usb_interrupt_write failed, retrying: %d\n", val);
        }
-       
-       memset (current_screen, ' ', sizeof (current_screen));
-       memset (pending_screen, ' ', sizeof (pending_screen));
-}
 
-void
-TranzportControlProtocol::lights_off ()
+       if (retry == MAX_RETRY) {
+               printf("Too many retries on a tranzport write, aborting\n");
+               }
+
+       if (val < 0) {
+               printf("usb_interrupt_write failed: %d\n", val);
+               return val;
+               }
+       if (val != 8) {
+               printf("usb_interrupt_write failed: %d\n", val);
+               return -1;
+               }
+       ++inflight;
+       return 0;
+#else
+       return (write_noretry(cmd,timeout_override));
+#endif
+
+}      
+
+#else
+#error Kernel API not defined yet for Tranzport
+// Something like open(/dev/surface/tranzport/event) for reading and raw for writing)
+#endif
+
+// We have a state "Unknown" - STOP USING SPACES FOR IT - switching to arrow character
+// We have another state - no_retry. Misleading, as we still retry on the next pass
+// I think it's pointless to keep no_retry and instead we should throttle writes 
+// We have an "displayed" screen
+// We always draw into the pending screen, which could be any of several screens
+// We have an active screen
+// Print arg - we have 
+// setactive
+// so someday I think we need a screen object.
+
+/*
+screen_flash.clear();
+screen_flash.print(0,0,"Undone:"); // Someday pull the undo stack from somewhere
+screen_flash.print(1,0,"Nextup:"); 
+
+if(flash_messages && lcd.getactive() != screen_flash) lcd.setactive(screen_flash,2000);
+
+screen::setactive(screen_name,duration); // duration in ms
+screen::getactive();
+*/
+
+
+int
+TranzportControlProtocol::flush ()
 {
-       uint8_t cmd[8];
+       int pending = 0;
+       if(!(pending = lights_flush())) {
+               pending = screen_flush(); 
+       } 
+       return pending;
+}
 
-       cmd[0] = 0x00;
-       cmd[1] = 0x00;
-       cmd[3] = 0x00;
-       cmd[4] = 0x00;
-       cmd[5] = 0x00;
-       cmd[6] = 0x00;
-       cmd[7] = 0x00;
+// doing these functions made me realize that screen_invalid should be lcd_isdamaged FIXME soon
 
-       cmd[2] = LightRecord;
-       if (write (cmd, 1000) == 0) {
-               lights[LightRecord] = false;
-       }
-       cmd[2] = LightTrackrec;
-       if (write (cmd, 1000) == 0) {
-               lights[LightTrackrec] = false;
-       }
-       cmd[2] = LightTrackmute;
-       if (write (cmd, 1000) == 0) {
-               lights[LightTrackmute] = false;
-       }
-       cmd[2] = LightTracksolo;
-       if (write (cmd, 1000) == 0) {
-               lights[LightTracksolo] = false;
-       }
-       cmd[2] = LightAnysolo;
-       if (write (cmd, 1000) == 0) {
-               lights[LightAnysolo] = false;
+bool TranzportControlProtocol::lcd_damage() 
+{
+       screen_invalidate();
+       return true;
+}
+
+bool TranzportControlProtocol::lcd_damage (int row, int col, int length)
+{
+       bool result = false;
+       int endcol = col+length-1;
+       if((endcol > 19)) { endcol = 19; } 
+       if((row >= 0 && row < 2) && (col >=0 && col < 20)) {
+               for(int c = col; c < endcol; c++) {
+                       screen_invalid[row][c] = true;
+               }
+               result = true;
        }
-       cmd[2] = LightLoop;
-       if (write (cmd, 1000) == 0) {
-               lights[LightLoop] = false;
+       return result;
+}
+
+// Gotta switch to bitfields, this is collossally dumb
+// Still working on the layering, arguably screen_invalid should be lcd_invalid
+
+bool TranzportControlProtocol::lcd_isdamaged () 
+{
+       for(int r = 0; r < 2; r++) {
+               for(int c = 0; c < 20; c++) {
+                       if(screen_invalid[r][c]) {
+#if DEBUG_TRANZPORT > 5        
+                               printf("row: %d,col: %d is damaged, should redraw it\n", r,c);
+#endif
+                               return true;
+                       }
+               }
        }
-       cmd[2] = LightPunch;
-       if (write (cmd, 1000) == 0) {
-               lights[LightPunch] = false;
+       return false;
+}
+
+bool TranzportControlProtocol::lcd_isdamaged (int row, int col, int length)
+{
+       bool result = 0;
+       int endcol = col+length;
+       if((endcol > 19)) { endcol = 19; } 
+       if((row >= 0 && row < 2) && (col >=0 && col < 20)) {
+               for(int c = col; c < endcol; c++) {
+                       if(screen_invalid[row][c]) {
+#if DEBUG_TRANZPORT > 5        
+                               printf("row: %d,col: %d is damaged, should redraw it\n", row,c);
+#endif
+                               return true;
+                       }
+               }
        }
+       return result;
 }
 
+// lcd_clear would be a separate function for a smart display
+// here it does nothing, but for the sake of completeness it should
+// probably write the lcd, and while I'm on the topic it should probably
+// take a row, col, length argument....
+
+void
+TranzportControlProtocol::lcd_clear ()
+{
+
+}
+
+// These lcd commands are not universally used yet and may drop out of the api
+
 int
-TranzportControlProtocol::light_on (LightID light)
+TranzportControlProtocol::lcd_flush ()
 {
-       uint8_t cmd[8];
+       return 0; 
+}
 
-       if (!lights[light]) {
+int 
+TranzportControlProtocol::lcd_write(uint8_t* cmd, uint32_t timeout_override)
+{
+       return write(cmd,timeout_override);
+}
 
-               cmd[0] = 0x00;
-               cmd[1] = 0x00;
-               cmd[2] = light;
-               cmd[3] = 0x01;
-               cmd[4] = 0x00;
-               cmd[5] = 0x00;
-               cmd[6] = 0x00;
-               cmd[7] = 0x00;
+void 
+TranzportControlProtocol::lcd_fill (uint8_t fill_char) 
+{
+}
 
-               if (write (cmd, 1000) == 0) {
-                       lights[light] = true;
-                       return 0;
-               } else {
-                       return -1;
-               }
+void 
+TranzportControlProtocol::lcd_print (int row, int col, const char* text) 
+{
+       print(row,col,text);
+}
 
-       } else {
-               return 0;
-       }
+void TranzportControlProtocol::lcd_print_noretry (int row, int col, const char* text)
+{
+       print(row,col,text);
 }
 
-int
-TranzportControlProtocol::light_off (LightID light)
+// Lights are buffered
+
+void
+TranzportControlProtocol::lights_on ()
 {
-       uint8_t cmd[8];
+       lights_pending[LightRecord] = lights_pending[LightTrackrec] = 
+               lights_pending[LightTrackmute] =  lights_pending[LightTracksolo] = 
+               lights_pending[LightAnysolo] =   lights_pending[LightLoop] = 
+               lights_pending[LightPunch] = true;
+}
 
-       if (lights[light]) {
+void
+TranzportControlProtocol::lights_off ()
+{
+       lights_pending[LightRecord] = lights_pending[LightTrackrec] = 
+               lights_pending[LightTrackmute] =  lights_pending[LightTracksolo] = 
+               lights_pending[LightAnysolo] =   lights_pending[LightLoop] = 
+               lights_pending[LightPunch] = false;
+}
 
-               cmd[0] = 0x00;
-               cmd[1] = 0x00;
-               cmd[2] = light;
-               cmd[3] = 0x00;
-               cmd[4] = 0x00;
-               cmd[5] = 0x00;
-               cmd[6] = 0x00;
-               cmd[7] = 0x00;
+int
+TranzportControlProtocol::light_on (LightID light)
+{
+       lights_pending[light] = true;
+       return 0;
+}
 
-               if (write (cmd, 1000) == 0) {
-                       lights[light] = false;
-                       return 0;
-               } else {
-                       return -1;
-               }
+int
+TranzportControlProtocol::light_off (LightID light)
+{
+       lights_pending[light] = false;
+       return 0;
+}
 
-       } else {
+int
+TranzportControlProtocol::light_set (LightID light, bool offon)
+{
+       uint8_t cmd[8];
+       cmd[0] = 0x00;  cmd[1] = 0x00;  cmd[2] = light;  cmd[3] = offon;
+       cmd[4] = 0x00;  cmd[5] = 0x00;  cmd[6] = 0x00;  cmd[7] = 0x00;
+
+       if (write (cmd) == 0) {
+               lights_current[light] = offon;
+               lights_invalid[light] = false;
                return 0;
+       } else {
+               return -1;
        }
 }
 
-void*
-TranzportControlProtocol::monitor_work ()
+int TranzportControlProtocol::rtpriority_set(int priority) 
 {
        struct sched_param rtparam;
        int err;
-       uint8_t buf[8];
-       int val;
-       bool first_time = true;
+       // preallocate and memlock some stack with memlock?
+       char *a = (char*) alloca(4096*2); a[0] = 'a'; a[4096] = 'b';
+       memset (&rtparam, 0, sizeof (rtparam));
+       rtparam.sched_priority = priority; /* XXX should be relative to audio (JACK) thread */
+       // Note - try SCHED_RR with a low limit 
+       // - we don't care if we can't write everything this ms
+       // and it will help if we lose the device
+       if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
+               PBD::info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
+               return 1;
+       } 
+       return 0;
+}
 
-       PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
+// Running with realtime privs is bad when you have problems
 
+int TranzportControlProtocol::rtpriority_unset(int priority) 
+{
+       struct sched_param rtparam;
+       int err;
        memset (&rtparam, 0, sizeof (rtparam));
-       rtparam.sched_priority = 3; /* XXX should be relative to audio (JACK) thread */
-       
+       rtparam.sched_priority = priority;      
        if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
-               // do we care? not particularly.
-               PBD::info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
+               PBD::info << string_compose (_("%1: can't stop realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
+               return 1;
        } 
+       PBD::info << string_compose (_("%1: realtime scheduling stopped (%2)"), name(), strerror (errno)) << endmsg;
+       return 0;
+}
+
+// Slowly breaking this into where I can make usb processing it's own thread.
+
+void*
+TranzportControlProtocol::monitor_work ()
+{
+       uint8_t buf[8];
+       int val = 0, pending = 0;
+       bool first_time = true;
+       uint8_t offline = 0;
+
 
+       PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
        pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
        pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
-
        next_track ();
+       rtpriority_set();
+       inflight=0;
+       flush();
 
        while (true) {
 
                /* bInterval for this beastie is 10ms */
 
-               /* anything to read ? */
-
                if (_device_status == STATUS_OFFLINE) {
-                       light_off (LightRecord);
-                       first_time = true;
-               }
-
-               pthread_testcancel();
-               val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 10);
-               pthread_testcancel();
-
-               if (val == 8) {
-                       process (buf);
-               }
-
-               if (_device_status != STATUS_OFFLINE) {
-                       if (first_time) {
-                               lcd_clear ();
-                               lights_off ();
-                               first_time = false;
-                       }
-                       /* update whatever needs updating */
-                       update_state ();
-               }
-       }
-
-       return (void*) 0;
-}
-
-int
-TranzportControlProtocol::update_state ()
-{
-       int row;
-       int col_base;
-       int col;
-       int cell;
-
-       /* do the text updates */
+                       first_time = true;
+                       if(offline++ == 1) { 
+                               cerr << "Transport has gone offline\n";
+                       }
+               } else { 
+                       offline = 0; // hate writing this
+               }
 
-       switch (display_mode) {
-       case DisplayBigMeter:
-               show_meter ();
-               break;
+               val = read(buf);
 
-       case DisplayNormal:
-               normal_update ();
-               break;
-       }
+               if (val == 8) {
+                       process (buf);
+               }
 
-       /* next: flush LCD */
+#if DEBUG_TRANZPORT > 2
+               if(inflight > 1) printf("Inflight: %d\n", inflight);
+#endif
 
-       cell = 0;
-       
-       for (row = 0; row < 2; ++row) {
-
-               for (col_base = 0, col = 0; col < 20; ) {
-                       
-                       if (pending_screen[row][col] != current_screen[row][col]) {
-
-                               /* something in this cell is different, so dump the cell
-                                  to the device.
-                               */
-
-                               uint8_t cmd[8];
-                               
-                               cmd[0] = 0x00;
-                               cmd[1] = 0x01;
-                               cmd[2] = cell;
-                               cmd[3] = pending_screen[row][col_base];
-                               cmd[4] = pending_screen[row][col_base+1];
-                               cmd[5] = pending_screen[row][col_base+2];
-                               cmd[6] = pending_screen[row][col_base+3];
-                               cmd[7] = 0x00;
 
-                               if (usb_interrupt_write (udev, WRITE_ENDPOINT, (char *) cmd, 8, 1000) == 8) {
-                                       /* successful write: copy to current */
-                                       memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
-                               }
+               if (_device_status != STATUS_OFFLINE) {
+                       if (first_time) {
+                               invalidate();
+                               lcd_clear ();
+                               lights_off ();
+                               first_time = false;
+                               offline = 0;
+                               pending = 3; // Give some time for the device to recover
+                       }
+                       /* update whatever needs updating */
+                       update_state ();
 
-                               /* skip the rest of the 4 character cell since we wrote+copied it already */
-                               
-                               col_base += 4;
-                               col = col_base;
-                               cell++;
+                       /* still struggling with a good means of exerting flow control */
+                       // pending = flush();
 
+                       if(pending == 0) {
+                               pending = flush(); 
                        } else {
-
-                               col++;
-                               
-                               if (col && col % 4 == 0) {
-                                       cell++;
-                                       col_base += 4;
+                               if(inflight > 0) {
+                                       pending = --inflight; // we just did a whole bunch of writes so wait
+                               } else {
+                                       pending = 0;
                                }
                        }
-               }
+                       // pending = 0;
+               } 
        }
 
-       /* now update LED's */
+       return (void*) 0;
+}
 
-       /* per track */
+int TranzportControlProtocol::lights_show_recording() 
+{
+       //   FIXME, flash recording light when recording and transport is moving
+       return     lights_show_normal();
+}
 
-       if (route_table[0]) {
-               boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack> (route_table[0]);
-               if (at && at->record_enabled()) {
-                       pending_lights[LightTrackrec] = true;
-               } else {
-                       pending_lights[LightTrackrec] = false;
-               }
-               if (route_get_muted (0)) {
-                       pending_lights[LightTrackmute] = true;
-               } else {
-                       pending_lights[LightTrackmute] = false;
-               }
-               if (route_get_soloed (0)) {
-                       pending_lights[LightTracksolo] = true;
-               } else {
-                       pending_lights[LightTracksolo] = false;
-               }
+// gotta do bling next!
 
-       } else {
-               pending_lights[LightTrackrec] = false;
-               pending_lights[LightTracksolo] = false;
-               pending_lights[LightTrackmute] = false;
+int TranzportControlProtocol::lights_show_bling() 
+{
+       switch (bling_mode) {
+               case BlingOff: break;
+               case BlingKit: break; // rotate rec/mute/solo/any solo back and forth
+               case BlingRotating: break; // switch between lights
+               case BlingPairs: break; // Show pairs of lights
+               case BlingRows: break; // light each row in sequence
+               case BlingFlashAll: break; // Flash everything randomly
        }
+       return 0;
+}
 
-       /* global */
+int TranzportControlProtocol::lights_show_normal() 
+{
+       /* Track only */
 
-       if (session->get_play_loop()) {
-               pending_lights[LightLoop] = true;
+       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);
        } else {
-               pending_lights[LightLoop] = false;
+               lights_pending[LightTrackrec]  = false;
+               lights_pending[LightTracksolo] = false;
+               lights_pending[LightTrackmute] = false;
        }
 
-       if (Config->get_punch_in() || Config->get_punch_out()) {
-               pending_lights[LightPunch] = true;
-       } else {
-               pending_lights[LightPunch] = false;
-       }
+       /* Global settings */
 
-       if (session->get_record_enabled()) {
-               pending_lights[LightRecord] = true;
-       } else {
-               pending_lights[LightRecord] = false;
-       }
+       lights_pending[LightLoop]        = session->get_play_loop(); 
+       lights_pending[LightPunch]       = Config->get_punch_in() || Config->get_punch_out();
+       lights_pending[LightRecord]      = session->get_record_enabled();
+       lights_pending[LightAnysolo]     = session->soloing();
 
-       if (session->soloing ()) {
-               pending_lights[LightAnysolo] = true;
-       } else {
-               pending_lights[LightAnysolo] = false;
-       }
+       return 0;
+}
 
-       /* flush changed light change */
+int TranzportControlProtocol::lights_show_tempo() 
+{
+       // someday soon fiddle with the lights based on the tempo 
+       return     lights_show_normal();
+}
 
-       if (pending_lights[LightRecord] != lights[LightRecord]) {
-               if (pending_lights[LightRecord]) {
-                       light_on (LightRecord);
-               } else {
-                       light_off (LightRecord);
-               }
-       }
+int
+TranzportControlProtocol::update_state ()
+{
+       /* do the text and light updates */
 
-       if (pending_lights[LightTracksolo] != lights[LightTracksolo]) {
-               if (pending_lights[LightTracksolo]) {
-                       light_on (LightTracksolo);
-               } else {
-                       light_off (LightTracksolo);
-               }
-       }
+       switch (display_mode) {
+               case DisplayBigMeter:
+                       lights_show_tempo();
+                       show_meter ();
+                       break;
 
-       if (pending_lights[LightTrackrec] != lights[LightTrackrec]) {
-               if (pending_lights[LightTrackrec]) {
-                       light_on (LightTrackrec);
-               } else {
-                       light_off (LightTrackrec);
-               }
-       }
+               case DisplayNormal:
+                       lights_show_normal();
+                       normal_update ();
+                       break;
 
-       if (pending_lights[LightTrackmute] != lights[LightTrackmute]) {
-               if (pending_lights[LightTrackmute]) {
-                       light_on (LightTrackmute);
-               } else {
-                       light_off (LightTrackmute);
-               }
-       }
+               case DisplayConfig:
+                       break;
 
-       if (pending_lights[LightTracksolo] != lights[LightTracksolo]) {
-               if (pending_lights[LightTracksolo]) {
-                       light_on (LightTracksolo);
-               } else {
-                       light_off (LightTracksolo);
-               }
-       }
+               case DisplayRecording:
+                       lights_show_recording();
+                       normal_update(); 
+                       break;
 
-       if (pending_lights[LightAnysolo] != lights[LightAnysolo]) {
-               if (pending_lights[LightAnysolo]) {
-                       light_on (LightAnysolo);
-               } else {
-                       light_off (LightAnysolo);
-               }
-       }
+               case DisplayRecordingMeter:
+                       lights_show_recording();
+                       show_meter(); 
+                       break;
 
-       if (pending_lights[LightLoop] != lights[LightLoop]) {
-               if (pending_lights[LightLoop]) {
-                       light_on (LightLoop);
-               } else {
-                       light_off (LightLoop);
-               }
-       }
+               case DisplayBling:
+                       lights_show_bling();
+                       normal_update();
+                       break;
 
-       if (pending_lights[LightPunch] != lights[LightPunch]) {
-               if (pending_lights[LightPunch]) {
-                       light_on (LightPunch);
-               } else {
-                       light_off (LightPunch);
-               }
+               case DisplayBlingMeter:
+                       lights_show_bling();
+                       show_meter();
+                       break;
        }
-
        return 0;
+
 }
 
+#define TRANZPORT_BUTTON_HANDLER(callback, button_arg) if (button_changes & button_arg) { \
+    if (buttonmask & button_arg) { \
+      callback##_press (buttonmask&ButtonShift); } else { callback##_release (buttonmask&ButtonShift); } }
+
 int
 TranzportControlProtocol::process (uint8_t* buf)
 {
@@ -820,6 +1201,7 @@ TranzportControlProtocol::process (uint8_t* buf)
        uint32_t button_changes;
 
        _device_status = buf[1];
+
        this_button_mask = 0;
        this_button_mask |= buf[2] << 24;
        this_button_mask |= buf[3] << 16;
@@ -834,157 +1216,50 @@ TranzportControlProtocol::process (uint8_t* buf)
                datawheel ();
        }
 
-       if (button_changes & ButtonBattery) {
-               if (buttonmask & ButtonBattery) {
-                       button_event_battery_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_battery_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonBacklight) {
-               if (buttonmask & ButtonBacklight) {
-                       button_event_backlight_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_backlight_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonTrackLeft) {
-               if (buttonmask & ButtonTrackLeft) {
-                       button_event_trackleft_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_trackleft_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonTrackRight) {
-               if (buttonmask & ButtonTrackRight) {
-                       button_event_trackright_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_trackright_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonTrackRec) {
-               if (buttonmask & ButtonTrackRec) {
-                       button_event_trackrec_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_trackrec_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonTrackMute) {
-               if (buttonmask & ButtonTrackMute) {
-                       button_event_trackmute_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_trackmute_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonTrackSolo) {
-               if (buttonmask & ButtonTrackSolo) {
-                       button_event_tracksolo_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_tracksolo_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonUndo) {
-               if (buttonmask & ButtonUndo) {
-                       button_event_undo_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_undo_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonIn) {
-               if (buttonmask & ButtonIn) {
-                       button_event_in_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_in_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonOut) {
-               if (buttonmask & ButtonOut) {
-                       button_event_out_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_out_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonPunch) {
-               if (buttonmask & ButtonPunch) {
-                       button_event_punch_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_punch_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonLoop) {
-               if (buttonmask & ButtonLoop) {
-                       button_event_loop_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_loop_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonPrev) {
-               if (buttonmask & ButtonPrev) {
-                       button_event_prev_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_prev_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonAdd) {
-               if (buttonmask & ButtonAdd) {
-                       button_event_add_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_add_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonNext) {
-               if (buttonmask & ButtonNext) {
-                       button_event_next_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_next_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonRewind) {
-               if (buttonmask & ButtonRewind) {
-                       button_event_rewind_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_rewind_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonFastForward) {
-               if (buttonmask & ButtonFastForward) {
-                       button_event_fastforward_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_fastforward_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonStop) {
-               if (buttonmask & ButtonStop) {
-                       button_event_stop_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_stop_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonPlay) {
-               if (buttonmask & ButtonPlay) {
-                       button_event_play_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_play_release (buttonmask&ButtonShift);
-               }
-       }
-       if (button_changes & ButtonRecord) {
-               if (buttonmask & ButtonRecord) {
-                       button_event_record_press (buttonmask&ButtonShift);
-               } else {
-                       button_event_record_release (buttonmask&ButtonShift);
-               }
-       }
-               
+       // SHIFT + STOP + PLAY for bling mode?
+       // if (button_changes & ButtonPlay & ButtonStop) {
+       // bling_mode_toggle();  
+       // } or something like that
+
+       TRANZPORT_BUTTON_HANDLER(button_event_battery,ButtonBattery);
+       TRANZPORT_BUTTON_HANDLER(button_event_backlight,ButtonBacklight);
+       TRANZPORT_BUTTON_HANDLER(button_event_trackleft,ButtonTrackLeft);
+       TRANZPORT_BUTTON_HANDLER(button_event_trackright,ButtonTrackRight);
+       TRANZPORT_BUTTON_HANDLER(button_event_trackrec,ButtonTrackRec);
+       TRANZPORT_BUTTON_HANDLER(button_event_trackmute,ButtonTrackMute);
+       TRANZPORT_BUTTON_HANDLER(button_event_tracksolo,ButtonTrackSolo);
+       TRANZPORT_BUTTON_HANDLER(button_event_undo,ButtonUndo);
+       TRANZPORT_BUTTON_HANDLER(button_event_in,ButtonIn);
+       TRANZPORT_BUTTON_HANDLER(button_event_out,ButtonOut);
+       TRANZPORT_BUTTON_HANDLER(button_event_punch,ButtonPunch);
+       TRANZPORT_BUTTON_HANDLER(button_event_loop,ButtonLoop);
+       TRANZPORT_BUTTON_HANDLER(button_event_prev,ButtonPrev);
+       TRANZPORT_BUTTON_HANDLER(button_event_add,ButtonAdd);
+       TRANZPORT_BUTTON_HANDLER(button_event_next,ButtonNext);
+       TRANZPORT_BUTTON_HANDLER(button_event_rewind,ButtonRewind);
+       TRANZPORT_BUTTON_HANDLER(button_event_fastforward,ButtonFastForward);
+       TRANZPORT_BUTTON_HANDLER(button_event_stop,ButtonStop);
+       TRANZPORT_BUTTON_HANDLER(button_event_play,ButtonPlay);
+       TRANZPORT_BUTTON_HANDLER(button_event_record,ButtonRecord);
        return 0;
 }
 
 void
 TranzportControlProtocol::show_current_track ()
 {
+       char pad[11];
+       char *v;
+       int len;
        if (route_table[0] == 0) {
-               print (0, 0, "--------");
+               print (0, 0, "----------");
+               last_track_gain = FLT_MAX;
        } else {
-               print (0, 0, route_get_name (0).substr (0, 8).c_str());
+               strcpy(pad,"          ");
+               v =  (char *)route_get_name (0).substr (0, 10).c_str();
+               if((len = strlen(v)) > 0) {
+                       strncpy(pad,(char *)v,len);
+               }
+               print (0, 0, pad);
        }
 }
 
@@ -1001,11 +1276,24 @@ TranzportControlProtocol::button_event_battery_release (bool shifted)
 void
 TranzportControlProtocol::button_event_backlight_press (bool shifted)
 {
+#if DEBUG_TRANZPORT
+       printf("backlight pressed\n");
+#endif
 }
 
 void
 TranzportControlProtocol::button_event_backlight_release (bool shifted)
 {
+#if DEBUG_TRANZPORT
+       printf("backlight released\n\n");
+#endif
+       if (shifted) {
+               lcd_damage();
+               lcd_clear();
+               last_where += 1; /* force time redisplay */
+               last_track_gain = FLT_MAX;
+               normal_update(); //  redraw_screen();  
+       }
 }
 
 void
@@ -1048,7 +1336,11 @@ TranzportControlProtocol::button_event_trackrec_release (bool shifted)
 void
 TranzportControlProtocol::button_event_trackmute_press (bool shifted)
 {
-       route_set_muted (0, !route_get_muted (0));
+       if (shifted) {
+         // Mute ALL? Something useful when a phone call comes in. Mute master?
+       } else {
+         route_set_muted (0, !route_get_muted (0));
+       }
 }
 
 void
@@ -1059,6 +1351,9 @@ TranzportControlProtocol::button_event_trackmute_release (bool shifted)
 void
 TranzportControlProtocol::button_event_tracksolo_press (bool shifted)
 {
+#if DEBUG_TRANZPORT
+       printf("solo pressed\n");
+#endif
        if (display_mode == DisplayBigMeter) {
                light_off (LightAnysolo);
                return;
@@ -1074,15 +1369,18 @@ TranzportControlProtocol::button_event_tracksolo_press (bool shifted)
 void
 TranzportControlProtocol::button_event_tracksolo_release (bool shifted)
 {
+#if DEBUG_TRANZPORT
+       printf("solo released\n");
+#endif
 }
 
 void
 TranzportControlProtocol::button_event_undo_press (bool shifted)
 {
        if (shifted) {
-               redo ();
+               redo (); // someday flash the screen with what was redone
        } else {
-               undo ();
+               undo (); // someday flash the screen with what was undone
        }
 }
 
@@ -1235,7 +1533,11 @@ TranzportControlProtocol::button_event_stop_release (bool shifted)
 void
 TranzportControlProtocol::button_event_play_press (bool shifted)
 {
-       transport_play ();
+       if (shifted) {
+         set_transport_speed (1.0f);
+       } else {
+         transport_play ();
+       }
 }
 
 void
@@ -1258,11 +1560,17 @@ TranzportControlProtocol::button_event_record_release (bool shifted)
 {
 }
 
+void button_event_mute (bool pressed, bool shifted)
+{
+       static int was_pressed = 0;
+       //  if(pressed) { }
+}
+
 void
 TranzportControlProtocol::datawheel ()
 {
        if ((buttonmask & ButtonTrackRight) || (buttonmask & ButtonTrackLeft)) {
-               
+
                /* track scrolling */
 
                if (_datawheel < WheelDirectionThreshold) {
@@ -1274,7 +1582,7 @@ TranzportControlProtocol::datawheel ()
                timerclear (&last_wheel_motion);
 
        } else if ((buttonmask & ButtonPrev) || (buttonmask & ButtonNext)) {
-               
+
                if (_datawheel < WheelDirectionThreshold) {
                        next_marker ();
                } else {
@@ -1289,23 +1597,27 @@ TranzportControlProtocol::datawheel ()
 
                if (route_table[0]) {
                        switch (wheel_shift_mode) {
-                       case WheelShiftGain:
-                               if (_datawheel < WheelDirectionThreshold) {
-                                       step_gain_up ();
-                               } else {
-                                       step_gain_down ();
-                               }
-                               break;
-                       case WheelShiftPan:
-                               if (_datawheel < WheelDirectionThreshold) {
-                                       step_pan_right ();
-                               } else {
-                                       step_pan_left ();
-                               }
-                               break;
+                               case WheelShiftGain:
+                                       if (_datawheel < WheelDirectionThreshold) {
+                                               step_gain_up ();
+                                       } else {
+                                               step_gain_down ();
+                                       }
+                                       break;
+                               case WheelShiftPan:
+                                       if (_datawheel < WheelDirectionThreshold) {
+                                               step_pan_right ();
+                                       } else {
+                                               step_pan_left ();
+                                       }
+                                       break;
+
+                               case WheelShiftMarker:
+                                       break;
+
+                               case WheelShiftMaster:
+                                       break;
 
-                       case WheelShiftMaster:
-                               break;
                        }
                }
 
@@ -1314,17 +1626,17 @@ TranzportControlProtocol::datawheel ()
        } else {
 
                switch (wheel_mode) {
-               case WheelTimeline:
-                       scroll ();
-                       break;
-                       
-               case WheelScrub:
-                       scrub ();
-                       break;
+                       case WheelTimeline:
+                               scroll ();
+                               break;
 
-               case WheelShuttle:
-                       shuttle ();
-                       break;
+                       case WheelScrub:
+                               scrub ();
+                               break;
+
+                       case WheelShuttle:
+                               shuttle ();
+                               break;
                }
        }
 }
@@ -1332,10 +1644,15 @@ TranzportControlProtocol::datawheel ()
 void
 TranzportControlProtocol::scroll ()
 {
+       float m = 1.0;
        if (_datawheel < WheelDirectionThreshold) {
-               ScrollTimeline (0.2);
+               m = 1.0;
        } else {
-               ScrollTimeline (-0.2);
+               m = -1.0;
+       }
+       switch(wheel_increment) {
+               case WheelIncrScreen: ScrollTimeline (0.2*m); break;
+               default: break; // other modes unimplemented as yet
        }
 }
 
@@ -1346,41 +1663,47 @@ TranzportControlProtocol::scrub ()
        struct timeval now;
        struct timeval delta;
        int dir;
-       
+
        gettimeofday (&now, 0);
-       
+
        if (_datawheel < WheelDirectionThreshold) {
                dir = 1;
        } else {
                dir = -1;
        }
-       
+
        if (dir != last_wheel_dir) {
                /* changed direction, start over */
                speed = 0.1f;
        } else {
                if (timerisset (&last_wheel_motion)) {
-                       
+
                        timersub (&now, &last_wheel_motion, &delta);
-                       
+
                        /* 10 clicks per second => speed == 1.0 */
-                       
+
                        speed = 100000.0f / (delta.tv_sec * 1000000 + delta.tv_usec);
-                       
+
                } else {
-                       
+
                        /* start at half-speed and see where we go from there */
-                       
+
                        speed = 0.5f;
                }
        }
-       
+
        last_wheel_motion = now;
        last_wheel_dir = dir;
-       
+
        set_transport_speed (speed * dir);
 }
 
+void
+TranzportControlProtocol::config ()
+{
+  // FIXME
+}
+
 void
 TranzportControlProtocol::shuttle ()
 {
@@ -1453,6 +1776,10 @@ TranzportControlProtocol::next_wheel_shift_mode ()
                break;
        case WheelShiftMaster:
                wheel_shift_mode = WheelShiftGain;
+               break;
+       case WheelShiftMarker: // Not done yet, disabled
+               wheel_shift_mode = WheelShiftGain;
+               break;
        }
 
        show_wheel_mode ();
@@ -1495,36 +1822,48 @@ TranzportControlProtocol::show_wheel_mode ()
        string text;
 
        switch (wheel_mode) {
-       case WheelTimeline:
-               text = "Time";
-               break;
-       case WheelScrub:
-               text = "Scrb";
-               break;
-       case WheelShuttle:
-               text = "Shtl";
-               break;
+               case WheelTimeline:
+                       text = "Time";
+                       break;
+               case WheelScrub:
+                       text = "Scrb";
+                       break;
+               case WheelShuttle:
+                       text = "Shtl";
+                       break;
        }
 
        switch (wheel_shift_mode) {
-       case WheelShiftGain:
-               text += ":Gain";
-               break;
+               case WheelShiftGain:
+                       text += ":Gain";
+                       break;
 
-       case WheelShiftPan:
-               text += ":Pan";
-               break;
+               case WheelShiftPan:
+                       text += ":Pan ";
+                       break;
 
-       case WheelShiftMaster:
-               text += ":Mstr";
-               break;
+               case WheelShiftMaster:
+                       text += ":Mstr";
+                       break;
+
+               case WheelShiftMarker:
+                       text += ":Mrkr";
+                       break;
        }
-       
+
        print (1, 0, text.c_str());
 }
 
+// Was going to keep state around saying to retry or not
+// haven't got to it yet, still not sure it's a good idea
+
+void
+TranzportControlProtocol::print (int row, int col, const char *text) {
+       print_noretry(row,col,text);
+}
+
 void
-TranzportControlProtocol::print (int row, int col, const char *text)
+TranzportControlProtocol::print_noretry (int row, int col, const char *text)
 {
        int cell;
        uint32_t left = strlen (text);
@@ -1564,7 +1903,7 @@ TranzportControlProtocol::print (int row, int col, const char *text)
 
                /* copy current cell contents into tmp */
                
-               memcpy (tmp, &pending_screen[row][base_col], 4);
+               memcpy (tmp, &screen_pending[row][base_col], 4);
                
                /* overwrite with new text */
                
@@ -1574,7 +1913,7 @@ TranzportControlProtocol::print (int row, int col, const char *text)
                
                /* copy it back to pending */
                
-               memcpy (&pending_screen[row][base_col], tmp, 4);
+               memcpy (&screen_pending[row][base_col], tmp, 4);
                
                text += tocopy;
                left -= tocopy;
@@ -1595,3 +1934,17 @@ TranzportControlProtocol::set_state (const XMLNode& node)
 {
        return 0;
 }
+
+int
+TranzportControlProtocol::save (char *name) 
+{
+       // Presently unimplemented
+       return 0;
+}
+
+int
+TranzportControlProtocol::load (char *name) 
+{
+       // Presently unimplemented
+       return 0;
+}
index e5193a761cdfa3d71306b034d5b6ceb672f9899a..f13e4a3a44d57cc7300ddbb87b6c8e5c8ba846f3 100644 (file)
@@ -1,3 +1,4 @@
+
 #ifndef ardour_tranzport_control_protocol_h
 #define ardour_tranzport_control_protocol_h
 
@@ -72,7 +73,8 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
        enum WheelShiftMode {
                WheelShiftGain,
                WheelShiftPan,
-               WheelShiftMaster
+               WheelShiftMaster,
+               WheelShiftMarker
        };
                
        enum WheelMode {
@@ -81,29 +83,68 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
                WheelShuttle
        };
 
+       // FIXME - look at gtk2_ardour for snap settings
+
+       enum WheelIncrement {
+              WheelIncrSlave,
+              WheelIncrScreen,
+              WheelIncrSample,
+              WheelIncrBeat,
+              WheelIncrBar,
+              WheelIncrSecond,
+              WheelIncrMinute
+       };
+         
        enum DisplayMode {
                DisplayNormal,
-               DisplayBigMeter
+               DisplayRecording,
+               DisplayRecordingMeter,
+               DisplayBigMeter,
+               DisplayConfig,
+               DisplayBling,
+               DisplayBlingMeter
+       };
+
+       enum BlingMode {
+               BlingOff,
+               BlingKit,
+               BlingRotating,
+               BlingPairs,
+               BlingRows,
+               BlingFlashAll
        };
        
        pthread_t       thread;
        uint32_t        buttonmask;
        uint32_t        timeout;
+       uint32_t        inflight;
        uint8_t        _datawheel;
        uint8_t        _device_status;
-       usb_dev_handle* udev;
-
        uint32_t        current_track_id;
        WheelMode       wheel_mode;
        WheelShiftMode  wheel_shift_mode;
        DisplayMode     display_mode;
+       BlingMode       bling_mode;
+       WheelIncrement  wheel_increment;
+       usb_dev_handle* udev;
+
        ARDOUR::gain_t  gain_fraction;
 
        Glib::Mutex update_lock;
-       char current_screen[2][20];
-       char pending_screen[2][20];
-       bool lights[7];
-       bool pending_lights[7];
+
+       bool screen_invalid[2][20];
+       char screen_current[2][20];
+       char screen_pending[2][20];
+       char screen_flash[2][20];
+
+       bool lights_invalid[7];
+       bool lights_current[7];
+       bool lights_pending[7];
+       bool lights_flash[7];
+
+       uint32_t       last_bars;
+       uint32_t       last_beats;
+       uint32_t       last_ticks;
 
        bool           last_negative;
        uint32_t       last_hrs;
@@ -119,28 +160,94 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
        Glib::Mutex io_lock;
 
        int open ();
-       int read (uint32_t timeout_override = 0);
+       int read (uint8_t *buf,uint32_t timeout_override = 0);
        int write (uint8_t* cmd, uint32_t timeout_override = 0);
+       int write_noretry (uint8_t* cmd, uint32_t timeout_override = 0);
        int close ();
+       int save(char *name = "default");
+       int load(char *name = "default");
+       void print (int row, int col, const char* text);
+       void print_noretry (int row, int col, const char* text);
+
+       int rtpriority_set(int priority = 52);
+       int rtpriority_unset(int priority = 0);
 
        int open_core (struct usb_device*);
 
+       static void* _monitor_work (void* arg);
+       void* monitor_work ();
+
+       int process (uint8_t *);
+       int update_state();
+       void invalidate();
+       int flush();
+       // bool isuptodate(); // think on this. It seems futile to update more than 30/sec
+
+       // A screen is a cache of what should be on the lcd
+
+       void screen_init();
+       void screen_validate();
+       void screen_invalidate();
+       int  screen_flush();
+       void screen_clear();
+       // bool screen_isuptodate(); // think on this - 
+
+       // Commands to write to the lcd 
+
+       int  lcd_init();
+        bool lcd_damage();
+       bool lcd_isdamaged();
+
+        bool lcd_damage(int row, int col = 0, int length = 20);
+       bool lcd_isdamaged(int row, int col = 0, int length = 20);
+
+       int  lcd_flush();
+       int  lcd_write(uint8_t* cmd, uint32_t timeout_override = 0); // pedantic alias for write
+       void lcd_fill (uint8_t fill_char);
        void lcd_clear ();
-       void print (int row, int col, const char* text);
+       void lcd_print (int row, int col, const char* text);
+       void lcd_print_noretry (int row, int col, const char* text);
+
+       // Commands to write to the lights
+       // FIXME - on some devices lights can have intensity and colors
+
+       void lights_init();
+       void lights_validate();
+       void lights_invalidate();
+       void light_validate(LightID light);
+       void light_invalidate(LightID light);
+       int  lights_flush();
+       int  lights_write(uint8_t* cmd,uint32_t timeout_override = 0); // pedantic alias to write
+
+       // a cache of what should be lit
+
+       void lights_off ();
+       void lights_on ();
+       int  light_set(LightID, bool offon = true);
        int  light_on (LightID);
        int  light_off (LightID);
-       void lights_off ();
+
+       // some modes for the lights, should probably be renamed
+
+       int  lights_show_normal();
+       int  lights_show_recording();
+       int  lights_show_tempo();
+       int  lights_show_bling();
 
        void enter_big_meter_mode ();
        void enter_normal_display_mode ();
+       void enter_config_mode();
+       void enter_recording_mode();
+       void enter_bling_mode();
 
        void next_display_mode ();
-
        void normal_update ();
 
        void show_current_track ();
        void show_track_gain ();
        void show_transport_time ();
+       void show_bbt (nframes_t where);        
+       void show_smpte (nframes_t where);
        void show_wheel_mode ();
        void show_gain ();
        void show_pan ();
@@ -150,6 +257,7 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
        void scrub ();
        void scroll ();
        void shuttle ();
+       void config ();
 
        void next_wheel_mode ();
        void next_wheel_shift_mode ();
@@ -162,8 +270,6 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
        void step_pan_right ();
        void step_pan_left ();
 
-       static void* _monitor_work (void* arg);
-       void* monitor_work ();
 
        void button_event_battery_press (bool shifted);
        void button_event_battery_release (bool shifted);
@@ -206,8 +312,8 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
        void button_event_record_press (bool shifted);
        void button_event_record_release (bool shifted);
 
-       int process (uint8_t *);
-       int update_state();
+       // new api
+       void button_event_mute (bool pressed, bool shifted);
 };