enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / surfaces / frontier / tranzport / tranzport_control_protocol.cc
1 /*
2     Copyright (C) 2006 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 /* Design notes: The tranzport is a unique device, basically a
21    20 lcd gui with 22 shift keys and 8 blinking lights.
22
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.
26
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.
30
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.
33
34    A more complex surface might have hundreds of lights and several displays.
35
36    mike.taht@gmail.com
37  */
38
39 #define DEFAULT_USB_TIMEOUT 10
40 #define MAX_RETRY 1
41 #define MAX_TRANZPORT_INFLIGHT 4
42 #define DEBUG_TRANZPORT 0
43 #define HAVE_TRANZPORT_KERNEL_DRIVER 0
44
45 #include <iostream>
46 #include <algorithm>
47 #include <cmath>
48
49 #define __STDC_FORMAT_MACROS
50 #include <inttypes.h>
51 #include <float.h>
52 #include <sys/time.h>
53 #include <errno.h>
54
55 #include "pbd/pthread_utils.h"
56
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"
62
63 #include "tranzport_control_protocol.h"
64
65 using namespace ARDOUR;
66 using namespace std;
67 using namespace sigc;
68 using namespace PBD;
69
70 #include "pbd/i18n.h"
71
72 #include "pbd/abstract_ui.cc"
73
74 BaseUI::RequestType LEDChange = BaseUI::new_request_type ();
75 BaseUI::RequestType Print = BaseUI::new_request_type ();
76 BaseUI::RequestType SetCurrentTrack = BaseUI::new_request_type ();
77
78 /* Base Tranzport cmd strings */
79
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 };
83
84 static inline double
85 gain_to_slider_position (ARDOUR::gain_t g)
86 {
87         if (g == 0) return 0;
88         return pow((6.0*log(g)/log(2.0)+192.0)/198.0, 8.0);
89
90 }
91
92 static inline ARDOUR::gain_t
93 slider_position_to_gain (double pos)
94 {
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);
98 }
99
100
101 TranzportControlProtocol::TranzportControlProtocol (Session& s)
102         : ControlProtocol  (s, X_("Tranzport"))
103 {
104         /* tranzport controls one track at a time */
105
106         set_route_table_size (1);
107         timeout = 6000; // what is this for?
108         buttonmask = 0;
109         _datawheel = 0;
110         _device_status = STATUS_OFFLINE;
111         udev = 0;
112         current_track_id = 0;
113         last_where = max_frames;
114         wheel_mode = WheelTimeline;
115         wheel_shift_mode = WheelShiftGain;
116         wheel_increment = WheelIncrScreen;
117         bling_mode = BlingOff;
118         timerclear (&last_wheel_motion);
119         last_wheel_dir = 1;
120         last_track_gain = FLT_MAX;
121         display_mode = DisplayNormal;
122         gain_fraction = 0.0;
123         invalidate();
124         screen_init();
125         lights_init();
126         print(0,0,"!!Welcome to Ardour!!");
127         print(1,0,"!Peace through Music!");
128 }
129
130 void TranzportControlProtocol::light_validate (LightID light)
131 {
132         lights_invalid[light] = 0;
133 }
134
135 void TranzportControlProtocol::light_invalidate (LightID light)
136 {
137         lights_invalid[light] = 1;
138 }
139
140 void TranzportControlProtocol::lights_validate ()
141 {
142         memset (lights_invalid, 0, sizeof (lights_invalid));
143 }
144
145 void TranzportControlProtocol::lights_invalidate ()
146 {
147         memset (lights_invalid, 1, sizeof (lights_invalid));
148 }
149
150 void TranzportControlProtocol::lights_init()
151 {
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;
155         }
156 }
157
158
159
160 int
161 TranzportControlProtocol::lights_flush ()
162 {
163         if ( _device_status == STATUS_OFFLINE) { return (0); }
164
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])) {
169         //       return i-1;
170         //    }
171         //  }
172         //}
173         if ((lights_pending[LightRecord] != lights_current[LightRecord]) || lights_invalid[LightRecord]) {
174                 if (light_set(LightRecord,lights_pending[LightRecord])) {
175                         return 1;
176                 }
177         }
178         if ((lights_pending[LightTrackrec] != lights_current[LightTrackrec]) || lights_invalid[LightTrackrec]) {
179                 if (light_set(LightTrackrec,lights_pending[LightTrackrec])) {
180                         return 1;
181                 }
182         }
183
184         if ((lights_pending[LightTrackmute] != lights_current[LightTrackmute]) || lights_invalid[LightTrackmute]) {
185                 if (light_set(LightTrackmute,lights_pending[LightTrackmute])) {
186                         return 1;
187                 }
188         }
189
190         if ((lights_pending[LightTracksolo] != lights_current[LightTracksolo]) || lights_invalid[LightTracksolo]) {
191                 if (light_set(LightTracksolo,lights_pending[LightTracksolo])) {
192                         return 1;
193                 }
194         }
195         if ((lights_pending[LightAnysolo] != lights_current[LightAnysolo]) || lights_invalid[LightAnysolo]) {
196                 if (light_set(LightAnysolo,lights_pending[LightAnysolo])) {
197                         return 1;
198                 }
199         }
200         if ((lights_pending[LightLoop] != lights_current[LightLoop]) || lights_invalid[LightLoop]) {
201                 if (light_set(LightLoop,lights_pending[LightLoop])) {
202                         return 1;
203                 }
204         }
205         if ((lights_pending[LightPunch] != lights_current[LightPunch]) || lights_invalid[LightPunch]) {
206                 if (light_set(LightPunch,lights_pending[LightPunch])) {
207                         return 1;
208                 }
209         }
210
211         return 0;
212 }
213
214 // Screen specific commands
215
216 void
217 TranzportControlProtocol::screen_clear ()
218 {
219         const char *blank = "                    ";
220         print(0,0,blank);
221         print(1,0,blank);
222 }
223
224 void TranzportControlProtocol::screen_invalidate ()
225 {
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] = ' ';
232                 }
233         }
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
236 }
237
238 void TranzportControlProtocol::screen_validate ()
239 {
240 }
241
242 void TranzportControlProtocol::screen_init ()
243 {
244         screen_invalidate();
245 }
246
247 int
248 TranzportControlProtocol::screen_flush ()
249 {
250         int cell = 0, row, col_base, col, pending = 0;
251         if ( _device_status == STATUS_OFFLINE) { return (-1); }
252
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]) {
257
258                                 /* something in this cell is different, so dump the cell to the device. */
259
260                                 uint8_t cmd[8];
261                                 cmd[0] = 0x00;
262                                 cmd[1] = 0x01;
263                                 cmd[2] = cell;
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];
268                                 cmd[7] = 0x00;
269
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]);
275 #endif
276                                         pending += 1;
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;
280
281                                 } else {
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);
286                                 }
287
288                                 /* skip the rest of the 4 character cell since we wrote+copied it already */
289
290                                 col_base += 4;
291                                 col = col_base;
292                                 cell++;
293
294                         } else {
295
296                                 col++;
297
298                                 if (col && col % 4 == 0) {
299                                         cell++;
300                                         col_base += 4;
301                                 }
302                         }
303                 }
304         }
305         return pending;
306 }
307
308
309 //  Tranzport specific
310
311 void TranzportControlProtocol::invalidate()
312 {
313         lcd_damage(); lights_invalidate(); screen_invalidate(); // one of these days lcds can be fine but screens not
314 }
315
316 TranzportControlProtocol::~TranzportControlProtocol ()
317 {
318         set_active (false);
319 }
320
321
322 int
323 TranzportControlProtocol::set_active (bool yn)
324 {
325         if (yn != _active) {
326
327                 if (yn) {
328
329                         if (open ()) {
330                                 return -1;
331                         }
332
333                         if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) {
334                                 _active = true;
335                         } else {
336                                 return -1;
337                         }
338
339                 } else {
340                         cerr << "Begin tranzport shutdown\n";
341                         screen_clear ();
342                         lcd_damage();
343                         lights_off ();
344                         for(int x = 0; x < 10 && flush(); x++) { usleep(1000); }
345                         pthread_cancel_one (thread);
346                         cerr << "Tranzport Thread dead\n";
347                         close ();
348                         _active = false;
349                         cerr << "End tranzport shutdown\n";
350                 }
351         }
352
353         return 0;
354 }
355
356 void
357 TranzportControlProtocol::show_track_gain ()
358 {
359         if (route_table[0]) {
360                 gain_t g = route_get_gain (0);
361                 if ((g != last_track_gain) || lcd_isdamaged(0,9,8)) {
362                         char buf[16];
363                         snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0)));
364                         print (0, 9, buf);
365                         last_track_gain = g;
366                 }
367         } else {
368                 print (0, 9, "        ");
369         }
370 }
371
372 void
373 TranzportControlProtocol::normal_update ()
374 {
375         show_current_track ();
376         show_transport_time ();
377         show_track_gain ();
378         show_wheel_mode ();
379 }
380
381 void
382 TranzportControlProtocol::next_display_mode ()
383 {
384         switch (display_mode) {
385
386                 case DisplayNormal:
387                         enter_big_meter_mode();
388                         break;
389
390                 case DisplayBigMeter:
391                         enter_normal_display_mode();
392                         break;
393
394                 case DisplayRecording:
395                         enter_normal_display_mode();
396                         break;
397
398                 case DisplayRecordingMeter:
399                         enter_big_meter_mode();
400                         break;
401
402                 case DisplayConfig:
403                 case DisplayBling:
404                 case DisplayBlingMeter:
405                         enter_normal_display_mode();
406                         break;
407         }
408 }
409
410 // FIXME, these 3 aren't done yet
411
412 void
413 TranzportControlProtocol::enter_recording_mode ()
414 {
415         lcd_damage(); // excessive
416         screen_clear ();
417         lights_off ();
418         display_mode = DisplayRecording;
419 }
420
421 void
422 TranzportControlProtocol::enter_bling_mode ()
423 {
424         lcd_damage();
425         screen_clear ();
426         lights_off ();
427         display_mode = DisplayBling;
428 }
429
430 void
431 TranzportControlProtocol::enter_config_mode ()
432 {
433         lcd_damage();
434         screen_clear ();
435         lights_off ();
436         display_mode = DisplayConfig;
437 }
438
439
440 void
441 TranzportControlProtocol::enter_big_meter_mode ()
442 {
443         screen_clear ();
444         lcd_damage();
445         lights_off ();
446         last_meter_fill = 0;
447         display_mode = DisplayBigMeter;
448 }
449
450 void
451 TranzportControlProtocol::enter_normal_display_mode ()
452 {
453         screen_clear ();
454         lcd_damage();
455         lights_off ();
456         display_mode = DisplayNormal;
457         //  normal_update();
458 }
459
460
461 float
462 log_meter (float db)
463 {
464         float def = 0.0f; /* Meter deflection %age */
465
466         if (db < -70.0f) return 0.0f;
467         if (db > 6.0f) return 1.0f;
468
469         if (db < -60.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;
481         }
482
483         /* 115 is the deflection %age that would be
484            when db=6.0. this is an arbitrary
485            endpoint for our scaling.
486            */
487
488         return def/115.0f;
489 }
490
491 void
492 TranzportControlProtocol::show_meter ()
493 {
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");
499                 return;
500         }
501
502         float level = route_get_peak_input_power (0, 0);
503         float fraction = log_meter (level);
504
505         /* Someday add a peak bar*/
506
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 ":"
510            */
511
512         uint32_t fill  = (uint32_t) floor (fraction * 40);
513         char buf[22];
514         uint32_t i;
515
516         if (fill == last_meter_fill) {
517                 /* nothing to do */
518                 return;
519         }
520
521         last_meter_fill = fill;
522
523         bool add_single_level = (fill % 2 != 0);
524         fill /= 2;
525
526         if (fraction > 0.98) {
527                 light_on (LightAnysolo);
528         }
529
530         /* add all full steps */
531
532         for (i = 0; i < fill; ++i) {
533                 buf[i] = 0x07; /* tranzport special code for 4 quadrant LCD block */
534         }
535
536         /* add a possible half-step */
537
538         if (i < 20 && add_single_level) {
539                 buf[i] = 0x03; /* tranzport special code for 2 left quadrant LCD block */
540                 ++i;
541         }
542
543         /* fill rest with space */
544
545         for (; i < 20; ++i) {
546                 buf[i] = ' ';
547         }
548
549         /* print() requires this */
550
551         buf[21] = '\0';
552
553         print (0, 0, buf);
554         print (1, 0, buf);
555 }
556
557 void
558 TranzportControlProtocol::show_bbt (framepos_t where)
559 {
560         if ((where != last_where) || lcd_isdamaged(1,9,8)) {
561                 char buf[16];
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;
568                 last_where = where;
569
570                 if(last_ticks < 1960) { print (1, 9, buf); } // save a write so we can do leds
571
572                 // if displaymode is recordmode show beats but not yet
573                 lights_pending[LightRecord] = false;
574                 lights_pending[LightAnysolo] = false;
575                 switch(last_beats) {
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;
578                 }
579
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);
587         }
588         }
589
590
591 void
592 TranzportControlProtocol::show_transport_time ()
593 {
594         show_bbt (session->transport_frame ());
595 }
596
597 void
598 TranzportControlProtocol::show_smpte (framepos_t where)
599 {
600         if ((where != last_where) || lcd_isdamaged(1,9,10)) {
601
602                 char buf[5];
603                 SMPTE::Time smpte;
604
605                 session->smpte_time (where, smpte);
606
607                 if (smpte.negative) {
608                         sprintf (buf, "-%02" PRIu32 ":", smpte.hours);
609                 } else {
610                         sprintf (buf, " %02" PRIu32 ":", smpte.hours);
611                 }
612                 print (1, 8, buf);
613
614                 sprintf (buf, "%02" PRIu32 ":", smpte.minutes);
615                 print (1, 12, buf);
616
617                 sprintf (buf, "%02" PRIu32 ":", smpte.seconds);
618                 print (1, 15, buf);
619
620                 sprintf (buf, "%02" PRIu32, smpte.frames);
621                 print_noretry (1, 18, buf);
622
623                 last_where = where;
624         }
625 }
626
627 void*
628 TranzportControlProtocol::_monitor_work (void* arg)
629 {
630         return static_cast<TranzportControlProtocol*>(arg)->monitor_work ();
631 }
632
633 // I note that these usb specific open, close, probe, read routines are basically
634 // pure boilerplate and could easily be abstracted elsewhere
635
636 #if !HAVE_TRANZPORT_KERNEL_DRIVER
637
638 bool
639 TranzportControlProtocol::probe ()
640 {
641         struct usb_bus *bus;
642         struct usb_device *dev;
643
644         usb_init();
645         usb_find_busses();
646         usb_find_devices();
647
648         for (bus = usb_busses; bus; bus = bus->next) {
649
650                 for(dev = bus->devices; dev; dev = dev->next) {
651                         if (dev->descriptor.idVendor == VENDORID && dev->descriptor.idProduct == PRODUCTID) {
652                                 return true;
653                         }
654                 }
655         }
656
657         return false;
658 }
659
660 int
661 TranzportControlProtocol::open ()
662 {
663         struct usb_bus *bus;
664         struct usb_device *dev;
665
666         usb_init();
667         usb_find_busses();
668         usb_find_devices();
669
670         for (bus = usb_busses; bus; bus = bus->next) {
671
672                 for(dev = bus->devices; dev; dev = dev->next) {
673                         if (dev->descriptor.idVendor != VENDORID)
674                                 continue;
675                         if (dev->descriptor.idProduct != PRODUCTID)
676                                 continue;
677                         return open_core (dev);
678                 }
679         }
680
681         error << _("Tranzport: no device detected") << endmsg;
682         return -1;
683 }
684
685 int
686 TranzportControlProtocol::open_core (struct usb_device* dev)
687 {
688         if (!(udev = usb_open (dev))) {
689                 error << _("Tranzport: cannot open USB transport") << endmsg;
690                 return -1;
691         }
692
693         if (usb_claim_interface (udev, 0) < 0) {
694                 error << _("Tranzport: cannot claim USB interface") << endmsg;
695                 usb_close (udev);
696                 udev = 0;
697                 return -1;
698         }
699
700         if (usb_set_configuration (udev, 1) < 0) {
701                 cerr << _("Tranzport: cannot configure USB interface") << endmsg;
702         }
703
704         return 0;
705 }
706
707 int
708 TranzportControlProtocol::close ()
709 {
710         int ret = 0;
711
712         if (udev == 0) {
713                 return 0;
714         }
715
716         if (usb_release_interface (udev, 0) < 0) {
717                 error << _("Tranzport: cannot release interface") << endmsg;
718                 ret = -1;
719         }
720
721         if (usb_close (udev)) {
722                 error << _("Tranzport: cannot close device") << endmsg;
723                 udev = 0;
724                 ret = 0;
725         }
726
727         return ret;
728 }
729
730 int TranzportControlProtocol::read(uint8_t *buf, uint32_t timeout_override)
731 {
732         int val;
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();
737         return val;
738 }
739
740
741 int
742 TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override)
743 {
744         int val;
745         if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
746         val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
747
748         if (val < 0) {
749 #if DEBUG_TRANZPORT
750                 printf("usb_interrupt_write failed: %d\n", val);
751 #endif
752                 return val;
753                 }
754
755         if (val != 8) {
756 #if DEBUG_TRANZPORT
757                 printf("usb_interrupt_write failed: %d\n", val);
758 #endif
759                 return -1;
760                 }
761         ++inflight;
762
763         return 0;
764
765 }
766
767 int
768 TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
769 {
770 #if MAX_RETRY > 1
771         int val;
772         int retry = 0;
773         if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
774
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);
777         }
778
779         if (retry == MAX_RETRY) {
780                 printf("Too many retries on a tranzport write, aborting\n");
781                 }
782
783         if (val < 0) {
784                 printf("usb_interrupt_write failed: %d\n", val);
785                 return val;
786                 }
787         if (val != 8) {
788                 printf("usb_interrupt_write failed: %d\n", val);
789                 return -1;
790                 }
791         ++inflight;
792         return 0;
793 #else
794         return (write_noretry(cmd,timeout_override));
795 #endif
796
797 }
798
799 #else
800 #error Kernel API not defined yet for Tranzport
801 // Something like open(/dev/surface/tranzport/event) for reading and raw for writing)
802 #endif
803
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
811 // setactive
812 // so someday I think we need a screen object.
813
814 /*
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:");
818
819 if(flash_messages && lcd.getactive() != screen_flash) lcd.setactive(screen_flash,2000);
820
821 screen::setactive(screen_name,duration); // duration in ms
822 screen::getactive();
823 */
824
825
826 int
827 TranzportControlProtocol::flush ()
828 {
829         int pending = 0;
830         if(!(pending = lights_flush())) {
831                 pending = screen_flush();
832         }
833         return pending;
834 }
835
836 // doing these functions made me realize that screen_invalid should be lcd_isdamaged FIXME soon
837
838 bool TranzportControlProtocol::lcd_damage()
839 {
840         screen_invalidate();
841         return true;
842 }
843
844 bool TranzportControlProtocol::lcd_damage (int row, int col, int length)
845 {
846         bool result = false;
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;
852                 }
853                 result = true;
854         }
855         return result;
856 }
857
858 // Gotta switch to bitfields, this is collossally dumb
859 // Still working on the layering, arguably screen_invalid should be lcd_invalid
860
861 bool TranzportControlProtocol::lcd_isdamaged ()
862 {
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);
868 #endif
869                                 return true;
870                         }
871                 }
872         }
873         return false;
874 }
875
876 bool TranzportControlProtocol::lcd_isdamaged (int row, int col, int length)
877 {
878         bool result = 0;
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);
886 #endif
887                                 return true;
888                         }
889                 }
890         }
891         return result;
892 }
893
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....
898
899 void
900 TranzportControlProtocol::lcd_clear ()
901 {
902
903 }
904
905 // These lcd commands are not universally used yet and may drop out of the api
906
907 int
908 TranzportControlProtocol::lcd_flush ()
909 {
910         return 0;
911 }
912
913 int
914 TranzportControlProtocol::lcd_write(uint8_t* cmd, uint32_t timeout_override)
915 {
916         return write(cmd,timeout_override);
917 }
918
919 void
920 TranzportControlProtocol::lcd_fill (uint8_t fill_char)
921 {
922 }
923
924 void
925 TranzportControlProtocol::lcd_print (int row, int col, const char* text)
926 {
927         print(row,col,text);
928 }
929
930 void TranzportControlProtocol::lcd_print_noretry (int row, int col, const char* text)
931 {
932         print(row,col,text);
933 }
934
935 // Lights are buffered
936
937 void
938 TranzportControlProtocol::lights_on ()
939 {
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;
944 }
945
946 void
947 TranzportControlProtocol::lights_off ()
948 {
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;
953 }
954
955 int
956 TranzportControlProtocol::light_on (LightID light)
957 {
958         lights_pending[light] = true;
959         return 0;
960 }
961
962 int
963 TranzportControlProtocol::light_off (LightID light)
964 {
965         lights_pending[light] = false;
966         return 0;
967 }
968
969 int
970 TranzportControlProtocol::light_set (LightID light, bool offon)
971 {
972         uint8_t cmd[8];
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;
975
976         if (write (cmd) == 0) {
977                 lights_current[light] = offon;
978                 lights_invalid[light] = false;
979                 return 0;
980         } else {
981                 return -1;
982         }
983 }
984
985 int TranzportControlProtocol::rtpriority_set(int priority)
986 {
987         struct sched_param rtparam;
988         int err;
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;
998                 return 1;
999         }
1000         return 0;
1001 }
1002
1003 // Running with realtime privs is bad when you have problems
1004
1005 int TranzportControlProtocol::rtpriority_unset(int priority)
1006 {
1007         struct sched_param rtparam;
1008         int err;
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;
1013                 return 1;
1014         }
1015         PBD::info << string_compose (_("%1: realtime scheduling stopped (%2)"), name(), strerror (errno)) << endmsg;
1016         return 0;
1017 }
1018
1019 // Slowly breaking this into where I can make usb processing it's own thread.
1020
1021 void*
1022 TranzportControlProtocol::monitor_work ()
1023 {
1024         uint8_t buf[8];
1025         int val = 0, pending = 0;
1026         bool first_time = true;
1027         uint8_t offline = 0;
1028
1029
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);
1033         next_track ();
1034         rtpriority_set();
1035         inflight=0;
1036         flush();
1037
1038         while (true) {
1039
1040                 /* bInterval for this beastie is 10ms */
1041
1042                 if (_device_status == STATUS_OFFLINE) {
1043                         first_time = true;
1044                         if(offline++ == 1) {
1045                                 cerr << "Transport has gone offline\n";
1046                         }
1047                 } else {
1048                         offline = 0; // hate writing this
1049                 }
1050
1051                 val = read(buf);
1052
1053                 if (val == 8) {
1054                         process (buf);
1055                 }
1056
1057 #if DEBUG_TRANZPORT > 2
1058                 if(inflight > 1) printf("Inflight: %d\n", inflight);
1059 #endif
1060
1061
1062                 if (_device_status != STATUS_OFFLINE) {
1063                         if (first_time) {
1064                                 invalidate();
1065                                 lcd_clear ();
1066                                 lights_off ();
1067                                 first_time = false;
1068                                 offline = 0;
1069                                 pending = 3; // Give some time for the device to recover
1070                         }
1071                         /* update whatever needs updating */
1072                         update_state ();
1073
1074                         /* still struggling with a good means of exerting flow control */
1075                         // pending = flush();
1076
1077                         if(pending == 0) {
1078                                 pending = flush();
1079                         } else {
1080                                 if(inflight > 0) {
1081                                         pending = --inflight; // we just did a whole bunch of writes so wait
1082                                 } else {
1083                                         pending = 0;
1084                                 }
1085                         }
1086                         // pending = 0;
1087                 }
1088         }
1089
1090         return (void*) 0;
1091 }
1092
1093 int TranzportControlProtocol::lights_show_recording()
1094 {
1095         //   FIXME, flash recording light when recording and transport is moving
1096         return     lights_show_normal();
1097 }
1098
1099 // gotta do bling next!
1100
1101 int TranzportControlProtocol::lights_show_bling()
1102 {
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
1110         }
1111         return 0;
1112 }
1113
1114 int TranzportControlProtocol::lights_show_normal()
1115 {
1116         /* Track only */
1117
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);
1123         } else {
1124                 lights_pending[LightTrackrec]  = false;
1125                 lights_pending[LightTracksolo] = false;
1126                 lights_pending[LightTrackmute] = false;
1127         }
1128
1129         /* Global settings */
1130
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();
1135
1136         return 0;
1137 }
1138
1139 int TranzportControlProtocol::lights_show_tempo()
1140 {
1141         // someday soon fiddle with the lights based on the tempo
1142         return     lights_show_normal();
1143 }
1144
1145 int
1146 TranzportControlProtocol::update_state ()
1147 {
1148         /* do the text and light updates */
1149
1150         switch (display_mode) {
1151                 case DisplayBigMeter:
1152                         lights_show_tempo();
1153                         show_meter ();
1154                         break;
1155
1156                 case DisplayNormal:
1157                         lights_show_normal();
1158                         normal_update ();
1159                         break;
1160
1161                 case DisplayConfig:
1162                         break;
1163
1164                 case DisplayRecording:
1165                         lights_show_recording();
1166                         normal_update();
1167                         break;
1168
1169                 case DisplayRecordingMeter:
1170                         lights_show_recording();
1171                         show_meter();
1172                         break;
1173
1174                 case DisplayBling:
1175                         lights_show_bling();
1176                         normal_update();
1177                         break;
1178
1179                 case DisplayBlingMeter:
1180                         lights_show_bling();
1181                         show_meter();
1182                         break;
1183         }
1184         return 0;
1185
1186 }
1187
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); } }
1191
1192 int
1193 TranzportControlProtocol::process (uint8_t* buf)
1194 {
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]);
1196
1197         uint32_t this_button_mask;
1198         uint32_t button_changes;
1199
1200         _device_status = buf[1];
1201
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];
1208
1209         button_changes = (this_button_mask ^ buttonmask);
1210         buttonmask = this_button_mask;
1211
1212         if (_datawheel) {
1213                 datawheel ();
1214         }
1215
1216         // SHIFT + STOP + PLAY for bling mode?
1217         // if (button_changes & ButtonPlay & ButtonStop) {
1218         // bling_mode_toggle();
1219         // } or something like that
1220
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);
1241         return 0;
1242 }
1243
1244 void
1245 TranzportControlProtocol::show_current_track ()
1246 {
1247         char pad[11];
1248         char *v;
1249         int len;
1250         if (route_table[0] == 0) {
1251                 print (0, 0, "----------");
1252                 last_track_gain = FLT_MAX;
1253         } else {
1254                 strcpy(pad,"          ");
1255                 v =  (char *)route_get_name (0).substr (0, 10).c_str();
1256                 if((len = strlen(v)) > 0) {
1257                         strncpy(pad,(char *)v,len);
1258                 }
1259                 print (0, 0, pad);
1260         }
1261 }
1262
1263 void
1264 TranzportControlProtocol::button_event_battery_press (bool shifted)
1265 {
1266 }
1267
1268 void
1269 TranzportControlProtocol::button_event_battery_release (bool shifted)
1270 {
1271 }
1272
1273 void
1274 TranzportControlProtocol::button_event_backlight_press (bool shifted)
1275 {
1276 #if DEBUG_TRANZPORT
1277         printf("backlight pressed\n");
1278 #endif
1279 }
1280
1281 void
1282 TranzportControlProtocol::button_event_backlight_release (bool shifted)
1283 {
1284 #if DEBUG_TRANZPORT
1285         printf("backlight released\n\n");
1286 #endif
1287         if (shifted) {
1288                 lcd_damage();
1289                 lcd_clear();
1290                 last_where += 1; /* force time redisplay */
1291                 last_track_gain = FLT_MAX;
1292                 normal_update(); //  redraw_screen();
1293         }
1294 }
1295
1296 void
1297 TranzportControlProtocol::button_event_trackleft_press (bool shifted)
1298 {
1299         prev_track ();
1300 }
1301
1302 void
1303 TranzportControlProtocol::button_event_trackleft_release (bool shifted)
1304 {
1305 }
1306
1307 void
1308 TranzportControlProtocol::button_event_trackright_press (bool shifted)
1309 {
1310         next_track ();
1311 }
1312
1313 void
1314 TranzportControlProtocol::button_event_trackright_release (bool shifted)
1315 {
1316 }
1317
1318 void
1319 TranzportControlProtocol::button_event_trackrec_press (bool shifted)
1320 {
1321         if (shifted) {
1322                 toggle_all_rec_enables ();
1323         } else {
1324                 route_set_rec_enable (0, !route_get_rec_enable (0));
1325         }
1326 }
1327
1328 void
1329 TranzportControlProtocol::button_event_trackrec_release (bool shifted)
1330 {
1331 }
1332
1333 void
1334 TranzportControlProtocol::button_event_trackmute_press (bool shifted)
1335 {
1336         if (shifted) {
1337           // Mute ALL? Something useful when a phone call comes in. Mute master?
1338         } else {
1339           route_set_muted (0, !route_get_muted (0));
1340         }
1341 }
1342
1343 void
1344 TranzportControlProtocol::button_event_trackmute_release (bool shifted)
1345 {
1346 }
1347
1348 void
1349 TranzportControlProtocol::button_event_tracksolo_press (bool shifted)
1350 {
1351 #if DEBUG_TRANZPORT
1352         printf("solo pressed\n");
1353 #endif
1354         if (display_mode == DisplayBigMeter) {
1355                 light_off (LightAnysolo);
1356                 return;
1357         }
1358
1359         if (shifted) {
1360                 session->set_all_solo (!session->soloing());
1361         } else {
1362                 route_set_soloed (0, !route_get_soloed (0));
1363         }
1364 }
1365
1366 void
1367 TranzportControlProtocol::button_event_tracksolo_release (bool shifted)
1368 {
1369 #if DEBUG_TRANZPORT
1370         printf("solo released\n");
1371 #endif
1372 }
1373
1374 void
1375 TranzportControlProtocol::button_event_undo_press (bool shifted)
1376 {
1377         if (shifted) {
1378                 redo (); // someday flash the screen with what was redone
1379         } else {
1380                 undo (); // someday flash the screen with what was undone
1381         }
1382 }
1383
1384 void
1385 TranzportControlProtocol::button_event_undo_release (bool shifted)
1386 {
1387 }
1388
1389 void
1390 TranzportControlProtocol::button_event_in_press (bool shifted)
1391 {
1392         if (shifted) {
1393                 toggle_punch_in ();
1394         } else {
1395                 ControlProtocol::ZoomIn (); /* EMIT SIGNAL */
1396         }
1397 }
1398
1399 void
1400 TranzportControlProtocol::button_event_in_release (bool shifted)
1401 {
1402 }
1403
1404 void
1405 TranzportControlProtocol::button_event_out_press (bool shifted)
1406 {
1407         if (shifted) {
1408                 toggle_punch_out ();
1409         } else {
1410                 ControlProtocol::ZoomOut (); /* EMIT SIGNAL */
1411         }
1412 }
1413
1414 void
1415 TranzportControlProtocol::button_event_out_release (bool shifted)
1416 {
1417 }
1418
1419 void
1420 TranzportControlProtocol::button_event_punch_press (bool shifted)
1421 {
1422 }
1423
1424 void
1425 TranzportControlProtocol::button_event_punch_release (bool shifted)
1426 {
1427 }
1428
1429 void
1430 TranzportControlProtocol::button_event_loop_press (bool shifted)
1431 {
1432         if (shifted) {
1433                 next_wheel_shift_mode ();
1434         } else {
1435                 loop_toggle ();
1436         }
1437 }
1438
1439 void
1440 TranzportControlProtocol::button_event_loop_release (bool shifted)
1441 {
1442 }
1443
1444 void
1445 TranzportControlProtocol::button_event_prev_press (bool shifted)
1446 {
1447         if (shifted) {
1448                 ControlProtocol::ZoomToSession (); /* EMIT SIGNAL */
1449         } else {
1450                 prev_marker ();
1451         }
1452 }
1453
1454 void
1455 TranzportControlProtocol::button_event_prev_release (bool shifted)
1456 {
1457 }
1458
1459 void
1460 TranzportControlProtocol::button_event_add_press (bool shifted)
1461 {
1462         add_marker ();
1463 }
1464
1465 void
1466 TranzportControlProtocol::button_event_add_release (bool shifted)
1467 {
1468 }
1469
1470 void
1471 TranzportControlProtocol::button_event_next_press (bool shifted)
1472 {
1473         if (shifted) {
1474                 next_wheel_mode ();
1475         } else {
1476                 next_marker ();
1477         }
1478 }
1479
1480 void
1481 TranzportControlProtocol::button_event_next_release (bool shifted)
1482 {
1483 }
1484
1485 void
1486 TranzportControlProtocol::button_event_rewind_press (bool shifted)
1487 {
1488         if (shifted) {
1489                 goto_start ();
1490         } else {
1491                 rewind ();
1492         }
1493 }
1494
1495 void
1496 TranzportControlProtocol::button_event_rewind_release (bool shifted)
1497 {
1498 }
1499
1500 void
1501 TranzportControlProtocol::button_event_fastforward_press (bool shifted)
1502 {
1503         if (shifted) {
1504                 goto_end ();
1505         } else {
1506                 ffwd ();
1507         }
1508 }
1509
1510 void
1511 TranzportControlProtocol::button_event_fastforward_release (bool shifted)
1512 {
1513 }
1514
1515 void
1516 TranzportControlProtocol::button_event_stop_press (bool shifted)
1517 {
1518         if (shifted) {
1519                 next_display_mode ();
1520         } else {
1521                 transport_stop ();
1522         }
1523 }
1524
1525 void
1526 TranzportControlProtocol::button_event_stop_release (bool shifted)
1527 {
1528 }
1529
1530 void
1531 TranzportControlProtocol::button_event_play_press (bool shifted)
1532 {
1533         if (shifted) {
1534           set_transport_speed (1.0f);
1535         } else {
1536           transport_play ();
1537         }
1538 }
1539
1540 void
1541 TranzportControlProtocol::button_event_play_release (bool shifted)
1542 {
1543 }
1544
1545 void
1546 TranzportControlProtocol::button_event_record_press (bool shifted)
1547 {
1548         if (shifted) {
1549                 save_state ();
1550         } else {
1551                 rec_enable_toggle ();
1552         }
1553 }
1554
1555 void
1556 TranzportControlProtocol::button_event_record_release (bool shifted)
1557 {
1558 }
1559
1560 void button_event_mute (bool pressed, bool shifted)
1561 {
1562         static int was_pressed = 0;
1563         //  if(pressed) { }
1564 }
1565
1566 void
1567 TranzportControlProtocol::datawheel ()
1568 {
1569         if ((buttonmask & ButtonTrackRight) || (buttonmask & ButtonTrackLeft)) {
1570
1571                 /* track scrolling */
1572
1573                 if (_datawheel < WheelDirectionThreshold) {
1574                         next_track ();
1575                 } else {
1576                         prev_track ();
1577                 }
1578
1579                 timerclear (&last_wheel_motion);
1580
1581         } else if ((buttonmask & ButtonPrev) || (buttonmask & ButtonNext)) {
1582
1583                 if (_datawheel < WheelDirectionThreshold) {
1584                         next_marker ();
1585                 } else {
1586                         prev_marker ();
1587                 }
1588
1589                 timerclear (&last_wheel_motion);
1590
1591         } else if (buttonmask & ButtonShift) {
1592
1593                 /* parameter control */
1594
1595                 if (route_table[0]) {
1596                         switch (wheel_shift_mode) {
1597                                 case WheelShiftGain:
1598                                         if (_datawheel < WheelDirectionThreshold) {
1599                                                 step_gain_up ();
1600                                         } else {
1601                                                 step_gain_down ();
1602                                         }
1603                                         break;
1604                                 case WheelShiftPan:
1605                                         if (_datawheel < WheelDirectionThreshold) {
1606                                                 step_pan_right ();
1607                                         } else {
1608                                                 step_pan_left ();
1609                                         }
1610                                         break;
1611
1612                                 case WheelShiftMarker:
1613                                         break;
1614
1615                                 case WheelShiftMaster:
1616                                         break;
1617
1618                         }
1619                 }
1620
1621                 timerclear (&last_wheel_motion);
1622
1623         } else {
1624
1625                 switch (wheel_mode) {
1626                         case WheelTimeline:
1627                                 scroll ();
1628                                 break;
1629
1630                         case WheelScrub:
1631                                 scrub ();
1632                                 break;
1633
1634                         case WheelShuttle:
1635                                 shuttle ();
1636                                 break;
1637                 }
1638         }
1639 }
1640
1641 void
1642 TranzportControlProtocol::scroll ()
1643 {
1644         float m = 1.0;
1645         if (_datawheel < WheelDirectionThreshold) {
1646                 m = 1.0;
1647         } else {
1648                 m = -1.0;
1649         }
1650         switch(wheel_increment) {
1651                 case WheelIncrScreen: ScrollTimeline (0.2*m); break;
1652                 default: break; // other modes unimplemented as yet
1653         }
1654 }
1655
1656 void
1657 TranzportControlProtocol::scrub ()
1658 {
1659         float speed;
1660         struct timeval now;
1661         struct timeval delta;
1662         int dir;
1663
1664         gettimeofday (&now, 0);
1665
1666         if (_datawheel < WheelDirectionThreshold) {
1667                 dir = 1;
1668         } else {
1669                 dir = -1;
1670         }
1671
1672         if (dir != last_wheel_dir) {
1673                 /* changed direction, start over */
1674                 speed = 0.1f;
1675         } else {
1676                 if (timerisset (&last_wheel_motion)) {
1677
1678                         timersub (&now, &last_wheel_motion, &delta);
1679
1680                         /* 10 clicks per second => speed == 1.0 */
1681
1682                         speed = 100000.0f / (delta.tv_sec * 1000000 + delta.tv_usec);
1683
1684                 } else {
1685
1686                         /* start at half-speed and see where we go from there */
1687
1688                         speed = 0.5f;
1689                 }
1690         }
1691
1692         last_wheel_motion = now;
1693         last_wheel_dir = dir;
1694
1695         set_transport_speed (speed * dir);
1696 }
1697
1698 void
1699 TranzportControlProtocol::config ()
1700 {
1701   // FIXME
1702 }
1703
1704 void
1705 TranzportControlProtocol::shuttle ()
1706 {
1707         if (_datawheel < WheelDirectionThreshold) {
1708                 if (session->transport_speed() < 0) {
1709                         session->request_transport_speed (1.0);
1710                 } else {
1711                         session->request_transport_speed_nonzero (session->transport_speed() + 0.1);
1712                 }
1713         } else {
1714                 if (session->transport_speed() > 0) {
1715                         session->request_transport_speed (-1.0);
1716                 } else {
1717                         session->request_transport_speed_nonzero (session->transport_speed() - 0.1);
1718                 }
1719         }
1720 }
1721
1722 void
1723 TranzportControlProtocol::step_gain_up ()
1724 {
1725         if (buttonmask & ButtonStop) {
1726                 gain_fraction += 0.001;
1727         } else {
1728                 gain_fraction += 0.01;
1729         }
1730
1731         if (gain_fraction > 2.0) {
1732                 gain_fraction = 2.0;
1733         }
1734
1735         route_set_gain (0, slider_position_to_gain (gain_fraction));
1736 }
1737
1738 void
1739 TranzportControlProtocol::step_gain_down ()
1740 {
1741         if (buttonmask & ButtonStop) {
1742                 gain_fraction -= 0.001;
1743         } else {
1744                 gain_fraction -= 0.01;
1745         }
1746
1747         if (gain_fraction < 0.0) {
1748                 gain_fraction = 0.0;
1749         }
1750
1751         route_set_gain (0, slider_position_to_gain (gain_fraction));
1752 }
1753
1754 void
1755 TranzportControlProtocol::step_pan_right ()
1756 {
1757 }
1758
1759 void
1760 TranzportControlProtocol::step_pan_left ()
1761 {
1762 }
1763
1764 void
1765 TranzportControlProtocol::next_wheel_shift_mode ()
1766 {
1767         switch (wheel_shift_mode) {
1768         case WheelShiftGain:
1769                 wheel_shift_mode = WheelShiftPan;
1770                 break;
1771         case WheelShiftPan:
1772                 wheel_shift_mode = WheelShiftMaster;
1773                 break;
1774         case WheelShiftMaster:
1775                 wheel_shift_mode = WheelShiftGain;
1776                 break;
1777         case WheelShiftMarker: // Not done yet, disabled
1778                 wheel_shift_mode = WheelShiftGain;
1779                 break;
1780         }
1781
1782         show_wheel_mode ();
1783 }
1784
1785 void
1786 TranzportControlProtocol::next_wheel_mode ()
1787 {
1788         switch (wheel_mode) {
1789         case WheelTimeline:
1790                 wheel_mode = WheelScrub;
1791                 break;
1792         case WheelScrub:
1793                 wheel_mode = WheelShuttle;
1794                 break;
1795         case WheelShuttle:
1796                 wheel_mode = WheelTimeline;
1797         }
1798
1799         show_wheel_mode ();
1800 }
1801
1802 void
1803 TranzportControlProtocol::next_track ()
1804 {
1805         ControlProtocol::next_track (current_track_id);
1806         gain_fraction = gain_to_slider_position (route_get_effective_gain (0));
1807 }
1808
1809 void
1810 TranzportControlProtocol::prev_track ()
1811 {
1812         ControlProtocol::prev_track (current_track_id);
1813         gain_fraction = gain_to_slider_position (route_get_effective_gain (0));
1814 }
1815
1816 void
1817 TranzportControlProtocol::show_wheel_mode ()
1818 {
1819         string text;
1820
1821         switch (wheel_mode) {
1822                 case WheelTimeline:
1823                         text = "Time";
1824                         break;
1825                 case WheelScrub:
1826                         text = "Scrb";
1827                         break;
1828                 case WheelShuttle:
1829                         text = "Shtl";
1830                         break;
1831         }
1832
1833         switch (wheel_shift_mode) {
1834                 case WheelShiftGain:
1835                         text += ":Gain";
1836                         break;
1837
1838                 case WheelShiftPan:
1839                         text += ":Pan ";
1840                         break;
1841
1842                 case WheelShiftMaster:
1843                         text += ":Mstr";
1844                         break;
1845
1846                 case WheelShiftMarker:
1847                         text += ":Mrkr";
1848                         break;
1849         }
1850
1851         print (1, 0, text.c_str());
1852 }
1853
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
1856
1857 void
1858 TranzportControlProtocol::print (int row, int col, const char *text) {
1859         print_noretry(row,col,text);
1860 }
1861
1862 void
1863 TranzportControlProtocol::print_noretry (int row, int col, const char *text)
1864 {
1865         int cell;
1866         uint32_t left = strlen (text);
1867         char tmp[5];
1868         int base_col;
1869
1870         if (row < 0 || row > 1) {
1871                 return;
1872         }
1873
1874         if (col < 0 || col > 19) {
1875                 return;
1876         }
1877
1878         while (left) {
1879
1880                 if (col >= 0 && col < 4) {
1881                         cell = 0;
1882                         base_col = 0;
1883                 } else if (col >= 4 && col < 8) {
1884                         cell = 1;
1885                         base_col = 4;
1886                 } else if (col >= 8 && col < 12) {
1887                         cell = 2;
1888                         base_col = 8;
1889                 } else if (col >= 12 && col < 16) {
1890                         cell = 3;
1891                         base_col = 12;
1892                 } else if (col >= 16 && col < 20) {
1893                         cell = 4;
1894                         base_col = 16;
1895                 } else {
1896                         return;
1897                 }
1898
1899                 int offset = col % 4;
1900
1901                 /* copy current cell contents into tmp */
1902
1903                 memcpy (tmp, &screen_pending[row][base_col], 4);
1904
1905                 /* overwrite with new text */
1906
1907                 uint32_t tocopy = min ((4U - offset), left);
1908
1909                 memcpy (tmp+offset, text, tocopy);
1910
1911                 /* copy it back to pending */
1912
1913                 memcpy (&screen_pending[row][base_col], tmp, 4);
1914
1915                 text += tocopy;
1916                 left -= tocopy;
1917                 col  += tocopy;
1918         }
1919 }
1920
1921 XMLNode&
1922 TranzportControlProtocol::get_state ()
1923 {
1924         return ControlProtocol::get_state();
1925 }
1926
1927 int
1928 TranzportControlProtocol::set_state (const XMLNode& node)
1929 {
1930         return 0;
1931 }
1932
1933 int
1934 TranzportControlProtocol::save (char *name)
1935 {
1936         // Presently unimplemented
1937         return 0;
1938 }
1939
1940 int
1941 TranzportControlProtocol::load (char *name)
1942 {
1943         // Presently unimplemented
1944         return 0;
1945 }