summaryrefslogtreecommitdiff
path: root/libs/surfaces/tranzport
diff options
context:
space:
mode:
authorSampo Savolainen <v2@iki.fi>2006-12-29 19:13:18 +0000
committerSampo Savolainen <v2@iki.fi>2006-12-29 19:13:18 +0000
commit34a813c1a13263c2f37b945771093f428efb9692 (patch)
tree43e88b1f2389d98186f22491bce48050128bfef5 /libs/surfaces/tranzport
parent7e8a7c9113887fbd079340e9b55afb8a92279791 (diff)
Mike Taht's great work on the tranzport driver.
git-svn-id: svn://localhost/ardour2/trunk@1252 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/surfaces/tranzport')
-rw-r--r--libs/surfaces/tranzport/tranzport_control_protocol.cc1455
-rw-r--r--libs/surfaces/tranzport/tranzport_control_protocol.h138
2 files changed, 1026 insertions, 567 deletions
diff --git a/libs/surfaces/tranzport/tranzport_control_protocol.cc b/libs/surfaces/tranzport/tranzport_control_protocol.cc
index ea85a32a77..7a88499193 100644
--- a/libs/surfaces/tranzport/tranzport_control_protocol.cc
+++ b/libs/surfaces/tranzport/tranzport_control_protocol.cc
@@ -18,6 +18,31 @@
$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;
+}
+
+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(lights)/sizeof(lights[0]); ++i) {
- 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;
+}
- for (uint32_t i = 0; i < sizeof(pending_lights)/sizeof(pending_lights[0]); ++i) {
- pending_lights[i] = false;
+// Screen specific commands
+
+void
+TranzportControlProtocol::screen_clear ()
+{
+ const char *blank = " ";
+ print(0,0,blank);
+ print(1,0,blank);
+}
+
+void TranzportControlProtocol::screen_invalidate ()
+{
+ 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
}
-TranzportControlProtocol::~TranzportControlProtocol ()
+void TranzportControlProtocol::screen_validate ()
{
- set_active (false);
}
-bool
-TranzportControlProtocol::probe ()
+void TranzportControlProtocol::screen_init ()
{
- struct usb_bus *bus;
- struct usb_device *dev;
+ screen_invalidate();
+}
- usb_init();
- usb_find_busses();
- usb_find_devices();
+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;
- for (bus = usb_busses; bus; bus = bus->next) {
+ 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;
- for(dev = bus->devices; dev; dev = dev->next) {
- if (dev->descriptor.idVendor == VENDORID && dev->descriptor.idProduct == PRODUCTID) {
- return 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,7 +549,7 @@ TranzportControlProtocol::show_meter ()
}
/* print() requires this */
-
+
buf[21] = '\0';
print (0, 0, buf);
@@ -318,17 +557,56 @@ TranzportControlProtocol::show_meter ()
}
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;
+ if(offline++ == 1) {
+ cerr << "Transport has gone offline\n";
+ }
+ } else {
+ offline = 0; // hate writing this
}
- pthread_testcancel();
- val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 10);
- pthread_testcancel();
+ val = read(buf);
if (val == 8) {
process (buf);
}
+#if DEBUG_TRANZPORT > 2
+ if(inflight > 1) printf("Inflight: %d\n", inflight);
+#endif
+
+
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 ();
- }
- }
-
- return (void*) 0;
-}
-
-int
-TranzportControlProtocol::update_state ()
-{
- int row;
- int col_base;
- int col;
- int cell;
- /* do the text updates */
-
- switch (display_mode) {
- case DisplayBigMeter:
- show_meter ();
- break;
-
- case DisplayNormal:
- normal_update ();
- break;
- }
-
- /* next: flush LCD */
-
- 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);
- }
-
- /* 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,42 +1663,48 @@ 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 ()
{
if (_datawheel < WheelDirectionThreshold) {
@@ -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)
+TranzportControlProtocol::print (int row, int col, const char *text) {
+ print_noretry(row,col,text);
+}
+
+void
+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;
+}
diff --git a/libs/surfaces/tranzport/tranzport_control_protocol.h b/libs/surfaces/tranzport/tranzport_control_protocol.h
index e5193a761c..f13e4a3a44 100644
--- a/libs/surfaces/tranzport/tranzport_control_protocol.h
+++ b/libs/surfaces/tranzport/tranzport_control_protocol.h
@@ -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);
};