2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Design notes: The tranzport is a unique device, basically a
21 20 lcd gui with 22 shift keys and 8 blinking lights.
23 As such it has several unique constraints. The device exerts flow control
24 by having a usb write fail. It is pointless to retry madly at that point,
25 the device is busy, and it's not going to become unbusy very quickly.
27 So writes need to be either "mandatory" or "unreliable", and therein
28 lies the rub, as the kernel can also drop writes, and missing an
29 interrupt in userspace is also generally bad.
31 It will be good one day, to break the gui, keyboard, and blinking light
32 components into separate parts, but for now, this remains monolithic.
34 A more complex surface might have hundreds of lights and several displays.
39 #define DEFAULT_USB_TIMEOUT 10
41 #define MAX_TRANZPORT_INFLIGHT 4
42 #define DEBUG_TRANZPORT 0
43 #define HAVE_TRANZPORT_KERNEL_DRIVER 0
49 #define __STDC_FORMAT_MACROS
55 #include "pbd/pthread_utils.h"
57 #include "ardour/route.h"
58 #include "ardour/audio_track.h"
59 #include "ardour/tempo.h"
60 #include "ardour/location.h"
61 #include "ardour/dB.h"
63 #include "tranzport_control_protocol.h"
65 using namespace ARDOUR;
72 #include "pbd/abstract_ui.cc"
74 BaseUI::RequestType LEDChange = BaseUI::new_request_type ();
75 BaseUI::RequestType Print = BaseUI::new_request_type ();
76 BaseUI::RequestType SetCurrentTrack = BaseUI::new_request_type ();
78 /* Base Tranzport cmd strings */
80 static const uint8_t cmd_light_on[] = { 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00 };
81 static const uint8_t cmd_light_off[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
82 static const uint8_t cmd_write_screen[] = { 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00 };
85 gain_to_slider_position (ARDOUR::gain_t g)
88 return pow((6.0*log(g)/log(2.0)+192.0)/198.0, 8.0);
92 static inline ARDOUR::gain_t
93 slider_position_to_gain (double pos)
95 /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
96 if (pos == 0.0) return 0;
97 return pow (2.0,(sqrt(sqrt(sqrt(pos)))*198.0-192.0)/6.0);
101 TranzportControlProtocol::TranzportControlProtocol (Session& s)
102 : ControlProtocol (s, X_("Tranzport"))
104 /* tranzport controls one track at a time */
106 set_route_table_size (1);
107 timeout = 6000; // what is this for?
110 _device_status = STATUS_OFFLINE;
112 current_track_id = 0;
113 last_where = max_samples;
114 wheel_mode = WheelTimeline;
115 wheel_shift_mode = WheelShiftGain;
116 wheel_increment = WheelIncrScreen;
117 bling_mode = BlingOff;
118 timerclear (&last_wheel_motion);
120 last_track_gain = FLT_MAX;
121 display_mode = DisplayNormal;
126 print(0,0,"!!Welcome to Ardour!!");
127 print(1,0,"!Peace through Music!");
130 void TranzportControlProtocol::light_validate (LightID light)
132 lights_invalid[light] = 0;
135 void TranzportControlProtocol::light_invalidate (LightID light)
137 lights_invalid[light] = 1;
140 void TranzportControlProtocol::lights_validate ()
142 memset (lights_invalid, 0, sizeof (lights_invalid));
145 void TranzportControlProtocol::lights_invalidate ()
147 memset (lights_invalid, 1, sizeof (lights_invalid));
150 void TranzportControlProtocol::lights_init()
152 for (uint32_t i = 0; i < sizeof(lights_current)/sizeof(lights_current[0]); i++) {
153 lights_invalid[i] = lights_current[i] =
154 lights_pending[i] = lights_flash[i] = false;
161 TranzportControlProtocol::lights_flush ()
163 if ( _device_status == STATUS_OFFLINE) { return (0); }
165 // Figure out iterators one day soon
166 // for (LightID i = i.start(), i = i.end(); i++) {
167 // if (lights_pending[i] != lights_current[i] || lights_invalid[i]) {
168 // if (light_set(i, lights_pending[i])) {
173 if ((lights_pending[LightRecord] != lights_current[LightRecord]) || lights_invalid[LightRecord]) {
174 if (light_set(LightRecord,lights_pending[LightRecord])) {
178 if ((lights_pending[LightTrackrec] != lights_current[LightTrackrec]) || lights_invalid[LightTrackrec]) {
179 if (light_set(LightTrackrec,lights_pending[LightTrackrec])) {
184 if ((lights_pending[LightTrackmute] != lights_current[LightTrackmute]) || lights_invalid[LightTrackmute]) {
185 if (light_set(LightTrackmute,lights_pending[LightTrackmute])) {
190 if ((lights_pending[LightTracksolo] != lights_current[LightTracksolo]) || lights_invalid[LightTracksolo]) {
191 if (light_set(LightTracksolo,lights_pending[LightTracksolo])) {
195 if ((lights_pending[LightAnysolo] != lights_current[LightAnysolo]) || lights_invalid[LightAnysolo]) {
196 if (light_set(LightAnysolo,lights_pending[LightAnysolo])) {
200 if ((lights_pending[LightLoop] != lights_current[LightLoop]) || lights_invalid[LightLoop]) {
201 if (light_set(LightLoop,lights_pending[LightLoop])) {
205 if ((lights_pending[LightPunch] != lights_current[LightPunch]) || lights_invalid[LightPunch]) {
206 if (light_set(LightPunch,lights_pending[LightPunch])) {
214 // Screen specific commands
217 TranzportControlProtocol::screen_clear ()
219 const char *blank = " ";
224 void TranzportControlProtocol::screen_invalidate ()
226 for(int row = 0; row < 2; row++) {
227 for(int col = 0; col < 20; col++) {
228 screen_invalid[row][col] = true;
229 screen_current[row][col] = 0x7f;
230 screen_pending[row][col] = ' ';
231 // screen_flash[row][col] = ' ';
234 // memset (&screen_invalid, 1, sizeof(screen_invalid));
235 // memset (&screen_current, 0x7F, sizeof (screen_current)); // fill cache with a character we otherwise never use
238 void TranzportControlProtocol::screen_validate ()
242 void TranzportControlProtocol::screen_init ()
248 TranzportControlProtocol::screen_flush ()
250 int cell = 0, row, col_base, col, pending = 0;
251 if ( _device_status == STATUS_OFFLINE) { return (-1); }
253 for (row = 0; row < 2 && pending == 0; row++) {
254 for (col_base = 0, col = 0; col < 20 && pending == 0; ) {
255 if ((screen_pending[row][col] != screen_current[row][col])
256 || screen_invalid[row][col]) {
258 /* something in this cell is different, so dump the cell to the device. */
264 cmd[3] = screen_pending[row][col_base];
265 cmd[4] = screen_pending[row][col_base+1];
266 cmd[5] = screen_pending[row][col_base+2];
267 cmd[6] = screen_pending[row][col_base+3];
270 if(write(cmd) != 0) {
271 /* try to update this cell on the next go-round */
272 #if DEBUG_TRANZPORT > 4
273 printf("usb screen update failed for some reason... why? \ncmd and data were %02x %02x %02x %02x %02x %02x %02x %02x\n",
274 cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]);
277 // Shouldn't need to do this
278 // screen_invalid[row][col_base] = screen_invalid[row][col_base+1] =
279 // screen_invalid[row][col_base+2] = screen_invalid[row][col_base+3] = true;
282 /* successful write: copy to current cached display */
283 screen_invalid[row][col_base] = screen_invalid[row][col_base+1] =
284 screen_invalid[row][col_base+2] = screen_invalid[row][col_base+3] = false;
285 memcpy (&screen_current[row][col_base], &screen_pending[row][col_base], 4);
288 /* skip the rest of the 4 character cell since we wrote+copied it already */
298 if (col && col % 4 == 0) {
309 // Tranzport specific
311 void TranzportControlProtocol::invalidate()
313 lcd_damage(); lights_invalidate(); screen_invalidate(); // one of these days lcds can be fine but screens not
316 TranzportControlProtocol::~TranzportControlProtocol ()
323 TranzportControlProtocol::set_active (bool yn)
333 if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) {
340 cerr << "Begin tranzport shutdown\n";
344 for(int x = 0; x < 10 && flush(); x++) { usleep(1000); }
345 pthread_cancel_one (thread);
346 cerr << "Tranzport Thread dead\n";
349 cerr << "End tranzport shutdown\n";
357 TranzportControlProtocol::show_track_gain ()
359 if (route_table[0]) {
360 gain_t g = route_get_gain (0);
361 if ((g != last_track_gain) || lcd_isdamaged(0,9,8)) {
363 snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0)));
373 TranzportControlProtocol::normal_update ()
375 show_current_track ();
376 show_transport_time ();
382 TranzportControlProtocol::next_display_mode ()
384 switch (display_mode) {
387 enter_big_meter_mode();
390 case DisplayBigMeter:
391 enter_normal_display_mode();
394 case DisplayRecording:
395 enter_normal_display_mode();
398 case DisplayRecordingMeter:
399 enter_big_meter_mode();
404 case DisplayBlingMeter:
405 enter_normal_display_mode();
410 // FIXME, these 3 aren't done yet
413 TranzportControlProtocol::enter_recording_mode ()
415 lcd_damage(); // excessive
418 display_mode = DisplayRecording;
422 TranzportControlProtocol::enter_bling_mode ()
427 display_mode = DisplayBling;
431 TranzportControlProtocol::enter_config_mode ()
436 display_mode = DisplayConfig;
441 TranzportControlProtocol::enter_big_meter_mode ()
447 display_mode = DisplayBigMeter;
451 TranzportControlProtocol::enter_normal_display_mode ()
456 display_mode = DisplayNormal;
464 float def = 0.0f; /* Meter deflection %age */
466 if (db < -70.0f) return 0.0f;
467 if (db > 6.0f) return 1.0f;
470 def = (db + 70.0f) * 0.25f;
471 } else if (db < -50.0f) {
472 def = (db + 60.0f) * 0.5f + 2.5f;
473 } else if (db < -40.0f) {
474 def = (db + 50.0f) * 0.75f + 7.5f;
475 } else if (db < -30.0f) {
476 def = (db + 40.0f) * 1.5f + 15.0f;
477 } else if (db < -20.0f) {
478 def = (db + 30.0f) * 2.0f + 30.0f;
479 } else if (db < 6.0f) {
480 def = (db + 20.0f) * 2.5f + 50.0f;
483 /* 115 is the deflection %age that would be
484 when db=6.0. this is an arbitrary
485 endpoint for our scaling.
492 TranzportControlProtocol::show_meter ()
494 // you only seem to get a route_table[0] on moving forward - bug elsewhere
495 if (route_table[0] == 0) {
496 // Principle of least surprise
497 print (0, 0, "No audio to meter!!!");
498 print (1, 0, "Select another track");
502 float level = route_get_peak_input_power (0, 0);
503 float fraction = log_meter (level);
505 /* Someday add a peak bar*/
507 /* we draw using a choice of a sort of double colon-like character ("::") or a single, left-aligned ":".
508 the screen is 20 chars wide, so we can display 40 different levels. compute the level,
509 then figure out how many "::" to fill. if the answer is odd, make the last one a ":"
512 uint32_t fill = (uint32_t) floor (fraction * 40);
516 if (fill == last_meter_fill) {
521 last_meter_fill = fill;
523 bool add_single_level = (fill % 2 != 0);
526 if (fraction > 0.98) {
527 light_on (LightAnysolo);
530 /* add all full steps */
532 for (i = 0; i < fill; ++i) {
533 buf[i] = 0x07; /* tranzport special code for 4 quadrant LCD block */
536 /* add a possible half-step */
538 if (i < 20 && add_single_level) {
539 buf[i] = 0x03; /* tranzport special code for 2 left quadrant LCD block */
543 /* fill rest with space */
545 for (; i < 20; ++i) {
549 /* print() requires this */
558 TranzportControlProtocol::show_bbt (samplepos_t where)
560 if ((where != last_where) || lcd_isdamaged(1,9,8)) {
562 Timecode::BBT_Time bbt;
563 session->tempo_map().bbt_time (where, bbt);
564 sprintf (buf, "%03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
565 last_bars = bbt.bars;
566 last_beats = bbt.beats;
567 last_ticks = bbt.ticks;
570 if(last_ticks < 1960) { print (1, 9, buf); } // save a write so we can do leds
572 // if displaymode is recordmode show beats but not yet
573 lights_pending[LightRecord] = false;
574 lights_pending[LightAnysolo] = false;
576 case 1: if(last_ticks < 500 || last_ticks > 1960) lights_pending[LightRecord] = true; break;
577 default: if(last_ticks < 250) lights_pending[LightAnysolo] = true;
580 // update lights for tempo one day
581 // if (bbt_upper_info_label) {
582 // TempoMap::Metric m (session->tempo_map().metric_at (when));
583 // sprintf (buf, "%-5.2f", m.tempo().beats_per_minute());
584 // bbt_lower_info_label->set_text (buf);
585 // sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor());
586 // bbt_upper_info_label->set_text (buf);
592 TranzportControlProtocol::show_transport_time ()
594 show_bbt (session->transport_sample ());
598 TranzportControlProtocol::show_smpte (samplepos_t where)
600 if ((where != last_where) || lcd_isdamaged(1,9,10)) {
605 session->smpte_time (where, smpte);
607 if (smpte.negative) {
608 sprintf (buf, "-%02" PRIu32 ":", smpte.hours);
610 sprintf (buf, " %02" PRIu32 ":", smpte.hours);
614 sprintf (buf, "%02" PRIu32 ":", smpte.minutes);
617 sprintf (buf, "%02" PRIu32 ":", smpte.seconds);
620 sprintf (buf, "%02" PRIu32, smpte.samples);
621 print_noretry (1, 18, buf);
628 TranzportControlProtocol::_monitor_work (void* arg)
630 return static_cast<TranzportControlProtocol*>(arg)->monitor_work ();
633 // I note that these usb specific open, close, probe, read routines are basically
634 // pure boilerplate and could easily be abstracted elsewhere
636 #if !HAVE_TRANZPORT_KERNEL_DRIVER
639 TranzportControlProtocol::probe ()
642 struct usb_device *dev;
648 for (bus = usb_busses; bus; bus = bus->next) {
650 for(dev = bus->devices; dev; dev = dev->next) {
651 if (dev->descriptor.idVendor == VENDORID && dev->descriptor.idProduct == PRODUCTID) {
661 TranzportControlProtocol::open ()
664 struct usb_device *dev;
670 for (bus = usb_busses; bus; bus = bus->next) {
672 for(dev = bus->devices; dev; dev = dev->next) {
673 if (dev->descriptor.idVendor != VENDORID)
675 if (dev->descriptor.idProduct != PRODUCTID)
677 return open_core (dev);
681 error << _("Tranzport: no device detected") << endmsg;
686 TranzportControlProtocol::open_core (struct usb_device* dev)
688 if (!(udev = usb_open (dev))) {
689 error << _("Tranzport: cannot open USB transport") << endmsg;
693 if (usb_claim_interface (udev, 0) < 0) {
694 error << _("Tranzport: cannot claim USB interface") << endmsg;
700 if (usb_set_configuration (udev, 1) < 0) {
701 cerr << _("Tranzport: cannot configure USB interface") << endmsg;
708 TranzportControlProtocol::close ()
716 if (usb_release_interface (udev, 0) < 0) {
717 error << _("Tranzport: cannot release interface") << endmsg;
721 if (usb_close (udev)) {
722 error << _("Tranzport: cannot close device") << endmsg;
730 int TranzportControlProtocol::read(uint8_t *buf, uint32_t timeout_override)
733 // Get smarter about handling usb errors soon. Like disconnect
734 // pthread_testcancel();
735 val = usb_interrupt_read (udev, READ_ENDPOINT, (char *) buf, 8, 10);
736 // pthread_testcancel();
742 TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override)
745 if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
746 val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
750 printf("usb_interrupt_write failed: %d\n", val);
757 printf("usb_interrupt_write failed: %d\n", val);
768 TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
773 if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
775 while((val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout))!=8 && retry++ < MAX_RETRY) {
776 printf("usb_interrupt_write failed, retrying: %d\n", val);
779 if (retry == MAX_RETRY) {
780 printf("Too many retries on a tranzport write, aborting\n");
784 printf("usb_interrupt_write failed: %d\n", val);
788 printf("usb_interrupt_write failed: %d\n", val);
794 return (write_noretry(cmd,timeout_override));
800 #error Kernel API not defined yet for Tranzport
801 // Something like open(/dev/surface/tranzport/event) for reading and raw for writing)
804 // We have a state "Unknown" - STOP USING SPACES FOR IT - switching to arrow character
805 // We have another state - no_retry. Misleading, as we still retry on the next pass
806 // I think it's pointless to keep no_retry and instead we should throttle writes
807 // We have an "displayed" screen
808 // We always draw into the pending screen, which could be any of several screens
809 // We have an active screen
810 // Print arg - we have
812 // so someday I think we need a screen object.
815 screen_flash.clear();
816 screen_flash.print(0,0,"Undone:"); // Someday pull the undo stack from somewhere
817 screen_flash.print(1,0,"Nextup:");
819 if(flash_messages && lcd.getactive() != screen_flash) lcd.setactive(screen_flash,2000);
821 screen::setactive(screen_name,duration); // duration in ms
827 TranzportControlProtocol::flush ()
830 if(!(pending = lights_flush())) {
831 pending = screen_flush();
836 // doing these functions made me realize that screen_invalid should be lcd_isdamaged FIXME soon
838 bool TranzportControlProtocol::lcd_damage()
844 bool TranzportControlProtocol::lcd_damage (int row, int col, int length)
847 int endcol = col+length-1;
848 if((endcol > 19)) { endcol = 19; }
849 if((row >= 0 && row < 2) && (col >=0 && col < 20)) {
850 for(int c = col; c < endcol; c++) {
851 screen_invalid[row][c] = true;
858 // Gotta switch to bitfields, this is collossally dumb
859 // Still working on the layering, arguably screen_invalid should be lcd_invalid
861 bool TranzportControlProtocol::lcd_isdamaged ()
863 for(int r = 0; r < 2; r++) {
864 for(int c = 0; c < 20; c++) {
865 if(screen_invalid[r][c]) {
866 #if DEBUG_TRANZPORT > 5
867 printf("row: %d,col: %d is damaged, should redraw it\n", r,c);
876 bool TranzportControlProtocol::lcd_isdamaged (int row, int col, int length)
879 int endcol = col+length;
880 if((endcol > 19)) { endcol = 19; }
881 if((row >= 0 && row < 2) && (col >=0 && col < 20)) {
882 for(int c = col; c < endcol; c++) {
883 if(screen_invalid[row][c]) {
884 #if DEBUG_TRANZPORT > 5
885 printf("row: %d,col: %d is damaged, should redraw it\n", row,c);
894 // lcd_clear would be a separate function for a smart display
895 // here it does nothing, but for the sake of completeness it should
896 // probably write the lcd, and while I'm on the topic it should probably
897 // take a row, col, length argument....
900 TranzportControlProtocol::lcd_clear ()
905 // These lcd commands are not universally used yet and may drop out of the api
908 TranzportControlProtocol::lcd_flush ()
914 TranzportControlProtocol::lcd_write(uint8_t* cmd, uint32_t timeout_override)
916 return write(cmd,timeout_override);
920 TranzportControlProtocol::lcd_fill (uint8_t fill_char)
925 TranzportControlProtocol::lcd_print (int row, int col, const char* text)
930 void TranzportControlProtocol::lcd_print_noretry (int row, int col, const char* text)
935 // Lights are buffered
938 TranzportControlProtocol::lights_on ()
940 lights_pending[LightRecord] = lights_pending[LightTrackrec] =
941 lights_pending[LightTrackmute] = lights_pending[LightTracksolo] =
942 lights_pending[LightAnysolo] = lights_pending[LightLoop] =
943 lights_pending[LightPunch] = true;
947 TranzportControlProtocol::lights_off ()
949 lights_pending[LightRecord] = lights_pending[LightTrackrec] =
950 lights_pending[LightTrackmute] = lights_pending[LightTracksolo] =
951 lights_pending[LightAnysolo] = lights_pending[LightLoop] =
952 lights_pending[LightPunch] = false;
956 TranzportControlProtocol::light_on (LightID light)
958 lights_pending[light] = true;
963 TranzportControlProtocol::light_off (LightID light)
965 lights_pending[light] = false;
970 TranzportControlProtocol::light_set (LightID light, bool offon)
973 cmd[0] = 0x00; cmd[1] = 0x00; cmd[2] = light; cmd[3] = offon;
974 cmd[4] = 0x00; cmd[5] = 0x00; cmd[6] = 0x00; cmd[7] = 0x00;
976 if (write (cmd) == 0) {
977 lights_current[light] = offon;
978 lights_invalid[light] = false;
985 int TranzportControlProtocol::rtpriority_set(int priority)
987 struct sched_param rtparam;
989 // preallocate and memlock some stack with memlock?
990 char *a = (char*) alloca(4096*2); a[0] = 'a'; a[4096] = 'b';
991 memset (&rtparam, 0, sizeof (rtparam));
992 rtparam.sched_priority = priority; /* XXX should be relative to audio (JACK) thread */
993 // Note - try SCHED_RR with a low limit
994 // - we don't care if we can't write everything this ms
995 // and it will help if we lose the device
996 if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
997 PBD::info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
1003 // Running with realtime privs is bad when you have problems
1005 int TranzportControlProtocol::rtpriority_unset(int priority)
1007 struct sched_param rtparam;
1009 memset (&rtparam, 0, sizeof (rtparam));
1010 rtparam.sched_priority = priority;
1011 if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
1012 PBD::info << string_compose (_("%1: can't stop realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
1015 PBD::info << string_compose (_("%1: realtime scheduling stopped (%2)"), name(), strerror (errno)) << endmsg;
1019 // Slowly breaking this into where I can make usb processing it's own thread.
1022 TranzportControlProtocol::monitor_work ()
1025 int val = 0, pending = 0;
1026 bool first_time = true;
1027 uint8_t offline = 0;
1030 PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("Tranzport"));
1031 pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
1032 pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
1040 /* bInterval for this beastie is 10ms */
1042 if (_device_status == STATUS_OFFLINE) {
1044 if(offline++ == 1) {
1045 cerr << "Transport has gone offline\n";
1048 offline = 0; // hate writing this
1057 #if DEBUG_TRANZPORT > 2
1058 if(inflight > 1) printf("Inflight: %d\n", inflight);
1062 if (_device_status != STATUS_OFFLINE) {
1069 pending = 3; // Give some time for the device to recover
1071 /* update whatever needs updating */
1074 /* still struggling with a good means of exerting flow control */
1075 // pending = flush();
1081 pending = --inflight; // we just did a whole bunch of writes so wait
1093 int TranzportControlProtocol::lights_show_recording()
1095 // FIXME, flash recording light when recording and transport is moving
1096 return lights_show_normal();
1099 // gotta do bling next!
1101 int TranzportControlProtocol::lights_show_bling()
1103 switch (bling_mode) {
1104 case BlingOff: break;
1105 case BlingKit: break; // rotate rec/mute/solo/any solo back and forth
1106 case BlingRotating: break; // switch between lights
1107 case BlingPairs: break; // Show pairs of lights
1108 case BlingRows: break; // light each row in sequence
1109 case BlingFlashAll: break; // Flash everything randomly
1114 int TranzportControlProtocol::lights_show_normal()
1118 if (route_table[0]) {
1119 boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack> (route_table[0]);
1120 lights_pending[LightTrackrec] = at && at->record_enabled();
1121 lights_pending[LightTrackmute] = route_get_muted(0);
1122 lights_pending[LightTracksolo] = route_get_soloed(0);
1124 lights_pending[LightTrackrec] = false;
1125 lights_pending[LightTracksolo] = false;
1126 lights_pending[LightTrackmute] = false;
1129 /* Global settings */
1131 lights_pending[LightLoop] = session->get_play_loop();
1132 lights_pending[LightPunch] = Config->get_punch_in() || Config->get_punch_out();
1133 lights_pending[LightRecord] = session->get_record_enabled();
1134 lights_pending[LightAnysolo] = session->soloing();
1139 int TranzportControlProtocol::lights_show_tempo()
1141 // someday soon fiddle with the lights based on the tempo
1142 return lights_show_normal();
1146 TranzportControlProtocol::update_state ()
1148 /* do the text and light updates */
1150 switch (display_mode) {
1151 case DisplayBigMeter:
1152 lights_show_tempo();
1157 lights_show_normal();
1164 case DisplayRecording:
1165 lights_show_recording();
1169 case DisplayRecordingMeter:
1170 lights_show_recording();
1175 lights_show_bling();
1179 case DisplayBlingMeter:
1180 lights_show_bling();
1188 #define TRANZPORT_BUTTON_HANDLER(callback, button_arg) if (button_changes & button_arg) { \
1189 if (buttonmask & button_arg) { \
1190 callback##_press (buttonmask&ButtonShift); } else { callback##_release (buttonmask&ButtonShift); } }
1193 TranzportControlProtocol::process (uint8_t* buf)
1195 // printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
1197 uint32_t this_button_mask;
1198 uint32_t button_changes;
1200 _device_status = buf[1];
1202 this_button_mask = 0;
1203 this_button_mask |= buf[2] << 24;
1204 this_button_mask |= buf[3] << 16;
1205 this_button_mask |= buf[4] << 8;
1206 this_button_mask |= buf[5];
1207 _datawheel = buf[6];
1209 button_changes = (this_button_mask ^ buttonmask);
1210 buttonmask = this_button_mask;
1216 // SHIFT + STOP + PLAY for bling mode?
1217 // if (button_changes & ButtonPlay & ButtonStop) {
1218 // bling_mode_toggle();
1219 // } or something like that
1221 TRANZPORT_BUTTON_HANDLER(button_event_battery,ButtonBattery);
1222 TRANZPORT_BUTTON_HANDLER(button_event_backlight,ButtonBacklight);
1223 TRANZPORT_BUTTON_HANDLER(button_event_trackleft,ButtonTrackLeft);
1224 TRANZPORT_BUTTON_HANDLER(button_event_trackright,ButtonTrackRight);
1225 TRANZPORT_BUTTON_HANDLER(button_event_trackrec,ButtonTrackRec);
1226 TRANZPORT_BUTTON_HANDLER(button_event_trackmute,ButtonTrackMute);
1227 TRANZPORT_BUTTON_HANDLER(button_event_tracksolo,ButtonTrackSolo);
1228 TRANZPORT_BUTTON_HANDLER(button_event_undo,ButtonUndo);
1229 TRANZPORT_BUTTON_HANDLER(button_event_in,ButtonIn);
1230 TRANZPORT_BUTTON_HANDLER(button_event_out,ButtonOut);
1231 TRANZPORT_BUTTON_HANDLER(button_event_punch,ButtonPunch);
1232 TRANZPORT_BUTTON_HANDLER(button_event_loop,ButtonLoop);
1233 TRANZPORT_BUTTON_HANDLER(button_event_prev,ButtonPrev);
1234 TRANZPORT_BUTTON_HANDLER(button_event_add,ButtonAdd);
1235 TRANZPORT_BUTTON_HANDLER(button_event_next,ButtonNext);
1236 TRANZPORT_BUTTON_HANDLER(button_event_rewind,ButtonRewind);
1237 TRANZPORT_BUTTON_HANDLER(button_event_fastforward,ButtonFastForward);
1238 TRANZPORT_BUTTON_HANDLER(button_event_stop,ButtonStop);
1239 TRANZPORT_BUTTON_HANDLER(button_event_play,ButtonPlay);
1240 TRANZPORT_BUTTON_HANDLER(button_event_record,ButtonRecord);
1245 TranzportControlProtocol::show_current_track ()
1250 if (route_table[0] == 0) {
1251 print (0, 0, "----------");
1252 last_track_gain = FLT_MAX;
1255 v = (char *)route_get_name (0).substr (0, 10).c_str();
1256 if((len = strlen(v)) > 0) {
1257 strncpy(pad,(char *)v,len);
1264 TranzportControlProtocol::button_event_battery_press (bool shifted)
1269 TranzportControlProtocol::button_event_battery_release (bool shifted)
1274 TranzportControlProtocol::button_event_backlight_press (bool shifted)
1277 printf("backlight pressed\n");
1282 TranzportControlProtocol::button_event_backlight_release (bool shifted)
1285 printf("backlight released\n\n");
1290 last_where += 1; /* force time redisplay */
1291 last_track_gain = FLT_MAX;
1292 normal_update(); // redraw_screen();
1297 TranzportControlProtocol::button_event_trackleft_press (bool shifted)
1303 TranzportControlProtocol::button_event_trackleft_release (bool shifted)
1308 TranzportControlProtocol::button_event_trackright_press (bool shifted)
1314 TranzportControlProtocol::button_event_trackright_release (bool shifted)
1319 TranzportControlProtocol::button_event_trackrec_press (bool shifted)
1322 toggle_all_rec_enables ();
1324 route_set_rec_enable (0, !route_get_rec_enable (0));
1329 TranzportControlProtocol::button_event_trackrec_release (bool shifted)
1334 TranzportControlProtocol::button_event_trackmute_press (bool shifted)
1337 // Mute ALL? Something useful when a phone call comes in. Mute master?
1339 route_set_muted (0, !route_get_muted (0));
1344 TranzportControlProtocol::button_event_trackmute_release (bool shifted)
1349 TranzportControlProtocol::button_event_tracksolo_press (bool shifted)
1352 printf("solo pressed\n");
1354 if (display_mode == DisplayBigMeter) {
1355 light_off (LightAnysolo);
1360 session->set_all_solo (!session->soloing());
1362 route_set_soloed (0, !route_get_soloed (0));
1367 TranzportControlProtocol::button_event_tracksolo_release (bool shifted)
1370 printf("solo released\n");
1375 TranzportControlProtocol::button_event_undo_press (bool shifted)
1378 redo (); // someday flash the screen with what was redone
1380 undo (); // someday flash the screen with what was undone
1385 TranzportControlProtocol::button_event_undo_release (bool shifted)
1390 TranzportControlProtocol::button_event_in_press (bool shifted)
1395 ControlProtocol::ZoomIn (); /* EMIT SIGNAL */
1400 TranzportControlProtocol::button_event_in_release (bool shifted)
1405 TranzportControlProtocol::button_event_out_press (bool shifted)
1408 toggle_punch_out ();
1410 ControlProtocol::ZoomOut (); /* EMIT SIGNAL */
1415 TranzportControlProtocol::button_event_out_release (bool shifted)
1420 TranzportControlProtocol::button_event_punch_press (bool shifted)
1425 TranzportControlProtocol::button_event_punch_release (bool shifted)
1430 TranzportControlProtocol::button_event_loop_press (bool shifted)
1433 next_wheel_shift_mode ();
1440 TranzportControlProtocol::button_event_loop_release (bool shifted)
1445 TranzportControlProtocol::button_event_prev_press (bool shifted)
1448 ControlProtocol::ZoomToSession (); /* EMIT SIGNAL */
1455 TranzportControlProtocol::button_event_prev_release (bool shifted)
1460 TranzportControlProtocol::button_event_add_press (bool shifted)
1466 TranzportControlProtocol::button_event_add_release (bool shifted)
1471 TranzportControlProtocol::button_event_next_press (bool shifted)
1481 TranzportControlProtocol::button_event_next_release (bool shifted)
1486 TranzportControlProtocol::button_event_rewind_press (bool shifted)
1496 TranzportControlProtocol::button_event_rewind_release (bool shifted)
1501 TranzportControlProtocol::button_event_fastforward_press (bool shifted)
1511 TranzportControlProtocol::button_event_fastforward_release (bool shifted)
1516 TranzportControlProtocol::button_event_stop_press (bool shifted)
1519 next_display_mode ();
1526 TranzportControlProtocol::button_event_stop_release (bool shifted)
1531 TranzportControlProtocol::button_event_play_press (bool shifted)
1534 set_transport_speed (1.0f);
1541 TranzportControlProtocol::button_event_play_release (bool shifted)
1546 TranzportControlProtocol::button_event_record_press (bool shifted)
1551 rec_enable_toggle ();
1556 TranzportControlProtocol::button_event_record_release (bool shifted)
1560 void button_event_mute (bool pressed, bool shifted)
1562 static int was_pressed = 0;
1567 TranzportControlProtocol::datawheel ()
1569 if ((buttonmask & ButtonTrackRight) || (buttonmask & ButtonTrackLeft)) {
1571 /* track scrolling */
1573 if (_datawheel < WheelDirectionThreshold) {
1579 timerclear (&last_wheel_motion);
1581 } else if ((buttonmask & ButtonPrev) || (buttonmask & ButtonNext)) {
1583 if (_datawheel < WheelDirectionThreshold) {
1589 timerclear (&last_wheel_motion);
1591 } else if (buttonmask & ButtonShift) {
1593 /* parameter control */
1595 if (route_table[0]) {
1596 switch (wheel_shift_mode) {
1597 case WheelShiftGain:
1598 if (_datawheel < WheelDirectionThreshold) {
1605 if (_datawheel < WheelDirectionThreshold) {
1612 case WheelShiftMarker:
1615 case WheelShiftMaster:
1621 timerclear (&last_wheel_motion);
1625 switch (wheel_mode) {
1642 TranzportControlProtocol::scroll ()
1645 if (_datawheel < WheelDirectionThreshold) {
1650 switch(wheel_increment) {
1651 case WheelIncrScreen: ScrollTimeline (0.2*m); break;
1652 default: break; // other modes unimplemented as yet
1657 TranzportControlProtocol::scrub ()
1661 struct timeval delta;
1664 gettimeofday (&now, 0);
1666 if (_datawheel < WheelDirectionThreshold) {
1672 if (dir != last_wheel_dir) {
1673 /* changed direction, start over */
1676 if (timerisset (&last_wheel_motion)) {
1678 timersub (&now, &last_wheel_motion, &delta);
1680 /* 10 clicks per second => speed == 1.0 */
1682 speed = 100000.0f / (delta.tv_sec * 1000000 + delta.tv_usec);
1686 /* start at half-speed and see where we go from there */
1692 last_wheel_motion = now;
1693 last_wheel_dir = dir;
1695 set_transport_speed (speed * dir);
1699 TranzportControlProtocol::config ()
1705 TranzportControlProtocol::shuttle ()
1707 if (_datawheel < WheelDirectionThreshold) {
1708 if (session->transport_speed() < 0) {
1709 session->request_transport_speed (1.0);
1711 session->request_transport_speed_nonzero (session->transport_speed() + 0.1);
1714 if (session->transport_speed() > 0) {
1715 session->request_transport_speed (-1.0);
1717 session->request_transport_speed_nonzero (session->transport_speed() - 0.1);
1723 TranzportControlProtocol::step_gain_up ()
1725 if (buttonmask & ButtonStop) {
1726 gain_fraction += 0.001;
1728 gain_fraction += 0.01;
1731 if (gain_fraction > 2.0) {
1732 gain_fraction = 2.0;
1735 route_set_gain (0, slider_position_to_gain (gain_fraction));
1739 TranzportControlProtocol::step_gain_down ()
1741 if (buttonmask & ButtonStop) {
1742 gain_fraction -= 0.001;
1744 gain_fraction -= 0.01;
1747 if (gain_fraction < 0.0) {
1748 gain_fraction = 0.0;
1751 route_set_gain (0, slider_position_to_gain (gain_fraction));
1755 TranzportControlProtocol::step_pan_right ()
1760 TranzportControlProtocol::step_pan_left ()
1765 TranzportControlProtocol::next_wheel_shift_mode ()
1767 switch (wheel_shift_mode) {
1768 case WheelShiftGain:
1769 wheel_shift_mode = WheelShiftPan;
1772 wheel_shift_mode = WheelShiftMaster;
1774 case WheelShiftMaster:
1775 wheel_shift_mode = WheelShiftGain;
1777 case WheelShiftMarker: // Not done yet, disabled
1778 wheel_shift_mode = WheelShiftGain;
1786 TranzportControlProtocol::next_wheel_mode ()
1788 switch (wheel_mode) {
1790 wheel_mode = WheelScrub;
1793 wheel_mode = WheelShuttle;
1796 wheel_mode = WheelTimeline;
1803 TranzportControlProtocol::next_track ()
1805 ControlProtocol::next_track (current_track_id);
1806 gain_fraction = gain_to_slider_position (route_get_effective_gain (0));
1810 TranzportControlProtocol::prev_track ()
1812 ControlProtocol::prev_track (current_track_id);
1813 gain_fraction = gain_to_slider_position (route_get_effective_gain (0));
1817 TranzportControlProtocol::show_wheel_mode ()
1821 switch (wheel_mode) {
1833 switch (wheel_shift_mode) {
1834 case WheelShiftGain:
1842 case WheelShiftMaster:
1846 case WheelShiftMarker:
1851 print (1, 0, text.c_str());
1854 // Was going to keep state around saying to retry or not
1855 // haven't got to it yet, still not sure it's a good idea
1858 TranzportControlProtocol::print (int row, int col, const char *text) {
1859 print_noretry(row,col,text);
1863 TranzportControlProtocol::print_noretry (int row, int col, const char *text)
1866 uint32_t left = strlen (text);
1870 if (row < 0 || row > 1) {
1874 if (col < 0 || col > 19) {
1880 if (col >= 0 && col < 4) {
1883 } else if (col >= 4 && col < 8) {
1886 } else if (col >= 8 && col < 12) {
1889 } else if (col >= 12 && col < 16) {
1892 } else if (col >= 16 && col < 20) {
1899 int offset = col % 4;
1901 /* copy current cell contents into tmp */
1903 memcpy (tmp, &screen_pending[row][base_col], 4);
1905 /* overwrite with new text */
1907 uint32_t tocopy = min ((4U - offset), left);
1909 memcpy (tmp+offset, text, tocopy);
1911 /* copy it back to pending */
1913 memcpy (&screen_pending[row][base_col], tmp, 4);
1922 TranzportControlProtocol::get_state ()
1924 return ControlProtocol::get_state();
1928 TranzportControlProtocol::set_state (const XMLNode& node)
1934 TranzportControlProtocol::save (char *name)
1936 // Presently unimplemented
1941 TranzportControlProtocol::load (char *name)
1943 // Presently unimplemented