Merged with trunk R1612.
[ardour.git] / libs / surfaces / tranzport / init.cc
1 /*
2  *   Copyright (C) 2006 Paul Davis 
3  *   Copyright (C) 2007 Michael Taht
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  
19  */
20
21 #include <tranzport_common.h>
22 #include <tranzport_control_protocol.h>
23
24 using namespace ARDOUR;
25 using namespace std;
26 using namespace sigc;
27 using namespace PBD;
28
29 #include "i18n.h"
30
31 #include <pbd/abstract_ui.cc>
32
33 void*
34 TranzportControlProtocol::_monitor_work (void* arg)
35 {
36         return static_cast<TranzportControlProtocol*>(arg)->monitor_work ();
37 }
38
39 TranzportControlProtocol::~TranzportControlProtocol ()
40 {
41         set_active (false);
42 }
43
44 int TranzportControlProtocol::rtpriority_set(int priority) 
45 {
46         struct sched_param rtparam;
47         int err;
48         char *a = (char*) alloca(4096*2); a[0] = 'a'; a[4096] = 'b';
49         memset (&rtparam, 0, sizeof (rtparam));
50         rtparam.sched_priority = priority; /* XXX should be relative to audio (JACK) thread */
51         // Note - try SCHED_RR with a low limit 
52         // - we don't care if we can't write everything this ms
53         // and it will help if we lose the device
54         if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
55                 PBD::info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
56                 return 1;
57         } 
58         return 0;
59 }
60
61 // Running with realtime privs is bad when you have problems
62
63 int TranzportControlProtocol::rtpriority_unset(int priority) 
64 {
65         struct sched_param rtparam;
66         int err;
67         memset (&rtparam, 0, sizeof (rtparam));
68         rtparam.sched_priority = priority;      
69         if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
70                 PBD::info << string_compose (_("%1: can't stop realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
71                 return 1;
72         } 
73         PBD::info << string_compose (_("%1: realtime scheduling stopped (%2)"), name(), strerror (errno)) << endmsg;
74         return 0;
75 }
76
77
78 int
79 TranzportControlProtocol::set_active (bool yn)
80 {
81         if (yn != _active) {
82
83                 if (yn) {
84
85                         if (open ()) {
86                                 return -1;
87                         }
88
89                         if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) {
90                                 _active = true;
91 #if TRANZPORT_THREADS                      
92                         if (pthread_create_and_store (X_("tranzport read"), &thread_read, 0, _read_work, this) == 0) {
93                                 _active_read = true;
94                         if (pthread_create_and_store (X_("tranzport write"), &thread_write, 0, _write_work, this) == 0) {
95                                 _active_write = true;
96                         if (pthread_create_and_store (X_("tranzport process"), &thread_process, 0, _process_work, this) == 0) {
97                                 _active_process = true;
98                         if (pthread_create_and_store (X_("tranzport timer"), &thread_timer, 0, _process_timer, this) == 0) {
99                                 _active_process = true;
100 #endif
101                         } else {
102                                 return -1;
103                         }
104
105                 } else {
106                         cerr << "Begin tranzport shutdown\n";
107 //                      if we got here due to an error, prettifying things will only make it worse
108 //                      And with threads involved, oh boy...
109                         if(!(last_write_error || last_read_error)) {
110                                 bling_mode   = BlingExit;
111                                 enter_bling_mode();
112 // thread FIXME - wait til all writes are done
113                                 for(int x = 0; (x < 20/MAX_TRANZPORT_INFLIGHT) && flush(); x++) { usleep(100); }
114                         }
115 #if TRANZPORT_THREADS                      
116                         pthread_cancel_one (_thread_timer);
117                         pthread_cancel_one (_thread_process);
118                         pthread_cancel_one (_thread_read);
119                         pthread_cancel_one (_thread_write);
120 #endif
121                         pthread_cancel_one (thread);
122
123                         cerr << "Tranzport Thread dead\n";
124                         close ();
125                         _active = false;
126                         cerr << "End tranzport shutdown\n";
127                 } 
128         }
129
130         return 0;
131 }
132
133 TranzportControlProtocol::TranzportControlProtocol (Session& s)
134         : ControlProtocol  (s, X_("Tranzport"))
135 {
136         /* tranzport controls one track at a time */
137
138         set_route_table_size (1);
139         timeout = 6000; // what is this for?
140         buttonmask = 0;
141         _datawheel = 0;
142         _device_status = STATUS_OFFLINE;
143         udev = 0;
144         current_track_id = 0;
145         last_where = max_frames;
146         wheel_mode = WheelTimeline;
147         wheel_shift_mode = WheelShiftGain;
148         wheel_increment = WheelIncrScreen;
149         bling_mode = BlingEnter;
150         last_notify_msg[0] = '\0';
151         last_notify = 0;
152         timerclear (&last_wheel_motion);
153         last_wheel_dir = 1;
154         last_track_gain = FLT_MAX;
155         last_write_error = 0;
156         last_read_error = 0;
157         display_mode = DisplayBling;
158         gain_fraction = 0.0;
159         invalidate();
160         screen_init();
161         lights_init();
162 // FIXME: Wait til device comes online somewhere
163 // About 3 reads is enough
164 // enter_bling_mode();
165
166 }
167
168 void*
169 TranzportControlProtocol::monitor_work ()
170 {
171         uint8_t buf[8]; //  = { 0,0,0,0,0,0,0,0 };
172         int val = 0, pending = 0;
173         bool first_time = true;
174         uint8_t offline = 0;
175
176         PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
177         pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
178         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
179         rtpriority_set();
180         inflight=0;
181         //int intro = 20;
182
183         // wait for the device to come online
184         invalidate();
185         screen_init();
186         lights_init();
187         update_state();
188 //      There has to be some specific command to enable the device!!
189 //      while((val = read(buf,DEFAULT_USB_TIMEOUT*5)) == -110 && pending !=0) {
190 //              pending = lights_flush(); // poke the device for a while
191 //      }
192
193 //      pending = 1;
194 //      while(intro-- > 0 && pending != 0) {
195 //              usleep(1000);
196 //              pending = screen_flush(); // kinder, gentler init
197 //      }
198 //      usleep(1000);
199 //      lights_on();
200 //      while(flush()!=0) ;
201 //      lights_off();
202         display_mode = DisplayNormal;
203
204         while (true) {
205
206                 /* bInterval for this beastie is 10ms */
207
208                 if (_device_status == STATUS_OFFLINE) {
209                         first_time = true; offline++; 
210 #if TRANZPORT_DEBUG > 3
211                         if(offline == 1) { 
212                                 cerr << "Transport has gone offline\n";
213                         }
214 #endif
215                 } else { 
216                         offline = 0; // hate writing this
217                 }
218                 unsigned int s = (last_write_error == 0) | ((last_read_error == 0) << 1);
219                 switch (s) {
220                 case 0: val = read(buf,DEFAULT_USB_TIMEOUT); break;
221                 case 1: val = read(buf,DEFAULT_USB_TIMEOUT); break;
222                 case 2: val = read(buf,DEFAULT_USB_TIMEOUT); break;
223                 case 3: val = read(buf,DEFAULT_USB_TIMEOUT*2); break; // Hoo, boy, we're in trouble
224                 default: break; // not reached
225                 }
226         
227 #if DEBUG_TRANZPORT_BITS > 9
228                 if(_device_status != STATUS_OFFLINE && _device_status != STATUS_ONLINE && _device_status != STATUS_OK) {
229                         printf("The device has more status bits than off or online: %d\n",_device_status);
230                 }
231 #endif
232
233 #if DEBUG_TRANZPORT_BITS > 99
234                 if (val != 8) {
235                         printf("val = %d errno = %d\n",val,errno);
236                         buf[0] = buf[1] = buf[2] = buf[3] = 
237                                 buf[4] = buf[5] = buf[6] = buf[7] = 
238                                 buf[8] = 0;
239                 }
240 #endif
241
242                 if(val == 8) {
243                         last_write_error = 0;
244                         process (buf);
245                 }
246
247 #if DEBUG_TRANZPORT > 9
248                 if(inflight > 1) printf("Inflight: %d\n", inflight);
249 #endif
250
251                 if (_device_status == STATUS_ONLINE) {
252                         if (first_time) {
253                                 invalidate();
254                                 lcd_clear ();
255                                 lights_off ();
256                                 first_time = false;
257                                 last_write_error = 0;
258                                 offline = 0;
259                                 pending = 3; // Give some time for the device to recover
260                         }
261 #if DEBUG_TRANZPORT_BITS > 10
262                         // Perhaps an online message indicates something
263
264                         if(_device_status != buf[1]) { 
265                                 printf("WTF- val: %d, device status != buf! %d != %d \n",val,_device_status,buf[1]); _device_status = buf[1]; 
266                         }
267 #endif
268     
269                 }
270     
271 #if DEBUG_TRANZPORT_BITS > 10
272
273                 if(val == 8) {
274
275                         if(_device_status == STATUS_ONLINE) {
276                                 printf("ONLINE   : %02x %02x %02x %02x %02x %02x %02x %02x\n", 
277                                        buf[0],buf[1],buf[2], buf[3], buf[4], buf[5],buf[6],buf[7]); 
278                         }
279                         if(_device_status == STATUS_OFFLINE) {
280                                 printf("OFFLINE  : %02x %02x %02x %02x %02x %02x %02x %02x\n", 
281                                        buf[0],buf[1],buf[2], buf[3], buf[4], buf[5],buf[6],buf[7]); 
282                         }
283         
284                         if(_device_status == STATUS_OK) {
285                                 printf("OK       : %02x %02x %02x %02x %02x %02x %02x %02x\n", 
286                                        buf[0],buf[1],buf[2], buf[3], buf[4], buf[5],buf[6],buf[7]); 
287                         }
288       
289                 }
290     
291 #endif
292     
293                 /* update whatever needs updating */
294                 if(last_write_error == 0 && (_device_status == STATUS_ONLINE || _device_status == STATUS_OK)) { 
295                         update_state ();
296       
297                         /* still struggling with a good means of exerting flow control without having to create threads */
298                         // pending = flush();
299       
300                         if(pending == 0) {
301                                 pending = flush(); 
302                         } else {
303                                 if(inflight > 0) {
304                                         pending = --inflight; // we just did a whole bunch of writes so wait
305                                 } else {
306                                         pending = 0;
307                                 }
308                         }
309                 }
310                 // pending = 0;
311         } 
312         return (void*) 0;
313 }
314