diff options
author | Sampo Savolainen <v2@iki.fi> | 2006-12-29 19:13:18 +0000 |
---|---|---|
committer | Sampo Savolainen <v2@iki.fi> | 2006-12-29 19:13:18 +0000 |
commit | 34a813c1a13263c2f37b945771093f428efb9692 (patch) | |
tree | 43e88b1f2389d98186f22491bce48050128bfef5 /libs/surfaces/tranzport | |
parent | 7e8a7c9113887fbd079340e9b55afb8a92279791 (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.cc | 1455 | ||||
-rw-r--r-- | libs/surfaces/tranzport/tranzport_control_protocol.h | 138 |
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 (¤t_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); }; |