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