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.
23 #include <sys/types.h>
26 #include "pbd/error.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"
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_Slave::frame_tolerance = 2;
52 MTC_Slave::MTC_Slave (Session& s, MidiPort& p)
56 can_notify_on_unknown_rate = true;
57 did_reset_tc_format = false;
59 reset_position = false;
62 engine_dll_initstate = 0;
63 busy_guard1 = busy_guard2 = 0;
65 last_mtc_fps_byte = session.get_mtc_timecode_bits ();
66 quarter_frame_duration = (double(session.frames_per_timecode_frame()) / 4.0);
68 mtc_timecode = session.config.get_timecode_format();
69 a3e_timecode = session.config.get_timecode_format();
70 printed_timecode_warning = false;
72 session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_Slave::parameter_changed, this, _1));
73 parse_timecode_offset();
76 parser.mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3));
77 parser.mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3));
78 parser.mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
82 MTC_Slave::~MTC_Slave()
84 port_connections.drop_connections();
85 config_connection.disconnect();
87 while (busy_guard1 != busy_guard2) {
88 /* make sure MIDI parser is not currently calling any callbacks in here,
89 * else there's a segfault ahead!
91 * XXX this is called from jack rt-context :(
92 * TODO fix libs/ardour/session_transport.cc:1321 (delete _slave;)
97 if (did_reset_tc_format) {
98 session.config.set_timecode_format (saved_tc_format);
103 MTC_Slave::process (pframes_t nframes)
105 MidiBuffer& mb (port->get_midi_buffer (nframes));
107 /* dump incoming MIDI to parser */
109 for (MidiBuffer::iterator b = mb.begin(); b != mb.end(); ++b) {
110 uint8_t* buf = (*b).buffer();
112 parser.set_timestamp ((*b).time());
114 uint32_t limit = (*b).size();
116 for (size_t n = 0; n < limit; ++n) {
117 parser.scanner (buf[n]);
125 MTC_Slave::rebind (MidiPort& p)
127 port_connections.drop_connections ();
134 MTC_Slave::parse_timecode_offset() {
135 Timecode::Time offset_tc;
136 Timecode::parse_timecode_format(session.config.get_slave_timecode_offset(), offset_tc);
137 offset_tc.rate = session.timecode_frames_per_second();
138 offset_tc.drop = session.timecode_drop_frames();
139 session.timecode_to_sample(offset_tc, timecode_offset, false, false);
140 timecode_negative_offset = offset_tc.negative;
144 MTC_Slave::parameter_changed (std::string const & p)
146 if (p == "slave-timecode-offset"
147 || p == "timecode-format"
149 parse_timecode_offset();
154 MTC_Slave::give_slave_full_control_over_transport_speed() const
156 return true; // DLL align to engine transport
157 // return false; // for Session-level computed varispeed
161 MTC_Slave::resolution () const
163 return (framecnt_t) quarter_frame_duration * 4.0;
167 MTC_Slave::seekahead_distance () const
169 return quarter_frame_duration * 8 * transport_direction;
173 MTC_Slave::outside_window (framepos_t pos) const
175 return ((pos < window_begin) || (pos > window_end));
180 MTC_Slave::locked () const
182 return parser.mtc_locked() && last_inbound_frame !=0 && engine_dll_initstate !=0;
186 MTC_Slave::ok() const
192 MTC_Slave::queue_reset (bool reset_pos)
194 Glib::Threads::Mutex::Lock lm (reset_lock);
197 reset_position = true;
202 MTC_Slave::maybe_reset ()
204 Glib::Threads::Mutex::Lock lm (reset_lock);
207 reset (reset_position);
209 reset_position = false;
214 MTC_Slave::reset (bool with_position)
216 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC_Slave reset %1\n", with_position?"with position":"without position"));
218 last_inbound_frame = 0;
220 current.position = 0;
221 current.timestamp = 0;
225 last_inbound_frame = 0;
227 current.timestamp = 0;
231 first_mtc_timestamp = 0;
234 transport_direction = 1;
239 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
242 DEBUG_TRACE (DEBUG::MTC, "MTC_Slave::handle_locate\n");
244 mtc[4] = last_mtc_fps_byte;
245 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
250 update_mtc_time (mtc, true, 0);
254 MTC_Slave::read_current (SafeTime *st) const
260 error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
267 } while (st->guard1 != st->guard2);
271 MTC_Slave::init_mtc_dll(framepos_t tme, double qtr)
273 omega = 2.0 * M_PI * qtr / 2.0 / double(session.frame_rate());
274 b = 1.4142135623730950488 * omega;
280 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
284 /* called from MIDI parser */
286 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now)
289 const double qtr_d = quarter_frame_duration;
291 mtc_frame_dll += qtr_d * (double) transport_direction;
292 mtc_frame = rint(mtc_frame_dll);
294 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
296 double mtc_speed = 0;
297 if (first_mtc_timestamp != 0) {
298 /* update MTC DLL and calculate speed */
299 const double e = mtc_frame_dll - (double)transport_direction * ((double)now - (double)current.timestamp + t0);
304 mtc_speed = (t1 - t0) / qtr_d;
305 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));
308 current.position = mtc_frame;
309 current.timestamp = now;
310 current.speed = mtc_speed;
313 last_inbound_frame = now;
321 /* called from MIDI parser _after_ update_mtc_qtr()
322 * when a full TC has been received
325 MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now)
329 /* "now" can be zero if this is called from a context where we do not have or do not want
330 to use a timestamp indicating when this MTC time was received. example: when we received
331 a locate command via MMC.
334 //DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self()));
335 TimecodeFormat tc_format;
336 bool reset_tc = true;
338 timecode.hours = msg[3];
339 timecode.minutes = msg[2];
340 timecode.seconds = msg[1];
341 timecode.frames = msg[0];
343 last_mtc_fps_byte = msg[4];
345 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
354 timecode.drop = false;
355 tc_format = timecode_24;
356 can_notify_on_unknown_rate = true;
360 timecode.drop = false;
361 tc_format = timecode_25;
362 can_notify_on_unknown_rate = true;
364 case MTC_30_FPS_DROP:
365 if (Config->get_timecode_source_2997()) {
366 tc_format = Timecode::timecode_2997000drop;
367 timecode.rate = (29970.0/1000.0);
369 tc_format = timecode_2997drop;
370 timecode.rate = (30000.0/1001.0);
372 timecode.drop = true;
373 can_notify_on_unknown_rate = true;
377 timecode.drop = false;
378 can_notify_on_unknown_rate = true;
379 tc_format = timecode_30;
382 /* throttle error messages about unknown MTC rates */
383 if (can_notify_on_unknown_rate) {
384 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
387 can_notify_on_unknown_rate = false;
389 timecode.rate = session.timecode_frames_per_second();
390 timecode.drop = session.timecode_drop_frames();
395 TimecodeFormat cur_timecode = session.config.get_timecode_format();
396 if (Config->get_timecode_sync_frame_rate()) {
397 /* enforce time-code */
398 if (!did_reset_tc_format) {
399 saved_tc_format = cur_timecode;
400 did_reset_tc_format = true;
402 if (cur_timecode != tc_format) {
403 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
404 warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."),
405 Timecode::timecode_format_name(cur_timecode),
406 Timecode::timecode_format_name(tc_format))
410 session.config.set_timecode_format (tc_format);
412 /* only warn about TC mismatch */
413 if (mtc_timecode != tc_format) printed_timecode_warning = false;
414 if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
416 if (cur_timecode != tc_format && ! printed_timecode_warning) {
417 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
418 warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."),
419 Timecode::timecode_format_name(tc_format),
421 Timecode::timecode_format_name(cur_timecode))
424 printed_timecode_warning = true;
427 mtc_timecode = tc_format;
428 a3e_timecode = cur_timecode;
430 speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode);
433 /* do a careful conversion of the timecode value to a position
434 so that we take drop/nondrop and all that nonsense into
438 quarter_frame_duration = (double(session.frame_rate()) / (double) timecode.rate / 4.0);
440 Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
441 double(session.frame_rate()),
442 session.config.get_subframes_per_frame(),
443 timecode_negative_offset, timecode_offset
446 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
447 now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
449 if (was_full || outside_window (mtc_frame)) {
450 DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC or outside window. - TID:%1\n", ::pthread_self()));
451 session.request_locate (mtc_frame, false);
452 session.request_transport_speed (0);
453 update_mtc_status (MIDI::MTC_Stopped);
455 reset_window (mtc_frame);
458 /* we've had the first set of 8 qtr frame messages, determine position
459 and allow continuing qtr frame messages to provide position
460 and speed information.
463 /* We received the last quarter frame 7 quarter frames (1.75 mtc
464 frames) after the instance when the contents of the mtc quarter
465 frames were decided. Add time to compensate for the elapsed 1.75
468 double qtr = quarter_frame_duration;
469 long int mtc_off = (long) rint(7.0 * qtr);
471 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
472 mtc_frame, (4.0*qtr), session.frames_per_timecode_frame()));
474 switch (parser.mtc_running()) {
476 mtc_frame -= mtc_off;
480 mtc_frame += mtc_off;
486 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
489 if (first_mtc_timestamp == 0 || current.timestamp == 0) {
490 first_mtc_timestamp = now;
491 init_mtc_dll(mtc_frame, qtr);
492 mtc_frame_dll = mtc_frame;
495 current.position = mtc_frame;
496 current.timestamp = now;
498 reset_window (mtc_frame);
503 last_inbound_frame = now;
509 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
511 /* XXX !!! thread safety ... called from MIDI I/O context
512 * on locate (via ::update_mtc_time())
514 DEBUG_TRACE (DEBUG::MTC, string_compose("MTC_Slave::update_mtc_status - TID:%1\n", ::pthread_self()));
515 return; // why was this fn needed anyway ? it just messes up things -> use reset.
521 current.position = mtc_frame;
522 current.timestamp = 0;
530 current.position = mtc_frame;
531 current.timestamp = 0;
538 current.position = mtc_frame;
539 current.timestamp = 0;
548 MTC_Slave::reset_window (framepos_t root)
550 /* if we're waiting for the master to catch us after seeking ahead, keep the window
551 of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
552 ahead of the window root (taking direction into account).
554 framecnt_t const d = (quarter_frame_duration * 4 * frame_tolerance);
556 switch (parser.mtc_running()) {
559 transport_direction = 1;
560 window_end = root + d;
564 transport_direction = -1;
566 window_begin = root - d;
579 DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
583 MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc)
585 /* the bandwidth of the DLL is a trade-off,
586 * because the max-speed of the transport in ardour is
587 * limited to +-8.0, a larger bandwidth would cause oscillations
589 * But this is only really a problem if the user performs manual
590 * seeks while transport is running and slaved to MTC.
592 oe = 2.0 * M_PI * double(inc) / 2.0 / double(session.frame_rate());
593 be = 1.4142135623730950488 * oe;
596 ee2 = double(transport_direction * inc);
599 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init Engine DLL %1 %2 %3\n", te0, te1, ee2));
602 /* main entry point from session_process.cc
603 xo * in process callback context */
605 MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
607 framepos_t now = session.engine().sample_time_at_cycle_start();
608 framepos_t sess_pos = session.transport_frame(); // corresponds to now
609 //sess_pos -= session.engine().frames_since_cycle_start();
612 frameoffset_t elapsed;
613 bool engine_dll_reinitialized = false;
615 read_current (&last);
617 /* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */
618 if (last.timestamp == 0) { engine_dll_initstate = 0; }
619 else if (engine_dll_initstate != transport_direction && last.speed != 0) {
620 engine_dll_initstate = transport_direction;
621 init_engine_dll(last.position, session.engine().samples_per_cycle());
622 engine_dll_reinitialized = true;
625 if (last.timestamp == 0) {
627 pos = session.transport_frame() ; // last.position;
628 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", pos));
632 /* no timecode for two frames - conclude that it's stopped */
633 if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > labs(seekahead_distance())) {
636 session.request_locate (pos, false);
637 session.request_transport_speed (0);
638 engine_dll_initstate = 0;
640 DEBUG_TRACE (DEBUG::MTC, "MTC not seen for 2 frames - reset pending\n");
645 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));
646 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position eng-tme: %1 eng-pos: %2\n", now, sess_pos));
648 double speed_flt = last.speed; ///< MTC speed from MTC-quarter-frame DLL
650 /* interpolate position according to speed and time since last quarter-frame*/
651 if (speed_flt == 0.0f) {
656 /* scale elapsed time by the current MTC speed */
657 elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp));
658 if (give_slave_full_control_over_transport_speed() && !engine_dll_reinitialized) {
659 /* there is an engine vs MTC position frame-delta.
660 * This mostly due to quantization and rounding of (speed * nframes)
661 * but can also due to the session-process not calling
662 * speed_and_position() every cycle under some circumstances.
663 * Thus we use an other DLL to align the engine and the MTC
666 /* update engine DLL and calculate speed */
667 const double e = double (last.position + elapsed - sess_pos);
671 speed_flt = (te1 - te0) / double(session.engine().samples_per_cycle());
672 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() ));
676 pos = last.position + elapsed;
679 /* may happen if the user performs a seek in the timeline while slaved to running MTC
680 * engine-DLL can oscillate back before 0.
681 * also see note in MTC_Slave::init_engine_dll
683 if (!session.actively_recording()
685 && ( (pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()) )
687 engine_dll_initstate = 0;
691 /* provide a .1% deadzone to lock the speed */
692 if (fabs(speed - 1.0) <= 0.001)
695 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n",
696 speed, pos, last.position, elapsed, pos - sess_pos));
698 current_delta = (pos - sess_pos);
703 Timecode::TimecodeFormat
704 MTC_Slave::apparent_timecode_format () const
710 MTC_Slave::approximate_current_position() const
713 read_current (&last);
714 if (last.timestamp == 0 || reset_pending) {
715 return " --:--:--:--";
717 return Timecode::timecode_format_sampletime(
719 double(session.frame_rate()),
720 Timecode::timecode_to_frames_per_second(mtc_timecode),
721 Timecode::timecode_has_drop_frames(mtc_timecode));
725 MTC_Slave::approximate_current_delta() const
729 read_current (&last);
730 if (last.timestamp == 0 || reset_pending) {
731 snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012");
733 snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
734 LEADINGZERO(abs(current_delta)), PLUSMINUS(-current_delta), abs(current_delta));
736 return std::string(delta);