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::frame_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.frames_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 (framecnt_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 (framepos_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;
219 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
222 DEBUG_TRACE (DEBUG::MTC, "MTC_Slave::handle_locate\n");
224 mtc[4] = last_mtc_fps_byte;
225 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
230 update_mtc_time (mtc, true, 0);
234 MTC_Slave::read_current (SafeTime *st) const
240 error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
247 } while (st->guard1 != st->guard2);
251 MTC_Slave::init_mtc_dll(framepos_t tme, double qtr)
253 omega = 2.0 * M_PI * qtr / 2.0 / double(session.frame_rate());
254 b = 1.4142135623730950488 * omega;
260 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
263 /* called from MIDI parser */
265 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now)
268 const double qtr_d = quarter_frame_duration;
270 mtc_frame_dll += qtr_d * (double) transport_direction;
271 mtc_frame = rint(mtc_frame_dll);
273 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
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);
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));
287 current.position = mtc_frame;
288 current.timestamp = now;
289 current.speed = mtc_speed;
292 last_inbound_frame = now;
300 /* called from MIDI parser _after_ update_mtc_qtr()
301 * when a full TC has been received
304 MTC_Slave::update_mtc_time (const MIDI::byte *msg, bool was_full, framepos_t now)
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.
313 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self().p));
315 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self()));
317 TimecodeFormat tc_format;
318 bool reset_tc = true;
320 timecode.hours = msg[3];
321 timecode.minutes = msg[2];
322 timecode.seconds = msg[1];
323 timecode.frames = msg[0];
325 last_mtc_fps_byte = msg[4];
327 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
336 timecode.drop = false;
337 tc_format = timecode_24;
338 can_notify_on_unknown_rate = true;
342 timecode.drop = false;
343 tc_format = timecode_25;
344 can_notify_on_unknown_rate = true;
346 case MTC_30_FPS_DROP:
347 if (Config->get_timecode_source_2997()) {
348 tc_format = Timecode::timecode_2997000drop;
349 timecode.rate = (29970.0/1000.0);
351 tc_format = timecode_2997drop;
352 timecode.rate = (30000.0/1001.0);
354 timecode.drop = true;
355 can_notify_on_unknown_rate = true;
359 timecode.drop = false;
360 can_notify_on_unknown_rate = true;
361 tc_format = timecode_30;
364 /* throttle error messages about unknown MTC rates */
365 if (can_notify_on_unknown_rate) {
366 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
369 can_notify_on_unknown_rate = false;
371 timecode.rate = session.timecode_frames_per_second();
372 timecode.drop = session.timecode_drop_frames();
377 TimecodeFormat cur_timecode = session.config.get_timecode_format();
378 if (Config->get_timecode_sync_frame_rate()) {
379 /* enforce time-code */
380 if (!did_reset_tc_format) {
381 saved_tc_format = cur_timecode;
382 did_reset_tc_format = true;
384 if (cur_timecode != tc_format) {
385 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
386 warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."),
387 Timecode::timecode_format_name(cur_timecode),
388 Timecode::timecode_format_name(tc_format))
392 session.config.set_timecode_format (tc_format);
394 /* only warn about TC mismatch */
395 if (mtc_timecode != tc_format) printed_timecode_warning = false;
396 if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
398 if (cur_timecode != tc_format && ! printed_timecode_warning) {
399 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
400 warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."),
401 Timecode::timecode_format_name(tc_format),
403 Timecode::timecode_format_name(cur_timecode))
406 printed_timecode_warning = true;
409 mtc_timecode = tc_format;
410 a3e_timecode = cur_timecode;
412 speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode);
415 /* do a careful conversion of the timecode value to a position
416 so that we take drop/nondrop and all that nonsense into
420 quarter_frame_duration = (double(session.frame_rate()) / (double) timecode.rate / 4.0);
422 Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
423 double(session.frame_rate()),
424 session.config.get_subframes_per_frame(),
425 timecode_negative_offset, timecode_offset
428 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
429 now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
431 if (was_full || outside_window (mtc_frame)) {
432 DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC %1 or outside window %2\n", was_full, outside_window (mtc_frame)));
433 session.request_locate (mtc_frame, false);
434 session.request_transport_speed (0);
435 update_mtc_status (MIDI::MTC_Stopped);
437 reset_window (mtc_frame);
440 /* we've had the first set of 8 qtr frame messages, determine position
441 and allow continuing qtr frame messages to provide position
442 and speed information.
445 /* We received the last quarter frame 7 quarter frames (1.75 mtc
446 frames) after the instance when the contents of the mtc quarter
447 frames were decided. Add time to compensate for the elapsed 1.75
450 double qtr = quarter_frame_duration;
451 long int mtc_off = (long) rint(7.0 * qtr);
453 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
454 mtc_frame, (4.0*qtr), session.frames_per_timecode_frame()));
456 switch (port->self_parser().mtc_running()) {
458 mtc_frame -= mtc_off;
462 mtc_frame += mtc_off;
468 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
471 if (first_mtc_timestamp == 0 || current.timestamp == 0) {
472 first_mtc_timestamp = now;
473 init_mtc_dll(mtc_frame, qtr);
474 mtc_frame_dll = mtc_frame;
477 current.position = mtc_frame;
478 current.timestamp = now;
480 reset_window (mtc_frame);
485 last_inbound_frame = now;
491 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
493 /* XXX !!! thread safety ... called from MIDI I/O context
494 * on locate (via ::update_mtc_time())
496 DEBUG_TRACE (DEBUG::MTC, string_compose("MTC_Slave::update_mtc_status - TID:%1\n", pthread_name()));
497 return; // why was this fn needed anyway ? it just messes up things -> use reset.
503 current.position = mtc_frame;
504 current.timestamp = 0;
512 current.position = mtc_frame;
513 current.timestamp = 0;
520 current.position = mtc_frame;
521 current.timestamp = 0;
530 MTC_Slave::reset_window (framepos_t root)
532 /* if we're waiting for the master to catch us after seeking ahead, keep the window
533 of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
534 ahead of the window root (taking direction into account).
537 framecnt_t const d = (quarter_frame_duration * 4 * frame_tolerance);
539 switch (port->self_parser().mtc_running()) {
542 transport_direction = 1;
543 window_end = root + d;
547 transport_direction = -1;
549 window_begin = root - d;
562 DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root));
566 MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc)
568 /* the bandwidth of the DLL is a trade-off,
569 * because the max-speed of the transport in ardour is
570 * limited to +-8.0, a larger bandwidth would cause oscillations
572 * But this is only really a problem if the user performs manual
573 * seeks while transport is running and slaved to MTC.
575 oe = 2.0 * M_PI * double(inc) / 2.0 / double(session.frame_rate());
576 be = 1.4142135623730950488 * oe;
579 ee2 = double(transport_direction * inc);
582 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init Engine DLL %1 %2 %3\n", te0, te1, ee2));
585 /* main entry point from session_process.cc
586 xo * in process callback context */
588 MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
590 framepos_t now = session.engine().sample_time_at_cycle_start();
591 framepos_t sess_pos = session.transport_frame(); // corresponds to now
592 //sess_pos -= session.engine().frames_since_cycle_start();
595 frameoffset_t elapsed;
596 bool engine_dll_reinitialized = false;
598 read_current (&last);
600 DEBUG_TRACE (DEBUG::MTC, string_compose ("speed&pos: timestamp %1 speed %2 initstate %3 dir %4 tpos %5 now %6 last-in %7\n",
603 engine_dll_initstate,
607 last_inbound_frame));
609 /* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */
610 if (last.timestamp == 0) {
611 engine_dll_initstate = 0;
612 } else if (engine_dll_initstate != transport_direction && last.speed != 0) {
613 engine_dll_initstate = transport_direction;
614 init_engine_dll(last.position, session.engine().samples_per_cycle());
615 engine_dll_reinitialized = true;
618 if (last.timestamp == 0) {
620 pos = session.transport_frame() ; // last.position;
621 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", pos));
625 /* no timecode for two frames - conclude that it's stopped */
626 if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > labs(seekahead_distance())) {
629 session.request_locate (pos, false);
630 session.request_transport_speed (0);
631 engine_dll_initstate = 0;
633 DEBUG_TRACE (DEBUG::MTC, "MTC not seen for 2 frames - reset pending\n");
638 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));
639 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position eng-tme: %1 eng-pos: %2\n", now, sess_pos));
641 double speed_flt = last.speed; ///< MTC speed from MTC-quarter-frame DLL
643 /* interpolate position according to speed and time since last quarter-frame*/
644 if (speed_flt == 0.0f) {
647 /* scale elapsed time by the current MTC speed */
648 elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp));
649 if (give_slave_full_control_over_transport_speed() && !engine_dll_reinitialized) {
650 /* there is an engine vs MTC position frame-delta.
651 * This mostly due to quantization and rounding of (speed * nframes)
652 * but can also due to the session-process not calling
653 * speed_and_position() every cycle under some circumstances.
654 * Thus we use an other DLL to align the engine and the MTC
657 /* update engine DLL and calculate speed */
658 const double e = double (last.position + elapsed - sess_pos);
662 speed_flt = (te1 - te0) / double(session.engine().samples_per_cycle());
663 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() ));
667 pos = last.position + elapsed;
670 /* may happen if the user performs a seek in the timeline while slaved to running MTC
671 * engine-DLL can oscillate back before 0.
672 * also see note in MTC_Slave::init_engine_dll
674 if (!session.actively_recording()
676 && ((pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()))) {
677 engine_dll_initstate = 0;
681 /* provide a .1% deadzone to lock the speed */
682 if (fabs (speed - 1.0) <= 0.001)
685 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n",
686 speed, pos, last.position, elapsed, pos - sess_pos));
688 current_delta = (pos - sess_pos);
693 Timecode::TimecodeFormat
694 MTC_Slave::apparent_timecode_format () const
700 MTC_Slave::approximate_current_position() const
703 read_current (&last);
704 if (last.timestamp == 0 || reset_pending) {
705 return " --:--:--:--";
707 return Timecode::timecode_format_sampletime(
709 double(session.frame_rate()),
710 Timecode::timecode_to_frames_per_second(mtc_timecode),
711 Timecode::timecode_has_drop_frames(mtc_timecode));
715 MTC_Slave::approximate_current_delta() const
719 read_current (&last);
720 if (last.timestamp == 0 || reset_pending) {
721 snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012");
723 snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
724 LEADINGZERO(abs(current_delta)), PLUSMINUS(-current_delta), abs(current_delta));
726 return std::string(delta);