MTC slave, don't touch position if no MTC was received
[ardour.git] / libs / ardour / mtc_slave.cc
1 /*
2     Copyright (C) 2002-4 Paul Davis
3     Overhaul 2012 Robin Gareus <robin@gareus.org>
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 #include <iostream>
21 #include <errno.h>
22 #include <poll.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 #include "pbd/error.h"
27
28 #include "midi++/port.h"
29 #include "ardour/debug.h"
30 #include "ardour/slave.h"
31 #include "ardour/session.h"
32 #include "ardour/audioengine.h"
33 #include "ardour/pi_controller.h"
34
35 #include "i18n.h"
36
37 using namespace std;
38 using namespace ARDOUR;
39 using namespace MIDI;
40 using namespace PBD;
41
42 /* length (in timecode frames) of the "window" that we consider legal given receipt of
43    a given timecode position. Ardour will try to chase within this window, and will
44    stop+locate+wait+chase if timecode arrives outside of it. The window extends entirely
45    in the current direction of motion, so if any timecode arrives that is before the most
46    recently received position (and without the direction of timecode reversing too), we
47    will stop+locate+wait+chase.
48 */
49 const int MTC_Slave::frame_tolerance = 2;
50
51 MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
52         : session (s)
53 {
54         can_notify_on_unknown_rate = true;
55         did_reset_tc_format = false;
56         reset_pending = 0;
57         reset_position = false;
58         mtc_frame = 0;
59         engine_dll_initstate = 0;
60         busy_guard1 = busy_guard2 = 0;
61
62         last_mtc_fps_byte = session.get_mtc_timecode_bits ();
63
64         mtc_timecode = timecode_60; // track changes of MTC timecode
65         a3e_timecode = timecode_60; // track canges of Ardour's timecode
66         printed_timecode_warning = false;
67
68         reset (true);
69         rebind (p);
70 }
71
72 MTC_Slave::~MTC_Slave()
73 {
74         port_connections.drop_connections();
75
76         while (busy_guard1 != busy_guard2) {
77                 /* make sure MIDI parser is not currently calling any callbacks in here,
78                  * else there's a segfault ahead!
79                  *
80                  * XXX this is called from jack rt-context :(
81                  * TODO fix libs/ardour/session_transport.cc:1321 (delete _slave;)
82                  */
83                 sched_yield();
84         }
85
86         if (did_reset_tc_format) {
87                 session.config.set_timecode_format (saved_tc_format);
88         }
89 }
90
91 void
92 MTC_Slave::rebind (MIDI::Port& p)
93 {
94         port_connections.drop_connections ();
95
96         port = &p;
97
98         port->parser()->mtc_time.connect_same_thread (port_connections,  boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3));
99         port->parser()->mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3));
100         port->parser()->mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
101 }
102
103 bool
104 MTC_Slave::give_slave_full_control_over_transport_speed() const
105 {
106         return true; // DLL align to engine transport
107         // return false; // for Session-level computed varispeed
108 }
109
110 ARDOUR::framecnt_t
111 MTC_Slave::resolution () const
112 {
113         return (framecnt_t) session.frames_per_timecode_frame();
114 }
115
116 ARDOUR::framecnt_t
117 MTC_Slave::seekahead_distance () const
118 {
119         return session.frames_per_timecode_frame() * 2 * transport_direction;
120 }
121
122 bool
123 MTC_Slave::outside_window (framepos_t pos) const
124 {
125         return ((pos < window_begin) || (pos > window_end));
126 }
127
128
129 bool
130 MTC_Slave::locked () const
131 {
132         return port->parser()->mtc_locked();
133 }
134
135 bool
136 MTC_Slave::ok() const
137 {
138         return true;
139 }
140
141 void
142 MTC_Slave::queue_reset (bool reset_pos)
143 {
144         Glib::Threads::Mutex::Lock lm (reset_lock);
145         reset_pending++;
146         if (reset_pos) {
147                 reset_position = true;
148         }
149 }
150
151 void
152 MTC_Slave::maybe_reset ()
153 {
154         Glib::Threads::Mutex::Lock lm (reset_lock);
155
156         if (reset_pending) {
157                 reset (reset_position);
158                 reset_pending = 0;
159                 reset_position = false;
160         }
161 }
162
163 void
164 MTC_Slave::reset (bool with_position)
165 {
166         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC_Slave reset %1\n", with_position?"with position":"without position"));
167         if (with_position) {
168                 last_inbound_frame = 0;
169                 current.guard1++;
170                 current.position = 0;
171                 current.timestamp = 0;
172                 current.speed = 0;
173                 current.guard2++;
174         } else {
175                 last_inbound_frame = 0;
176                 current.guard1++;
177                 current.timestamp = 0;
178                 current.speed = 0;
179                 current.guard2++;
180         }
181         first_mtc_timestamp = 0;
182         window_begin = 0;
183         window_end = 0;
184         transport_direction = 1;
185 }
186
187 void
188 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
189 {
190         MIDI::byte mtc[5];
191         DEBUG_TRACE (DEBUG::MTC, "MTC_Slave::handle_locate\n");
192
193         mtc[4] = last_mtc_fps_byte;
194         mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
195         mtc[2] = mmc_tc[1];
196         mtc[1] = mmc_tc[2];
197         mtc[0] = mmc_tc[3];
198
199         update_mtc_time (mtc, true, 0);
200 }
201
202 void
203 MTC_Slave::read_current (SafeTime *st) const
204 {
205         int tries = 0;
206
207         do {
208                 if (tries == 10) {
209                         error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
210                         usleep (20);
211                         tries = 0;
212                 }
213                 *st = current;
214                 tries++;
215
216         } while (st->guard1 != st->guard2);
217 }
218
219 void
220 MTC_Slave::init_mtc_dll(framepos_t mtc_frame, double qtr)
221 {
222         omega = 2.0 * M_PI * (session.frames_per_timecode_frame() / 4.0) / double(session.frame_rate());
223         b = 1.4142135623730950488 * omega;
224         c = omega * omega;
225
226         e2 = qtr;
227         t0 = double(mtc_frame);
228         t1 = t0 + e2;
229         DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
230 }
231
232
233 /* called from MIDI parser */
234 void
235 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now)
236 {
237         busy_guard1++;
238         const framepos_t qtr = (session.frames_per_timecode_frame() / 4);
239         const double qtr_d = (session.frames_per_timecode_frame() / 4.0);
240
241         mtc_frame += qtr * transport_direction;
242
243         DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
244
245         double mtc_speed = 0;
246         if (first_mtc_timestamp != 0) {
247                 /* update MTC DLL and calculate speed */
248                 const double e = double(transport_direction) * (double(now) - double(current.timestamp) - qtr_d);
249                 t0 = t1;
250                 t1 += b * e + e2;
251                 e2 += c * e;
252
253                 mtc_speed = (t1 - t0)  / qtr_d;
254                 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame DLL t0:%1 t1:%2 err:%3 spd:%4\n", t0, t1, e, mtc_speed));
255         }
256
257         current.guard1++;
258         current.position = mtc_frame;
259         current.timestamp = now;
260         current.speed = mtc_speed;
261         current.guard2++;
262
263         maybe_reset ();
264         last_inbound_frame = now;
265
266         busy_guard2++;
267 }
268
269 /* called from MIDI parser _after_ update_mtc_qtr()
270  * when a full TC has been received
271  * OR on locate */
272 void
273 MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now)
274 {
275         busy_guard1++;
276
277         /* "now" can be zero if this is called from a context where we do not have or do not want
278            to use a timestamp indicating when this MTC time was received. example: when we received
279            a locate command via MMC.
280         */
281
282         //DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self()));
283         Timecode::Time timecode;
284         TimecodeFormat tc_format;
285         bool reset_tc = true;
286
287         timecode.hours = msg[3];
288         timecode.minutes = msg[2];
289         timecode.seconds = msg[1];
290         timecode.frames = msg[0];
291
292         last_mtc_fps_byte = msg[4];
293
294         DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
295
296         if (now) {
297                 maybe_reset ();
298         }
299
300         switch (msg[4]) {
301         case MTC_24_FPS:
302                 timecode.rate = 24;
303                 timecode.drop = false;
304                 tc_format = timecode_24;
305                 can_notify_on_unknown_rate = true;
306                 break;
307         case MTC_25_FPS:
308                 timecode.rate = 25;
309                 timecode.drop = false;
310                 tc_format = timecode_25;
311                 can_notify_on_unknown_rate = true;
312                 break;
313         case MTC_30_FPS_DROP:
314                 timecode.rate = 30;
315                 timecode.drop = true;
316                 tc_format = timecode_30drop;
317                 can_notify_on_unknown_rate = true;
318                 break;
319         case MTC_30_FPS:
320                 timecode.rate = 30;
321                 timecode.drop = false;
322                 can_notify_on_unknown_rate = true;
323                 tc_format = timecode_30;
324                 break;
325         default:
326                 /* throttle error messages about unknown MTC rates */
327                 if (can_notify_on_unknown_rate) {
328                         error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
329                                                  (int) msg[4])
330                               << endmsg;
331                         can_notify_on_unknown_rate = false;
332                 }
333                 timecode.rate = session.timecode_frames_per_second();
334                 timecode.drop = session.timecode_drop_frames();
335                 reset_tc = false;
336         }
337
338         if (reset_tc) {
339                 TimecodeFormat cur_timecode = session.config.get_timecode_format();
340                 if (0 /* TODO preferences  */) {
341                         /* enforce time-code */
342                         if (!did_reset_tc_format) {
343                                 saved_tc_format = cur_timecode;
344                                 did_reset_tc_format = true;
345                         }
346                         if (cur_timecode != tc_format) {
347                                 warning << _("Session and MTC framerate mismatch.") << endmsg;
348                         }
349                         session.config.set_timecode_format (tc_format);
350                 } else {
351                         /* only warn about TC mismatch */
352                         if (mtc_timecode != tc_format) printed_timecode_warning = false;
353                         if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
354
355                         if (cur_timecode != tc_format && ! printed_timecode_warning) {
356                                 warning << _("Session and MTC framerate mismatch.") << endmsg;
357                                 printed_timecode_warning = true;
358                         }
359                 }
360                 mtc_timecode = tc_format;
361                 a3e_timecode = cur_timecode;
362         }
363
364         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4)\n",
365                                                  now, timecode, mtc_frame, was_full));
366
367         if (was_full || outside_window (mtc_frame)) {
368                 DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC or outside window. - TID:%1\n", ::pthread_self()));
369                 session.timecode_to_sample (timecode, mtc_frame, true, false); // sets mtc_frame
370                 session.request_locate (mtc_frame, false);
371                 session.request_transport_speed (0);
372                 update_mtc_status (MIDI::MTC_Stopped);
373                 reset (false);
374                 reset_window (mtc_frame);
375
376         } else {
377
378                 /* we've had the first set of 8 qtr frame messages, determine position
379                    and allow continuing qtr frame messages to provide position
380                    and speed information.
381                 */
382
383                 /* do a careful conversion of the timecode value to a position
384                    so that we take drop/nondrop and all that nonsense into
385                    consideration.
386                 */
387
388                 session.timecode_to_sample (timecode, mtc_frame, true, false); // sets mtc_frame
389
390                 /* We received the last quarter frame 7 quarter frames (1.75 mtc
391                    frames) after the instance when the contents of the mtc quarter
392                    frames were decided. Add time to compensate for the elapsed 1.75
393                    frames. Also compensate for audio latency.
394                 */
395
396                 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame = %1 | %2 FPTC | LAT: %3\n",
397                                                          mtc_frame, session.frames_per_timecode_frame(), session.worst_playback_latency()));
398
399                 long int mtc_off = (long) (1.75 * session.frames_per_timecode_frame()) + session.worst_playback_latency();
400                 double qtr = (session.frames_per_timecode_frame() / 4.0);
401
402                 switch (port->parser()->mtc_running()) {
403                 case MTC_Backward:
404                         mtc_frame -= mtc_off;
405                         qtr *= -1.0;
406                         break;
407                 case MTC_Forward:
408                         mtc_frame += mtc_off;
409                         break;
410                 default:
411                         break;
412                 }
413
414                 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/latency comp) = %1\n", mtc_frame));
415
416                 if (now) {
417                         if (first_mtc_timestamp == 0 || current.timestamp == 0) {
418                                 first_mtc_timestamp = now;
419                                 init_mtc_dll(mtc_frame, qtr);
420                         }
421                         current.guard1++;
422                         current.position = mtc_frame;
423                         current.timestamp = now;
424                         current.guard2++;
425                         reset_window (mtc_frame);
426                 }
427         }
428
429         if (now) {
430                 last_inbound_frame = now;
431         }
432         busy_guard2++;
433 }
434
435 void
436 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
437 {
438         /* XXX !!! thread safety ... called from MIDI I/O context
439          * on locate (via ::update_mtc_time())
440          */
441         DEBUG_TRACE (DEBUG::MTC, string_compose("MTC_Slave::update_mtc_status - TID:%1\n", ::pthread_self()));
442         return; // why was this fn needed anyway ? it just messes up things -> use reset.
443         busy_guard1++;
444
445         switch (status) {
446         case MTC_Stopped:
447                 current.guard1++;
448                 current.position = mtc_frame;
449                 current.timestamp = 0;
450                 current.speed = 0;
451                 current.guard2++;
452
453                 break;
454
455         case MTC_Forward:
456                 current.guard1++;
457                 current.position = mtc_frame;
458                 current.timestamp = 0;
459                 current.speed = 0;
460                 current.guard2++;
461                 break;
462
463         case MTC_Backward:
464                 current.guard1++;
465                 current.position = mtc_frame;
466                 current.timestamp = 0;
467                 current.speed = 0;
468                 current.guard2++;
469                 break;
470         }
471         busy_guard2++;
472 }
473
474 void
475 MTC_Slave::reset_window (framepos_t root)
476 {
477         /* if we're waiting for the master to catch us after seeking ahead, keep the window
478            of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
479            ahead of the window root (taking direction into account).
480         */
481
482         switch (port->parser()->mtc_running()) {
483         case MTC_Forward:
484                 window_begin = root;
485                 transport_direction = 1;
486                 if (session.slave_state() == Session::Running) {
487                         window_end = root + (session.frames_per_timecode_frame() * frame_tolerance);
488                 } else {
489                         window_end = root + labs(seekahead_distance ());
490                 }
491                 break;
492
493         case MTC_Backward:
494                 transport_direction = -1;
495                 if (session.slave_state() == Session::Running) {
496                         framecnt_t const d = session.frames_per_timecode_frame() * frame_tolerance;
497                         if (root > d) {
498                                 window_begin = root - d;
499                                 window_end = root;
500                         } else {
501                                 window_begin = 0;
502                         }
503                 } else {
504                         framecnt_t const d = labs(seekahead_distance ());
505                         if (root > d) {
506                                 window_begin = root - d;
507                         } else {
508                                 window_begin = 0;
509                         }
510                 }
511                 window_end = root;
512                 break;
513
514         default:
515                 /* do nothing */
516                 break;
517         }
518
519         DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
520 }
521
522 void
523 MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc)
524 {
525         /* the bandwidth of the DLL is a trade-off,
526          * because the max-speed of the transport in ardour is
527          * limited to +-8.0, a larger bandwidth would cause oscillations
528          *
529          * But this is only really a problem if the user performs manual
530          * seeks while transport is running and slaved to MTC.
531          */
532         oe = 2.0 * M_PI * double(inc/6.0) / double(session.frame_rate());
533         be = 1.4142135623730950488 * oe;
534         ce = oe * oe;
535
536         ee2 = double(transport_direction * inc);
537         te0 = double(pos);
538         te1 = te0 + ee2;
539         DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init Engine DLL %1 %2 %3\n", te0, te1, ee2));
540 }
541
542 /* main entry point from session_process.cc
543  * in jack_process callback context */
544 bool
545 MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
546 {
547         framepos_t now = session.engine().frame_time_at_cycle_start();
548         framepos_t sess_pos = session.transport_frame(); // corresponds to now
549
550         SafeTime last;
551         framecnt_t elapsed;
552
553         read_current (&last);
554
555         /* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */
556         if (last.timestamp == 0) { engine_dll_initstate = 0; }
557         else if (engine_dll_initstate != transport_direction) { 
558                 engine_dll_initstate = transport_direction;
559                 init_engine_dll(last.position, session.engine().frames_per_cycle());
560         }
561
562         if (last.timestamp == 0) {
563                 speed = 0;
564                 pos = session.transport_frame() ; // last.position;
565                 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", pos));
566                 return true;
567         }
568
569         /* no timecode for two frames - conclude that it's stopped */
570         if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > labs(seekahead_distance())) {
571                 speed = 0;
572                 pos = last.position;
573                 session.request_locate (pos, false);
574                 session.request_transport_speed (0);
575                 queue_reset (false);
576                 DEBUG_TRACE (DEBUG::MTC, "MTC not seen for 2 frames - reset pending\n");
577                 return false;
578         }
579
580
581
582         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position mtc-tme: %1 mtc-pos: %2\n", last.timestamp, last.position));
583         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position eng-tme: %1 eng-pos: %2\n", now, sess_pos));
584
585         double speed_flt = last.speed; ///< MTC speed from MTC-quarter-frame DLL
586
587         /* interpolate position according to speed and time since last quarter-frame*/
588         if (speed_flt == 0.0f) {
589                 elapsed = 0;
590         }
591         else
592         {
593                 /* scale elapsed time by the current MTC speed */
594                 if (last.timestamp && (now > last.timestamp)) {
595                         elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp));
596                 } else {
597                         elapsed = 0;
598                 }
599                 if (give_slave_full_control_over_transport_speed()) {
600                         /* there is a frame-delta engine vs MTC position
601                          * mostly due to quantization and rounding of (speed * nframes)
602                          * thus we use an other DLL..
603                          */
604
605                         /* update engine DLL and calculate speed */
606                         const double e = double (last.position + elapsed - sess_pos);
607                         te0 = te1;
608                         te1 += be * e + ee2;
609                         ee2 += ce * e;
610                         speed_flt = (te1 - te0) / double(session.engine().frames_per_cycle());
611                         DEBUG_TRACE (DEBUG::MTC, string_compose ("engine DLL t0:%1 t1:%2 err:%3 spd:%4\n", te0, te1, e, speed_flt));
612                 }
613         }
614
615         pos = last.position + elapsed;
616         speed = speed_flt;
617
618         /* may happen if the user performs a seek in the timeline while slaved to running MTC
619          * engine-DLL can oscillate back before 0.
620          * also see note in MTC_Slave::init_engine_dll
621          */
622         if (pos <0) queue_reset(true);
623
624         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n",
625                                                  speed, pos, last.position, elapsed,  pos - sess_pos));
626
627         return true;
628 }