MTC: map between timecodes
authorRobin Gareus <robin@gareus.org>
Fri, 12 Oct 2012 01:08:32 +0000 (01:08 +0000)
committerRobin Gareus <robin@gareus.org>
Fri, 12 Oct 2012 01:08:32 +0000 (01:08 +0000)
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
libs/ardour/mtc_slave.cc
libs/ardour/session_time.cc

index 59f219cba6441d96f1909afdee96ce5bcd64dafa..a8bf28b8cef0d199e007f141879b0545e4973466 100644 (file)
@@ -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;
index af6394a215c3d61b87f1930fc5822370307cf5d0..1191a4f0b452758d7c584887a46f92aceeff8aaa 100644 (file)
@@ -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() ));
                }
        }
 
index 36f4aac01fef21506fcec7c1976edae8d0263f2f..5de0055a0d0a1d3e8779e3b3e42c5c4fca75dfe7 100644 (file)
@@ -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 <per@sigmond.no>
 
                // 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) {