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"
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_TransportMaster::sample_tolerance = 2;
52 MTC_TransportMaster::MTC_TransportMaster (std::string const & name)
53 : TimecodeTransportMaster (name, MTC)
54 , can_notify_on_unknown_rate (true)
57 , last_inbound_frame (0)
60 , first_mtc_timestamp (0)
61 , did_reset_tc_format (false)
63 , reset_position (false)
64 , transport_direction (1)
67 , printed_timecode_warning (false)
69 if ((_port = create_midi_port (string_compose ("%1 in", name))) == 0) {
70 throw failed_constructor();
73 DEBUG_TRACE (DEBUG::Slave, string_compose ("MTC registered %1\n", _port->name()));
78 MTC_TransportMaster::~MTC_TransportMaster()
80 port_connections.drop_connections();
81 config_connection.disconnect();
83 while (busy_guard1 != busy_guard2) {
84 /* make sure MIDI parser is not currently calling any callbacks in here,
85 * else there's a segfault ahead!
87 * XXX this is called from jack rt-context :(
88 * TODO fix libs/ardour/session_transport.cc:1321 (delete _slave;)
93 if (did_reset_tc_format) {
94 _session->config.set_timecode_format (saved_tc_format);
99 MTC_TransportMaster::init ()
105 MTC_TransportMaster::set_session (Session *s)
107 config_connection.disconnect ();
108 port_connections.drop_connections();
114 last_mtc_fps_byte = _session->get_mtc_timecode_bits ();
115 quarter_frame_duration = (double) (_session->samples_per_timecode_frame() / 4.0);
116 mtc_timecode = _session->config.get_timecode_format();
117 a3e_timecode = _session->config.get_timecode_format();
119 parse_timecode_offset ();
122 parser.mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_time, this, _1, _2, _3));
123 parser.mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_qtr, this, _1, _2, _3));
124 parser.mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_status, this, _1));
126 _session->config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_TransportMaster::parameter_changed, this, _1));
131 MTC_TransportMaster::pre_process (MIDI::pframes_t nframes, samplepos_t now, boost::optional<samplepos_t> session_pos)
133 /* 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::update_interval() const
171 return AudioEngine::instance()->sample_rate() / timecode.rate;
174 return AudioEngine::instance()->sample_rate(); /* useless but what other answer is there? */
178 MTC_TransportMaster::resolution () const
180 return (samplecnt_t) quarter_frame_duration * 4.0;
184 MTC_TransportMaster::seekahead_distance () const
186 return quarter_frame_duration * 8 * transport_direction;
190 MTC_TransportMaster::outside_window (samplepos_t pos) const
192 return ((pos < window_begin) || (pos > window_end));
197 MTC_TransportMaster::locked () const
199 DEBUG_TRACE (DEBUG::MTC, string_compose ("locked ? %1 last %2\n", parser.mtc_locked(), last_inbound_frame));
200 return parser.mtc_locked() && last_inbound_frame !=0;
204 MTC_TransportMaster::ok() const
210 MTC_TransportMaster::queue_reset (bool reset_pos)
212 Glib::Threads::Mutex::Lock lm (reset_lock);
215 reset_position = true;
220 MTC_TransportMaster::maybe_reset ()
222 Glib::Threads::Mutex::Lock lm (reset_lock);
225 reset (reset_position);
227 reset_position = false;
232 MTC_TransportMaster::reset (bool with_position)
234 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC_TransportMaster reset %1\n", with_position?"with position":"without position"));
237 current.update (current.position, 0, 0);
241 first_mtc_timestamp = 0;
244 transport_direction = 1;
249 MTC_TransportMaster::handle_locate (const MIDI::byte* mmc_tc)
252 DEBUG_TRACE (DEBUG::MTC, "MTC_TransportMaster::handle_locate\n");
254 mtc[4] = last_mtc_fps_byte;
255 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
260 update_mtc_time (mtc, true, 0);
264 MTC_TransportMaster::init_mtc_dll(samplepos_t tme, double qtr)
266 const double omega = 2.0 * M_PI * qtr / 2.0 / double(_session->sample_rate());
267 b = 1.4142135623730950488 * omega;
273 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
276 /* called from MIDI parser */
278 MTC_TransportMaster::update_mtc_qtr (Parser& p, int which_qtr, samplepos_t now)
281 const double qtr_d = quarter_frame_duration;
283 mtc_frame_dll += qtr_d * (double) transport_direction;
284 mtc_frame = rint(mtc_frame_dll);
286 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr sample %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
288 double mtc_speed = 0;
289 if (first_mtc_timestamp != 0) {
290 /* update MTC DLL and calculate speed */
291 const double e = mtc_frame_dll - (double)transport_direction * ((double)now - (double)current.timestamp + t0);
296 mtc_speed = (t1 - t0) / qtr_d;
297 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));
299 current.update (mtc_frame, now, mtc_speed);
301 last_inbound_frame = now;
309 /* called from MIDI parser _after_ update_mtc_qtr()
310 * when a full TC has been received
313 MTC_TransportMaster::update_mtc_time (const MIDI::byte *msg, bool was_full, samplepos_t now)
317 /* "now" can be zero if this is called from a context where we do not have or do not want
318 to use a timestamp indicating when this MTC time was received. example: when we received
319 a locate command via MMC.
321 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", pthread_name()));
322 TimecodeFormat tc_format;
323 bool reset_tc = true;
325 timecode.hours = msg[3];
326 timecode.minutes = msg[2];
327 timecode.seconds = msg[1];
328 timecode.frames = msg[0];
330 last_mtc_fps_byte = msg[4];
332 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
341 timecode.drop = false;
342 tc_format = timecode_24;
343 can_notify_on_unknown_rate = true;
347 timecode.drop = false;
348 tc_format = timecode_25;
349 can_notify_on_unknown_rate = true;
351 case MTC_30_FPS_DROP:
353 tc_format = Timecode::timecode_2997000drop;
354 timecode.rate = (29970.0/1000.0);
356 tc_format = timecode_2997drop;
357 timecode.rate = (30000.0/1001.0);
359 timecode.drop = true;
360 can_notify_on_unknown_rate = true;
364 timecode.drop = false;
365 can_notify_on_unknown_rate = true;
366 tc_format = timecode_30;
369 /* throttle error messages about unknown MTC rates */
370 if (can_notify_on_unknown_rate) {
371 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
374 can_notify_on_unknown_rate = false;
376 timecode.rate = _session->timecode_frames_per_second();
377 timecode.drop = _session->timecode_drop_frames();
382 TimecodeFormat cur_timecode = _session->config.get_timecode_format();
383 if (Config->get_timecode_sync_frame_rate()) {
384 /* enforce time-code */
385 if (!did_reset_tc_format) {
386 saved_tc_format = cur_timecode;
387 did_reset_tc_format = true;
389 if (cur_timecode != tc_format) {
390 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
391 warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."),
392 Timecode::timecode_format_name(cur_timecode),
393 Timecode::timecode_format_name(tc_format))
397 _session->config.set_timecode_format (tc_format);
399 /* only warn about TC mismatch */
400 if (mtc_timecode != tc_format) printed_timecode_warning = false;
401 if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
403 if (cur_timecode != tc_format && ! printed_timecode_warning) {
404 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
405 warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."),
406 Timecode::timecode_format_name(tc_format),
408 Timecode::timecode_format_name(cur_timecode))
411 printed_timecode_warning = true;
414 mtc_timecode = tc_format;
415 a3e_timecode = cur_timecode;
417 speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode);
420 /* do a careful conversion of the timecode value to a position
421 so that we take drop/nondrop and all that nonsense into
425 quarter_frame_duration = (double(_session->sample_rate()) / (double) timecode.rate / 4.0);
427 Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
428 double(_session->sample_rate()),
429 _session->config.get_subframes_per_frame(),
430 timecode_negative_offset, timecode_offset
433 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
434 now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
436 if (was_full || outside_window (mtc_frame)) {
437 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));
438 _session->set_requested_return_sample (-1);
439 _session->request_transport_speed (0, TRS_MTC);
440 _session->request_locate (mtc_frame, false, TRS_MTC);
441 update_mtc_status (MIDI::MTC_Stopped);
443 reset_window (mtc_frame);
446 /* we've had the first set of 8 qtr sample messages, determine position
447 and allow continuing qtr sample messages to provide position
448 and speed information.
451 /* We received the last quarter frame 7 quarter frames (1.75 mtc
452 samples) after the instance when the contents of the mtc quarter
453 samples were decided. Add time to compensate for the elapsed 1.75
456 double qtr = quarter_frame_duration;
457 long int mtc_off = (long) rint(7.0 * qtr);
459 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
460 mtc_frame, (4.0*qtr), _session->samples_per_timecode_frame()));
462 switch (parser.mtc_running()) {
464 mtc_frame -= mtc_off;
468 mtc_frame += mtc_off;
474 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
477 if (first_mtc_timestamp == 0 || current.timestamp == 0) {
478 first_mtc_timestamp = now;
479 init_mtc_dll(mtc_frame, qtr);
480 mtc_frame_dll = mtc_frame;
482 current.update (mtc_frame, now, current.speed);
483 reset_window (mtc_frame);
491 MTC_TransportMaster::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_TransportMaster::update_mtc_status - TID:%1 MTC:%2\n", pthread_name(), mtc_frame));
497 return; // why was this fn needed anyway ? it just messes up things -> use reset.
502 current.update (mtc_frame, 0, 0);
506 current.update (mtc_frame, 0, 0);
510 current.update (mtc_frame, 0, 0);
517 MTC_TransportMaster::reset_window (samplepos_t root)
519 /* if we're waiting for the master to catch us after seeking ahead, keep the window
520 of acceptable MTC samples wide open. otherwise, shrink it down to just 2 video frames
521 ahead of the window root (taking direction into account).
524 samplecnt_t const d = (quarter_frame_duration * 4 * sample_tolerance);
526 switch (parser.mtc_running()) {
529 transport_direction = 1;
530 window_end = root + d;
534 transport_direction = -1;
536 window_begin = root - d;
549 DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root));
552 Timecode::TimecodeFormat
553 MTC_TransportMaster::apparent_timecode_format () const
559 MTC_TransportMaster::position_string() const
562 current.safe_read (last);
563 if (last.timestamp == 0 || reset_pending) {
564 return " --:--:--:--";
566 return Timecode::timecode_format_sampletime(
568 double(_session->sample_rate()),
569 Timecode::timecode_to_frames_per_second(mtc_timecode),
570 Timecode::timecode_has_drop_frames(mtc_timecode));
574 MTC_TransportMaster::delta_string () const
578 current.safe_read (last);
582 if (last.timestamp == 0 || reset_pending) {
583 snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012");
585 snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
586 LEADINGZERO(abs(_current_delta)), PLUSMINUS(-_current_delta), abs(_current_delta));
588 return std::string(delta);
592 MTC_TransportMaster::unregister_port ()
595 TransportMaster::unregister_port ();