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.
22 #include <sys/types.h>
25 #include "pbd/error.h"
26 #include "pbd/pthread_utils.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"
35 #include <glibmm/timer.h>
40 using namespace ARDOUR;
43 using namespace Timecode;
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.
52 const int MTC_Slave::sample_tolerance = 2;
54 MTC_Slave::MTC_Slave (Session& s, MidiPort& p)
58 can_notify_on_unknown_rate = true;
59 did_reset_tc_format = false;
61 reset_position = false;
64 engine_dll_initstate = 0;
65 busy_guard1 = busy_guard2 = 0;
67 last_mtc_fps_byte = session.get_mtc_timecode_bits ();
68 quarter_frame_duration = (double(session.samples_per_timecode_frame()) / 4.0);
70 mtc_timecode = session.config.get_timecode_format();
71 a3e_timecode = session.config.get_timecode_format();
72 printed_timecode_warning = false;
74 session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_Slave::parameter_changed, this, _1));
75 parse_timecode_offset();
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));
83 MTC_Slave::~MTC_Slave()
85 port_connections.drop_connections();
86 config_connection.disconnect();
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!
92 * XXX this is called from jack rt-context :(
93 * TODO fix libs/ardour/session_transport.cc:1321 (delete _slave;)
98 if (did_reset_tc_format) {
99 session.config.set_timecode_format (saved_tc_format);
104 MTC_Slave::rebind (MidiPort& p)
106 port_connections.drop_connections ();
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;
123 MTC_Slave::parameter_changed (std::string const & p)
125 if (p == "slave-timecode-offset"
126 || p == "timecode-format"
128 parse_timecode_offset();
133 MTC_Slave::give_slave_full_control_over_transport_speed() const
135 return true; // DLL align to engine transport
136 // return false; // for Session-level computed varispeed
140 MTC_Slave::resolution () const
142 return (samplecnt_t) quarter_frame_duration * 4.0;
146 MTC_Slave::seekahead_distance () const
148 return quarter_frame_duration * 8 * transport_direction;
152 MTC_Slave::outside_window (samplepos_t pos) const
154 return ((pos < window_begin) || (pos > window_end));
159 MTC_Slave::locked () const
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;
166 MTC_Slave::ok() const
172 MTC_Slave::queue_reset (bool reset_pos)
174 Glib::Threads::Mutex::Lock lm (reset_lock);
177 reset_position = true;
182 MTC_Slave::maybe_reset ()
184 Glib::Threads::Mutex::Lock lm (reset_lock);
187 reset (reset_position);
189 reset_position = false;
194 MTC_Slave::reset (bool with_position)
196 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC_Slave reset %1\n", with_position?"with position":"without position"));
198 last_inbound_frame = 0;
200 current.position = 0;
201 current.timestamp = 0;
205 last_inbound_frame = 0;
207 current.timestamp = 0;
211 first_mtc_timestamp = 0;
214 transport_direction = 1;
216 ActiveChanged(false);
220 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
223 DEBUG_TRACE (DEBUG::MTC, "MTC_Slave::handle_locate\n");
225 mtc[4] = last_mtc_fps_byte;
226 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
231 update_mtc_time (mtc, true, 0);
235 MTC_Slave::read_current (SafeTime *st) const
241 error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
248 } while (st->guard1 != st->guard2);
252 MTC_Slave::init_mtc_dll(samplepos_t tme, double qtr)
254 omega = 2.0 * M_PI * qtr / 2.0 / double(session.sample_rate());
255 b = 1.4142135623730950488 * omega;
261 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
264 /* called from MIDI parser */
266 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, samplepos_t now)
269 const double qtr_d = quarter_frame_duration;
271 mtc_frame_dll += qtr_d * (double) transport_direction;
272 mtc_frame = rint(mtc_frame_dll);
274 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr sample %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
276 double mtc_speed = 0;
277 if (first_mtc_timestamp != 0) {
278 /* update MTC DLL and calculate speed */
279 const double e = mtc_frame_dll - (double)transport_direction * ((double)now - (double)current.timestamp + t0);
284 mtc_speed = (t1 - t0) / qtr_d;
285 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr sample DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", t0, t1, e, mtc_speed, e2 - qtr_d));
288 current.position = mtc_frame;
289 current.timestamp = now;
290 current.speed = mtc_speed;
293 last_inbound_frame = now;
301 /* called from MIDI parser _after_ update_mtc_qtr()
302 * when a full TC has been received
305 MTC_Slave::update_mtc_time (const MIDI::byte *msg, bool was_full, samplepos_t now)
309 /* "now" can be zero if this is called from a context where we do not have or do not want
310 to use a timestamp indicating when this MTC time was received. example: when we received
311 a locate command via MMC.
313 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", pthread_name()));
314 TimecodeFormat tc_format;
315 bool reset_tc = true;
317 timecode.hours = msg[3];
318 timecode.minutes = msg[2];
319 timecode.seconds = msg[1];
320 timecode.frames = msg[0];
322 last_mtc_fps_byte = msg[4];
324 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
333 timecode.drop = false;
334 tc_format = timecode_24;
335 can_notify_on_unknown_rate = true;
339 timecode.drop = false;
340 tc_format = timecode_25;
341 can_notify_on_unknown_rate = true;
343 case MTC_30_FPS_DROP:
344 if (Config->get_timecode_source_2997()) {
345 tc_format = Timecode::timecode_2997000drop;
346 timecode.rate = (29970.0/1000.0);
348 tc_format = timecode_2997drop;
349 timecode.rate = (30000.0/1001.0);
351 timecode.drop = true;
352 can_notify_on_unknown_rate = true;
356 timecode.drop = false;
357 can_notify_on_unknown_rate = true;
358 tc_format = timecode_30;
361 /* throttle error messages about unknown MTC rates */
362 if (can_notify_on_unknown_rate) {
363 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
366 can_notify_on_unknown_rate = false;
368 timecode.rate = session.timecode_frames_per_second();
369 timecode.drop = session.timecode_drop_frames();
374 TimecodeFormat cur_timecode = session.config.get_timecode_format();
375 if (Config->get_timecode_sync_frame_rate()) {
376 /* enforce time-code */
377 if (!did_reset_tc_format) {
378 saved_tc_format = cur_timecode;
379 did_reset_tc_format = true;
381 if (cur_timecode != tc_format) {
382 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
383 warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."),
384 Timecode::timecode_format_name(cur_timecode),
385 Timecode::timecode_format_name(tc_format))
389 session.config.set_timecode_format (tc_format);
391 /* only warn about TC mismatch */
392 if (mtc_timecode != tc_format) printed_timecode_warning = false;
393 if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
395 if (cur_timecode != tc_format && ! printed_timecode_warning) {
396 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
397 warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."),
398 Timecode::timecode_format_name(tc_format),
400 Timecode::timecode_format_name(cur_timecode))
403 printed_timecode_warning = true;
406 mtc_timecode = tc_format;
407 a3e_timecode = cur_timecode;
409 speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode);
412 /* do a careful conversion of the timecode value to a position
413 so that we take drop/nondrop and all that nonsense into
417 quarter_frame_duration = (double(session.sample_rate()) / (double) timecode.rate / 4.0);
419 Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
420 double(session.sample_rate()),
421 session.config.get_subframes_per_frame(),
422 timecode_negative_offset, timecode_offset
425 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
426 now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
428 if (was_full || outside_window (mtc_frame)) {
429 DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC %1 or outside window %2 MTC %3\n", was_full, outside_window (mtc_frame), mtc_frame));
430 session.set_requested_return_sample (-1);
431 session.request_transport_speed (0);
432 session.request_locate (mtc_frame, false);
433 update_mtc_status (MIDI::MTC_Stopped);
435 reset_window (mtc_frame);
438 /* we've had the first set of 8 qtr sample messages, determine position
439 and allow continuing qtr sample messages to provide position
440 and speed information.
443 /* We received the last quarter frame 7 quarter frames (1.75 mtc
444 samples) after the instance when the contents of the mtc quarter
445 samples were decided. Add time to compensate for the elapsed 1.75
448 double qtr = quarter_frame_duration;
449 long int mtc_off = (long) rint(7.0 * qtr);
451 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
452 mtc_frame, (4.0*qtr), session.samples_per_timecode_frame()));
454 switch (port->self_parser().mtc_running()) {
456 mtc_frame -= mtc_off;
460 mtc_frame += mtc_off;
466 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
469 if (first_mtc_timestamp == 0 || current.timestamp == 0) {
470 first_mtc_timestamp = now;
471 init_mtc_dll(mtc_frame, qtr);
472 mtc_frame_dll = mtc_frame;
473 ActiveChanged (true); // emit signal
476 current.position = mtc_frame;
477 current.timestamp = now;
479 reset_window (mtc_frame);
484 last_inbound_frame = now;
490 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
492 /* XXX !!! thread safety ... called from MIDI I/O context
493 * on locate (via ::update_mtc_time())
495 DEBUG_TRACE (DEBUG::MTC, string_compose("MTC_Slave::update_mtc_status - TID:%1 MTC:%2\n", pthread_name(), mtc_frame));
496 return; // why was this fn needed anyway ? it just messes up things -> use reset.
502 current.position = mtc_frame;
503 current.timestamp = 0;
511 current.position = mtc_frame;
512 current.timestamp = 0;
519 current.position = mtc_frame;
520 current.timestamp = 0;
529 MTC_Slave::reset_window (samplepos_t root)
531 /* if we're waiting for the master to catch us after seeking ahead, keep the window
532 of acceptable MTC samples wide open. otherwise, shrink it down to just 2 video frames
533 ahead of the window root (taking direction into account).
536 samplecnt_t const d = (quarter_frame_duration * 4 * sample_tolerance);
538 switch (port->self_parser().mtc_running()) {
541 transport_direction = 1;
542 window_end = root + d;
546 transport_direction = -1;
548 window_begin = root - d;
561 DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root));
565 MTC_Slave::init_engine_dll (samplepos_t pos, samplepos_t inc)
567 /* the bandwidth of the DLL is a trade-off,
568 * because the max-speed of the transport in ardour is
569 * limited to +-8.0, a larger bandwidth would cause oscillations
571 * But this is only really a problem if the user performs manual
572 * seeks while transport is running and slaved to MTC.
574 oe = 2.0 * M_PI * double(inc) / 2.0 / double(session.sample_rate());
575 be = 1.4142135623730950488 * oe;
578 ee2 = double(transport_direction * inc);
581 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init Engine DLL %1 %2 %3\n", te0, te1, ee2));
584 /* main entry point from session_process.cc
585 xo * in process callback context */
587 MTC_Slave::speed_and_position (double& speed, samplepos_t& pos)
589 samplepos_t now = session.engine().sample_time_at_cycle_start();
590 samplepos_t sess_pos = session.transport_sample(); // corresponds to now
591 //sess_pos -= session.engine().samples_since_cycle_start();
594 sampleoffset_t elapsed;
595 bool engine_dll_reinitialized = false;
597 read_current (&last);
599 DEBUG_TRACE (DEBUG::MTC, string_compose ("speed&pos: timestamp %1 speed %2 initstate %3 dir %4 tpos %5 now %6 last-in %7\n",
602 engine_dll_initstate,
606 last_inbound_frame));
608 /* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */
609 if (last.timestamp == 0) {
610 engine_dll_initstate = 0;
611 } else if (engine_dll_initstate != transport_direction && last.speed != 0) {
612 engine_dll_initstate = transport_direction;
613 init_engine_dll(last.position, session.engine().samples_per_cycle());
614 engine_dll_reinitialized = true;
617 if (last.timestamp == 0) {
619 pos = session.transport_sample() ; // last.position;
620 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", pos));
624 /* no timecode for two samples - conclude that it's stopped */
625 if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > labs(seekahead_distance())) {
628 session.set_requested_return_sample (-1);
629 session.request_locate (pos, false);
630 session.request_transport_speed (0);
631 engine_dll_initstate = 0;
633 ActiveChanged (false);
634 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC not seen for 2 samples - reset pending, pos = %1\n", pos));
639 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position mtc-tme: %1 mtc-pos: %2 mtc-spd: %3\n", last.timestamp, last.position, last.speed));
640 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position eng-tme: %1 eng-pos: %2\n", now, sess_pos));
642 double speed_flt = last.speed; ///< MTC speed from MTC-quarter-frame DLL
644 /* interpolate position according to speed and time since last quarter-frame*/
645 if (speed_flt == 0.0f) {
648 /* scale elapsed time by the current MTC speed */
649 elapsed = (samplecnt_t) rint (speed_flt * (now - last.timestamp));
650 if (give_slave_full_control_over_transport_speed() && !engine_dll_reinitialized) {
651 /* there is an engine vs MTC position sample-delta.
652 * This mostly due to quantization and rounding of (speed * nframes)
653 * but can also due to the session-process not calling
654 * speed_and_position() every cycle under some circumstances.
655 * Thus we use an other DLL to align the engine and the MTC
658 /* update engine DLL and calculate speed */
659 const double e = double (last.position + elapsed - sess_pos);
663 speed_flt = (te1 - te0) / double(session.engine().samples_per_cycle());
664 DEBUG_TRACE (DEBUG::MTC, string_compose ("engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", te0, te1, e, speed_flt, ee2 - session.engine().samples_per_cycle() ));
668 pos = last.position + elapsed;
671 /* may happen if the user performs a seek in the timeline while slaved to running MTC
672 * engine-DLL can oscillate back before 0.
673 * also see note in MTC_Slave::init_engine_dll
675 if (!session.actively_recording()
677 && ((pos < 0) || (labs(pos - sess_pos) > 3 * session.sample_rate()))) {
678 engine_dll_initstate = 0;
682 /* provide a .1% deadzone to lock the speed */
683 if (fabs (speed - 1.0) <= 0.001)
686 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n",
687 speed, pos, last.position, elapsed, pos - sess_pos));
689 current_delta = (pos - sess_pos);
694 Timecode::TimecodeFormat
695 MTC_Slave::apparent_timecode_format () const
701 MTC_Slave::approximate_current_position() const
704 read_current (&last);
705 if (last.timestamp == 0 || reset_pending) {
706 return " --:--:--:--";
708 return Timecode::timecode_format_sampletime(
710 double(session.sample_rate()),
711 Timecode::timecode_to_frames_per_second(mtc_timecode),
712 Timecode::timecode_has_drop_frames(mtc_timecode));
716 MTC_Slave::approximate_current_delta() const
720 read_current (&last);
721 if (last.timestamp == 0 || reset_pending) {
722 snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012");
724 snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
725 LEADINGZERO(abs(current_delta)), PLUSMINUS(-current_delta), abs(current_delta));
727 return std::string(delta);