adjust to use timestamped MTC messages
[ardour.git] / libs / ardour / mtc_slave.cc
index a0570f42bc38bb95041f64a8b0756a17a056bb53..4ca86236b737c113f8587bdffabfad3f30b61d43 100644 (file)
@@ -44,7 +44,8 @@ MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
        : session (s)
 {
        can_notify_on_unknown_rate = true;
-
+       did_reset_tc_format = false;
+       
        last_mtc_fps_byte = session.get_mtc_timecode_bits ();
 
        rebind (p);
@@ -53,6 +54,9 @@ MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
 
 MTC_Slave::~MTC_Slave()
 {
+       if (did_reset_tc_format) {
+               session.config.set_timecode_format (saved_tc_format);
+       }
 }
 
 void
@@ -70,28 +74,51 @@ MTC_Slave::rebind (MIDI::Port& p)
 }
 
 void
-MTC_Slave::update_mtc_qtr (Parser& /*p*/)
+MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, nframes_t now)
 {
-       nframes64_t now = session.engine().frame_time();
-       nframes_t qtr;
+       DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2, valid-for-time? %3\n", which_qtr, now, qtr_frame_messages_valid_for_time));
 
-       qtr = (long) (session.frames_per_timecode_frame() / 4);
-       mtc_frame += qtr;
+       if (qtr_frame_messages_valid_for_time) {
 
-       current.guard1++;
-       current.position = mtc_frame;
-       current.timestamp = now;
-       current.guard2++;
+               if (which_qtr != 7) {
+
+                       /* leave position and speed updates for the last
+                          qtr frame message of the 8 to be taken
+                          care of in update_mtc_time(), invoked
+                          by the Parser right after this.
+                       */
 
-       last_inbound_frame = now;
+                       nframes_t qtr;
+                       
+                       qtr = (long) (session.frames_per_timecode_frame() / 4);
+                       mtc_frame += qtr;
+                       
+                       double speed = compute_apparent_speed (now);
+                       
+                       current.guard1++;
+                       current.position = mtc_frame;
+                       current.timestamp = now;
+                       current.speed = speed;
+                       current.guard2++;
+               }
+
+               last_inbound_frame = now;
+       }
 }
 
 void
-MTC_Slave::update_mtc_time (const byte *msg, bool was_full)
+MTC_Slave::update_mtc_time (const byte *msg, bool was_full, nframes_t now)
 {
-       nframes64_t now = session.engine().frame_time();
-       Timecode::Time timecode;
+       /* "now" can be zero if this is called from a context where we do not have or do not want
+          to use a timestamp indicating when this MTC time was received.
+       */
 
+       Timecode::Time timecode;
+       TimecodeFormat tc_format;
+       bool reset_tc = true;
+       
+       DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
+       
        timecode.hours = msg[3];
        timecode.minutes = msg[2];
        timecode.seconds = msg[1];
@@ -103,22 +130,26 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full)
        case MTC_24_FPS:
                timecode.rate = 24;
                timecode.drop = false;
+               tc_format = timecode_24;
                can_notify_on_unknown_rate = true;
                break;
        case MTC_25_FPS:
                timecode.rate = 25;
                timecode.drop = false;
+               tc_format = timecode_25;
                can_notify_on_unknown_rate = true;
                break;
        case MTC_30_FPS_DROP:
                timecode.rate = 30;
                timecode.drop = true;
+               tc_format = timecode_30drop;
                can_notify_on_unknown_rate = true;
                break;
        case MTC_30_FPS:
                timecode.rate = 30;
                timecode.drop = false;
                can_notify_on_unknown_rate = true;
+               tc_format = timecode_30;
                break;
        default:
                /* throttle error messages about unknown MTC rates */
@@ -130,20 +161,23 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full)
                }
                timecode.rate = session.timecode_frames_per_second();
                timecode.drop = session.timecode_drop_frames();
+               reset_tc = false;
        }
 
-       session.timecode_to_sample (timecode, mtc_frame, true, false);
+       if (reset_tc) {
+               if (!did_reset_tc_format) {
+                       saved_tc_format = session.config.get_timecode_format();
+                       did_reset_tc_format = true;
+               }
+               session.config.set_timecode_format (tc_format);
+       }
 
-       DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC time now %1 = frame %2 (from full message ? %3)\n", timecode, mtc_frame, was_full));
+       DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC time timestamp = %1 TC %2 = frame %3 (from full message ? %4)\n", 
+                                                now, timecode, mtc_frame, was_full));
 
        if (was_full) {
 
-               current.guard1++;
-               current.position = mtc_frame;
-               current.timestamp = 0;
-               current.speed = 0;
-               current.guard2++;
-
+               session.timecode_to_sample (timecode, mtc_frame, true, false);
                session.request_locate (mtc_frame, false);
                session.request_transport_speed (0);
                update_mtc_status (MIDI::Parser::MTC_Stopped);
@@ -152,54 +186,74 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full)
 
        } else {
 
-               double speed;
-
+                       
+               /* we've had the first set of 8 qtr frame messages, determine position
+                          and allow continuing qtr frame messages to provide position
+                          and speed information.
+               */
+               
+               qtr_frame_messages_valid_for_time = true;
+               session.timecode_to_sample (timecode, mtc_frame, true, false);
+               
                /* 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. Also compensate for audio latency.
                */
-
+               
                mtc_frame += (long) (1.75 * session.frames_per_timecode_frame()) + session.worst_output_latency();
 
-               if (current.timestamp != 0) {
-
-                       speed = (double) ((mtc_frame - current.position) / (double) (now - current.timestamp));
-                       DEBUG_TRACE (DEBUG::MTC, string_compose ("instantaneous speed = %1 from %2 - %3 / %4 - %5\n",
-                                                                speed, mtc_frame, current.position, now, current.timestamp));
+               if (now) {
+                       double speed = compute_apparent_speed (now);
+                       
+                       current.guard1++;
+                       current.position = mtc_frame;
+                       current.timestamp = now;
+                       current.speed = speed;
+                       current.guard2++;
+               }
+       }
 
-                       accumulator[accumulator_index++] = speed;
+       if (now) {
+               last_inbound_frame = now;
+       }
+}
 
-                       if (accumulator_index >= accumulator_size) {
-                               have_first_accumulated_speed = true;
-                               accumulator_index = 0;
+double
+MTC_Slave::compute_apparent_speed (nframes64_t now)
+{
+       if (current.timestamp != 0) {
+               
+               double speed = (double) ((mtc_frame - current.position) / (double) (now - current.timestamp));
+               DEBUG_TRACE (DEBUG::MTC, string_compose ("instantaneous speed = %1 from %2 / %3\n",
+                                                        speed, mtc_frame - current.position, now - current.timestamp));
+               
+               /* crude low pass filter/smoother for speed */
+
+               accumulator[accumulator_index++] = speed;
+               
+               if (accumulator_index >= accumulator_size) {
+                       have_first_accumulated_speed = true;
+                       accumulator_index = 0;
+               }
+               
+               if (have_first_accumulated_speed) {
+                       double total = 0;
+                       
+                       for (int32_t i = 0; i < accumulator_size; ++i) {
+                               total += accumulator[i];
                        }
                        
-                       if (have_first_accumulated_speed) {
-                               double total = 0;
-                               
-                               for (int32_t i = 0; i < accumulator_size; ++i) {
-                                       total += accumulator[i];
-                               }
-                               
-                               speed = total / accumulator_size;
-                               DEBUG_TRACE (DEBUG::MTC, string_compose ("speed smoothed to %1\n", speed));
-                       } 
-                                    
-               } else {
-
-                       speed = 0;
-               }
+                       speed = total / accumulator_size;
+                       DEBUG_TRACE (DEBUG::MTC, string_compose ("speed smoothed to %1\n", speed));
+               } 
 
-               current.guard1++;
-               current.position = mtc_frame;
-               current.timestamp = now;
-               current.speed = speed;
-               current.guard2++;
+               return speed;
+               
+       } else {
+               
+               return 0;
        }
-
-       last_inbound_frame = now;
 }
 
 void
@@ -213,7 +267,7 @@ MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
        mtc[1] = mmc_tc[2];
        mtc[0] = mmc_tc[3];
 
-       update_mtc_time (mtc, true);
+       update_mtc_time (mtc, true, 0);
 }
 
 void
@@ -263,13 +317,13 @@ void
 MTC_Slave::read_current (SafeTime *st) const
 {
        int tries = 0;
+
        do {
                if (tries == 10) {
                        error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
                        usleep (20);
                        tries = 0;
                }
-
                *st = current;
                tries++;
 
@@ -328,7 +382,9 @@ MTC_Slave::speed_and_position (double& speed, nframes64_t& pos)
                /* scale elapsed time by the current MTC speed */
 
                if (last.timestamp && (now > last.timestamp)) {
-                       elapsed = (nframes_t) floor (speed * (now - last.timestamp));
+                       elapsed = (nframes_t) floor (last.speed * (now - last.timestamp));
+                       DEBUG_TRACE (DEBUG::MTC, string_compose ("last timecode received @ %1, now = %2, elapsed frames = %3 w/speed= %4\n",
+                                                                last.timestamp, now, elapsed, speed));
                } else {
                        elapsed = 0; /* XXX is this right? */
                }
@@ -368,4 +424,5 @@ MTC_Slave::reset ()
        current.guard2++;
        accumulator_index = 0;
        have_first_accumulated_speed = false;
+       qtr_frame_messages_valid_for_time = false;
 }