From 617d291d1bd8a89e4132fec04541e6ec77733829 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 12 Oct 2012 01:08:32 +0000 Subject: [PATCH] MTC: map between timecodes allow external timecode device to supply timecode with framerates other than ardour session's framerate. also fix latency overcompensation. The slave is supposed to provide transport time - and not audible frame time. git-svn-id: svn://localhost/ardour2/branches/3.0@13250 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/slave.h | 2 + libs/ardour/mtc_slave.cc | 82 ++++++++++++++++--------------------- libs/ardour/session_time.cc | 12 ++++-- 3 files changed, 45 insertions(+), 51 deletions(-) diff --git a/libs/ardour/ardour/slave.h b/libs/ardour/ardour/slave.h index 59f219cba6..a8bf28b8ce 100644 --- a/libs/ardour/ardour/slave.h +++ b/libs/ardour/ardour/slave.h @@ -260,6 +260,8 @@ class MTC_Slave : public Slave { int busy_guard1; int busy_guard2; + double speedup_due_to_tc_mismatch; + double quarter_frame_duration; Timecode::TimecodeFormat mtc_timecode; Timecode::TimecodeFormat a3e_timecode; bool printed_timecode_warning; diff --git a/libs/ardour/mtc_slave.cc b/libs/ardour/mtc_slave.cc index af6394a215..1191a4f0b4 100644 --- a/libs/ardour/mtc_slave.cc +++ b/libs/ardour/mtc_slave.cc @@ -61,6 +61,7 @@ MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p) 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 @@ -111,13 +112,13 @@ MTC_Slave::give_slave_full_control_over_transport_speed() const ARDOUR::framecnt_t MTC_Slave::resolution () const { - return (framecnt_t) session.frames_per_timecode_frame(); + return (framecnt_t) quarter_frame_duration * 4.0; } ARDOUR::framecnt_t MTC_Slave::seekahead_distance () const { - return session.frames_per_timecode_frame() * 2 * transport_direction; + return quarter_frame_duration * 8 * transport_direction; } bool @@ -218,14 +219,14 @@ MTC_Slave::read_current (SafeTime *st) const } void -MTC_Slave::init_mtc_dll(framepos_t mtc_frame, double qtr) +MTC_Slave::init_mtc_dll(framepos_t tme, double qtr) { - omega = 2.0 * M_PI * (session.frames_per_timecode_frame() / 4.0) / double(session.frame_rate()); + omega = 2.0 * M_PI * qtr / double(session.frame_rate()); b = 1.4142135623730950488 * omega; c = omega * omega; e2 = qtr; - t0 = double(mtc_frame); + t0 = double(tme); t1 = t0 + e2; DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2)); } @@ -236,8 +237,8 @@ void MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now) { busy_guard1++; - const framepos_t qtr = (session.frames_per_timecode_frame() / 4); - const double qtr_d = (session.frames_per_timecode_frame() / 4.0); + const double qtr_d = quarter_frame_duration; + const framepos_t qtr = rint(qtr_d); mtc_frame += qtr * transport_direction; @@ -251,8 +252,8 @@ MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now) 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\n", t0, t1, e, mtc_speed)); + 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++; @@ -360,20 +361,28 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now) } mtc_timecode = tc_format; a3e_timecode = cur_timecode; + + speedup_due_to_tc_mismatch = timecode.rate / Timecode::timecode_to_frames_per_second(a3e_timecode); } - DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4)\n", - now, timecode, mtc_frame, was_full)); + /* do a careful conversion of the timecode value to a position + so that we take drop/nondrop and all that nonsense into + consideration. + */ + + 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 + + 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())); - session.timecode_to_sample (timecode, mtc_frame, true, false); // sets mtc_frame session.request_locate (mtc_frame, false); session.request_transport_speed (0); update_mtc_status (MIDI::MTC_Stopped); reset (false); reset_window (mtc_frame); - } else { /* we've had the first set of 8 qtr frame messages, determine position @@ -381,24 +390,16 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now) and speed information. */ - /* do a careful conversion of the timecode value to a position - so that we take drop/nondrop and all that nonsense into - consideration. - */ - - session.timecode_to_sample (timecode, mtc_frame, true, false); // sets mtc_frame - /* We received the last quarter frame 7 quarter frames (1.75 mtc frames) after the instance when the contents of the mtc quarter frames were decided. Add time to compensate for the elapsed 1.75 - frames. Also compensate for audio latency. + frames. */ + double qtr = quarter_frame_duration; + long int mtc_off = (long) rint(7.0 * qtr); - DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame = %1 | %2 FPTC | LAT: %3\n", - mtc_frame, session.frames_per_timecode_frame(), session.worst_playback_latency())); - - long int mtc_off = (long) (1.75 * session.frames_per_timecode_frame()) + session.worst_playback_latency(); - double qtr = (session.frames_per_timecode_frame() / 4.0); + 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()) { case MTC_Backward: @@ -412,7 +413,7 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now) break; } - DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/latency comp) = %1\n", mtc_frame)); + DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame)); if (now) { if (first_mtc_timestamp == 0 || current.timestamp == 0) { @@ -479,35 +480,22 @@ 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()) { case MTC_Forward: window_begin = root; transport_direction = 1; - if (session.slave_state() == Session::Running) { - window_end = root + (session.frames_per_timecode_frame() * frame_tolerance); - } else { - window_end = root + labs(seekahead_distance ()); - } + window_end = root + d; break; case MTC_Backward: transport_direction = -1; - if (session.slave_state() == Session::Running) { - framecnt_t const d = session.frames_per_timecode_frame() * frame_tolerance; - if (root > d) { - window_begin = root - d; - window_end = root; - } else { - window_begin = 0; - } + if (root > d) { + window_begin = root - d; + window_end = root; } else { - framecnt_t const d = labs(seekahead_distance ()); - if (root > d) { - window_begin = root - d; - } else { - window_begin = 0; - } + window_begin = 0; } window_end = root; break; @@ -609,7 +597,7 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) 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\n", te0, te1, e, speed_flt)); + 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() )); } } diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc index 36f4aac01f..5de0055a0d 100644 --- a/libs/ardour/session_time.cc +++ b/libs/ardour/session_time.cc @@ -100,6 +100,10 @@ Session::sync_time_vars () void Session::timecode_to_sample( Timecode::Time& timecode, framepos_t& sample, bool use_offset, bool use_subframes ) const { + double my_frames_per_timecode_frame = _frames_per_timecode_frame; + if (timecode.rate > 0) { + my_frames_per_timecode_frame = (double) _current_frame_rate / (double) timecode.rate; + } if (timecode.drop) { // The drop frame format was created to better approximate the 30000/1001 = 29.97002997002997.... @@ -142,13 +146,13 @@ Session::timecode_to_sample( Timecode::Time& timecode, framepos_t& sample, bool // Per Sigmond // Samples inside time dividable by 10 minutes (real time accurate) - framecnt_t base_samples = (framecnt_t) (((timecode.hours * 107892) + ((timecode.minutes / 10) * 17982)) * _frames_per_timecode_frame); + framecnt_t base_samples = (framecnt_t) (((timecode.hours * 107892) + ((timecode.minutes / 10) * 17982)) * my_frames_per_timecode_frame); // Samples inside time exceeding the nearest 10 minutes (always offset, see above) int32_t exceeding_df_minutes = timecode.minutes % 10; int32_t exceeding_df_seconds = (exceeding_df_minutes * 60) + timecode.seconds; int32_t exceeding_df_frames = (30 * exceeding_df_seconds) + timecode.frames - (2 * exceeding_df_minutes); - framecnt_t exceeding_samples = (framecnt_t) rint(exceeding_df_frames * _frames_per_timecode_frame); + framecnt_t exceeding_samples = (framecnt_t) rint(exceeding_df_frames * my_frames_per_timecode_frame); sample = base_samples + exceeding_samples; } else { /* @@ -158,11 +162,11 @@ Session::timecode_to_sample( Timecode::Time& timecode, framepos_t& sample, bool frame_rate() in the non-integer Timecode rate case. */ - sample = (framecnt_t)rint((((timecode.hours * 60 * 60) + (timecode.minutes * 60) + timecode.seconds) * (rint(timecode.rate) * _frames_per_timecode_frame)) + (timecode.frames * _frames_per_timecode_frame)); + sample = (framecnt_t)rint((((timecode.hours * 60 * 60) + (timecode.minutes * 60) + timecode.seconds) * (rint(timecode.rate) * my_frames_per_timecode_frame)) + (timecode.frames * my_frames_per_timecode_frame)); } if (use_subframes) { - sample += (int32_t) (((double)timecode.subframes * _frames_per_timecode_frame) / config.get_subframes_per_frame()); + sample += (int32_t) (((double)timecode.subframes * my_frames_per_timecode_frame) / config.get_subframes_per_frame()); } if (use_offset) {