diff options
Diffstat (limited to 'libs/ardour/ardour/transport_master.h')
-rw-r--r-- | libs/ardour/ardour/transport_master.h | 148 |
1 files changed, 83 insertions, 65 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<ARDOUR::TransportRequestType> 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<int> guard1; + samplepos_t position; + samplepos_t timestamp; + double speed; + boost::atomic<int> 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<std::string> _name; @@ -264,6 +341,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful { PBD::Property<bool> _collect; PBD::Property<bool> _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<int> guard1; - samplepos_t position; - samplepos_t timestamp; - double speed; - boost::atomic<int> 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<samplepos_t>); - 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<samplepos_t>); - 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<samplepos_t>); 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<samplepos_t>); - 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; |