more tweaks to MTC slave code (still not functional), including removing race conditi...
[ardour.git] / libs / ardour / mtc_slave.cc
1 /*
2     Copyright (C) 2002-4 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 #include <iostream>
20 #include <errno.h>
21 #include <poll.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include "pbd/error.h"
25 #include "pbd/enumwriter.h"
26 #include "pbd/failed_constructor.h"
27 #include "pbd/pthread_utils.h"
28
29 #include "midi++/port.h"
30 #include "ardour/debug.h"
31 #include "ardour/slave.h"
32 #include "ardour/session.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/pi_controller.h"
35
36 #include "i18n.h"
37
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace sigc;
41 using namespace MIDI;
42 using namespace PBD;
43
44 /* length (in timecode frames) of the "window" that we consider legal given receipt of
45    a given timecode position. Ardour will try to chase within this window, and will
46    stop+locate+wait+chase if timecode arrives outside of it. The window extends entirely
47    in the current direction of motion, so if any timecode arrives that is before the most
48    recently received position (and without the direction of timecode reversing too), we
49    will stop+locate+wait+chase.
50 */
51
52 const int MTC_Slave::frame_tolerance = 2;
53
54 MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
55         : session (s)
56 {
57         can_notify_on_unknown_rate = true;
58         did_reset_tc_format = false;
59
60         pic = new PIController (1.0, 8);
61         
62         last_mtc_fps_byte = session.get_mtc_timecode_bits ();
63         mtc_frame = 0;
64
65         speed_accumulator_size = 16;
66         speed_accumulator = new double[speed_accumulator_size];
67
68         rebind (p);
69         reset ();
70 }
71
72 MTC_Slave::~MTC_Slave()
73 {
74         if (did_reset_tc_format) {
75                 session.config.set_timecode_format (saved_tc_format);
76         }
77         delete pic;
78         delete [] speed_accumulator;
79 }
80
81 bool 
82 MTC_Slave::give_slave_full_control_over_transport_speed() const
83 {
84         // return true; // for PiC control */
85         return false; // for Session-level computed varispeed
86 }
87
88 void
89 MTC_Slave::rebind (MIDI::Port& p)
90 {
91         for (vector<sigc::connection>::iterator i = connections.begin(); i != connections.end(); ++i) {
92                 (*i).disconnect ();
93         }
94
95         port = &p;
96
97         connections.push_back (port->input()->mtc_time.connect (mem_fun (*this, &MTC_Slave::update_mtc_time)));
98         connections.push_back (port->input()->mtc_qtr.connect (mem_fun (*this, &MTC_Slave::update_mtc_qtr)));
99         connections.push_back (port->input()->mtc_status.connect (mem_fun (*this, &MTC_Slave::update_mtc_status)));
100 }
101
102 void
103 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, nframes_t now)
104 {
105         maybe_reset ();
106
107         DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2\n", which_qtr, now));
108         last_inbound_frame = now;
109 }
110
111 void
112 MTC_Slave::update_mtc_time (const byte *msg, bool was_full, nframes_t now)
113 {
114         /* "now" can be zero if this is called from a context where we do not have or do not want
115            to use a timestamp indicating when this MTC time was received. example: when we received
116            a locate command via MMC.
117         */
118
119         if (now) {
120                 maybe_reset ();
121         }
122
123         Timecode::Time timecode;
124         TimecodeFormat tc_format;
125         bool reset_tc = true;
126         nframes64_t window_root = -1;
127
128         DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
129         
130         timecode.hours = msg[3];
131         timecode.minutes = msg[2];
132         timecode.seconds = msg[1];
133         timecode.frames = msg[0];
134
135         last_mtc_fps_byte = msg[4];
136
137         switch (msg[4]) {
138         case MTC_24_FPS:
139                 timecode.rate = 24;
140                 timecode.drop = false;
141                 tc_format = timecode_24;
142                 can_notify_on_unknown_rate = true;
143                 break;
144         case MTC_25_FPS:
145                 timecode.rate = 25;
146                 timecode.drop = false;
147                 tc_format = timecode_25;
148                 can_notify_on_unknown_rate = true;
149                 break;
150         case MTC_30_FPS_DROP:
151                 timecode.rate = 30;
152                 timecode.drop = true;
153                 tc_format = timecode_30drop;
154                 can_notify_on_unknown_rate = true;
155                 break;
156         case MTC_30_FPS:
157                 timecode.rate = 30;
158                 timecode.drop = false;
159                 can_notify_on_unknown_rate = true;
160                 tc_format = timecode_30;
161                 break;
162         default:
163                 /* throttle error messages about unknown MTC rates */
164                 if (can_notify_on_unknown_rate) {
165                         error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
166                                                  (int) msg[4])
167                               << endmsg;
168                         can_notify_on_unknown_rate = false;
169                 }
170                 timecode.rate = session.timecode_frames_per_second();
171                 timecode.drop = session.timecode_drop_frames();
172                 reset_tc = false;
173         }
174
175         if (reset_tc) {
176                 if (!did_reset_tc_format) {
177                         saved_tc_format = session.config.get_timecode_format();
178                         did_reset_tc_format = true;
179                 }
180                 session.config.set_timecode_format (tc_format);
181         }
182
183         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC time timestamp = %1 TC %2 = frame %3 (from full message ? %4)\n", 
184                                                  now, timecode, mtc_frame, was_full));
185
186         if (was_full || outside_window (mtc_frame)) {
187
188                 session.timecode_to_sample (timecode, mtc_frame, true, false);
189                 session.request_locate (mtc_frame, false);
190                 session.request_transport_speed (0);
191                 update_mtc_status (MIDI::MTC_Stopped);
192                 reset_window (mtc_frame);
193                 reset ();
194
195         } else {
196                         
197                 /* we've had the first set of 8 qtr frame messages, determine position
198                    and allow continuing qtr frame messages to provide position
199                    and speed information.
200                 */
201                 
202                 /* do a careful conversion of the timecode value to a position
203                    so that we take drop/nondrop and all that nonsense into 
204                    consideration.
205                 */
206
207                 session.timecode_to_sample (timecode, mtc_frame, true, false);
208                 
209                 /* We received the last quarter frame 7 quarter frames (1.75 mtc
210                    frames) after the instance when the contents of the mtc quarter
211                    frames were decided. Add time to compensate for the elapsed 1.75
212                    frames. Also compensate for audio latency.
213                 */
214                 
215                 mtc_frame += (long) (1.75 * session.frames_per_timecode_frame()) + session.worst_output_latency();
216
217
218                 if (now) {
219
220                         if (last_mtc_timestamp == 0) {
221
222                                 last_mtc_timestamp = now;
223                                 last_mtc_frame = mtc_frame;
224
225                         } else {
226
227                                 if (give_slave_full_control_over_transport_speed()) {
228                                         /* PIC 
229                                          * 
230                                          * its not the average, but we will assign it to current.speed below
231                                          */
232                                         
233                                         average_speed = pic->get_ratio (session.audible_frame() - mtc_frame);
234                                         
235                                 } else {
236
237                                         /* Non-PiC 
238                                          */
239
240                                         nframes64_t time_delta = (now - last_mtc_timestamp);
241                                         
242                                         if (time_delta != 0) {
243                                                 double apparent_speed = (mtc_frame - last_mtc_frame) / (double) (time_delta);
244                                                 
245                                                 process_apparent_speed (apparent_speed);
246                                                 DEBUG_TRACE (DEBUG::Slave, string_compose ("apparent speed was %1 average is now %2\n", apparent_speed, average_speed));
247                                         } else {
248                                                 DEBUG_TRACE (DEBUG::Slave, string_compose ("no apparent calc, average is %1\n", average_speed));
249                                         }
250                                         
251                                         /* every second, recalibrate the starting point for the speed measurement */
252                                         if (mtc_frame - last_mtc_frame > session.frame_rate()) {
253                                                 last_mtc_timestamp = now;
254                                                 last_mtc_frame = mtc_frame;
255                                         }
256                                 }
257                         }
258
259                         current.guard1++;
260                         current.position = mtc_frame;
261                         current.timestamp = now;
262                         current.speed = average_speed;
263                         current.guard2++;
264                         window_root = mtc_frame;
265                 }
266         }
267
268         if (now) {
269                 last_inbound_frame = now;
270         }
271
272         if (window_root >= 0) {
273                 reset_window (window_root);
274         }
275 }
276
277 void
278 MTC_Slave::process_apparent_speed (double this_speed)
279 {
280         DEBUG_TRACE (DEBUG::MTC, string_compose ("speed cnt %1 sz %2 have %3\n", speed_accumulator_cnt, speed_accumulator_size, have_first_speed_accumulator));
281
282         /* clamp to an expected range */
283
284         if (this_speed > 4.0 || this_speed < -4.0) {
285                 this_speed = average_speed;
286         }
287
288         if (speed_accumulator_cnt >= speed_accumulator_size) {
289                 have_first_speed_accumulator = true;
290                 speed_accumulator_cnt = 0;
291         }
292
293         speed_accumulator[speed_accumulator_cnt++] = this_speed;
294
295         if (have_first_speed_accumulator) {
296                 average_speed = 0.0;
297                 for (size_t i = 0; i < speed_accumulator_size; ++i) {
298                         average_speed += speed_accumulator[i];
299                 }
300                 average_speed /= speed_accumulator_size;
301         }
302 }
303
304 void
305 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
306 {
307         MIDI::byte mtc[5];
308
309         mtc[4] = last_mtc_fps_byte;
310         mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
311         mtc[2] = mmc_tc[1];
312         mtc[1] = mmc_tc[2];
313         mtc[0] = mmc_tc[3];
314
315         update_mtc_time (mtc, true, 0);
316 }
317
318 void
319 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
320 {
321         /* XXX !!! thread safety ... called from MIDI I/O context
322            and process() context (via ::speed_and_position())
323         */
324
325
326         DEBUG_TRACE (DEBUG::MTC, string_compose ("new MTC status %1\n", enum_2_string (status)));
327         switch (status) {
328         case MTC_Stopped:
329                 current.guard1++;
330                 current.position = mtc_frame;
331                 current.timestamp = 0;
332                 current.speed = 0;
333                 current.guard2++;
334
335                 break;
336
337         case MTC_Forward:
338                 current.guard1++;
339                 current.position = mtc_frame;
340                 current.timestamp = 0;
341                 current.speed = 0;
342                 current.guard2++;
343                 break;
344
345         case MTC_Backward:
346                 current.guard1++;
347                 current.position = mtc_frame;
348                 current.timestamp = 0;
349                 current.speed = 0;
350                 current.guard2++;
351                 break;
352         }
353         
354 }
355
356 void
357 MTC_Slave::read_current (SafeTime *st) const
358 {
359         int tries = 0;
360
361         do {
362                 if (tries == 10) {
363                         error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
364                         usleep (20);
365                         tries = 0;
366                 }
367                 *st = current;
368                 tries++;
369
370         } while (st->guard1 != st->guard2);
371 }
372
373 bool
374 MTC_Slave::locked () const
375 {
376         return port->input()->mtc_locked();
377 }
378
379 bool
380 MTC_Slave::ok() const
381 {
382         return true;
383 }
384
385 bool
386 MTC_Slave::speed_and_position (double& speed, nframes64_t& pos)
387 {
388         nframes64_t now = session.engine().frame_time();
389         SafeTime last;
390         nframes_t elapsed;
391
392         read_current (&last);
393
394         if (last.timestamp == 0) {
395                 speed = 0;
396                 pos = last.position;
397                 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", last.position));
398                 return true;
399         }
400
401         /* no timecode for 1/4 second ? conclude that its stopped */
402
403         if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > session.frame_rate() / 4) {
404                 speed = 0;
405                 pos = last.position;
406                 session.request_locate (pos, false);
407                 session.request_transport_speed (0);
408                 queue_reset ();
409                 DEBUG_TRACE (DEBUG::MTC, "MTC not seen for 1/4 second - reset pending\n");
410                 return false;
411         }
412
413         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position %1 %2\n", last.speed, last.position));
414
415         if (last.speed == 0.0f) {
416
417                 elapsed = 0;
418
419         } else {
420
421                 /* scale elapsed time by the current MTC speed */
422
423                 if (last.timestamp && (now > last.timestamp)) {
424                         elapsed = (nframes_t) floor (last.speed * (now - last.timestamp));
425                         DEBUG_TRACE (DEBUG::MTC, string_compose ("last timecode received @ %1, now = %2, elapsed frames = %3 w/speed= %4\n",
426                                                                  last.timestamp, now, elapsed, last.speed));
427                 } else {
428                         elapsed = 0; /* XXX is this right? */
429                 }
430         }
431
432         /* now add the most recent timecode value plus the estimated elapsed interval */
433
434         pos =  elapsed + last.position;
435         speed = last.speed;
436
437         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position FINAL %1 %2\n", last.speed, pos));
438
439         return true;
440 }
441
442 ARDOUR::nframes_t
443 MTC_Slave::resolution() const
444 {
445         return (nframes_t) session.frames_per_timecode_frame();
446 }
447
448 void
449 MTC_Slave::queue_reset ()
450 {
451         Glib::Mutex::Lock lm (reset_lock);
452         reset_pending++;
453 }
454
455 void
456 MTC_Slave::maybe_reset ()
457 {
458         reset_lock.lock ();
459
460         if (reset_pending) {
461                 reset ();
462                 reset_pending = 0;
463         } 
464
465         reset_lock.unlock ();
466 }
467
468 void
469 MTC_Slave::reset ()
470 {
471         port->input()->reset_mtc_state ();
472
473         last_inbound_frame = 0;
474         current.guard1++;
475         current.position = 0;
476         current.timestamp = 0;
477         current.speed = 0;
478         current.guard2++;
479
480         window_begin = 0;
481         window_end = 0;
482         last_mtc_frame = 0;
483         last_mtc_timestamp = 0;
484
485         average_speed = 0;
486         have_first_speed_accumulator = false;
487         speed_accumulator_cnt = 0;
488
489         pic->out_of_bounds();
490 }
491
492 void
493 MTC_Slave::reset_window (nframes64_t root)
494 {
495         
496         /* if we're waiting for the master to catch us after seeking ahead, keep the window
497            of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
498            ahead of the window root (taking direction into account).
499         */
500
501         switch (port->input()->mtc_running()) {
502         case MTC_Forward:
503                 window_begin = root;
504                 if (session.slave_state() == Session::Running) {
505                         window_end = root + (session.frames_per_timecode_frame() * frame_tolerance);
506                 } else {
507                         window_end = root + seekahead_distance ();
508                 }
509                 DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
510                 break;
511
512         case MTC_Backward:
513                 if (session.slave_state() == Session::Running) {
514                         nframes_t d = session.frames_per_timecode_frame() * frame_tolerance;
515                         if (root > d) {
516                                 window_begin = root - d;
517                                 window_end = root;
518                         } else {
519                                 window_begin = 0;
520                         }
521                 } else {
522                         nframes_t d = seekahead_distance ();
523                         if (root > d) {
524                                 window_begin = root - d;
525                         } else {
526                                 window_begin = 0;
527                         }
528                 }
529                 window_end = root;
530                 DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
531                 break;
532                 
533         default:
534                 /* do nothing */
535                 break;
536         }
537 }
538
539 nframes64_t
540 MTC_Slave::seekahead_distance () const
541 {
542         /* 1 second */
543         return session.frame_rate();
544 }
545
546 bool
547 MTC_Slave::outside_window (nframes64_t pos) const
548 {
549         return ((pos < window_begin) || (pos > window_end));
550 }