/*
- Copyright (C) 2006 Paul Davis
+ Copyright (C) 2006 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: tranzport_control_protocol.cc 1252 2006-12-29 19:13:18Z sampo $
*/
-/* Design notes: The tranzport is a unique device, basically a
- 20 lcd gui with 22 shift keys and 8 blinking lights.
+/* 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.
+ 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
+ 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.
#include <sys/time.h>
#include <errno.h>
-#include <pbd/pthread_utils.h>
+#include "pbd/pthread_utils.h"
-#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>
+#include "ardour/route.h"
+#include "ardour/audio_track.h"
+#include "ardour/tempo.h"
+#include "ardour/location.h"
+#include "ardour/dB.h"
#include "tranzport_control_protocol.h"
using namespace sigc;
using namespace PBD;
-#include "i18n.h"
+#include "pbd/i18n.h"
-#include <pbd/abstract_ui.cc>
+#include "pbd/abstract_ui.cc"
BaseUI::RequestType LEDChange = BaseUI::new_request_type ();
BaseUI::RequestType Print = BaseUI::new_request_type ();
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
+static inline double
gain_to_slider_position (ARDOUR::gain_t g)
{
if (g == 0) return 0;
}
-static inline ARDOUR::gain_t
+static inline ARDOUR::gain_t
slider_position_to_gain (double pos)
{
/* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
print(1,0,"!Peace through Music!");
}
-void TranzportControlProtocol::light_validate (LightID light)
+void TranzportControlProtocol::light_validate (LightID light)
{
lights_invalid[light] = 0;
}
-void TranzportControlProtocol::light_invalidate (LightID light)
+void TranzportControlProtocol::light_invalidate (LightID light)
{
lights_invalid[light] = 1;
}
-void TranzportControlProtocol::lights_validate ()
+void TranzportControlProtocol::lights_validate ()
{
- memset (lights_invalid, 0, sizeof (lights_invalid));
+ memset (lights_invalid, 0, sizeof (lights_invalid));
}
-void TranzportControlProtocol::lights_invalidate ()
+void TranzportControlProtocol::lights_invalidate ()
{
- memset (lights_invalid, 1, sizeof (lights_invalid));
+ 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_invalid[i] = lights_current[i] =
lights_pending[i] = lights_flash[i] = false;
}
}
// 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])) {
+ // if (light_set(i, lights_pending[i])) {
// return i-1;
- // }
+ // }
// }
//}
if ((lights_pending[LightRecord] != lights_current[LightRecord]) || lights_invalid[LightRecord]) {
TranzportControlProtocol::screen_clear ()
{
const char *blank = " ";
- print(0,0,blank);
+ print(0,0,blank);
print(1,0,blank);
}
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])
+ 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];
+ 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[5] = screen_pending[row][col_base+2];
cmd[6] = screen_pending[row][col_base+3];
cmd[7] = 0x00;
if(write(cmd) != 0) {
/* try to update this cell on the next go-round */
#if DEBUG_TRANZPORT > 4
- printf("usb screen update failed for some reason... why? \ncmd and data were %02x %02x %02x %02x %02x %02x %02x %02x\n",
- cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]);
+ 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;
+ pending += 1;
// Shouldn't need to do this
- // screen_invalid[row][col_base] = screen_invalid[row][col_base+1] =
+ // screen_invalid[row][col_base] = screen_invalid[row][col_base+1] =
// screen_invalid[row][col_base+2] = screen_invalid[row][col_base+3] = true;
} else {
/* successful write: copy to current cached display */
- screen_invalid[row][col_base] = screen_invalid[row][col_base+1] =
+ screen_invalid[row][col_base] = 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);
}
// Tranzport specific
-void TranzportControlProtocol::invalidate()
+void TranzportControlProtocol::invalidate()
{
lcd_damage(); lights_invalidate(); screen_invalidate(); // one of these days lcds can be fine but screens not
}
close ();
_active = false;
cerr << "End tranzport shutdown\n";
- }
+ }
}
return 0;
if (route_table[0]) {
gain_t g = route_get_gain (0);
if ((g != last_track_gain) || lcd_isdamaged(0,9,8)) {
- char buf[16];
+ char buf[16];
snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0)));
- print (0, 9, buf);
+ print (0, 9, buf);
last_track_gain = g;
}
} else {
- print (0, 9, " ");
+ print (0, 9, " ");
}
}
enter_big_meter_mode();
break;
- case DisplayConfig:
+ case DisplayConfig:
case DisplayBling:
case DisplayBlingMeter:
enter_normal_display_mode();
def = (db + 20.0f) * 2.5f + 50.0f;
}
- /* 115 is the deflection %age that would be
+ /* 115 is the deflection %age that would be
when db=6.0. this is an arbitrary
endpoint for our scaling.
*/
if (route_table[0] == 0) {
// Principle of least surprise
print (0, 0, "No audio to meter!!!");
- print (1, 0, "Select another track");
+ print (1, 0, "Select another track");
return;
}
*/
uint32_t fill = (uint32_t) floor (fraction * 40);
- char buf[21];
+ char buf[22];
uint32_t i;
if (fill == last_meter_fill) {
for (i = 0; i < fill; ++i) {
buf[i] = 0x07; /* tranzport special code for 4 quadrant LCD block */
- }
+ }
/* add a possible half-step */
}
void
-TranzportControlProtocol::show_bbt (nframes_t where)
-{
+TranzportControlProtocol::show_bbt (framepos_t where)
+{
if ((where != last_where) || lcd_isdamaged(1,9,8)) {
char buf[16];
- BBT_Time bbt;
+ Timecode::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;
void
TranzportControlProtocol::show_transport_time ()
{
- nframes_t where = session->transport_frame();
- show_bbt(where);
-}
+ show_bbt (session->transport_frame ());
+}
void
-TranzportControlProtocol::show_smpte (nframes_t where)
+TranzportControlProtocol::show_smpte (framepos_t where)
{
if ((where != last_where) || lcd_isdamaged(1,9,10)) {
print (1, 15, buf);
sprintf (buf, "%02" PRIu32, smpte.frames);
- print_noretry (1, 18, buf);
+ print_noretry (1, 18, buf);
last_where = where;
}
return static_cast<TranzportControlProtocol*>(arg)->monitor_work ();
}
-// I note that these usb specific open, close, probe, read routines are basically
+// 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
for(dev = bus->devices; dev; dev = dev->next) {
if (dev->descriptor.idVendor == VENDORID && dev->descriptor.idProduct == PRODUCTID) {
- return true;
+ return true;
}
}
}
error << _("Tranzport: cannot open USB transport") << endmsg;
return -1;
}
-
+
if (usb_claim_interface (udev, 0) < 0) {
error << _("Tranzport: cannot claim USB interface") << endmsg;
usb_close (udev);
return ret;
}
-int TranzportControlProtocol::read(uint8_t *buf, uint32_t timeout_override)
+int TranzportControlProtocol::read(uint8_t *buf, uint32_t timeout_override)
{
int val;
// Get smarter about handling usb errors soon. Like disconnect
val = usb_interrupt_read (udev, READ_ENDPOINT, (char *) buf, 8, 10);
// pthread_testcancel();
return val;
-}
+}
+
-
int
TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override)
{
return 0;
-}
+}
int
TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
int val;
int retry = 0;
if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
-
+
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);
}
return (write_noretry(cmd,timeout_override));
#endif
-}
+}
#else
#error Kernel API not defined yet for Tranzport
// 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
+// 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
+// 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:");
+screen_flash.print(1,0,"Nextup:");
if(flash_messages && lcd.getactive() != screen_flash) lcd.setactive(screen_flash,2000);
{
int pending = 0;
if(!(pending = lights_flush())) {
- pending = screen_flush();
- }
+ pending = screen_flush();
+ }
return pending;
}
// doing these functions made me realize that screen_invalid should be lcd_isdamaged FIXME soon
-bool TranzportControlProtocol::lcd_damage()
+bool TranzportControlProtocol::lcd_damage()
{
screen_invalidate();
return true;
{
bool result = false;
int endcol = col+length-1;
- if((endcol > 19)) { endcol = 19; }
+ 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;
// Gotta switch to bitfields, this is collossally dumb
// Still working on the layering, arguably screen_invalid should be lcd_invalid
-bool TranzportControlProtocol::lcd_isdamaged ()
+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
+#if DEBUG_TRANZPORT > 5
printf("row: %d,col: %d is damaged, should redraw it\n", r,c);
#endif
return true;
{
bool result = 0;
int endcol = col+length;
- if((endcol > 19)) { endcol = 19; }
+ 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
+#if DEBUG_TRANZPORT > 5
printf("row: %d,col: %d is damaged, should redraw it\n", row,c);
#endif
return true;
int
TranzportControlProtocol::lcd_flush ()
{
- return 0;
+ return 0;
}
-int
+int
TranzportControlProtocol::lcd_write(uint8_t* cmd, uint32_t timeout_override)
{
return write(cmd,timeout_override);
}
-void
-TranzportControlProtocol::lcd_fill (uint8_t fill_char)
+void
+TranzportControlProtocol::lcd_fill (uint8_t fill_char)
{
}
-void
-TranzportControlProtocol::lcd_print (int row, int col, const char* text)
+void
+TranzportControlProtocol::lcd_print (int row, int col, const char* text)
{
print(row,col,text);
}
void
TranzportControlProtocol::lights_on ()
{
- lights_pending[LightRecord] = lights_pending[LightTrackrec] =
- lights_pending[LightTrackmute] = lights_pending[LightTracksolo] =
- lights_pending[LightAnysolo] = lights_pending[LightLoop] =
+ lights_pending[LightRecord] = lights_pending[LightTrackrec] =
+ lights_pending[LightTrackmute] = lights_pending[LightTracksolo] =
+ lights_pending[LightAnysolo] = lights_pending[LightLoop] =
lights_pending[LightPunch] = true;
}
void
TranzportControlProtocol::lights_off ()
{
- lights_pending[LightRecord] = lights_pending[LightTrackrec] =
- lights_pending[LightTrackmute] = lights_pending[LightTracksolo] =
- lights_pending[LightAnysolo] = lights_pending[LightLoop] =
+ lights_pending[LightRecord] = lights_pending[LightTrackrec] =
+ lights_pending[LightTrackmute] = lights_pending[LightTracksolo] =
+ lights_pending[LightAnysolo] = lights_pending[LightLoop] =
lights_pending[LightPunch] = false;
}
}
}
-int TranzportControlProtocol::rtpriority_set(int priority)
+int TranzportControlProtocol::rtpriority_set(int priority)
{
struct sched_param rtparam;
int err;
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
+ // 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;
}
// Running with realtime privs is bad when you have problems
-int TranzportControlProtocol::rtpriority_unset(int priority)
+int TranzportControlProtocol::rtpriority_unset(int priority)
{
struct sched_param rtparam;
int err;
memset (&rtparam, 0, sizeof (rtparam));
- rtparam.sched_priority = priority;
+ rtparam.sched_priority = priority;
if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
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;
}
uint8_t offline = 0;
- PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
+ PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("Tranzport"));
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
next_track ();
if (_device_status == STATUS_OFFLINE) {
first_time = true;
- if(offline++ == 1) {
+ if(offline++ == 1) {
cerr << "Transport has gone offline\n";
}
- } else {
+ } else {
offline = 0; // hate writing this
}
// pending = flush();
if(pending == 0) {
- pending = flush();
+ pending = flush();
} else {
if(inflight > 0) {
pending = --inflight; // we just did a whole bunch of writes so wait
}
}
// pending = 0;
- }
+ }
}
return (void*) 0;
}
-int TranzportControlProtocol::lights_show_recording()
+int TranzportControlProtocol::lights_show_recording()
{
// FIXME, flash recording light when recording and transport is moving
return lights_show_normal();
// gotta do bling next!
-int TranzportControlProtocol::lights_show_bling()
+int TranzportControlProtocol::lights_show_bling()
{
switch (bling_mode) {
case BlingOff: break;
return 0;
}
-int TranzportControlProtocol::lights_show_normal()
+int TranzportControlProtocol::lights_show_normal()
{
/* Track only */
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[LightTrackmute] = route_get_muted(0);
lights_pending[LightTracksolo] = route_get_soloed(0);
} else {
lights_pending[LightTrackrec] = false;
/* Global settings */
- lights_pending[LightLoop] = session->get_play_loop();
+ 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();
return 0;
}
-int TranzportControlProtocol::lights_show_tempo()
+int TranzportControlProtocol::lights_show_tempo()
{
- // someday soon fiddle with the lights based on the tempo
+ // someday soon fiddle with the lights based on the tempo
return lights_show_normal();
}
case DisplayRecording:
lights_show_recording();
- normal_update();
+ normal_update();
break;
case DisplayRecordingMeter:
lights_show_recording();
- show_meter();
+ show_meter();
break;
case DisplayBling:
// SHIFT + STOP + PLAY for bling mode?
// if (button_changes & ButtonPlay & ButtonStop) {
- // bling_mode_toggle();
+ // bling_mode_toggle();
// } or something like that
TRANZPORT_BUTTON_HANDLER(button_event_battery,ButtonBattery);
lcd_clear();
last_where += 1; /* force time redisplay */
last_track_gain = FLT_MAX;
- normal_update(); // redraw_screen();
+ normal_update(); // redraw_screen();
}
}
if (session->transport_speed() < 0) {
session->request_transport_speed (1.0);
} else {
- session->request_transport_speed (session->transport_speed() + 0.1);
+ session->request_transport_speed_nonzero (session->transport_speed() + 0.1);
}
} else {
if (session->transport_speed() > 0) {
session->request_transport_speed (-1.0);
} else {
- session->request_transport_speed (session->transport_speed() - 0.1);
+ session->request_transport_speed_nonzero (session->transport_speed() - 0.1);
}
}
}
if (gain_fraction > 2.0) {
gain_fraction = 2.0;
}
-
+
route_set_gain (0, slider_position_to_gain (gain_fraction));
}
if (gain_fraction < 0.0) {
gain_fraction = 0.0;
}
-
+
route_set_gain (0, slider_position_to_gain (gain_fraction));
}
uint32_t left = strlen (text);
char tmp[5];
int base_col;
-
+
if (row < 0 || row > 1) {
return;
}
int offset = col % 4;
/* copy current cell contents into tmp */
-
+
memcpy (tmp, &screen_pending[row][base_col], 4);
-
+
/* overwrite with new text */
-
+
uint32_t tocopy = min ((4U - offset), left);
-
+
memcpy (tmp+offset, text, tocopy);
-
+
/* copy it back to pending */
-
+
memcpy (&screen_pending[row][base_col], tmp, 4);
-
+
text += tocopy;
left -= tocopy;
col += tocopy;
}
-}
+}
XMLNode&
-TranzportControlProtocol::get_state ()
+TranzportControlProtocol::get_state ()
{
- XMLNode* node = new XMLNode (X_("Protocol"));
- node->add_property (X_("name"), _name);
- return *node;
+ return ControlProtocol::get_state();
}
int
}
int
-TranzportControlProtocol::save (char *name)
+TranzportControlProtocol::save (char *name)
{
// Presently unimplemented
return 0;
}
int
-TranzportControlProtocol::load (char *name)
+TranzportControlProtocol::load (char *name)
{
// Presently unimplemented
return 0;