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 "midi++/port.h"
29 #include "ardour/debug.h"
30 #include "ardour/slave.h"
31 #include "ardour/session.h"
32 #include "ardour/audioengine.h"
37 using namespace ARDOUR;
40 using namespace Timecode;
42 /* length (in timecode frames) of the "window" that we consider legal given receipt of
43 a given timecode position. Ardour will try to chase within this window, and will
44 stop+locate+wait+chase if timecode arrives outside of it. The window extends entirely
45 in the current direction of motion, so if any timecode arrives that is before the most
46 recently received position (and without the direction of timecode reversing too), we
47 will stop+locate+wait+chase.
49 const int MTC_Slave::frame_tolerance = 2;
51 MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
54 can_notify_on_unknown_rate = true;
55 did_reset_tc_format = false;
57 reset_position = false;
60 engine_dll_initstate = 0;
61 busy_guard1 = busy_guard2 = 0;
63 last_mtc_fps_byte = session.get_mtc_timecode_bits ();
64 quarter_frame_duration = (double(session.frames_per_timecode_frame()) / 4.0);
66 mtc_timecode = session.config.get_timecode_format();
67 a3e_timecode = session.config.get_timecode_format();
68 printed_timecode_warning = false;
70 session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_Slave::parameter_changed, this, _1));
71 parse_timecode_offset();
76 MTC_Slave::~MTC_Slave()
78 port_connections.drop_connections();
79 config_connection.disconnect();
81 while (busy_guard1 != busy_guard2) {
82 /* make sure MIDI parser is not currently calling any callbacks in here,
83 * else there's a segfault ahead!
85 * XXX this is called from jack rt-context :(
86 * TODO fix libs/ardour/session_transport.cc:1321 (delete _slave;)
91 if (did_reset_tc_format) {
92 session.config.set_timecode_format (saved_tc_format);
97 MTC_Slave::rebind (MIDI::Port& p)
99 port_connections.drop_connections ();
103 port->parser()->mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3));
104 port->parser()->mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3));
105 port->parser()->mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
109 MTC_Slave::parse_timecode_offset() {
110 Timecode::Time offset_tc;
111 Timecode::parse_timecode_format(session.config.get_slave_timecode_offset(), offset_tc);
112 offset_tc.rate = session.timecode_frames_per_second();
113 offset_tc.drop = session.timecode_drop_frames();
114 session.timecode_to_sample(offset_tc, timecode_offset, false, false);
115 timecode_negative_offset = offset_tc.negative;
119 MTC_Slave::parameter_changed (std::string const & p)
121 if (p == "slave-timecode-offset"
122 || p == "timecode-format"
124 parse_timecode_offset();
129 MTC_Slave::give_slave_full_control_over_transport_speed() const
131 return true; // DLL align to engine transport
132 // return false; // for Session-level computed varispeed
136 MTC_Slave::resolution () const
138 return (framecnt_t) quarter_frame_duration * 4.0;
142 MTC_Slave::seekahead_distance () const
144 return quarter_frame_duration * 8 * transport_direction;
148 MTC_Slave::outside_window (framepos_t pos) const
150 return ((pos < window_begin) || (pos > window_end));
155 MTC_Slave::locked () const
157 return port->parser()->mtc_locked() && last_inbound_frame !=0 && engine_dll_initstate !=0;
161 MTC_Slave::ok() const
167 MTC_Slave::queue_reset (bool reset_pos)
169 Glib::Threads::Mutex::Lock lm (reset_lock);
172 reset_position = true;
177 MTC_Slave::maybe_reset ()
179 Glib::Threads::Mutex::Lock lm (reset_lock);
182 reset (reset_position);
184 reset_position = false;
189 MTC_Slave::reset (bool with_position)
191 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC_Slave reset %1\n", with_position?"with position":"without position"));
193 last_inbound_frame = 0;
195 current.position = 0;
196 current.timestamp = 0;
200 last_inbound_frame = 0;
202 current.timestamp = 0;
206 first_mtc_timestamp = 0;
209 transport_direction = 1;
214 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
217 DEBUG_TRACE (DEBUG::MTC, "MTC_Slave::handle_locate\n");
219 mtc[4] = last_mtc_fps_byte;
220 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
225 update_mtc_time (mtc, true, 0);
229 MTC_Slave::read_current (SafeTime *st) const
235 error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
242 } while (st->guard1 != st->guard2);
246 MTC_Slave::init_mtc_dll(framepos_t tme, double qtr)
248 omega = 2.0 * M_PI * qtr / 2.0 / double(session.frame_rate());
249 b = 1.4142135623730950488 * omega;
255 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
259 /* called from MIDI parser */
261 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now)
264 const double qtr_d = quarter_frame_duration;
266 mtc_frame_dll += qtr_d * (double) transport_direction;
267 mtc_frame = rint(mtc_frame_dll);
269 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
271 double mtc_speed = 0;
272 if (first_mtc_timestamp != 0) {
273 /* update MTC DLL and calculate speed */
274 const double e = mtc_frame_dll - (double)transport_direction * ((double)now - (double)current.timestamp + t0);
279 mtc_speed = (t1 - t0) / qtr_d;
280 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));
283 current.position = mtc_frame;
284 current.timestamp = now;
285 current.speed = mtc_speed;
288 last_inbound_frame = now;
296 /* called from MIDI parser _after_ update_mtc_qtr()
297 * when a full TC has been received
300 MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now)
304 /* "now" can be zero if this is called from a context where we do not have or do not want
305 to use a timestamp indicating when this MTC time was received. example: when we received
306 a locate command via MMC.
309 //DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self()));
310 TimecodeFormat tc_format;
311 bool reset_tc = true;
313 timecode.hours = msg[3];
314 timecode.minutes = msg[2];
315 timecode.seconds = msg[1];
316 timecode.frames = msg[0];
318 last_mtc_fps_byte = msg[4];
320 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
329 timecode.drop = false;
330 tc_format = timecode_24;
331 can_notify_on_unknown_rate = true;
335 timecode.drop = false;
336 tc_format = timecode_25;
337 can_notify_on_unknown_rate = true;
339 case MTC_30_FPS_DROP:
340 if (Config->get_timecode_source_2997()) {
341 tc_format = Timecode::timecode_2997000drop;
342 timecode.rate = (29970.0/1000.0);
344 tc_format = timecode_2997drop;
345 timecode.rate = (30000.0/1001.0);
347 timecode.drop = true;
348 can_notify_on_unknown_rate = true;
352 timecode.drop = false;
353 can_notify_on_unknown_rate = true;
354 tc_format = timecode_30;
357 /* throttle error messages about unknown MTC rates */
358 if (can_notify_on_unknown_rate) {
359 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
362 can_notify_on_unknown_rate = false;
364 timecode.rate = session.timecode_frames_per_second();
365 timecode.drop = session.timecode_drop_frames();
370 TimecodeFormat cur_timecode = session.config.get_timecode_format();
371 if (Config->get_timecode_sync_frame_rate()) {
372 /* enforce time-code */
373 if (!did_reset_tc_format) {
374 saved_tc_format = cur_timecode;
375 did_reset_tc_format = true;
377 if (cur_timecode != tc_format) {
378 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
379 warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."),
380 Timecode::timecode_format_name(cur_timecode),
381 Timecode::timecode_format_name(tc_format))
385 session.config.set_timecode_format (tc_format);
387 /* only warn about TC mismatch */
388 if (mtc_timecode != tc_format) printed_timecode_warning = false;
389 if (a3e_timecode != cur_timecode) printed_timecode_warning = false;
391 if (cur_timecode != tc_format && ! printed_timecode_warning) {
392 if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) {
393 warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."),
394 Timecode::timecode_format_name(tc_format),
396 Timecode::timecode_format_name(cur_timecode))
399 printed_timecode_warning = true;
402 mtc_timecode = tc_format;
403 a3e_timecode = cur_timecode;
405 speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode);
408 /* do a careful conversion of the timecode value to a position
409 so that we take drop/nondrop and all that nonsense into
413 quarter_frame_duration = (double(session.frame_rate()) / (double) timecode.rate / 4.0);
415 Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
416 double(session.frame_rate()),
417 session.config.get_subframes_per_frame(),
418 timecode_negative_offset, timecode_offset
421 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
422 now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
424 if (was_full || outside_window (mtc_frame)) {
425 DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC or outside window. - TID:%1\n", ::pthread_self()));
426 session.request_locate (mtc_frame, false);
427 session.request_transport_speed (0);
428 update_mtc_status (MIDI::MTC_Stopped);
430 reset_window (mtc_frame);
433 /* we've had the first set of 8 qtr frame messages, determine position
434 and allow continuing qtr frame messages to provide position
435 and speed information.
438 /* We received the last quarter frame 7 quarter frames (1.75 mtc
439 frames) after the instance when the contents of the mtc quarter
440 frames were decided. Add time to compensate for the elapsed 1.75
443 double qtr = quarter_frame_duration;
444 long int mtc_off = (long) rint(7.0 * qtr);
446 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
447 mtc_frame, (4.0*qtr), session.frames_per_timecode_frame()));
449 switch (port->parser()->mtc_running()) {
451 mtc_frame -= mtc_off;
455 mtc_frame += mtc_off;
461 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
464 if (first_mtc_timestamp == 0 || current.timestamp == 0) {
465 first_mtc_timestamp = now;
466 init_mtc_dll(mtc_frame, qtr);
467 mtc_frame_dll = mtc_frame;
470 current.position = mtc_frame;
471 current.timestamp = now;
473 reset_window (mtc_frame);
478 last_inbound_frame = now;
484 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
486 /* XXX !!! thread safety ... called from MIDI I/O context
487 * on locate (via ::update_mtc_time())
489 DEBUG_TRACE (DEBUG::MTC, string_compose("MTC_Slave::update_mtc_status - TID:%1\n", ::pthread_self()));
490 return; // why was this fn needed anyway ? it just messes up things -> use reset.
496 current.position = mtc_frame;
497 current.timestamp = 0;
505 current.position = mtc_frame;
506 current.timestamp = 0;
513 current.position = mtc_frame;
514 current.timestamp = 0;
523 MTC_Slave::reset_window (framepos_t root)
525 /* if we're waiting for the master to catch us after seeking ahead, keep the window
526 of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
527 ahead of the window root (taking direction into account).
529 framecnt_t const d = (quarter_frame_duration * 4 * frame_tolerance);
531 switch (port->parser()->mtc_running()) {
534 transport_direction = 1;
535 window_end = root + d;
539 transport_direction = -1;
541 window_begin = root - d;
554 DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
558 MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc)
560 /* the bandwidth of the DLL is a trade-off,
561 * because the max-speed of the transport in ardour is
562 * limited to +-8.0, a larger bandwidth would cause oscillations
564 * But this is only really a problem if the user performs manual
565 * seeks while transport is running and slaved to MTC.
567 oe = 2.0 * M_PI * double(inc) / 2.0 / double(session.frame_rate());
568 be = 1.4142135623730950488 * oe;
571 ee2 = double(transport_direction * inc);
574 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init Engine DLL %1 %2 %3\n", te0, te1, ee2));
577 /* main entry point from session_process.cc
578 * in jack_process callback context */
580 MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
582 framepos_t now = session.engine().sample_time_at_cycle_start();
583 framepos_t sess_pos = session.transport_frame(); // corresponds to now
584 //sess_pos -= session.engine().frames_since_cycle_start();
587 frameoffset_t elapsed;
588 bool engine_dll_reinitialized = false;
590 read_current (&last);
592 /* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */
593 if (last.timestamp == 0) { engine_dll_initstate = 0; }
594 else if (engine_dll_initstate != transport_direction && last.speed != 0) {
595 engine_dll_initstate = transport_direction;
596 init_engine_dll(last.position, session.engine().samples_per_cycle());
597 engine_dll_reinitialized = true;
600 if (last.timestamp == 0) {
602 pos = session.transport_frame() ; // last.position;
603 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", pos));
607 /* no timecode for two frames - conclude that it's stopped */
608 if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > labs(seekahead_distance())) {
611 session.request_locate (pos, false);
612 session.request_transport_speed (0);
613 engine_dll_initstate = 0;
615 DEBUG_TRACE (DEBUG::MTC, "MTC not seen for 2 frames - reset pending\n");
620 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));
621 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position eng-tme: %1 eng-pos: %2\n", now, sess_pos));
623 double speed_flt = last.speed; ///< MTC speed from MTC-quarter-frame DLL
625 /* interpolate position according to speed and time since last quarter-frame*/
626 if (speed_flt == 0.0f) {
631 /* scale elapsed time by the current MTC speed */
632 elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp));
633 if (give_slave_full_control_over_transport_speed() && !engine_dll_reinitialized) {
634 /* there is an engine vs MTC position frame-delta.
635 * This mostly due to quantization and rounding of (speed * nframes)
636 * but can also due to the session-process not calling
637 * speed_and_position() every cycle under some circumstances.
638 * Thus we use an other DLL to align the engine and the MTC
641 /* update engine DLL and calculate speed */
642 const double e = double (last.position + elapsed - sess_pos);
646 speed_flt = (te1 - te0) / double(session.engine().samples_per_cycle());
647 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() ));
651 pos = last.position + elapsed;
654 /* may happen if the user performs a seek in the timeline while slaved to running MTC
655 * engine-DLL can oscillate back before 0.
656 * also see note in MTC_Slave::init_engine_dll
658 if (!session.actively_recording()
660 && ( (pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()) )
662 engine_dll_initstate = 0;
666 /* provide a .1% deadzone to lock the speed */
667 if (fabs(speed - 1.0) <= 0.001)
670 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n",
671 speed, pos, last.position, elapsed, pos - sess_pos));
673 current_delta = (pos - sess_pos);
678 Timecode::TimecodeFormat
679 MTC_Slave::apparent_timecode_format () const
685 MTC_Slave::approximate_current_position() const
688 read_current (&last);
689 if (last.timestamp == 0 || reset_pending) {
690 return " --:--:--:--";
692 return Timecode::timecode_format_sampletime(
694 double(session.frame_rate()),
695 Timecode::timecode_to_frames_per_second(mtc_timecode),
696 Timecode::timecode_has_drop_frames(mtc_timecode));
700 MTC_Slave::approximate_current_delta() const
704 read_current (&last);
705 if (last.timestamp == 0 || reset_pending) {
706 snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012");
708 snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
709 LEADINGZERO(abs(current_delta)), PLUSMINUS(-current_delta), abs(current_delta));
711 return std::string(delta);