X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmtc_slave.cc;h=e9071af61917690bc7d86d4eb25205c2ebdb2f24;hb=caac41a9cb2d86985595c590b151b0197b7b0c4c;hp=c568cd5d6a461bc6f0e7fc4a4ffe3a25e83dd910;hpb=eaaca760c9e2e259f87801bce59442f5cf9d2210;p=ardour.git diff --git a/libs/ardour/mtc_slave.cc b/libs/ardour/mtc_slave.cc index c568cd5d6a..e9071af619 100644 --- a/libs/ardour/mtc_slave.cc +++ b/libs/ardour/mtc_slave.cc @@ -25,11 +25,12 @@ #include "pbd/error.h" -#include "midi++/port.h" +#include "ardour/audioengine.h" #include "ardour/debug.h" -#include "ardour/slave.h" +#include "ardour/midi_buffer.h" +#include "ardour/midi_port.h" #include "ardour/session.h" -#include "ardour/audioengine.h" +#include "ardour/slave.h" #include "i18n.h" @@ -48,31 +49,39 @@ using namespace Timecode; */ const int MTC_Slave::frame_tolerance = 2; -MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p) +MTC_Slave::MTC_Slave (Session& s, MidiPort& p) : session (s) + , port (&p) { can_notify_on_unknown_rate = true; did_reset_tc_format = false; reset_pending = 0; reset_position = false; mtc_frame = 0; + mtc_frame_dll = 0; engine_dll_initstate = 0; busy_guard1 = busy_guard2 = 0; last_mtc_fps_byte = session.get_mtc_timecode_bits (); quarter_frame_duration = (double(session.frames_per_timecode_frame()) / 4.0); - mtc_timecode = timecode_60; // track changes of MTC timecode - a3e_timecode = timecode_60; // track canges of Ardour's timecode + mtc_timecode = session.config.get_timecode_format(); + a3e_timecode = session.config.get_timecode_format(); printed_timecode_warning = false; + session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_Slave::parameter_changed, this, _1)); + parse_timecode_offset(); reset (true); - rebind (p); + + port->self_parser().mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3)); + port->self_parser().mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3)); + port->self_parser().mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1)); } MTC_Slave::~MTC_Slave() { port_connections.drop_connections(); + config_connection.disconnect(); while (busy_guard1 != busy_guard2) { /* make sure MIDI parser is not currently calling any callbacks in here, @@ -90,15 +99,32 @@ MTC_Slave::~MTC_Slave() } void -MTC_Slave::rebind (MIDI::Port& p) +MTC_Slave::rebind (MidiPort& p) { port_connections.drop_connections (); port = &p; - port->parser()->mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3)); - port->parser()->mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3)); - port->parser()->mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1)); +} + +void +MTC_Slave::parse_timecode_offset() { + Timecode::Time offset_tc; + Timecode::parse_timecode_format(session.config.get_slave_timecode_offset(), offset_tc); + offset_tc.rate = session.timecode_frames_per_second(); + offset_tc.drop = session.timecode_drop_frames(); + session.timecode_to_sample(offset_tc, timecode_offset, false, false); + timecode_negative_offset = offset_tc.negative; +} + +void +MTC_Slave::parameter_changed (std::string const & p) +{ + if (p == "slave-timecode-offset" + || p == "timecode-format" + ) { + parse_timecode_offset(); + } } bool @@ -130,7 +156,8 @@ MTC_Slave::outside_window (framepos_t pos) const bool MTC_Slave::locked () const { - return port->parser()->mtc_locked(); + DEBUG_TRACE (DEBUG::MTC, string_compose ("locked ? %1 last %2 initstate %3\n", port->self_parser().mtc_locked(), last_inbound_frame, engine_dll_initstate)); + return port->self_parser().mtc_locked() && last_inbound_frame !=0 && engine_dll_initstate !=0; } bool @@ -183,6 +210,7 @@ MTC_Slave::reset (bool with_position) window_begin = 0; window_end = 0; transport_direction = 1; + current_delta = 0; } void @@ -220,7 +248,7 @@ MTC_Slave::read_current (SafeTime *st) const void MTC_Slave::init_mtc_dll(framepos_t tme, double qtr) { - omega = 2.0 * M_PI * qtr / double(session.frame_rate()); + omega = 2.0 * M_PI * qtr / 2.0 / double(session.frame_rate()); b = 1.4142135623730950488 * omega; c = omega * omega; @@ -230,39 +258,39 @@ MTC_Slave::init_mtc_dll(framepos_t tme, double qtr) DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2)); } - /* called from MIDI parser */ void MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now) { busy_guard1++; const double qtr_d = quarter_frame_duration; - const framepos_t qtr = rint(qtr_d); - mtc_frame += qtr * transport_direction; + mtc_frame_dll += qtr_d * (double) transport_direction; + mtc_frame = rint(mtc_frame_dll); DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame)); double mtc_speed = 0; if (first_mtc_timestamp != 0) { /* update MTC DLL and calculate speed */ - const double e = double(transport_direction) * (double(now) - double(current.timestamp) - qtr_d); + const double e = mtc_frame_dll - (double)transport_direction * ((double)now - (double)current.timestamp + t0); t0 = t1; t1 += b * e + e2; e2 += c * e; mtc_speed = (t1 - t0) / qtr_d; 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)); - } - current.guard1++; - current.position = mtc_frame; - current.timestamp = now; - current.speed = mtc_speed; - current.guard2++; + current.guard1++; + current.position = mtc_frame; + current.timestamp = now; + current.speed = mtc_speed; + current.guard2++; + + last_inbound_frame = now; + } maybe_reset (); - last_inbound_frame = now; busy_guard2++; } @@ -280,8 +308,7 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now) a locate command via MMC. */ - //DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self())); - Time timecode; + DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self())); TimecodeFormat tc_format; bool reset_tc = true; @@ -312,9 +339,14 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now) can_notify_on_unknown_rate = true; break; case MTC_30_FPS_DROP: - timecode.rate = 30; + if (Config->get_timecode_source_2997()) { + tc_format = Timecode::timecode_2997000drop; + timecode.rate = (29970.0/1000.0); + } else { + tc_format = timecode_2997drop; + timecode.rate = (30000.0/1001.0); + } timecode.drop = true; - tc_format = timecode_30drop; can_notify_on_unknown_rate = true; break; case MTC_30_FPS: @@ -345,7 +377,12 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now) did_reset_tc_format = true; } if (cur_timecode != tc_format) { - warning << _("Session and MTC framerate mismatch.") << endmsg; + if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) { + warning << string_compose(_("Session framerate adjusted from %1 TO: MTC's %2."), + Timecode::timecode_format_name(cur_timecode), + Timecode::timecode_format_name(tc_format)) + << endmsg; + } } session.config.set_timecode_format (tc_format); } else { @@ -354,7 +391,13 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now) if (a3e_timecode != cur_timecode) printed_timecode_warning = false; if (cur_timecode != tc_format && ! printed_timecode_warning) { - warning << _("Session and MTC framerate mismatch.") << endmsg; + if (ceil(Timecode::timecode_to_frames_per_second(cur_timecode)) != ceil(Timecode::timecode_to_frames_per_second(tc_format))) { + warning << string_compose(_("Session and MTC framerate mismatch: MTC:%1 %2:%3."), + Timecode::timecode_format_name(tc_format), + PROGRAM_NAME, + Timecode::timecode_format_name(cur_timecode)) + << endmsg; + } printed_timecode_warning = true; } } @@ -370,13 +413,18 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now) */ quarter_frame_duration = (double(session.frame_rate()) / (double) timecode.rate / 4.0); - session.timecode_to_sample (timecode, mtc_frame, true, false); // audio-frame according to Ardour's FPS + + Timecode::timecode_to_sample (timecode, mtc_frame, true, false, + double(session.frame_rate()), + session.config.get_subframes_per_frame(), + timecode_negative_offset, timecode_offset + ); DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n", now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch)); if (was_full || outside_window (mtc_frame)) { - DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC or outside window. - TID:%1\n", ::pthread_self())); + DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC %1 or outside window %2\n", was_full, outside_window (mtc_frame))); session.request_locate (mtc_frame, false); session.request_transport_speed (0); update_mtc_status (MIDI::MTC_Stopped); @@ -400,7 +448,7 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now) DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n", mtc_frame, (4.0*qtr), session.frames_per_timecode_frame())); - switch (port->parser()->mtc_running()) { + switch (port->self_parser().mtc_running()) { case MTC_Backward: mtc_frame -= mtc_off; qtr *= -1.0; @@ -418,6 +466,7 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now) if (first_mtc_timestamp == 0 || current.timestamp == 0) { first_mtc_timestamp = now; init_mtc_dll(mtc_frame, qtr); + mtc_frame_dll = mtc_frame; } current.guard1++; current.position = mtc_frame; @@ -479,9 +528,10 @@ MTC_Slave::reset_window (framepos_t root) of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames ahead of the window root (taking direction into account). */ + framecnt_t const d = (quarter_frame_duration * 4 * frame_tolerance); - switch (port->parser()->mtc_running()) { + switch (port->self_parser().mtc_running()) { case MTC_Forward: window_begin = root; transport_direction = 1; @@ -504,7 +554,7 @@ MTC_Slave::reset_window (framepos_t root) break; } - DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end)); + DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root)); } void @@ -517,7 +567,7 @@ MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc) * But this is only really a problem if the user performs manual * seeks while transport is running and slaved to MTC. */ - oe = 2.0 * M_PI * double(inc/6.0) / double(session.frame_rate()); + oe = 2.0 * M_PI * double(inc) / 2.0 / double(session.frame_rate()); be = 1.4142135623730950488 * oe; ce = oe * oe; @@ -528,23 +578,36 @@ MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc) } /* main entry point from session_process.cc - * in jack_process callback context */ +xo * in process callback context */ bool MTC_Slave::speed_and_position (double& speed, framepos_t& pos) { - framepos_t now = session.engine().frame_time_at_cycle_start(); + framepos_t now = session.engine().sample_time_at_cycle_start(); framepos_t sess_pos = session.transport_frame(); // corresponds to now + //sess_pos -= session.engine().frames_since_cycle_start(); SafeTime last; - framecnt_t elapsed; + frameoffset_t elapsed; + bool engine_dll_reinitialized = false; read_current (&last); + DEBUG_TRACE (DEBUG::MTC, string_compose ("speed&pos: timestamp %1 speed %2 initstate %3 dir %4 tpos %5 now %6 last-in %7\n", + last.timestamp, + last.speed, + engine_dll_initstate, + transport_direction, + sess_pos, + now, + last_inbound_frame)); + /* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */ - if (last.timestamp == 0) { engine_dll_initstate = 0; } - else if (engine_dll_initstate != transport_direction) { + if (last.timestamp == 0) { + engine_dll_initstate = 0; + } else if (engine_dll_initstate != transport_direction && last.speed != 0) { engine_dll_initstate = transport_direction; - init_engine_dll(last.position, session.engine().frames_per_cycle()); + init_engine_dll(last.position, session.engine().samples_per_cycle()); + engine_dll_reinitialized = true; } if (last.timestamp == 0) { @@ -567,8 +630,7 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) } - - DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position mtc-tme: %1 mtc-pos: %2\n", last.timestamp, last.position)); + 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)); DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position eng-tme: %1 eng-pos: %2\n", now, sess_pos)); double speed_flt = last.speed; ///< MTC speed from MTC-quarter-frame DLL @@ -576,19 +638,15 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) /* interpolate position according to speed and time since last quarter-frame*/ if (speed_flt == 0.0f) { elapsed = 0; - } - else - { + } else { /* scale elapsed time by the current MTC speed */ - if (last.timestamp && (now > last.timestamp)) { - elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp)); - } else { - elapsed = 0; - } - if (give_slave_full_control_over_transport_speed()) { - /* there is a frame-delta engine vs MTC position - * mostly due to quantization and rounding of (speed * nframes) - * thus we use an other DLL.. + elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp)); + if (give_slave_full_control_over_transport_speed() && !engine_dll_reinitialized) { + /* there is an engine vs MTC position frame-delta. + * This mostly due to quantization and rounding of (speed * nframes) + * but can also due to the session-process not calling + * speed_and_position() every cycle under some circumstances. + * Thus we use an other DLL to align the engine and the MTC */ /* update engine DLL and calculate speed */ @@ -596,8 +654,8 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) te0 = te1; te1 += be * e + ee2; ee2 += ce * e; - speed_flt = (te1 - te0) / double(session.engine().frames_per_cycle()); - 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().frames_per_cycle() )); + speed_flt = (te1 - te0) / double(session.engine().samples_per_cycle()); + 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() )); } } @@ -609,15 +667,21 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) * also see note in MTC_Slave::init_engine_dll */ if (!session.actively_recording() - && ( (pos < 0) || (labs(pos - sess_pos) > 4 * resolution()) ) - ) { + && speed != 0 + && ((pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()))) { engine_dll_initstate = 0; queue_reset (false); } + /* provide a .1% deadzone to lock the speed */ + if (fabs (speed - 1.0) <= 0.001) + speed = 1.0; + DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n", speed, pos, last.position, elapsed, pos - sess_pos)); + current_delta = (pos - sess_pos); + return true; } @@ -627,8 +691,32 @@ MTC_Slave::apparent_timecode_format () const return mtc_timecode; } -std::string +std::string MTC_Slave::approximate_current_position() const { - return "88:88:88:88"; + SafeTime last; + read_current (&last); + if (last.timestamp == 0 || reset_pending) { + return " --:--:--:--"; + } + return Timecode::timecode_format_sampletime( + last.position, + double(session.frame_rate()), + Timecode::timecode_to_frames_per_second(mtc_timecode), + Timecode::timecode_has_drop_frames(mtc_timecode)); +} + +std::string +MTC_Slave::approximate_current_delta() const +{ + char delta[80]; + SafeTime last; + read_current (&last); + if (last.timestamp == 0 || reset_pending) { + snprintf(delta, sizeof(delta), "\u2012\u2012\u2012\u2012"); + } else { + snprintf(delta, sizeof(delta), "\u0394%s%s%" PRIi64 "sm", + LEADINGZERO(abs(current_delta)), PLUSMINUS(-current_delta), abs(current_delta)); + } + return std::string(delta); }