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/transport_master.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_TransportMaster::sample_tolerance = 2;
54 MTC_TransportMaster::MTC_TransportMaster (std::string const & name)
55 : TimecodeTransportMaster (name, MTC)
56 , can_notify_on_unknown_rate (true)
59 , last_inbound_frame (0)
62 , first_mtc_timestamp (0)
63 , did_reset_tc_format (false)
65 , reset_position (false)
66 , transport_direction (1)
69 , printed_timecode_warning (false)
71 if ((_port = create_midi_port (string_compose ("%1 in", name))) == 0) {
72 throw failed_constructor();
75 DEBUG_TRACE (DEBUG::Slave, string_compose ("MTC registered %1\n", _port->name()));
80 MTC_TransportMaster::~MTC_TransportMaster()
82 port_connections.drop_connections();
83 config_connection.disconnect();
85 while (busy_guard1 != busy_guard2) {
86 /* make sure MIDI parser is not currently calling any callbacks in here,
87 * else there's a segfault ahead!
89 * XXX this is called from jack rt-context :(
90 * TODO fix libs/ardour/session_transport.cc:1321 (delete _slave;)
95 if (did_reset_tc_format) {
96 _session->config.set_timecode_format (saved_tc_format);
101 MTC_TransportMaster::init ()
107 MTC_TransportMaster::set_session (Session *s)
109 config_connection.disconnect ();
110 port_connections.drop_connections();
116 last_mtc_fps_byte = _session->get_mtc_timecode_bits ();
117 quarter_frame_duration = (double) (_session->samples_per_timecode_frame() / 4.0);
118 mtc_timecode = _session->config.get_timecode_format();
119 a3e_timecode = _session->config.get_timecode_format();
121 parse_timecode_offset ();
124 parser.mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_time, this, _1, _2, _3));
125 parser.mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_qtr, this, _1, _2, _3));
126 parser.mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_status, this, _1));
128 _session->config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_TransportMaster::parameter_changed, this, _1));
133 MTC_TransportMaster::pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t> session_pos)
135 /* Read and parse incoming MIDI */
137 _midi_port->read_and_parse_entire_midi_buffer_with_no_speed_adjustment (nframes, parser, now);
140 const samplepos_t current_pos = current.position + ((now - current.timestamp) * current.speed);
141 _current_delta = current_pos - *session_pos;
148 MTC_TransportMaster::parse_timecode_offset() {
149 Timecode::Time offset_tc;
150 Timecode::parse_timecode_format (_session->config.get_slave_timecode_offset(), offset_tc);
151 offset_tc.rate = _session->timecode_frames_per_second();
152 offset_tc.drop = _session->timecode_drop_frames();
153 _session->timecode_to_sample(offset_tc, timecode_offset, false, false);
154 timecode_negative_offset = offset_tc.negative;
158 MTC_TransportMaster::parameter_changed (std::string const & p)
160 if (p == "slave-timecode-offset"
161 || p == "timecode-format"
163 parse_timecode_offset();
168 MTC_TransportMaster::resolution () const
170 return (samplecnt_t) quarter_frame_duration * 4.0;
174 MTC_TransportMaster::seekahead_distance () const
176 return quarter_frame_duration * 8 * transport_direction;
180 MTC_TransportMaster::outside_window (samplepos_t pos) const
182 return ((pos < window_begin) || (pos > window_end));
187 MTC_TransportMaster::locked () const
189 DEBUG_TRACE (DEBUG::MTC, string_compose ("locked ? %1 last %2\n", parser.mtc_locked(), last_inbound_frame));
190 return parser.mtc_locked() && last_inbound_frame !=0;
194 MTC_TransportMaster::ok() const
200 MTC_TransportMaster::queue_reset (bool reset_pos)
202 Glib::Threads::Mutex::Lock lm (reset_lock);
205 reset_position = true;
210 MTC_TransportMaster::maybe_reset ()
212 Glib::Threads::Mutex::Lock lm (reset_lock);
215 reset (reset_position);
217 reset_position = false;
222 MTC_TransportMaster::reset (bool with_position)
224 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC_TransportMaster reset %1\n", with_position?"with position":"without position"));
227 last_inbound_frame = 0;
229 current.position = 0;
230 current.timestamp = 0;
234 last_inbound_frame = 0;
236 current.timestamp = 0;
240 first_mtc_timestamp = 0;
243 transport_direction = 1;
248 MTC_TransportMaster::handle_locate (const MIDI::byte* mmc_tc)
251 DEBUG_TRACE (DEBUG::MTC, "MTC_TransportMaster::handle_locate\n");
253 mtc[4] = last_mtc_fps_byte;
254 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
259 update_mtc_time (mtc, true, 0);
263 MTC_TransportMaster::read_current (SafeTime *st) const
269 error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
276 } while (st->guard1 != st->guard2);
280 MTC_TransportMaster::init_mtc_dll(samplepos_t tme, double qtr)
282 const double omega = 2.0 * M_PI * qtr / 2.0 / double(_session->sample_rate());
283 b = 1.4142135623730950488 * omega;
289 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
292 /* called from MIDI parser */
294 MTC_TransportMaster::update_mtc_qtr (Parser& p, int which_qtr, samplepos_t now)
297 const double qtr_d = quarter_frame_duration;
299 mtc_frame_dll += qtr_d * (double) transport_direction;
300 mtc_frame = rint(mtc_frame_dll);
302 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr sample %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
304 double mtc_speed = 0;
305 if (first_mtc_timestamp != 0) {
306 /* update MTC DLL and calculate speed */
307 const double e = mtc_frame_dll - (double)transport_direction * ((double)now - (double)current.timestamp + t0);
312 mtc_speed = (t1 - t0) / qtr_d;
313 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));
316 current.position = mtc_frame;
317 current.timestamp = now;
318 current.speed = mtc_speed;
321 last_inbound_frame = now;
329 /* called from MIDI parser _after_ update_mtc_qtr()
330 * when a full TC has been received
333 MTC_TransportMaster::update_mtc_time (const MIDI::byte *msg, bool was_full, samplepos_t now)
337 /* "now" can be zero if this is called from a context where we do not have or do not want
338 to use a timestamp indicating when this MTC time was received. example: when we received
339 a locate command via MMC.
341 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", pthread_name()));
342 TimecodeFormat tc_format;
343 bool reset_tc = true;
345 timecode.hours = msg[3];
346 timecode.minutes = msg[2];
347 timecode.seconds = msg[1];
348 timecode.frames = msg[0];
350 last_mtc_fps_byte = msg[4];
352 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
361 timecode.drop = false;
362 tc_format = timecode_24;
363 can_notify_on_unknown_rate = true;
367 timecode.drop = false;
368 tc_format = timecode_25;
369 can_notify_on_unknown_rate = true;
371 case MTC_30_FPS_DROP:
373 tc_format = Timecode::timecode_2997000drop;
374 timecode.rate = (29970.0/1000.0);
376 tc_format = timecode_2997drop;
377 timecode.rate = (30000.0/1001.0);
379 timecode.drop = true;
380 can_notify_on_unknown_rate = true;
384 timecode.drop = false;
385 can_notify_on_unknown_rate = true;
386 tc_format = timecode_30;
389 /* throttle error messages about unknown MTC rates */
390 if (can_notify_on_unknown_rate) {
391 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
394 can_notify_on_unknown_rate = false;
396 timecode.rate = _session->timecode_frames_per_second();
397 timecode.drop = _session->timecode_drop_frames();
402 TimecodeFormat cur_timecode = _session->config.get_timecode_format();
403 if (Config->get_timecode_sync_frame_rate()) {
404 /* enforce time-code */
405 if (!did_reset_tc_format) {
406 saved_tc_format = cur_timecode;
407 did_reset_tc_format = true;
409 if (cur_timecode != tc_format) {
410 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
411 warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."),
412 Timecode::timecode_format_name(cur_timecode),
413 Timecode::timecode_format_name(tc_format))
417 _session->config.set_timecode_format (tc_format);
419 /* only warn about TC mismatch */
420 if (mtc_timecode != tc_format) printed_timecode_warning = false;
421 if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
423 if (cur_timecode != tc_format && ! printed_timecode_warning) {
424 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
425 warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."),
426 Timecode::timecode_format_name(tc_format),
428 Timecode::timecode_format_name(cur_timecode))
431 printed_timecode_warning = true;
434 mtc_timecode = tc_format;
435 a3e_timecode = cur_timecode;
437 speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode);
440 /* do a careful conversion of the timecode value to a position
441 so that we take drop/nondrop and all that nonsense into
445 quarter_frame_duration = (double(_session->sample_rate()) / (double) timecode.rate / 4.0);
447 Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
448 double(_session->sample_rate()),
449 _session->config.get_subframes_per_frame(),
450 timecode_negative_offset, timecode_offset
453 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
454 now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
456 if (was_full || outside_window (mtc_frame)) {
457 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));
458 _session->set_requested_return_sample (-1);
459 _session->request_transport_speed (0, TRS_MTC);
460 _session->request_locate (mtc_frame, false, TRS_MTC);
461 update_mtc_status (MIDI::MTC_Stopped);
463 reset_window (mtc_frame);
466 /* we've had the first set of 8 qtr sample messages, determine position
467 and allow continuing qtr sample messages to provide position
468 and speed information.
471 /* We received the last quarter frame 7 quarter frames (1.75 mtc
472 samples) after the instance when the contents of the mtc quarter
473 samples were decided. Add time to compensate for the elapsed 1.75
476 double qtr = quarter_frame_duration;
477 long int mtc_off = (long) rint(7.0 * qtr);
479 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
480 mtc_frame, (4.0*qtr), _session->samples_per_timecode_frame()));
482 switch (parser.mtc_running()) {
484 mtc_frame -= mtc_off;
488 mtc_frame += mtc_off;
494 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
497 if (first_mtc_timestamp == 0 || current.timestamp == 0) {
498 first_mtc_timestamp = now;
499 init_mtc_dll(mtc_frame, qtr);
500 mtc_frame_dll = mtc_frame;
503 current.position = mtc_frame;
504 current.timestamp = now;
506 reset_window (mtc_frame);
511 last_inbound_frame = now;
517 MTC_TransportMaster::update_mtc_status (MIDI::MTC_Status status)
519 /* XXX !!! thread safety ... called from MIDI I/O context
520 * on locate (via ::update_mtc_time())
522 DEBUG_TRACE (DEBUG::MTC, string_compose("MTC_TransportMaster::update_mtc_status - TID:%1 MTC:%2\n", pthread_name(), mtc_frame));
523 return; // why was this fn needed anyway ? it just messes up things -> use reset.
529 current.position = mtc_frame;
530 current.timestamp = 0;
538 current.position = mtc_frame;
539 current.timestamp = 0;
546 current.position = mtc_frame;
547 current.timestamp = 0;
556 MTC_TransportMaster::reset_window (samplepos_t root)
558 /* if we're waiting for the master to catch us after seeking ahead, keep the window
559 of acceptable MTC samples wide open. otherwise, shrink it down to just 2 video frames
560 ahead of the window root (taking direction into account).
563 samplecnt_t const d = (quarter_frame_duration * 4 * sample_tolerance);
565 switch (parser.mtc_running()) {
568 transport_direction = 1;
569 window_end = root + d;
573 transport_direction = -1;
575 window_begin = root - d;
588 DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root));
591 /* main entry point from session_process.cc
592 xo * in process callback context */
594 MTC_TransportMaster::speed_and_position (double& speed, samplepos_t& pos, samplepos_t now)
602 read_current (&last);
604 DEBUG_TRACE (DEBUG::MTC, string_compose ("speed&pos: timestamp %1 speed %2 dir %4 now %5 last-in %6\n",
609 last_inbound_frame));
611 if (last.timestamp == 0) {
615 if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > labs(seekahead_distance())) {
616 /* no timecode for two cycles - conclude that it's stopped */
618 if (!Config->get_transport_masters_just_roll_when_sync_lost()) {
623 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC not seen for 2 samples - reset pending, pos = %1\n", pos));
629 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));
633 /* provide a .1% deadzone to lock the speed */
634 if (fabs (speed - 1.0) <= 0.001) {
639 pos += (now - last.timestamp) * speed;
641 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 | elapsed: %4\n",
642 speed, pos, last.position, (now - last.timestamp)));
647 Timecode::TimecodeFormat
648 MTC_TransportMaster::apparent_timecode_format () const
654 MTC_TransportMaster::position_string() const
657 read_current (&last);
658 if (last.timestamp == 0 || reset_pending) {
659 return " --:--:--:--";
661 return Timecode::timecode_format_sampletime(
663 double(_session->sample_rate()),
664 Timecode::timecode_to_frames_per_second(mtc_timecode),
665 Timecode::timecode_has_drop_frames(mtc_timecode));
669 MTC_TransportMaster::delta_string () const
673 read_current (&last);
677 if (last.timestamp == 0 || reset_pending) {
678 snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012");
680 snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
681 LEADINGZERO(abs(_current_delta)), PLUSMINUS(-_current_delta), abs(_current_delta));
683 return std::string(delta);