2 Copyright (C) 2002-4 Paul Davis
3 Overhaul 2012 Robin Gareus <robin@gareus.org>
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.
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.
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.
23 #include <sys/types.h>
26 #include "pbd/error.h"
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"
37 using namespace ARDOUR;
40 using namespace Timecode;
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.
49 const int MTC_Slave::frame_tolerance = 2;
51 MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
54 can_notify_on_unknown_rate = true;
55 did_reset_tc_format = false;
57 reset_position = false;
59 engine_dll_initstate = 0;
60 busy_guard1 = busy_guard2 = 0;
62 last_mtc_fps_byte = session.get_mtc_timecode_bits ();
63 quarter_frame_duration = (double(session.frames_per_timecode_frame()) / 4.0);
65 mtc_timecode = session.config.get_timecode_format();
66 a3e_timecode = session.config.get_timecode_format();
67 printed_timecode_warning = false;
69 session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_Slave::parameter_changed, this, _1));
70 parse_timecode_offset();
75 MTC_Slave::~MTC_Slave()
77 port_connections.drop_connections();
78 config_connection.disconnect();
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!
84 * XXX this is called from jack rt-context :(
85 * TODO fix libs/ardour/session_transport.cc:1321 (delete _slave;)
90 if (did_reset_tc_format) {
91 session.config.set_timecode_format (saved_tc_format);
96 MTC_Slave::rebind (MIDI::Port& p)
98 port_connections.drop_connections ();
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));
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;
118 MTC_Slave::parameter_changed (std::string const & p)
120 if (p == "slave-timecode-offset"
121 || p == "timecode-format"
123 parse_timecode_offset();
128 MTC_Slave::give_slave_full_control_over_transport_speed() const
130 return true; // DLL align to engine transport
131 // return false; // for Session-level computed varispeed
135 MTC_Slave::resolution () const
137 return (framecnt_t) quarter_frame_duration * 4.0;
141 MTC_Slave::seekahead_distance () const
143 return quarter_frame_duration * 8 * transport_direction;
147 MTC_Slave::outside_window (framepos_t pos) const
149 return ((pos < window_begin) || (pos > window_end));
154 MTC_Slave::locked () const
156 return port->parser()->mtc_locked() && last_inbound_frame !=0 && engine_dll_initstate !=0;
160 MTC_Slave::ok() const
166 MTC_Slave::queue_reset (bool reset_pos)
168 Glib::Threads::Mutex::Lock lm (reset_lock);
171 reset_position = true;
176 MTC_Slave::maybe_reset ()
178 Glib::Threads::Mutex::Lock lm (reset_lock);
181 reset (reset_position);
183 reset_position = false;
188 MTC_Slave::reset (bool with_position)
190 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC_Slave reset %1\n", with_position?"with position":"without position"));
192 last_inbound_frame = 0;
194 current.position = 0;
195 current.timestamp = 0;
199 last_inbound_frame = 0;
201 current.timestamp = 0;
205 first_mtc_timestamp = 0;
208 transport_direction = 1;
213 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
216 DEBUG_TRACE (DEBUG::MTC, "MTC_Slave::handle_locate\n");
218 mtc[4] = last_mtc_fps_byte;
219 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
224 update_mtc_time (mtc, true, 0);
228 MTC_Slave::read_current (SafeTime *st) const
234 error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
241 } while (st->guard1 != st->guard2);
245 MTC_Slave::init_mtc_dll(framepos_t tme, double qtr)
247 omega = 2.0 * M_PI * qtr / 2.0 / double(session.frame_rate());
248 b = 1.4142135623730950488 * omega;
254 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
258 /* called from MIDI parser */
260 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now)
263 const double qtr_d = quarter_frame_duration;
264 const framepos_t qtr = rint(qtr_d);
266 mtc_frame += qtr * transport_direction;
268 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
270 double mtc_speed = 0;
271 if (first_mtc_timestamp != 0) {
272 /* update MTC DLL and calculate speed */
273 const double e = mtc_frame - (double(transport_direction) * (double(now) - double(current.timestamp) + t0));
278 mtc_speed = (t1 - t0) / qtr_d;
279 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));
282 current.position = mtc_frame;
283 current.timestamp = now;
284 current.speed = mtc_speed;
287 last_inbound_frame = now;
295 /* called from MIDI parser _after_ update_mtc_qtr()
296 * when a full TC has been received
299 MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now)
303 /* "now" can be zero if this is called from a context where we do not have or do not want
304 to use a timestamp indicating when this MTC time was received. example: when we received
305 a locate command via MMC.
308 //DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self()));
309 TimecodeFormat tc_format;
310 bool reset_tc = true;
312 timecode.hours = msg[3];
313 timecode.minutes = msg[2];
314 timecode.seconds = msg[1];
315 timecode.frames = msg[0];
317 last_mtc_fps_byte = msg[4];
319 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
328 timecode.drop = false;
329 tc_format = timecode_24;
330 can_notify_on_unknown_rate = true;
334 timecode.drop = false;
335 tc_format = timecode_25;
336 can_notify_on_unknown_rate = true;
338 case MTC_30_FPS_DROP:
339 if (Config->get_timecode_source_2997()) {
340 tc_format = Timecode::timecode_2997000drop;
341 timecode.rate = (29970.0/1000.0);
343 tc_format = timecode_2997drop;
344 timecode.rate = (30000.0/1001.0);
346 timecode.drop = true;
347 can_notify_on_unknown_rate = true;
351 timecode.drop = false;
352 can_notify_on_unknown_rate = true;
353 tc_format = timecode_30;
356 /* throttle error messages about unknown MTC rates */
357 if (can_notify_on_unknown_rate) {
358 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
361 can_notify_on_unknown_rate = false;
363 timecode.rate = session.timecode_frames_per_second();
364 timecode.drop = session.timecode_drop_frames();
369 TimecodeFormat cur_timecode = session.config.get_timecode_format();
370 if (Config->get_timecode_sync_frame_rate()) {
371 /* enforce time-code */
372 if (!did_reset_tc_format) {
373 saved_tc_format = cur_timecode;
374 did_reset_tc_format = true;
376 if (cur_timecode != tc_format) {
377 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
378 warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."),
379 Timecode::timecode_format_name(cur_timecode),
380 Timecode::timecode_format_name(tc_format))
384 session.config.set_timecode_format (tc_format);
386 /* only warn about TC mismatch */
387 if (mtc_timecode != tc_format) printed_timecode_warning = false;
388 if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
390 if (cur_timecode != tc_format && ! printed_timecode_warning) {
391 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
392 warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."),
393 Timecode::timecode_format_name(tc_format),
395 Timecode::timecode_format_name(cur_timecode))
398 printed_timecode_warning = true;
401 mtc_timecode = tc_format;
402 a3e_timecode = cur_timecode;
404 speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode);
407 /* do a careful conversion of the timecode value to a position
408 so that we take drop/nondrop and all that nonsense into
412 quarter_frame_duration = (double(session.frame_rate()) / (double) timecode.rate / 4.0);
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
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));
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);
429 reset_window (mtc_frame);
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.
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
442 double qtr = quarter_frame_duration;
443 long int mtc_off = (long) rint(7.0 * qtr);
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()));
448 switch (port->parser()->mtc_running()) {
450 mtc_frame -= mtc_off;
454 mtc_frame += mtc_off;
460 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
463 if (first_mtc_timestamp == 0 || current.timestamp == 0) {
464 first_mtc_timestamp = now;
465 init_mtc_dll(mtc_frame, qtr);
468 current.position = mtc_frame;
469 current.timestamp = now;
471 reset_window (mtc_frame);
476 last_inbound_frame = now;
482 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
484 /* XXX !!! thread safety ... called from MIDI I/O context
485 * on locate (via ::update_mtc_time())
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.
494 current.position = mtc_frame;
495 current.timestamp = 0;
503 current.position = mtc_frame;
504 current.timestamp = 0;
511 current.position = mtc_frame;
512 current.timestamp = 0;
521 MTC_Slave::reset_window (framepos_t root)
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).
527 framecnt_t const d = (quarter_frame_duration * 4 * frame_tolerance);
529 switch (port->parser()->mtc_running()) {
532 transport_direction = 1;
533 window_end = root + d;
537 transport_direction = -1;
539 window_begin = root - d;
552 DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
556 MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc)
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
562 * But this is only really a problem if the user performs manual
563 * seeks while transport is running and slaved to MTC.
565 oe = 2.0 * M_PI * double(inc) / 2.0 / double(session.frame_rate());
566 be = 1.4142135623730950488 * oe;
569 ee2 = double(transport_direction * inc);
572 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init Engine DLL %1 %2 %3\n", te0, te1, ee2));
575 /* main entry point from session_process.cc
576 * in jack_process callback context */
578 MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
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();
585 frameoffset_t elapsed;
586 bool engine_dll_reinitialized = false;
588 read_current (&last);
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;
598 if (last.timestamp == 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));
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())) {
609 session.request_locate (pos, false);
610 session.request_transport_speed (0);
611 engine_dll_initstate = 0;
613 DEBUG_TRACE (DEBUG::MTC, "MTC not seen for 2 frames - reset pending\n");
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));
621 double speed_flt = last.speed; ///< MTC speed from MTC-quarter-frame DLL
623 /* interpolate position according to speed and time since last quarter-frame*/
624 if (speed_flt == 0.0f) {
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
639 /* update engine DLL and calculate speed */
640 const double e = double (last.position + elapsed - sess_pos);
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() ));
649 pos = last.position + elapsed;
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
656 if (!session.actively_recording()
658 && ( (pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()) )
660 engine_dll_initstate = 0;
664 /* provide a .1% deadzone to lock the speed */
665 if (fabs(speed - 1.0) <= 0.001)
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));
671 current_delta = (pos - sess_pos);
676 Timecode::TimecodeFormat
677 MTC_Slave::apparent_timecode_format () const
683 MTC_Slave::approximate_current_position() const
686 read_current (&last);
687 if (last.timestamp == 0 || reset_pending) {
688 return " \u2012\u2012:\u2012\u2012:\u2012\u2012:\u2012\u2012";
690 return Timecode::timecode_format_sampletime(
692 double(session.frame_rate()),
693 Timecode::timecode_to_frames_per_second(mtc_timecode),
694 Timecode::timecode_has_drop_frames(mtc_timecode));
698 MTC_Slave::approximate_current_delta() const
702 read_current (&last);
703 if (last.timestamp == 0 || reset_pending) {
704 snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012");
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));
709 return std::string(delta);