X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmtc_slave.cc;h=8ce0722d8b790b5e1f6616050bce3803d141b55a;hb=ef0c4ed0e6907ff7cc0c9f139495619a9f242ff5;hp=c568cd5d6a461bc6f0e7fc4a4ffe3a25e83dd910;hpb=eaaca760c9e2e259f87801bce59442f5cf9d2210;p=ardour.git diff --git a/libs/ardour/mtc_slave.cc b/libs/ardour/mtc_slave.cc index c568cd5d6a..8ce0722d8b 100644 --- a/libs/ardour/mtc_slave.cc +++ b/libs/ardour/mtc_slave.cc @@ -56,16 +56,19 @@ MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p) 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); } @@ -73,6 +76,7 @@ MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p) 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, @@ -101,6 +105,26 @@ MTC_Slave::rebind (MIDI::Port& p) 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 MTC_Slave::give_slave_full_control_over_transport_speed() const { @@ -130,7 +154,7 @@ MTC_Slave::outside_window (framepos_t pos) const bool MTC_Slave::locked () const { - return port->parser()->mtc_locked(); + return port->parser()->mtc_locked() && last_inbound_frame !=0 && engine_dll_initstate !=0; } bool @@ -183,6 +207,7 @@ MTC_Slave::reset (bool with_position) window_begin = 0; window_end = 0; transport_direction = 1; + current_delta = 0; } void @@ -220,7 +245,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; @@ -237,32 +262,33 @@ 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++; } @@ -281,7 +307,6 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now) */ //DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self())); - Time timecode; TimecodeFormat tc_format; bool reset_tc = true; @@ -312,9 +337,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 +375,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 +389,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,7 +411,12 @@ 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)); @@ -418,6 +464,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; @@ -517,7 +564,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; @@ -534,17 +581,20 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) { framepos_t now = session.engine().frame_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); /* 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) { + 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()); + engine_dll_reinitialized = true; } if (last.timestamp == 0) { @@ -567,8 +617,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 @@ -580,15 +629,13 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) 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 */ @@ -609,15 +656,22 @@ 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 +681,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); }