Merge branch 'master' into cairocanvas
[ardour.git] / libs / ardour / mtc_slave.cc
1 /*
2     Copyright (C) 2002-4 Paul Davis
3     Overhaul 2012 Robin Gareus <robin@gareus.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20 #include <iostream>
21 #include <errno.h>
22 #include <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         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", pthread_name()));
313         TimecodeFormat tc_format;
314         bool reset_tc = true;
315
316         timecode.hours = msg[3];
317         timecode.minutes = msg[2];
318         timecode.seconds = msg[1];
319         timecode.frames = msg[0];
320
321         last_mtc_fps_byte = msg[4];
322
323         DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
324
325         if (now) {
326                 maybe_reset ();
327         }
328
329         switch (msg[4]) {
330         case MTC_24_FPS:
331                 timecode.rate = 24;
332                 timecode.drop = false;
333                 tc_format = timecode_24;
334                 can_notify_on_unknown_rate = true;
335                 break;
336         case MTC_25_FPS:
337                 timecode.rate = 25;
338                 timecode.drop = false;
339                 tc_format = timecode_25;
340                 can_notify_on_unknown_rate = true;
341                 break;
342         case MTC_30_FPS_DROP:
343                 if (Config->get_timecode_source_2997()) {
344                         tc_format = Timecode::timecode_2997000drop;
345                         timecode.rate = (29970.0/1000.0);
346                 } else {
347                         tc_format = timecode_2997drop;
348                         timecode.rate = (30000.0/1001.0);
349                 }
350                 timecode.drop = true;
351                 can_notify_on_unknown_rate = true;
352                 break;
353         case MTC_30_FPS:
354                 timecode.rate = 30;
355                 timecode.drop = false;
356                 can_notify_on_unknown_rate = true;
357                 tc_format = timecode_30;
358                 break;
359         default:
360                 /* throttle error messages about unknown MTC rates */
361                 if (can_notify_on_unknown_rate) {
362                         error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
363                                                  (int) msg[4])
364                               << endmsg;
365                         can_notify_on_unknown_rate = false;
366                 }
367                 timecode.rate = session.timecode_frames_per_second();
368                 timecode.drop = session.timecode_drop_frames();
369                 reset_tc = false;
370         }
371
372         if (reset_tc) {
373                 TimecodeFormat cur_timecode = session.config.get_timecode_format();
374                 if (Config->get_timecode_sync_frame_rate()) {
375                         /* enforce time-code */
376                         if (!did_reset_tc_format) {
377                                 saved_tc_format = cur_timecode;
378                                 did_reset_tc_format = true;
379                         }
380                         if (cur_timecode != tc_format) {
381                                 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
382                                         warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."),
383                                                         Timecode::timecode_format_name(cur_timecode),
384                                                         Timecode::timecode_format_name(tc_format))
385                                                 << endmsg;
386                                 }
387                         }
388                         session.config.set_timecode_format (tc_format);
389                 } else {
390                         /* only warn about TC mismatch */
391                         if (mtc_timecode != tc_format) printed_timecode_warning = false;
392                         if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
393
394                         if (cur_timecode != tc_format && ! printed_timecode_warning) {
395                                 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
396                                         warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."),
397                                                                   Timecode::timecode_format_name(tc_format),
398                                                                   PROGRAM_NAME,
399                                                                   Timecode::timecode_format_name(cur_timecode))
400                                                 << endmsg;
401                                 }
402                                 printed_timecode_warning = true;
403                         }
404                 }
405                 mtc_timecode = tc_format;
406                 a3e_timecode = cur_timecode;
407
408                 speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode);
409         }
410
411         /* do a careful conversion of the timecode value to a position
412            so that we take drop/nondrop and all that nonsense into
413            consideration.
414         */
415
416         quarter_frame_duration = (double(session.frame_rate()) / (double) timecode.rate / 4.0);
417
418         Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
419                 double(session.frame_rate()),
420                 session.config.get_subframes_per_frame(),
421                 timecode_negative_offset, timecode_offset
422                 );
423
424         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
425                                                  now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
426
427         if (was_full || outside_window (mtc_frame)) {
428                 DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC %1 or outside window %2\n", was_full, outside_window (mtc_frame)));
429                 session.request_locate (mtc_frame, false);
430                 session.request_transport_speed (0);
431                 update_mtc_status (MIDI::MTC_Stopped);
432                 reset (false);
433                 reset_window (mtc_frame);
434         } else {
435
436                 /* we've had the first set of 8 qtr frame messages, determine position
437                    and allow continuing qtr frame messages to provide position
438                    and speed information.
439                 */
440
441                 /* We received the last quarter frame 7 quarter frames (1.75 mtc
442                    frames) after the instance when the contents of the mtc quarter
443                    frames were decided. Add time to compensate for the elapsed 1.75
444                    frames.
445                 */
446                 double qtr = quarter_frame_duration;
447                 long int mtc_off = (long) rint(7.0 * qtr);
448
449                 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
450                                                          mtc_frame, (4.0*qtr), session.frames_per_timecode_frame()));
451
452                 switch (port->self_parser().mtc_running()) {
453                 case MTC_Backward:
454                         mtc_frame -= mtc_off;
455                         qtr *= -1.0;
456                         break;
457                 case MTC_Forward:
458                         mtc_frame += mtc_off;
459                         break;
460                 default:
461                         break;
462                 }
463
464                 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
465
466                 if (now) {
467                         if (first_mtc_timestamp == 0 || current.timestamp == 0) {
468                                 first_mtc_timestamp = now;
469                                 init_mtc_dll(mtc_frame, qtr);
470                                 mtc_frame_dll = mtc_frame;
471                         }
472                         current.guard1++;
473                         current.position = mtc_frame;
474                         current.timestamp = now;
475                         current.guard2++;
476                         reset_window (mtc_frame);
477                 }
478         }
479
480         if (now) {
481                 last_inbound_frame = now;
482         }
483         busy_guard2++;
484 }
485
486 void
487 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
488 {
489         /* XXX !!! thread safety ... called from MIDI I/O context
490          * on locate (via ::update_mtc_time())
491          */
492         DEBUG_TRACE (DEBUG::MTC, string_compose("MTC_Slave::update_mtc_status - TID:%1\n", pthread_name()));
493         return; // why was this fn needed anyway ? it just messes up things -> use reset.
494         busy_guard1++;
495
496         switch (status) {
497         case MTC_Stopped:
498                 current.guard1++;
499                 current.position = mtc_frame;
500                 current.timestamp = 0;
501                 current.speed = 0;
502                 current.guard2++;
503
504                 break;
505
506         case MTC_Forward:
507                 current.guard1++;
508                 current.position = mtc_frame;
509                 current.timestamp = 0;
510                 current.speed = 0;
511                 current.guard2++;
512                 break;
513
514         case MTC_Backward:
515                 current.guard1++;
516                 current.position = mtc_frame;
517                 current.timestamp = 0;
518                 current.speed = 0;
519                 current.guard2++;
520                 break;
521         }
522         busy_guard2++;
523 }
524
525 void
526 MTC_Slave::reset_window (framepos_t root)
527 {
528         /* if we're waiting for the master to catch us after seeking ahead, keep the window
529            of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
530            ahead of the window root (taking direction into account).
531         */
532
533         framecnt_t const d = (quarter_frame_duration * 4 * frame_tolerance);
534
535         switch (port->self_parser().mtc_running()) {
536         case MTC_Forward:
537                 window_begin = root;
538                 transport_direction = 1;
539                 window_end = root + d;
540                 break;
541
542         case MTC_Backward:
543                 transport_direction = -1;
544                 if (root > d) {
545                         window_begin = root - d;
546                         window_end = root;
547                 } else {
548                         window_begin = 0;
549                 }
550                 window_end = root;
551                 break;
552
553         default:
554                 /* do nothing */
555                 break;
556         }
557
558         DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root));
559 }
560
561 void
562 MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc)
563 {
564         /* the bandwidth of the DLL is a trade-off,
565          * because the max-speed of the transport in ardour is
566          * limited to +-8.0, a larger bandwidth would cause oscillations
567          *
568          * But this is only really a problem if the user performs manual
569          * seeks while transport is running and slaved to MTC.
570          */
571         oe = 2.0 * M_PI * double(inc) / 2.0 / double(session.frame_rate());
572         be = 1.4142135623730950488 * oe;
573         ce = oe * oe;
574
575         ee2 = double(transport_direction * inc);
576         te0 = double(pos);
577         te1 = te0 + ee2;
578         DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init Engine DLL %1 %2 %3\n", te0, te1, ee2));
579 }
580
581 /* main entry point from session_process.cc
582 xo * in process callback context */
583 bool
584 MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
585 {
586         framepos_t now = session.engine().sample_time_at_cycle_start();
587         framepos_t sess_pos = session.transport_frame(); // corresponds to now
588         //sess_pos -= session.engine().frames_since_cycle_start();
589
590         SafeTime last;
591         frameoffset_t elapsed;
592         bool engine_dll_reinitialized = false;
593
594         read_current (&last);
595
596         DEBUG_TRACE (DEBUG::MTC, string_compose ("speed&pos: timestamp %1 speed %2 initstate %3 dir %4 tpos %5 now %6 last-in %7\n",
597                                                  last.timestamp, 
598                                                  last.speed,
599                                                  engine_dll_initstate,
600                                                  transport_direction,
601                                                  sess_pos,
602                                                  now,
603                                                  last_inbound_frame));
604
605         /* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */
606         if (last.timestamp == 0) { 
607                 engine_dll_initstate = 0; 
608         } else if (engine_dll_initstate != transport_direction && last.speed != 0) {
609                 engine_dll_initstate = transport_direction;
610                 init_engine_dll(last.position, session.engine().samples_per_cycle());
611                 engine_dll_reinitialized = true;
612         }
613
614         if (last.timestamp == 0) {
615                 speed = 0;
616                 pos = session.transport_frame() ; // last.position;
617                 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", pos));
618                 return true;
619         }
620
621         /* no timecode for two frames - conclude that it's stopped */
622         if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > labs(seekahead_distance())) {
623                 speed = 0;
624                 pos = last.position;
625                 session.request_locate (pos, false);
626                 session.request_transport_speed (0);
627                 engine_dll_initstate = 0;
628                 queue_reset (false);
629                 DEBUG_TRACE (DEBUG::MTC, "MTC not seen for 2 frames - reset pending\n");
630                 return false;
631         }
632
633
634         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));
635         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position eng-tme: %1 eng-pos: %2\n", now, sess_pos));
636
637         double speed_flt = last.speed; ///< MTC speed from MTC-quarter-frame DLL
638
639         /* interpolate position according to speed and time since last quarter-frame*/
640         if (speed_flt == 0.0f) {
641                 elapsed = 0;
642         } else {
643                 /* scale elapsed time by the current MTC speed */
644                 elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp));
645                 if (give_slave_full_control_over_transport_speed() && !engine_dll_reinitialized) {
646                         /* there is an engine vs MTC position frame-delta.
647                          * This mostly due to quantization and rounding of (speed * nframes)
648                          * but can also due to the session-process not calling
649                          * speed_and_position() every cycle under some circumstances.
650                          * Thus we use an other DLL to align the engine and the MTC
651                          */
652
653                         /* update engine DLL and calculate speed */
654                         const double e = double (last.position + elapsed - sess_pos);
655                         te0 = te1;
656                         te1 += be * e + ee2;
657                         ee2 += ce * e;
658                         speed_flt = (te1 - te0) / double(session.engine().samples_per_cycle());
659                         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() ));
660                 }
661         }
662
663         pos = last.position + elapsed;
664         speed = speed_flt;
665
666         /* may happen if the user performs a seek in the timeline while slaved to running MTC
667          * engine-DLL can oscillate back before 0.
668          * also see note in MTC_Slave::init_engine_dll
669          */
670         if (!session.actively_recording()
671             && speed != 0
672             && ((pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()))) {
673                 engine_dll_initstate = 0;
674                 queue_reset (false);
675         }
676
677         /* provide a .1% deadzone to lock the speed */
678         if (fabs (speed - 1.0) <= 0.001)
679                 speed = 1.0;
680
681         DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n",
682                                                  speed, pos, last.position, elapsed,  pos - sess_pos));
683
684         current_delta = (pos - sess_pos);
685
686         return true;
687 }
688
689 Timecode::TimecodeFormat
690 MTC_Slave::apparent_timecode_format () const
691 {
692         return mtc_timecode;
693 }
694
695 std::string
696 MTC_Slave::approximate_current_position() const
697 {
698         SafeTime last;
699         read_current (&last);
700         if (last.timestamp == 0 || reset_pending) {
701                 return " --:--:--:--";
702         }
703         return Timecode::timecode_format_sampletime(
704                 last.position,
705                 double(session.frame_rate()),
706                 Timecode::timecode_to_frames_per_second(mtc_timecode),
707                 Timecode::timecode_has_drop_frames(mtc_timecode));
708 }
709
710 std::string
711 MTC_Slave::approximate_current_delta() const
712 {
713         char delta[80];
714         SafeTime last;
715         read_current (&last);
716         if (last.timestamp == 0 || reset_pending) {
717                 snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012");
718         } else {
719                 snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
720                                 LEADINGZERO(abs(current_delta)), PLUSMINUS(-current_delta), abs(current_delta));
721         }
722         return std::string(delta);
723 }