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