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 "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"
38 using namespace ARDOUR;
41 using namespace Timecode;
43 /* length (in timecode frames) of the "window" that we consider legal given receipt of
44 a given timecode position. Ardour will try to chase within this window, and will
45 stop+locate+wait+chase if timecode arrives outside of it. The window extends entirely
46 in the current direction of motion, so if any timecode arrives that is before the most
47 recently received position (and without the direction of timecode reversing too), we
48 will stop+locate+wait+chase.
50 const int MTC_Slave::frame_tolerance = 2;
52 MTC_Slave::MTC_Slave (Session& s, MidiPort& p)
56 can_notify_on_unknown_rate = true;
57 did_reset_tc_format = false;
59 reset_position = false;
62 engine_dll_initstate = 0;
63 busy_guard1 = busy_guard2 = 0;
65 last_mtc_fps_byte = session.get_mtc_timecode_bits ();
66 quarter_frame_duration = (double(session.frames_per_timecode_frame()) / 4.0);
68 mtc_timecode = session.config.get_timecode_format();
69 a3e_timecode = session.config.get_timecode_format();
70 printed_timecode_warning = false;
72 session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_Slave::parameter_changed, this, _1));
73 parse_timecode_offset();
76 port->self_parser().mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3));
77 port->self_parser().mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3));
78 port->self_parser().mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
81 MTC_Slave::~MTC_Slave()
83 port_connections.drop_connections();
84 config_connection.disconnect();
86 while (busy_guard1 != busy_guard2) {
87 /* make sure MIDI parser is not currently calling any callbacks in here,
88 * else there's a segfault ahead!
90 * XXX this is called from jack rt-context :(
91 * TODO fix libs/ardour/session_transport.cc:1321 (delete _slave;)
96 if (did_reset_tc_format) {
97 session.config.set_timecode_format (saved_tc_format);
102 MTC_Slave::rebind (MidiPort& p)
104 port_connections.drop_connections ();
111 MTC_Slave::parse_timecode_offset() {
112 Timecode::Time offset_tc;
113 Timecode::parse_timecode_format(session.config.get_slave_timecode_offset(), offset_tc);
114 offset_tc.rate = session.timecode_frames_per_second();
115 offset_tc.drop = session.timecode_drop_frames();
116 session.timecode_to_sample(offset_tc, timecode_offset, false, false);
117 timecode_negative_offset = offset_tc.negative;
121 MTC_Slave::parameter_changed (std::string const & p)
123 if (p == "slave-timecode-offset"
124 || p == "timecode-format"
126 parse_timecode_offset();
131 MTC_Slave::give_slave_full_control_over_transport_speed() const
133 return true; // DLL align to engine transport
134 // return false; // for Session-level computed varispeed
138 MTC_Slave::resolution () const
140 return (framecnt_t) quarter_frame_duration * 4.0;
144 MTC_Slave::seekahead_distance () const
146 return quarter_frame_duration * 8 * transport_direction;
150 MTC_Slave::outside_window (framepos_t pos) const
152 return ((pos < window_begin) || (pos > window_end));
157 MTC_Slave::locked () const
159 DEBUG_TRACE (DEBUG::MTC, string_compose ("locked ? %1 last %2 initstate %3\n", port->self_parser().mtc_locked(), last_inbound_frame, engine_dll_initstate));
160 return port->self_parser().mtc_locked() && last_inbound_frame !=0 && engine_dll_initstate !=0;
164 MTC_Slave::ok() const
170 MTC_Slave::queue_reset (bool reset_pos)
172 Glib::Threads::Mutex::Lock lm (reset_lock);
175 reset_position = true;
180 MTC_Slave::maybe_reset ()
182 Glib::Threads::Mutex::Lock lm (reset_lock);
185 reset (reset_position);
187 reset_position = false;
192 MTC_Slave::reset (bool with_position)
194 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC_Slave reset %1\n", with_position?"with position":"without position"));
196 last_inbound_frame = 0;
198 current.position = 0;
199 current.timestamp = 0;
203 last_inbound_frame = 0;
205 current.timestamp = 0;
209 first_mtc_timestamp = 0;
212 transport_direction = 1;
217 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
220 DEBUG_TRACE (DEBUG::MTC, "MTC_Slave::handle_locate\n");
222 mtc[4] = last_mtc_fps_byte;
223 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
228 update_mtc_time (mtc, true, 0);
232 MTC_Slave::read_current (SafeTime *st) const
238 error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
245 } while (st->guard1 != st->guard2);
249 MTC_Slave::init_mtc_dll(framepos_t tme, double qtr)
251 omega = 2.0 * M_PI * qtr / 2.0 / double(session.frame_rate());
252 b = 1.4142135623730950488 * omega;
258 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
261 /* called from MIDI parser */
263 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now)
266 const double qtr_d = quarter_frame_duration;
268 mtc_frame_dll += qtr_d * (double) transport_direction;
269 mtc_frame = rint(mtc_frame_dll);
271 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
273 double mtc_speed = 0;
274 if (first_mtc_timestamp != 0) {
275 /* update MTC DLL and calculate speed */
276 const double e = mtc_frame_dll - (double)transport_direction * ((double)now - (double)current.timestamp + t0);
281 mtc_speed = (t1 - t0) / qtr_d;
282 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 current.position = mtc_frame;
286 current.timestamp = now;
287 current.speed = mtc_speed;
290 last_inbound_frame = now;
298 /* called from MIDI parser _after_ update_mtc_qtr()
299 * when a full TC has been received
302 MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now)
306 /* "now" can be zero if this is called from a context where we do not have or do not want
307 to use a timestamp indicating when this MTC time was received. example: when we received
308 a locate command via MMC.
311 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self()));
312 TimecodeFormat tc_format;
313 bool reset_tc = true;
315 timecode.hours = msg[3];
316 timecode.minutes = msg[2];
317 timecode.seconds = msg[1];
318 timecode.frames = msg[0];
320 last_mtc_fps_byte = msg[4];
322 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
331 timecode.drop = false;
332 tc_format = timecode_24;
333 can_notify_on_unknown_rate = true;
337 timecode.drop = false;
338 tc_format = timecode_25;
339 can_notify_on_unknown_rate = true;
341 case MTC_30_FPS_DROP:
342 if (Config->get_timecode_source_2997()) {
343 tc_format = Timecode::timecode_2997000drop;
344 timecode.rate = (29970.0/1000.0);
346 tc_format = timecode_2997drop;
347 timecode.rate = (30000.0/1001.0);
349 timecode.drop = true;
350 can_notify_on_unknown_rate = true;
354 timecode.drop = false;
355 can_notify_on_unknown_rate = true;
356 tc_format = timecode_30;
359 /* throttle error messages about unknown MTC rates */
360 if (can_notify_on_unknown_rate) {
361 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
364 can_notify_on_unknown_rate = false;
366 timecode.rate = session.timecode_frames_per_second();
367 timecode.drop = session.timecode_drop_frames();
372 TimecodeFormat cur_timecode = session.config.get_timecode_format();
373 if (Config->get_timecode_sync_frame_rate()) {
374 /* enforce time-code */
375 if (!did_reset_tc_format) {
376 saved_tc_format = cur_timecode;
377 did_reset_tc_format = true;
379 if (cur_timecode != tc_format) {
380 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
381 warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."),
382 Timecode::timecode_format_name(cur_timecode),
383 Timecode::timecode_format_name(tc_format))
387 session.config.set_timecode_format (tc_format);
389 /* only warn about TC mismatch */
390 if (mtc_timecode != tc_format) printed_timecode_warning = false;
391 if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
393 if (cur_timecode != tc_format && ! printed_timecode_warning) {
394 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
395 warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."),
396 Timecode::timecode_format_name(tc_format),
398 Timecode::timecode_format_name(cur_timecode))
401 printed_timecode_warning = true;
404 mtc_timecode = tc_format;
405 a3e_timecode = cur_timecode;
407 speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode);
410 /* do a careful conversion of the timecode value to a position
411 so that we take drop/nondrop and all that nonsense into
415 quarter_frame_duration = (double(session.frame_rate()) / (double) timecode.rate / 4.0);
417 Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
418 double(session.frame_rate()),
419 session.config.get_subframes_per_frame(),
420 timecode_negative_offset, timecode_offset
423 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
424 now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
426 if (was_full || outside_window (mtc_frame)) {
427 DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC %1 or outside window %2\n", was_full, outside_window (mtc_frame)));
428 session.request_locate (mtc_frame, false);
429 session.request_transport_speed (0);
430 update_mtc_status (MIDI::MTC_Stopped);
432 reset_window (mtc_frame);
435 /* we've had the first set of 8 qtr frame messages, determine position
436 and allow continuing qtr frame messages to provide position
437 and speed information.
440 /* We received the last quarter frame 7 quarter frames (1.75 mtc
441 frames) after the instance when the contents of the mtc quarter
442 frames were decided. Add time to compensate for the elapsed 1.75
445 double qtr = quarter_frame_duration;
446 long int mtc_off = (long) rint(7.0 * qtr);
448 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
449 mtc_frame, (4.0*qtr), session.frames_per_timecode_frame()));
451 switch (port->self_parser().mtc_running()) {
453 mtc_frame -= mtc_off;
457 mtc_frame += mtc_off;
463 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
466 if (first_mtc_timestamp == 0 || current.timestamp == 0) {
467 first_mtc_timestamp = now;
468 init_mtc_dll(mtc_frame, qtr);
469 mtc_frame_dll = mtc_frame;
472 current.position = mtc_frame;
473 current.timestamp = now;
475 reset_window (mtc_frame);
480 last_inbound_frame = now;
486 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
488 /* XXX !!! thread safety ... called from MIDI I/O context
489 * on locate (via ::update_mtc_time())
491 DEBUG_TRACE (DEBUG::MTC, string_compose("MTC_Slave::update_mtc_status - TID:%1\n", ::pthread_self()));
492 return; // why was this fn needed anyway ? it just messes up things -> use reset.
498 current.position = mtc_frame;
499 current.timestamp = 0;
507 current.position = mtc_frame;
508 current.timestamp = 0;
515 current.position = mtc_frame;
516 current.timestamp = 0;
525 MTC_Slave::reset_window (framepos_t root)
527 /* if we're waiting for the master to catch us after seeking ahead, keep the window
528 of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
529 ahead of the window root (taking direction into account).
532 framecnt_t const d = (quarter_frame_duration * 4 * frame_tolerance);
534 switch (port->self_parser().mtc_running()) {
537 transport_direction = 1;
538 window_end = root + d;
542 transport_direction = -1;
544 window_begin = root - d;
557 DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root));
561 MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc)
563 /* the bandwidth of the DLL is a trade-off,
564 * because the max-speed of the transport in ardour is
565 * limited to +-8.0, a larger bandwidth would cause oscillations
567 * But this is only really a problem if the user performs manual
568 * seeks while transport is running and slaved to MTC.
570 oe = 2.0 * M_PI * double(inc) / 2.0 / double(session.frame_rate());
571 be = 1.4142135623730950488 * oe;
574 ee2 = double(transport_direction * inc);
577 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init Engine DLL %1 %2 %3\n", te0, te1, ee2));
580 /* main entry point from session_process.cc
581 xo * in process callback context */
583 MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
585 framepos_t now = session.engine().sample_time_at_cycle_start();
586 framepos_t sess_pos = session.transport_frame(); // corresponds to now
587 //sess_pos -= session.engine().frames_since_cycle_start();
590 frameoffset_t elapsed;
591 bool engine_dll_reinitialized = false;
593 read_current (&last);
595 DEBUG_TRACE (DEBUG::MTC, string_compose ("speed&pos: timestamp %1 speed %2 initstate %3 dir %4 tpos %5 now %6 last-in %7\n",
598 engine_dll_initstate,
602 last_inbound_frame));
604 /* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */
605 if (last.timestamp == 0) {
606 engine_dll_initstate = 0;
607 } else if (engine_dll_initstate != transport_direction && last.speed != 0) {
608 engine_dll_initstate = transport_direction;
609 init_engine_dll(last.position, session.engine().samples_per_cycle());
610 engine_dll_reinitialized = true;
613 if (last.timestamp == 0) {
615 pos = session.transport_frame() ; // last.position;
616 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", pos));
620 /* no timecode for two frames - conclude that it's stopped */
621 if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > labs(seekahead_distance())) {
624 session.request_locate (pos, false);
625 session.request_transport_speed (0);
626 engine_dll_initstate = 0;
628 DEBUG_TRACE (DEBUG::MTC, "MTC not seen for 2 frames - reset pending\n");
633 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));
634 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position eng-tme: %1 eng-pos: %2\n", now, sess_pos));
636 double speed_flt = last.speed; ///< MTC speed from MTC-quarter-frame DLL
638 /* interpolate position according to speed and time since last quarter-frame*/
639 if (speed_flt == 0.0f) {
642 /* scale elapsed time by the current MTC speed */
643 elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp));
644 if (give_slave_full_control_over_transport_speed() && !engine_dll_reinitialized) {
645 /* there is an engine vs MTC position frame-delta.
646 * This mostly due to quantization and rounding of (speed * nframes)
647 * but can also due to the session-process not calling
648 * speed_and_position() every cycle under some circumstances.
649 * Thus we use an other DLL to align the engine and the MTC
652 /* update engine DLL and calculate speed */
653 const double e = double (last.position + elapsed - sess_pos);
657 speed_flt = (te1 - te0) / double(session.engine().samples_per_cycle());
658 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() ));
662 pos = last.position + elapsed;
665 /* may happen if the user performs a seek in the timeline while slaved to running MTC
666 * engine-DLL can oscillate back before 0.
667 * also see note in MTC_Slave::init_engine_dll
669 if (!session.actively_recording()
671 && ((pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()))) {
672 engine_dll_initstate = 0;
676 /* provide a .1% deadzone to lock the speed */
677 if (fabs (speed - 1.0) <= 0.001)
680 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n",
681 speed, pos, last.position, elapsed, pos - sess_pos));
683 current_delta = (pos - sess_pos);
688 Timecode::TimecodeFormat
689 MTC_Slave::apparent_timecode_format () const
695 MTC_Slave::approximate_current_position() const
698 read_current (&last);
699 if (last.timestamp == 0 || reset_pending) {
700 return " --:--:--:--";
702 return Timecode::timecode_format_sampletime(
704 double(session.frame_rate()),
705 Timecode::timecode_to_frames_per_second(mtc_timecode),
706 Timecode::timecode_has_drop_frames(mtc_timecode));
710 MTC_Slave::approximate_current_delta() const
714 read_current (&last);
715 if (last.timestamp == 0 || reset_pending) {
716 snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012");
718 snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
719 LEADINGZERO(abs(current_delta)), PLUSMINUS(-current_delta), abs(current_delta));
721 return std::string(delta);