2 * Copyright (C) 2006-2012 David Robillard <d@drobilla.net>
3 * Copyright (C) 2008-2019 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2009-2010 Carl Hetherington <carl@carlh.net>
5 * Copyright (C) 2012-2017 Robin Gareus <robin@gareus.org>
6 * Copyright (C) 2013-2018 John Emmas <john@creativepost.co.uk>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <sys/types.h>
27 #include "pbd/error.h"
28 #include "pbd/pthread_utils.h"
30 #include "ardour/audioengine.h"
31 #include "ardour/debug.h"
32 #include "ardour/midi_buffer.h"
33 #include "ardour/midi_port.h"
34 #include "ardour/session.h"
35 #include "ardour/transport_master.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 DEBUG_TRACE (DEBUG::Slave, string_compose ("MTC registered %1\n", _port->name()));
76 MTC_TransportMaster::~MTC_TransportMaster()
78 port_connections.drop_connections();
79 config_connection.disconnect();
81 if (_session && did_reset_tc_format) {
82 _session->config.set_timecode_format (saved_tc_format);
87 MTC_TransportMaster::init ()
93 MTC_TransportMaster::create_port ()
95 if ((_port = create_midi_port (string_compose ("%1 in", _name))) == 0) {
96 throw failed_constructor();
101 MTC_TransportMaster::set_session (Session *s)
103 config_connection.disconnect ();
104 port_connections.drop_connections();
110 last_mtc_fps_byte = _session->get_mtc_timecode_bits ();
111 quarter_frame_duration = (double) (_session->samples_per_timecode_frame() / 4.0);
112 mtc_timecode = _session->config.get_timecode_format();
113 a3e_timecode = _session->config.get_timecode_format();
115 parse_timecode_offset ();
118 parser.mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_time, this, _1, _2, _3));
119 parser.mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_qtr, this, _1, _2, _3));
120 parser.mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_status, this, _1));
122 _session->config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_TransportMaster::parameter_changed, this, _1));
127 MTC_TransportMaster::pre_process (MIDI::pframes_t nframes, samplepos_t now, boost::optional<samplepos_t> session_pos)
129 /* Read and parse incoming MIDI */
133 _midi_port->read_and_parse_entire_midi_buffer_with_no_speed_adjustment (nframes, parser, now);
136 const samplepos_t current_pos = current.position + ((now - current.timestamp) * current.speed);
137 _current_delta = current_pos - *session_pos;
144 MTC_TransportMaster::parse_timecode_offset() {
145 Timecode::Time offset_tc;
146 Timecode::parse_timecode_format (_session->config.get_slave_timecode_offset(), offset_tc);
147 offset_tc.rate = _session->timecode_frames_per_second();
148 offset_tc.drop = _session->timecode_drop_frames();
149 _session->timecode_to_sample(offset_tc, timecode_offset, false, false);
150 timecode_negative_offset = offset_tc.negative;
154 MTC_TransportMaster::parameter_changed (std::string const & p)
156 if (p == "slave-timecode-offset"
157 || p == "timecode-format"
159 parse_timecode_offset();
164 MTC_TransportMaster::update_interval() const
167 return AudioEngine::instance()->sample_rate() / timecode.rate;
170 return AudioEngine::instance()->sample_rate(); /* useless but what other answer is there? */
174 MTC_TransportMaster::resolution () const
176 return (samplecnt_t) quarter_frame_duration * 4.0;
180 MTC_TransportMaster::seekahead_distance () const
182 return quarter_frame_duration * 8 * transport_direction;
186 MTC_TransportMaster::outside_window (samplepos_t pos) const
188 return ((pos < window_begin) || (pos > window_end));
193 MTC_TransportMaster::locked () const
195 DEBUG_TRACE (DEBUG::MTC, string_compose ("locked ? %1 last %2\n", parser.mtc_locked(), last_inbound_frame));
196 return parser.mtc_locked() && last_inbound_frame !=0;
200 MTC_TransportMaster::ok() const
206 MTC_TransportMaster::queue_reset (bool reset_pos)
208 Glib::Threads::Mutex::Lock lm (reset_lock);
211 reset_position = true;
216 MTC_TransportMaster::maybe_reset ()
218 Glib::Threads::Mutex::Lock lm (reset_lock);
221 reset (reset_position);
223 reset_position = false;
228 MTC_TransportMaster::reset (bool with_position)
230 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC_TransportMaster reset %1\n", with_position?"with position":"without position"));
233 current.update (current.position, 0, 0);
237 first_mtc_timestamp = 0;
240 transport_direction = 1;
245 MTC_TransportMaster::handle_locate (const MIDI::byte* mmc_tc)
248 DEBUG_TRACE (DEBUG::MTC, "MTC_TransportMaster::handle_locate\n");
250 mtc[4] = last_mtc_fps_byte;
251 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
256 update_mtc_time (mtc, true, 0);
260 MTC_TransportMaster::init_mtc_dll(samplepos_t tme, double qtr)
262 const double omega = 2.0 * M_PI * qtr / 2.0 / double(_session->sample_rate());
263 b = 1.4142135623730950488 * omega;
269 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
272 /* called from MIDI parser */
274 MTC_TransportMaster::update_mtc_qtr (Parser& p, int which_qtr, samplepos_t now)
277 const double qtr_d = quarter_frame_duration;
279 mtc_frame_dll += qtr_d * (double) transport_direction;
280 mtc_frame = rint(mtc_frame_dll);
282 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr sample %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
284 double mtc_speed = 0;
285 if (first_mtc_timestamp != 0) {
286 /* update MTC DLL and calculate speed */
287 const double e = mtc_frame_dll - (double)transport_direction * ((double)now - (double)current.timestamp + t0);
292 mtc_speed = (t1 - t0) / qtr_d;
293 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));
295 current.update (mtc_frame, now, mtc_speed);
297 last_inbound_frame = now;
305 /* called from MIDI parser _after_ update_mtc_qtr()
306 * when a full TC has been received
309 MTC_TransportMaster::update_mtc_time (const MIDI::byte *msg, bool was_full, samplepos_t now)
313 /* "now" can be zero if this is called from a context where we do not have or do not want
314 to use a timestamp indicating when this MTC time was received. example: when we received
315 a locate command via MMC.
317 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", pthread_name()));
318 TimecodeFormat tc_format;
319 bool reset_tc = true;
321 timecode.hours = msg[3];
322 timecode.minutes = msg[2];
323 timecode.seconds = msg[1];
324 timecode.frames = msg[0];
326 last_mtc_fps_byte = msg[4];
328 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
337 timecode.drop = false;
338 tc_format = timecode_24;
339 can_notify_on_unknown_rate = true;
343 timecode.drop = false;
344 tc_format = timecode_25;
345 can_notify_on_unknown_rate = true;
347 case MTC_30_FPS_DROP:
349 tc_format = Timecode::timecode_2997000drop;
350 timecode.rate = (29970.0/1000.0);
352 tc_format = timecode_2997drop;
353 timecode.rate = (30000.0/1001.0);
355 timecode.drop = true;
356 can_notify_on_unknown_rate = true;
360 timecode.drop = false;
361 can_notify_on_unknown_rate = true;
362 tc_format = timecode_30;
365 /* throttle error messages about unknown MTC rates */
366 if (can_notify_on_unknown_rate) {
367 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
370 can_notify_on_unknown_rate = false;
372 timecode.rate = _session->timecode_frames_per_second();
373 timecode.drop = _session->timecode_drop_frames();
378 TimecodeFormat cur_timecode = _session->config.get_timecode_format();
379 if (Config->get_timecode_sync_frame_rate()) {
380 /* enforce time-code */
381 if (!did_reset_tc_format) {
382 saved_tc_format = cur_timecode;
383 did_reset_tc_format = true;
385 if (cur_timecode != tc_format) {
386 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
387 warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."),
388 Timecode::timecode_format_name(cur_timecode),
389 Timecode::timecode_format_name(tc_format))
393 _session->config.set_timecode_format (tc_format);
395 /* only warn about TC mismatch */
396 if (mtc_timecode != tc_format) printed_timecode_warning = false;
397 if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
399 if (cur_timecode != tc_format && ! printed_timecode_warning) {
400 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
401 warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."),
402 Timecode::timecode_format_name(tc_format),
404 Timecode::timecode_format_name(cur_timecode))
407 printed_timecode_warning = true;
410 mtc_timecode = tc_format;
411 a3e_timecode = cur_timecode;
413 speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode);
416 /* do a careful conversion of the timecode value to a position
417 so that we take drop/nondrop and all that nonsense into
421 quarter_frame_duration = (double(_session->sample_rate()) / (double) timecode.rate / 4.0);
423 Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
424 double(_session->sample_rate()),
425 _session->config.get_subframes_per_frame(),
426 timecode_negative_offset, timecode_offset
429 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
430 now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
432 if (was_full || outside_window (mtc_frame)) {
433 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));
434 _session->set_requested_return_sample (-1);
435 _session->request_transport_speed (0, TRS_MTC);
436 _session->request_locate (mtc_frame, false, TRS_MTC);
437 update_mtc_status (MIDI::MTC_Stopped);
439 reset_window (mtc_frame);
442 /* we've had the first set of 8 qtr sample messages, determine position
443 and allow continuing qtr sample messages to provide position
444 and speed information.
447 /* We received the last quarter frame 7 quarter frames (1.75 mtc
448 samples) after the instance when the contents of the mtc quarter
449 samples were decided. Add time to compensate for the elapsed 1.75
452 double qtr = quarter_frame_duration;
453 long int mtc_off = (long) rint(7.0 * qtr);
455 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
456 mtc_frame, (4.0*qtr), _session->samples_per_timecode_frame()));
458 switch (parser.mtc_running()) {
460 mtc_frame -= mtc_off;
464 mtc_frame += mtc_off;
470 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
473 if (first_mtc_timestamp == 0 || current.timestamp == 0) {
474 first_mtc_timestamp = now;
475 init_mtc_dll(mtc_frame, qtr);
476 mtc_frame_dll = mtc_frame;
478 current.update (mtc_frame, now, current.speed);
479 reset_window (mtc_frame);
487 MTC_TransportMaster::update_mtc_status (MIDI::MTC_Status status)
489 /* XXX !!! thread safety ... called from MIDI I/O context
490 * on locate (via ::update_mtc_time())
492 DEBUG_TRACE (DEBUG::MTC, string_compose("MTC_TransportMaster::update_mtc_status - TID:%1 MTC:%2\n", pthread_name(), mtc_frame));
493 return; // why was this fn needed anyway ? it just messes up things -> use reset.
498 current.update (mtc_frame, 0, 0);
502 current.update (mtc_frame, 0, 0);
506 current.update (mtc_frame, 0, 0);
513 MTC_TransportMaster::reset_window (samplepos_t root)
515 /* if we're waiting for the master to catch us after seeking ahead, keep the window
516 of acceptable MTC samples wide open. otherwise, shrink it down to just 2 video frames
517 ahead of the window root (taking direction into account).
520 samplecnt_t const d = (quarter_frame_duration * 4 * sample_tolerance);
522 switch (parser.mtc_running()) {
525 transport_direction = 1;
526 window_end = root + d;
530 transport_direction = -1;
532 window_begin = root - d;
545 DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root));
548 Timecode::TimecodeFormat
549 MTC_TransportMaster::apparent_timecode_format () const
555 MTC_TransportMaster::position_string() const
558 current.safe_read (last);
559 if (last.timestamp == 0 || reset_pending) {
560 return " --:--:--:--";
562 return Timecode::timecode_format_sampletime(
564 double(_session->sample_rate()),
565 Timecode::timecode_to_frames_per_second(mtc_timecode),
566 Timecode::timecode_has_drop_frames(mtc_timecode));
570 MTC_TransportMaster::delta_string () const
574 current.safe_read (last);
578 if (last.timestamp == 0 || reset_pending) {
579 snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012");
581 snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
582 LEADINGZERO(abs(_current_delta)), PLUSMINUS(-_current_delta), abs(_current_delta));
584 return std::string(delta);
588 MTC_TransportMaster::unregister_port ()
591 TransportMaster::unregister_port ();