fix crash caused by trying to connect monitor section before it has ports.
[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 "pbd/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.samples_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         ActiveChanged(false);
217 }
218
219 void
220 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
221 {
222         MIDI::byte mtc[5];
223         DEBUG_TRACE (DEBUG::MTC, "MTC_Slave::handle_locate\n");
224
225         mtc[4] = last_mtc_fps_byte;
226         mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
227         mtc[2] = mmc_tc[1];
228         mtc[1] = mmc_tc[2];
229         mtc[0] = mmc_tc[3];
230
231         update_mtc_time (mtc, true, 0);
232 }
233
234 void
235 MTC_Slave::read_current (SafeTime *st) const
236 {
237         int tries = 0;
238
239         do {
240                 if (tries == 10) {
241                         error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
242                         Glib::usleep (20);
243                         tries = 0;
244                 }
245                 *st = current;
246                 tries++;
247
248         } while (st->guard1 != st->guard2);
249 }
250
251 void
252 MTC_Slave::init_mtc_dll(framepos_t tme, double qtr)
253 {
254         omega = 2.0 * M_PI * qtr / 2.0 / double(session.frame_rate());
255         b = 1.4142135623730950488 * omega;
256         c = omega * omega;
257
258         e2 = qtr;
259         t0 = double(tme);
260         t1 = t0 + e2;
261         DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
262 }
263
264 /* called from MIDI parser */
265 void
266 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now)
267 {
268         busy_guard1++;
269         const double qtr_d = quarter_frame_duration;
270
271         mtc_frame_dll += qtr_d * (double) transport_direction;
272         mtc_frame = rint(mtc_frame_dll);
273
274         DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
275
276         double mtc_speed = 0;
277         if (first_mtc_timestamp != 0) {
278                 /* update MTC DLL and calculate speed */
279                 const double e = mtc_frame_dll - (double)transport_direction * ((double)now - (double)current.timestamp + t0);
280                 t0 = t1;
281                 t1 += b * e + e2;
282                 e2 += c * e;
283
284                 mtc_speed = (t1 - t0) / qtr_d;
285                 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));
286
287                 current.guard1++;
288                 current.position = mtc_frame;
289                 current.timestamp = now;
290                 current.speed = mtc_speed;
291                 current.guard2++;
292
293                 last_inbound_frame = now;
294         }
295
296         maybe_reset ();
297
298         busy_guard2++;
299 }
300
301 /* called from MIDI parser _after_ update_mtc_qtr()
302  * when a full TC has been received
303  * OR on locate */
304 void
305 MTC_Slave::update_mtc_time (const MIDI::byte *msg, bool was_full, framepos_t now)
306 {
307         busy_guard1++;
308
309         /* "now" can be zero if this is called from a context where we do not have or do not want
310            to use a timestamp indicating when this MTC time was received. example: when we received
311            a locate command via MMC.
312         */
313         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", pthread_name()));
314         TimecodeFormat tc_format;
315         bool reset_tc = true;
316
317         timecode.hours = msg[3];
318         timecode.minutes = msg[2];
319         timecode.seconds = msg[1];
320         timecode.frames = msg[0];
321
322         last_mtc_fps_byte = msg[4];
323
324         DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
325
326         if (now) {
327                 maybe_reset ();
328         }
329
330         switch (msg[4]) {
331         case MTC_24_FPS:
332                 timecode.rate = 24;
333                 timecode.drop = false;
334                 tc_format = timecode_24;
335                 can_notify_on_unknown_rate = true;
336                 break;
337         case MTC_25_FPS:
338                 timecode.rate = 25;
339                 timecode.drop = false;
340                 tc_format = timecode_25;
341                 can_notify_on_unknown_rate = true;
342                 break;
343         case MTC_30_FPS_DROP:
344                 if (Config->get_timecode_source_2997()) {
345                         tc_format = Timecode::timecode_2997000drop;
346                         timecode.rate = (29970.0/1000.0);
347                 } else {
348                         tc_format = timecode_2997drop;
349                         timecode.rate = (30000.0/1001.0);
350                 }
351                 timecode.drop = true;
352                 can_notify_on_unknown_rate = true;
353                 break;
354         case MTC_30_FPS:
355                 timecode.rate = 30;
356                 timecode.drop = false;
357                 can_notify_on_unknown_rate = true;
358                 tc_format = timecode_30;
359                 break;
360         default:
361                 /* throttle error messages about unknown MTC rates */
362                 if (can_notify_on_unknown_rate) {
363                         error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
364                                                  (int) msg[4])
365                               << endmsg;
366                         can_notify_on_unknown_rate = false;
367                 }
368                 timecode.rate = session.timecode_frames_per_second();
369                 timecode.drop = session.timecode_drop_frames();
370                 reset_tc = false;
371         }
372
373         if (reset_tc) {
374                 TimecodeFormat cur_timecode = session.config.get_timecode_format();
375                 if (Config->get_timecode_sync_frame_rate()) {
376                         /* enforce time-code */
377                         if (!did_reset_tc_format) {
378                                 saved_tc_format = cur_timecode;
379                                 did_reset_tc_format = true;
380                         }
381                         if (cur_timecode != tc_format) {
382                                 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
383                                         warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."),
384                                                         Timecode::timecode_format_name(cur_timecode),
385                                                         Timecode::timecode_format_name(tc_format))
386                                                 << endmsg;
387                                 }
388                         }
389                         session.config.set_timecode_format (tc_format);
390                 } else {
391                         /* only warn about TC mismatch */
392                         if (mtc_timecode != tc_format) printed_timecode_warning = false;
393                         if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
394
395                         if (cur_timecode != tc_format && ! printed_timecode_warning) {
396                                 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
397                                         warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."),
398                                                                   Timecode::timecode_format_name(tc_format),
399                                                                   PROGRAM_NAME,
400                                                                   Timecode::timecode_format_name(cur_timecode))
401                                                 << endmsg;
402                                 }
403                                 printed_timecode_warning = true;
404                         }
405                 }
406                 mtc_timecode = tc_format;
407                 a3e_timecode = cur_timecode;
408
409                 speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode);
410         }
411
412         /* do a careful conversion of the timecode value to a position
413            so that we take drop/nondrop and all that nonsense into
414            consideration.
415         */
416
417         quarter_frame_duration = (double(session.frame_rate()) / (double) timecode.rate / 4.0);
418
419         Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
420                 double(session.frame_rate()),
421                 session.config.get_subframes_per_frame(),
422                 timecode_negative_offset, timecode_offset
423                 );
424
425         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
426                                                  now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
427
428         if (was_full || outside_window (mtc_frame)) {
429                 DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC %1 or outside window %2 MTC %3\n", was_full, outside_window (mtc_frame), mtc_frame));
430                 session.set_requested_return_frame (-1);
431                 session.request_transport_speed (0);
432                 session.request_locate (mtc_frame, false);
433                 update_mtc_status (MIDI::MTC_Stopped);
434                 reset (false);
435                 reset_window (mtc_frame);
436         } else {
437
438                 /* we've had the first set of 8 qtr frame messages, determine position
439                    and allow continuing qtr frame messages to provide position
440                    and speed information.
441                 */
442
443                 /* We received the last quarter frame 7 quarter frames (1.75 mtc
444                    frames) after the instance when the contents of the mtc quarter
445                    frames were decided. Add time to compensate for the elapsed 1.75
446                    frames.
447                 */
448                 double qtr = quarter_frame_duration;
449                 long int mtc_off = (long) rint(7.0 * qtr);
450
451                 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
452                                                          mtc_frame, (4.0*qtr), session.samples_per_timecode_frame()));
453
454                 switch (port->self_parser().mtc_running()) {
455                 case MTC_Backward:
456                         mtc_frame -= mtc_off;
457                         qtr *= -1.0;
458                         break;
459                 case MTC_Forward:
460                         mtc_frame += mtc_off;
461                         break;
462                 default:
463                         break;
464                 }
465
466                 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
467
468                 if (now) {
469                         if (first_mtc_timestamp == 0 || current.timestamp == 0) {
470                                 first_mtc_timestamp = now;
471                                 init_mtc_dll(mtc_frame, qtr);
472                                 mtc_frame_dll = mtc_frame;
473                                 ActiveChanged (true); // emit signal
474                         }
475                         current.guard1++;
476                         current.position = mtc_frame;
477                         current.timestamp = now;
478                         current.guard2++;
479                         reset_window (mtc_frame);
480                 }
481         }
482
483         if (now) {
484                 last_inbound_frame = now;
485         }
486         busy_guard2++;
487 }
488
489 void
490 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
491 {
492         /* XXX !!! thread safety ... called from MIDI I/O context
493          * on locate (via ::update_mtc_time())
494          */
495         DEBUG_TRACE (DEBUG::MTC, string_compose("MTC_Slave::update_mtc_status - TID:%1 MTC:%2\n", pthread_name(), mtc_frame));
496         return; // why was this fn needed anyway ? it just messes up things -> use reset.
497         busy_guard1++;
498
499         switch (status) {
500         case MTC_Stopped:
501                 current.guard1++;
502                 current.position = mtc_frame;
503                 current.timestamp = 0;
504                 current.speed = 0;
505                 current.guard2++;
506
507                 break;
508
509         case MTC_Forward:
510                 current.guard1++;
511                 current.position = mtc_frame;
512                 current.timestamp = 0;
513                 current.speed = 0;
514                 current.guard2++;
515                 break;
516
517         case MTC_Backward:
518                 current.guard1++;
519                 current.position = mtc_frame;
520                 current.timestamp = 0;
521                 current.speed = 0;
522                 current.guard2++;
523                 break;
524         }
525         busy_guard2++;
526 }
527
528 void
529 MTC_Slave::reset_window (framepos_t root)
530 {
531         /* if we're waiting for the master to catch us after seeking ahead, keep the window
532            of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
533            ahead of the window root (taking direction into account).
534         */
535
536         framecnt_t const d = (quarter_frame_duration * 4 * frame_tolerance);
537
538         switch (port->self_parser().mtc_running()) {
539         case MTC_Forward:
540                 window_begin = root;
541                 transport_direction = 1;
542                 window_end = root + d;
543                 break;
544
545         case MTC_Backward:
546                 transport_direction = -1;
547                 if (root > d) {
548                         window_begin = root - d;
549                         window_end = root;
550                 } else {
551                         window_begin = 0;
552                 }
553                 window_end = root;
554                 break;
555
556         default:
557                 /* do nothing */
558                 break;
559         }
560
561         DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root));
562 }
563
564 void
565 MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc)
566 {
567         /* the bandwidth of the DLL is a trade-off,
568          * because the max-speed of the transport in ardour is
569          * limited to +-8.0, a larger bandwidth would cause oscillations
570          *
571          * But this is only really a problem if the user performs manual
572          * seeks while transport is running and slaved to MTC.
573          */
574         oe = 2.0 * M_PI * double(inc) / 2.0 / double(session.frame_rate());
575         be = 1.4142135623730950488 * oe;
576         ce = oe * oe;
577
578         ee2 = double(transport_direction * inc);
579         te0 = double(pos);
580         te1 = te0 + ee2;
581         DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init Engine DLL %1 %2 %3\n", te0, te1, ee2));
582 }
583
584 /* main entry point from session_process.cc
585 xo * in process callback context */
586 bool
587 MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
588 {
589         framepos_t now = session.engine().sample_time_at_cycle_start();
590         framepos_t sess_pos = session.transport_frame(); // corresponds to now
591         //sess_pos -= session.engine().frames_since_cycle_start();
592
593         SafeTime last;
594         frameoffset_t elapsed;
595         bool engine_dll_reinitialized = false;
596
597         read_current (&last);
598
599         DEBUG_TRACE (DEBUG::MTC, string_compose ("speed&pos: timestamp %1 speed %2 initstate %3 dir %4 tpos %5 now %6 last-in %7\n",
600                                                  last.timestamp,
601                                                  last.speed,
602                                                  engine_dll_initstate,
603                                                  transport_direction,
604                                                  sess_pos,
605                                                  now,
606                                                  last_inbound_frame));
607
608         /* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */
609         if (last.timestamp == 0) {
610                 engine_dll_initstate = 0;
611         } else if (engine_dll_initstate != transport_direction && last.speed != 0) {
612                 engine_dll_initstate = transport_direction;
613                 init_engine_dll(last.position, session.engine().samples_per_cycle());
614                 engine_dll_reinitialized = true;
615         }
616
617         if (last.timestamp == 0) {
618                 speed = 0;
619                 pos = session.transport_frame() ; // last.position;
620                 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", pos));
621                 return true;
622         }
623
624         /* no timecode for two frames - conclude that it's stopped */
625         if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > labs(seekahead_distance())) {
626                 speed = 0;
627                 pos = last.position;
628                 session.set_requested_return_frame (-1);
629                 session.request_locate (pos, false);
630                 session.request_transport_speed (0);
631                 engine_dll_initstate = 0;
632                 queue_reset (false);
633                 ActiveChanged (false);
634                 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC not seen for 2 frames - reset pending, pos = %1\n", pos));
635                 return false;
636         }
637
638
639         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));
640         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position eng-tme: %1 eng-pos: %2\n", now, sess_pos));
641
642         double speed_flt = last.speed; ///< MTC speed from MTC-quarter-frame DLL
643
644         /* interpolate position according to speed and time since last quarter-frame*/
645         if (speed_flt == 0.0f) {
646                 elapsed = 0;
647         } else {
648                 /* scale elapsed time by the current MTC speed */
649                 elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp));
650                 if (give_slave_full_control_over_transport_speed() && !engine_dll_reinitialized) {
651                         /* there is an engine vs MTC position frame-delta.
652                          * This mostly due to quantization and rounding of (speed * nframes)
653                          * but can also due to the session-process not calling
654                          * speed_and_position() every cycle under some circumstances.
655                          * Thus we use an other DLL to align the engine and the MTC
656                          */
657
658                         /* update engine DLL and calculate speed */
659                         const double e = double (last.position + elapsed - sess_pos);
660                         te0 = te1;
661                         te1 += be * e + ee2;
662                         ee2 += ce * e;
663                         speed_flt = (te1 - te0) / double(session.engine().samples_per_cycle());
664                         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() ));
665                 }
666         }
667
668         pos = last.position + elapsed;
669         speed = speed_flt;
670
671         /* may happen if the user performs a seek in the timeline while slaved to running MTC
672          * engine-DLL can oscillate back before 0.
673          * also see note in MTC_Slave::init_engine_dll
674          */
675         if (!session.actively_recording()
676             && speed != 0
677             && ((pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()))) {
678                 engine_dll_initstate = 0;
679                 queue_reset (false);
680         }
681
682         /* provide a .1% deadzone to lock the speed */
683         if (fabs (speed - 1.0) <= 0.001)
684                 speed = 1.0;
685
686         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n",
687                                                  speed, pos, last.position, elapsed,  pos - sess_pos));
688
689         current_delta = (pos - sess_pos);
690
691         return true;
692 }
693
694 Timecode::TimecodeFormat
695 MTC_Slave::apparent_timecode_format () const
696 {
697         return mtc_timecode;
698 }
699
700 std::string
701 MTC_Slave::approximate_current_position() const
702 {
703         SafeTime last;
704         read_current (&last);
705         if (last.timestamp == 0 || reset_pending) {
706                 return " --:--:--:--";
707         }
708         return Timecode::timecode_format_sampletime(
709                 last.position,
710                 double(session.frame_rate()),
711                 Timecode::timecode_to_frames_per_second(mtc_timecode),
712                 Timecode::timecode_has_drop_frames(mtc_timecode));
713 }
714
715 std::string
716 MTC_Slave::approximate_current_delta() const
717 {
718         char delta[80];
719         SafeTime last;
720         read_current (&last);
721         if (last.timestamp == 0 || reset_pending) {
722                 snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012");
723         } else {
724                 snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
725                                 LEADINGZERO(abs(current_delta)), PLUSMINUS(-current_delta), abs(current_delta));
726         }
727         return std::string(delta);
728 }