From c4fcd0c268bffef82a9426e4763717772656f19f Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 25 Sep 2018 17:46:59 -0400 Subject: consolidate all transport masters on a SafeTime object that is a member of the TransportMaster base class. This seems to have broken some aspects of chasing/locking --- libs/ardour/ardour/transport_master.h | 148 ++++++++++++++++++-------------- libs/ardour/engine_slave.cc | 5 +- libs/ardour/ltc_slave.cc | 87 +++++++------------ libs/ardour/midi_clock_slave.cc | 71 +++++---------- libs/ardour/mtc_slave.cc | 76 +--------------- libs/ardour/session_transport.cc | 3 +- libs/ardour/transport_master.cc | 54 ++++++++++++ libs/ardour/transport_master_manager.cc | 3 +- 8 files changed, 204 insertions(+), 243 deletions(-) diff --git a/libs/ardour/ardour/transport_master.h b/libs/ardour/ardour/transport_master.h index 81d250dda9..f6dae4bff1 100644 --- a/libs/ardour/ardour/transport_master.h +++ b/libs/ardour/ardour/transport_master.h @@ -66,6 +66,80 @@ namespace Properties { LIBARDOUR_API extern PBD::PropertyDescriptor allowed_transport_requests; }; +struct LIBARDOUR_API SafeTime { + + /* This object uses memory fences to provide psuedo-atomic updating of + * non-atomic data. If after reading guard1 and guard2 with correct + * memory fencing they have the same value, then we know that the other + * members are all internally consistent. + * + * Traditionally, one might do this with a mutex, but this object + * provides lock-free write update. The reader might block while + * waiting for consistency, but this is extraordinarily unlikely. In + * this sense, the design is similar to a spinlock. + * + * any update starts by incrementing guard1, with a memory fence to + * ensure no reordering of this w.r.t later operations. + * + * then we update the "non-atomic" data members. + * + * then we update guard2, with another memory fence to prevent + * reordering. + * + * ergo, if guard1 == guard2, the update of the non-atomic members is + * complete and the values stored there are consistent. + */ + + boost::atomic guard1; + samplepos_t position; + samplepos_t timestamp; + double speed; + boost::atomic guard2; + + SafeTime() { + guard1.store (0); + position = 0; + timestamp = 0; + speed = 0; + guard2.store (0); + } + + void reset () { + guard1.store (0); + position = 0; + timestamp = 0; + speed = 0; + guard2.store (0); + } + + void update (samplepos_t p, samplepos_t t, double s) { + guard1.fetch_add (1, boost::memory_order_acquire); + position = p; + timestamp = t; + speed = s; + guard2.fetch_add (1, boost::memory_order_acquire); + } + + void safe_read (SafeTime& dst) const { + int tries = 0; + + do { + if (tries == 10) { + std::cerr << X_("SafeTime: atomic read of current time failed, sleeping!") << std::endl; + Glib::usleep (20); + tries = 0; + } + dst.guard1.store (guard1.load (boost::memory_order_seq_cst), boost::memory_order_seq_cst); + dst.position = position; + dst.timestamp = timestamp; + dst.speed = speed; + dst.guard2.store (guard2.load (boost::memory_order_seq_cst), boost::memory_order_seq_cst); + tries++; + + } while (dst.guard1.load (boost::memory_order_seq_cst) != dst.guard2.load (boost::memory_order_seq_cst)); + } +}; + /** * @class TransportMaster * @@ -138,7 +212,7 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful { * @param position - The transport position requested * @return - The return value is currently ignored (see Session::follow_slave) */ - virtual bool speed_and_position (double& speed, samplepos_t& position, samplepos_t now) = 0; + virtual bool speed_and_position (double& speed, samplepos_t& position, samplepos_t & lp, samplepos_t & when, samplepos_t now); /** * reports to ARDOUR whether the TransportMaster is currently synced to its external @@ -252,6 +326,9 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful { TransportRequestType request_mask() const { return _request_mask; } void set_request_mask (TransportRequestType); + + void get_current (double&, samplepos_t&, samplepos_t); + protected: SyncSource _type; PBD::Property _name; @@ -264,6 +341,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful { PBD::Property _collect; PBD::Property _connected; + SafeTime current; + /* DLL - chase incoming data */ int transport_direction; @@ -284,57 +363,6 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful { virtual void register_properties (); }; -struct LIBARDOUR_API SafeTime { - boost::atomic guard1; - samplepos_t position; - samplepos_t timestamp; - double speed; - boost::atomic guard2; - - SafeTime() { - guard1.store (0); - position = 0; - timestamp = 0; - speed = 0; - guard2.store (0); - } - - SafeTime (SafeTime const & other) - : guard1 (other.guard1.load (boost::memory_order_acquire)) - , position (other.position) - , timestamp (other.timestamp) - , speed (other.speed) - , guard2 (other.guard2.load (boost::memory_order_acquire)) - {} - - void update (samplepos_t p, samplepos_t t, double s) { - guard1.fetch_add (1, boost::memory_order_acquire); - position = p; - timestamp = t; - speed = s; - guard2.fetch_add (1, boost::memory_order_acquire); - } - - void safe_read (SafeTime& dst) const { - int tries = 0; - - do { - if (tries == 10) { - std::cerr << X_("SafeTime: atomic read of current time failed, sleeping!") << std::endl; - Glib::usleep (20); - tries = 0; - } - dst.guard1.store (guard1.load (boost::memory_order_seq_cst), boost::memory_order_seq_cst); - dst.position = position; - dst.timestamp = timestamp; - dst.speed = speed; - dst.guard2.store (guard2.load (boost::memory_order_seq_cst), boost::memory_order_seq_cst); - tries++; - - } while (dst.guard1.load (boost::memory_order_seq_cst) != dst.guard2.load (boost::memory_order_seq_cst)); - } -}; - /** a helper class for any TransportMaster that receives its input via a MIDI * port */ @@ -377,8 +405,6 @@ class LIBARDOUR_API MTC_TransportMaster : public TimecodeTransportMaster, public void pre_process (pframes_t nframes, samplepos_t now, boost::optional); - bool speed_and_position (double&, samplepos_t&, samplepos_t); - bool locked() const; bool ok() const; void handle_locate (const MIDI::byte*); @@ -399,7 +425,6 @@ class LIBARDOUR_API MTC_TransportMaster : public TimecodeTransportMaster, public static const int sample_tolerance; - SafeTime current; samplepos_t mtc_frame; /* current time */ double mtc_frame_dll; samplepos_t last_inbound_frame; /* when we got it; audio clocked */ @@ -430,7 +455,6 @@ class LIBARDOUR_API MTC_TransportMaster : public TimecodeTransportMaster, public void update_mtc_qtr (MIDI::Parser&, int, samplepos_t); void update_mtc_time (const MIDI::byte *, bool, samplepos_t); void update_mtc_status (MIDI::MTC_Status); - void read_current (SafeTime *) const; void reset_window (samplepos_t); bool outside_window (samplepos_t) const; void init_mtc_dll(samplepos_t, double); @@ -446,7 +470,6 @@ public: void set_session (Session*); void pre_process (pframes_t nframes, samplepos_t now, boost::optional); - bool speed_and_position (double&, samplepos_t&, samplepos_t); bool locked() const; bool ok() const; @@ -482,10 +505,7 @@ public: LTCFrameExt prev_sample; bool fps_detected; - samplecnt_t monotonic_cnt; - samplecnt_t last_timestamp; - samplecnt_t last_ltc_sample; - double ltc_speed; + samplecnt_t monotonic_cnt; int delayedlocked; int ltc_detect_fps_cnt; @@ -513,7 +533,6 @@ class LIBARDOUR_API MIDIClock_TransportMaster : public TransportMaster, public T void pre_process (pframes_t nframes, samplepos_t now, boost::optional); void rebind (MidiPort&); - bool speed_and_position (double&, samplepos_t&, samplepos_t); bool locked() const; bool ok() const; @@ -558,13 +577,12 @@ class LIBARDOUR_API MIDIClock_TransportMaster : public TransportMaster, public T void start (MIDI::Parser& parser, samplepos_t timestamp); void contineu (MIDI::Parser& parser, samplepos_t timestamp); void stop (MIDI::Parser& parser, samplepos_t timestamp); - void position (MIDI::Parser& parser, MIDI::byte* message, size_t size); + void position (MIDI::Parser& parser, MIDI::byte* message, size_t size, samplepos_t timestamp); // we can't use continue because it is a C++ keyword void calculate_one_ppqn_in_samples_at(samplepos_t time); samplepos_t calculate_song_position(uint16_t song_position_in_sixteenth_notes); void calculate_filter_coefficients (double qpm); void update_midi_clock (MIDI::Parser& parser, samplepos_t timestamp); - void read_current (SafeTime *) const; }; class LIBARDOUR_API Engine_TransportMaster : public TransportMaster @@ -574,7 +592,7 @@ class LIBARDOUR_API Engine_TransportMaster : public TransportMaster ~Engine_TransportMaster (); void pre_process (pframes_t nframes, samplepos_t now, boost::optional); - bool speed_and_position (double& speed, samplepos_t& pos, samplepos_t); + bool speed_and_position (double& speed, samplepos_t& pos, samplepos_t &, samplepos_t &, samplepos_t); bool starting() const { return _starting; } bool locked() const; diff --git a/libs/ardour/engine_slave.cc b/libs/ardour/engine_slave.cc index 12b7d313a2..a308557bca 100644 --- a/libs/ardour/engine_slave.cc +++ b/libs/ardour/engine_slave.cc @@ -76,7 +76,7 @@ Engine_TransportMaster::pre_process (pframes_t, samplepos_t, boost::optional backend = engine.current_backend(); @@ -88,6 +88,9 @@ Engine_TransportMaster::speed_and_position (double& sp, samplepos_t& position, s return true; } + lp = now; + when = now; + _current_delta = 0; return false; diff --git a/libs/ardour/ltc_slave.cc b/libs/ardour/ltc_slave.cc index 4f47b904bd..71e560c2f5 100644 --- a/libs/ardour/ltc_slave.cc +++ b/libs/ardour/ltc_slave.cc @@ -53,8 +53,6 @@ LTC_TransportMaster::LTC_TransportMaster (std::string const & name) , samples_per_ltc_frame (0) , fps_detected (false) , monotonic_cnt (0) - , last_timestamp (0) - , last_ltc_sample (0) , delayedlocked (10) , ltc_detect_fps_cnt (0) , ltc_detect_fps_max (0) @@ -185,11 +183,10 @@ LTC_TransportMaster::reset (bool with_ts) { DEBUG_TRACE (DEBUG::LTC, "LTC reset()\n"); if (with_ts) { - last_timestamp = 0; + current.update (current.position, 0, current.speed); _current_delta = 0; } transport_direction = 0; - ltc_speed = 0; sync_lock_broken = false; monotonic_cnt = 0; } @@ -460,53 +457,30 @@ LTC_TransportMaster::process_ltc(samplepos_t const now) */ samplepos_t cur_timestamp = sample.off_end + 1; + double ltc_speed = current.speed; - DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC S: %1 LS: %2 N: %3 L: %4\n", ltc_sample, last_ltc_sample, cur_timestamp, last_timestamp)); + DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC S: %1 LS: %2 N: %3 L: %4\n", ltc_sample, current.position, cur_timestamp, current.timestamp)); - if (cur_timestamp <= last_timestamp || last_timestamp == 0) { - DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC speed: UNCHANGED: %1\n", ltc_speed)); + if (cur_timestamp <= current.timestamp || current.timestamp == 0) { + DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC speed: UNCHANGED: %1\n", current.speed)); } else { - ltc_speed = double (ltc_sample - last_ltc_sample) / double (cur_timestamp - last_timestamp); - DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC speed: %1\n", ltc_speed)); - } - - if (fabs (ltc_speed) > 10.0) { - ltc_speed = 0; - } - - last_timestamp = cur_timestamp; - last_ltc_sample = ltc_sample; - - } /* end foreach decoded LTC sample */ -} + ltc_speed = double (ltc_sample - current.position) / double (cur_timestamp - current.timestamp); -bool -LTC_TransportMaster::speed_and_position (double& speed, samplepos_t& pos, samplepos_t now) -{ - if (!_collect || last_timestamp == 0) { - return false; - } - - /* XXX these are not atomics and maybe modified in a thread other other than the one - that is executing this. - */ - - speed = ltc_speed; + /* provide a .1% deadzone to lock the speed */ + if (fabs (ltc_speed - 1.0) <= 0.001) { + ltc_speed = 1.0; + } - /* provide a .1% deadzone to lock the speed */ - if (fabs (speed - 1.0) <= 0.001) { - speed = 1.0; - } + if (fabs (ltc_speed) > 10.0) { + ltc_speed = 0; + } - if (speed != 0 && delayedlocked == 0 && fabs(speed) != 1.0) { - sync_lock_broken = true; - DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC speed not locked %1 based on %2\n", speed, ltc_speed)); - } + DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC speed: %1\n", ltc_speed)); + } - pos = last_ltc_sample; - pos += (now - last_timestamp) * speed; + current.update (ltc_sample, cur_timestamp, ltc_speed); - return true; + } /* end foreach decoded LTC sample */ } void @@ -517,14 +491,14 @@ LTC_TransportMaster::pre_process (ARDOUR::pframes_t nframes, samplepos_t now, bo monotonic_cnt = now; DEBUG_TRACE (DEBUG::LTC, string_compose ("pre-process - TID:%1 | latency: %2 | skip %3 | session ? %4| last %5 | dir %6 | sp %7\n", - pthread_name(), ltc_slave_latency.max, skip, (_session ? 'y' : 'n'), last_timestamp, transport_direction, ltc_speed)); + pthread_name(), ltc_slave_latency.max, skip, (_session ? 'y' : 'n'), current.timestamp, transport_direction, current.speed)); - if (last_timestamp == 0) { + if (current.timestamp == 0) { if (delayedlocked < 10) { ++delayedlocked; } - } else if (ltc_speed != 0) { + } else if (current.speed != 0) { } @@ -555,11 +529,11 @@ LTC_TransportMaster::pre_process (ARDOUR::pframes_t nframes, samplepos_t now, bo process_ltc (now); - if (last_timestamp == 0) { + if (current.timestamp == 0) { DEBUG_TRACE (DEBUG::LTC, "last timestamp == 0\n"); return; - } else if (ltc_speed != 0) { - DEBUG_TRACE (DEBUG::LTC, string_compose ("speed non-zero (%1)\n", ltc_speed)); + } else if (current.speed != 0) { + DEBUG_TRACE (DEBUG::LTC, string_compose ("speed non-zero (%1)\n", current.speed)); if (delayedlocked > 1) { delayedlocked--; } else if (_current_delta == 0) { @@ -567,7 +541,7 @@ LTC_TransportMaster::pre_process (ARDOUR::pframes_t nframes, samplepos_t now, bo } } - if (abs (now - last_timestamp) > FLYWHEEL_TIMEOUT) { + if (abs (now - current.timestamp) > FLYWHEEL_TIMEOUT) { DEBUG_TRACE (DEBUG::LTC, "flywheel timeout\n"); reset(); /* don't change position from last known */ @@ -575,8 +549,13 @@ LTC_TransportMaster::pre_process (ARDOUR::pframes_t nframes, samplepos_t now, bo return; } + if (!sync_lock_broken && current.speed != 0 && delayedlocked == 0 && fabs(current.speed) != 1.0) { + sync_lock_broken = true; + DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC speed not locked based on %1\n", current.speed)); + } + if (session_pos) { - const samplepos_t current_pos = last_ltc_sample + ((now - last_timestamp) * ltc_speed); + const samplepos_t current_pos = current.position + ((now - current.timestamp) * current.speed); _current_delta = current_pos - *session_pos; } else { _current_delta = 0; @@ -606,7 +585,7 @@ LTC_TransportMaster::apparent_timecode_format () const std::string LTC_TransportMaster::position_string() const { - if (!_collect || last_timestamp == 0) { + if (!_collect || current.timestamp == 0) { return " --:--:--:--"; } return Timecode::timecode_format_time(timecode); @@ -617,9 +596,9 @@ LTC_TransportMaster::delta_string() const { char delta[80]; - if (!_collect || last_timestamp == 0) { + if (!_collect || current.timestamp == 0) { snprintf (delta, sizeof(delta), "\u2012\u2012\u2012\u2012"); - } else if ((monotonic_cnt - last_timestamp) > 2 * samples_per_ltc_frame) { + } else if ((monotonic_cnt - current.timestamp) > 2 * samples_per_ltc_frame) { snprintf (delta, sizeof(delta), "%s", _("flywheel")); } else { snprintf (delta, sizeof(delta), "\u0394%s%s%lldsm", diff --git a/libs/ardour/midi_clock_slave.cc b/libs/ardour/midi_clock_slave.cc index 04fa259519..7fd1c7a58e 100644 --- a/libs/ardour/midi_clock_slave.cc +++ b/libs/ardour/midi_clock_slave.cc @@ -49,10 +49,7 @@ using namespace PBD; MIDIClock_TransportMaster::MIDIClock_TransportMaster (std::string const & name, int ppqn) : TransportMaster (MIDIClock, name) , ppqn (ppqn) - , last_timestamp (0) - , should_be_position (0) , midi_clock_count (0) - , _speed (0) , _running (false) , _bpm (0) { @@ -70,7 +67,7 @@ void MIDIClock_TransportMaster::init () { midi_clock_count = 0; - last_timestamp = 0; + current.reset (); } void @@ -88,46 +85,26 @@ MIDIClock_TransportMaster::set_session (Session *session) parser.start.connect_same_thread (port_connections, boost::bind (&MIDIClock_TransportMaster::start, this, _1, _2)); parser.contineu.connect_same_thread (port_connections, boost::bind (&MIDIClock_TransportMaster::contineu, this, _1, _2)); parser.stop.connect_same_thread (port_connections, boost::bind (&MIDIClock_TransportMaster::stop, this, _1, _2)); - parser.position.connect_same_thread (port_connections, boost::bind (&MIDIClock_TransportMaster::position, this, _1, _2, 3)); + parser.position.connect_same_thread (port_connections, boost::bind (&MIDIClock_TransportMaster::position, this, _1, _2, _3, _4)); reset (); } } -bool -MIDIClock_TransportMaster::speed_and_position (double& speed, samplepos_t& pos, samplepos_t now) -{ - if (!_running || !_collect) { - return false; - } - - if (fabs (_speed - 1.0) < 0.001) { - speed = 1.0; - } else { - speed = _speed; - } - - pos = should_be_position; - pos += (now - last_timestamp) * _speed; - - return true; -} - void MIDIClock_TransportMaster::pre_process (MIDI::pframes_t nframes, samplepos_t now, boost::optional session_pos) { /* Read and parse incoming MIDI */ - DEBUG_TRACE (DEBUG::MidiClock, string_compose ("preprocess with lt = %1 @ %2, running ? %3\n", last_timestamp, now, _running)); + DEBUG_TRACE (DEBUG::MidiClock, string_compose ("preprocess with lt = %1 @ %2, running ? %3\n", current.timestamp, now, _running)); _midi_port->read_and_parse_entire_midi_buffer_with_no_speed_adjustment (nframes, parser, now); /* no clock messages ever, or no clock messages for 1/4 second ? conclude that its stopped */ if (!last_timestamp || (now > last_timestamp && ((now - last_timestamp) > (ENGINE->sample_rate() / 4)))) { - _speed = 0.0; + current.update (current.position, 0, 0); _bpm = 0.0; - last_timestamp = 0; _running = false; _current_delta = 0; midi_clock_count = 0; @@ -137,18 +114,18 @@ MIDIClock_TransportMaster::pre_process (MIDI::pframes_t nframes, samplepos_t now } if (!_running && midi_clock_count == 0 && session_pos) { - should_be_position = *session_pos; - DEBUG_TRACE (DEBUG::MidiClock, string_compose ("set sbp to %1\n", should_be_position)); + current.update (*session_pos, now, current.speed); + DEBUG_TRACE (DEBUG::MidiClock, string_compose ("set sbp to %1\n", current.position)); } if (session_pos) { - const samplepos_t current_pos = should_be_position + ((now - last_timestamp) * _speed); + const samplepos_t current_pos = current.position + ((now - current.timestamp) * current.speed); _current_delta = current_pos - *session_pos; } else { _current_delta = 0; } - DEBUG_TRACE (DEBUG::MidiClock, string_compose ("speed_and_position: speed %1 should-be %2 transport %3 \n", _speed, should_be_position, _session->transport_sample())); + DEBUG_TRACE (DEBUG::MidiClock, string_compose ("speed_and_position: speed %1 should-be %2 transport %3 \n", current.speed, current.position, _session->transport_sample())); } void @@ -197,21 +174,21 @@ MIDIClock_TransportMaster::update_midi_clock (Parser& /*parser*/, samplepos_t ti samplepos_t elapsed_since_start = timestamp - first_timestamp; double e = 0; - calculate_one_ppqn_in_samples_at (should_be_position); + calculate_one_ppqn_in_samples_at (current.position); - DEBUG_TRACE (DEBUG::MidiClock, string_compose ("clock count %1, sbp %2\n", midi_clock_count, should_be_position)); + DEBUG_TRACE (DEBUG::MidiClock, string_compose ("clock count %1, sbp %2\n", midi_clock_count, current.position)); if (midi_clock_count == 0) { /* second 0xf8 message after start/reset has arrived */ first_timestamp = timestamp; - last_timestamp = timestamp; + current.update (0, timestamp, 0); DEBUG_TRACE (DEBUG::MidiClock, string_compose ("first clock message after start received @ %1\n", timestamp)); midi_clock_count++; - should_be_position += one_ppqn_in_samples; + current.position += one_ppqn_in_samples; } else if (midi_clock_count == 1) { @@ -233,7 +210,7 @@ MIDIClock_TransportMaster::update_midi_clock (Parser& /*parser*/, samplepos_t ti t1 = t0 + e2; /* timestamp we predict for the next 0xf8 clock message */ midi_clock_count++; - should_be_position += one_ppqn_in_samples; + current.update (one_ppqn_in_samples, timestamp, 0); } else { @@ -254,7 +231,7 @@ MIDIClock_TransportMaster::update_midi_clock (Parser& /*parser*/, samplepos_t ti /* _speed is relative to session tempo map */ - _speed = predicted_clock_interval_in_samples / one_ppqn_in_samples; + double speed = predicted_clock_interval_in_samples / one_ppqn_in_samples; /* _bpm (really, _qpm) is absolute */ @@ -278,14 +255,14 @@ MIDIClock_TransportMaster::update_midi_clock (Parser& /*parser*/, samplepos_t ti } midi_clock_count++; - should_be_position += one_ppqn_in_samples; + current.update (current.position + one_ppqn_in_samples, timestamp, speed); } DEBUG_TRACE (DEBUG::MidiClock, string_compose ("clock #%1 @ %2 should-be %3 transport %4 error %5 appspeed %6 " "read-delta %7 should-be delta %8 t1-t0 %9 t0 %10 t1 %11 framerate %12 engine %13 running %14\n", midi_clock_count, // # elapsed_since_start, // @ - should_be_position, // should-be + current.position, // should-be _session->transport_sample(), // transport e, // error (t1 - t0) / one_ppqn_in_samples, // appspeed @@ -311,7 +288,7 @@ MIDIClock_TransportMaster::start (Parser& /*parser*/, samplepos_t timestamp) if (!_running) { reset(); _running = true; - should_be_position = _session->transport_sample(); + current.update (_session->transport_sample(), timestamp, 0); } } @@ -320,9 +297,7 @@ MIDIClock_TransportMaster::reset () { DEBUG_TRACE (DEBUG::MidiClock, string_compose ("MidiClock Master reset(): calculated filter for period size %2\n", ENGINE->samples_per_cycle())); - should_be_position = _session->transport_sample(); - _speed = 0; - last_timestamp = 0; + current.update (_session->transport_sample(), 0, 0); _running = false; _current_delta = 0; @@ -337,7 +312,7 @@ MIDIClock_TransportMaster::contineu (Parser& /*parser*/, samplepos_t /*timestamp } void -MIDIClock_TransportMaster::stop (Parser& /*parser*/, samplepos_t /*timestamp*/) +MIDIClock_TransportMaster::stop (Parser& /*parser*/, samplepos_t timestamp) { DEBUG_TRACE (DEBUG::MidiClock, "MIDIClock_TransportMaster got stop message\n"); @@ -356,12 +331,12 @@ MIDIClock_TransportMaster::stop (Parser& /*parser*/, samplepos_t /*timestamp*/) // // find out the last MIDI beat: go back #midi_clocks mod 6 // and lets hope the tempo didnt change in those last 6 beats :) - should_be_position -= (midi_clock_count % 6) * one_ppqn_in_samples; + current.update (current.position - (midi_clock_count % 6) * one_ppqn_in_samples, timestamp, current.speed); } } void -MIDIClock_TransportMaster::position (Parser& /*parser*/, MIDI::byte* message, size_t size) +MIDIClock_TransportMaster::position (Parser& /*parser*/, MIDI::byte* message, size_t size, samplepos_t timestamp) { // we are not supposed to get position messages while we are running // so lets be robust and ignore those @@ -379,9 +354,7 @@ MIDIClock_TransportMaster::position (Parser& /*parser*/, MIDI::byte* message, si DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Song Position: %1 samples: %2\n", position_in_sixteenth_notes, position_in_samples)); - should_be_position = position_in_samples; - last_timestamp = 0; - + current.update (position_in_samples, timestamp, current.speed); } bool diff --git a/libs/ardour/mtc_slave.cc b/libs/ardour/mtc_slave.cc index d110c2a32d..c081de23b5 100644 --- a/libs/ardour/mtc_slave.cc +++ b/libs/ardour/mtc_slave.cc @@ -132,6 +132,8 @@ MTC_TransportMaster::pre_process (MIDI::pframes_t nframes, samplepos_t now, boos { /* Read and parse incoming MIDI */ + maybe_reset (); + _midi_port->read_and_parse_entire_midi_buffer_with_no_speed_adjustment (nframes, parser, now); if (session_pos) { @@ -222,18 +224,9 @@ MTC_TransportMaster::reset (bool with_position) DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC_TransportMaster reset %1\n", with_position?"with position":"without position")); if (with_position) { - last_inbound_frame = 0; - current.guard1++; - current.position = 0; - current.timestamp = 0; - current.speed = 0; - current.guard2++; + current.update (0, 0, 0); } else { - last_inbound_frame = 0; - current.guard1++; - current.timestamp = 0; - current.speed = 0; - current.guard2++; + current.update (current.position, 0, 0); } first_mtc_timestamp = 0; window_begin = 0; @@ -294,8 +287,6 @@ MTC_TransportMaster::update_mtc_qtr (Parser& p, int which_qtr, samplepos_t now) DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr sample DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", t0, t1, e, mtc_speed, e2 - qtr_d)); current.update (mtc_frame, now, mtc_speed); - - last_inbound_frame = now; } maybe_reset (); @@ -481,9 +472,6 @@ MTC_TransportMaster::update_mtc_time (const MIDI::byte *msg, bool was_full, samp } } - if (now) { - last_inbound_frame = now; - } busy_guard2++; } @@ -549,62 +537,6 @@ MTC_TransportMaster::reset_window (samplepos_t root) DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root)); } -/* main entry point from session_process.cc -xo * in process callback context */ -bool -MTC_TransportMaster::speed_and_position (double& speed, samplepos_t& pos, samplepos_t now) -{ - SafeTime last; - - if (!_collect) { - return false; - } - - current.safe_read (last); - - DEBUG_TRACE (DEBUG::MTC, string_compose ("speed&pos: timestamp %1 speed %2 dir %4 now %5 last-in %6\n", - last.timestamp, - last.speed, - transport_direction, - now, - last_inbound_frame)); - - if (last.timestamp == 0) { - return false; - } - - if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > labs(seekahead_distance())) { - /* no timecode for two cycles - conclude that it's stopped */ - - if (!Config->get_transport_masters_just_roll_when_sync_lost()) { - speed = 0; - pos = last.position; - _current_delta = 0; - queue_reset (false); - DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC not seen for 2 samples - reset pending, pos = %1\n", pos)); - return false; - } - } - - - 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)); - - speed = last.speed; - - /* provide a .1% deadzone to lock the speed */ - if (fabs (speed - 1.0) <= 0.001) { - speed = 1.0; - } - - pos = last.position; - pos += (now - last.timestamp) * speed; - - DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 | elapsed: %4\n", - speed, pos, last.position, (now - last.timestamp))); - - return true; -} - Timecode::TimecodeFormat MTC_TransportMaster::apparent_timecode_format () const { diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 95daf7495b..cf10cf11c7 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1119,8 +1119,9 @@ Session::start_locate (samplepos_t target_sample, bool with_roll, bool with_flus double sp; samplepos_t pos; + samplepos_t ignore1, ignore2; - transport_master()->speed_and_position (sp, pos, 0); + transport_master()->speed_and_position (sp, pos, ignore1, ignore2, 0); if (target_sample != pos) { diff --git a/libs/ardour/transport_master.cc b/libs/ardour/transport_master.cc index 4dec88272a..135e022dbf 100644 --- a/libs/ardour/transport_master.cc +++ b/libs/ardour/transport_master.cc @@ -22,6 +22,7 @@ #include "pbd/debug.h" #include "ardour/audioengine.h" +#include "ardour/debug.h" #include "ardour/midi_port.h" #include "ardour/session.h" #include "ardour/transport_master.h" @@ -84,6 +85,59 @@ TransportMaster::~TransportMaster() delete _session; } +bool +TransportMaster::speed_and_position (double& speed, samplepos_t& pos, samplepos_t& lp, samplepos_t& when, samplepos_t now) +{ + if (!_collect) { + return false; + } + + SafeTime last; + current.safe_read (last); + + if (last.timestamp == 0) { + return false; + } + + if (last.timestamp && now > last.timestamp && now - last.timestamp > labs (seekahead_distance())) { + /* no timecode for two cycles - conclude that it's stopped */ + + if (!Config->get_transport_masters_just_roll_when_sync_lost()) { + speed = 0; + pos = last.position; + lp = last.position; + when = last.timestamp; + _current_delta = 0; + // queue_reset (false); + DEBUG_TRACE (DEBUG::Slave, string_compose ("%1 not seen for 2 samples - reset pending, pos = %2\n", name(), pos)); + return false; + } + } + + lp = last.position; + when = last.timestamp; + speed = last.speed; + pos = last.position + (now - last.timestamp) * last.speed; + + DEBUG_TRACE (DEBUG::Slave, string_compose ("%1: speed_and_position tme: %2 pos: %3 spd: %4\n", name(), last.timestamp, last.position, last.speed)); + + lp = last.position; + when = last.timestamp; + speed = last.speed; + + /* provide a .1% deadzone to lock the speed */ + if (fabs (speed - 1.0) <= 0.001) { + speed = 1.0; + } + + pos = last.position + (now - last.timestamp) * speed; + + DEBUG_TRACE (DEBUG::Slave, string_compose ("%1 sync spd: %2 pos: %3 | last-pos: %4 | elapsed: %5\n", + name(), speed, pos, last.position, (now - last.timestamp))); + + return true; +} + void TransportMaster::register_properties () { diff --git a/libs/ardour/transport_master_manager.cc b/libs/ardour/transport_master_manager.cc index 80d62b3adc..dee67bc6ed 100644 --- a/libs/ardour/transport_master_manager.cc +++ b/libs/ardour/transport_master_manager.cc @@ -175,8 +175,9 @@ TransportMasterManager::pre_process_transport_masters (pframes_t nframes, sample } double engine_speed; + samplepos_t ignore1, ignore2; - if (!_current_master->speed_and_position (_master_speed, _master_position, now)) { + if (!_current_master->speed_and_position (_master_speed, _master_position, ignore1, ignore2, now)) { return 1.0; } -- cgit v1.2.3