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