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