diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2019-09-17 18:26:03 -0600 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2019-09-17 18:26:03 -0600 |
commit | bd229936ec2cb30fe3360f2d83ffc7197fd6fb97 (patch) | |
tree | 84e41e048d76541d5d491ccefe8c7feaa14f4d5c /libs/ardour/ardour | |
parent | fc3e7623e7a4c7fdeebf84c4d47eaf04d9bc744d (diff) |
add finite state machine to control/manage transport state
Diffstat (limited to 'libs/ardour/ardour')
-rw-r--r-- | libs/ardour/ardour/debug.h | 4 | ||||
-rw-r--r-- | libs/ardour/ardour/disk_io.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/disk_reader.h | 15 | ||||
-rw-r--r-- | libs/ardour/ardour/export_status.h | 4 | ||||
-rw-r--r-- | libs/ardour/ardour/session.h | 38 | ||||
-rw-r--r-- | libs/ardour/ardour/session_event.h | 1 | ||||
-rw-r--r-- | libs/ardour/ardour/transport_api.h | 47 | ||||
-rw-r--r-- | libs/ardour/ardour/transport_fsm.h | 213 | ||||
-rw-r--r-- | libs/ardour/ardour/transport_master.h | 14 | ||||
-rw-r--r-- | libs/ardour/ardour/transport_master_manager.h | 3 |
10 files changed, 315 insertions, 26 deletions
diff --git a/libs/ardour/ardour/debug.h b/libs/ardour/ardour/debug.h index f8cfef4009..cd96aba33a 100644 --- a/libs/ardour/ardour/debug.h +++ b/libs/ardour/ardour/debug.h @@ -34,7 +34,7 @@ namespace PBD { namespace DEBUG { LIBARDOUR_API extern DebugBits MidiSourceIO; LIBARDOUR_API extern DebugBits MidiPlaylistIO; - LIBARDOUR_API extern DebugBits MidiDiskstreamIO; + LIBARDOUR_API extern DebugBits MidiDiskIO; LIBARDOUR_API extern DebugBits MidiRingBuffer; LIBARDOUR_API extern DebugBits SnapBBT; LIBARDOUR_API extern DebugBits Latency; @@ -49,6 +49,8 @@ namespace PBD { LIBARDOUR_API extern DebugBits LTC; LIBARDOUR_API extern DebugBits TXLTC; LIBARDOUR_API extern DebugBits Transport; + LIBARDOUR_API extern DebugBits TFSMEvents; + LIBARDOUR_API extern DebugBits TFSMState; LIBARDOUR_API extern DebugBits Slave; LIBARDOUR_API extern DebugBits SessionEvents; LIBARDOUR_API extern DebugBits MidiIO; diff --git a/libs/ardour/ardour/disk_io.h b/libs/ardour/ardour/disk_io.h index 60ac7be124..52cfe45d0e 100644 --- a/libs/ardour/ardour/disk_io.h +++ b/libs/ardour/ardour/disk_io.h @@ -117,8 +117,6 @@ protected: protected: Flag _flags; uint32_t i_am_the_modifier; - double _actual_speed; - double _target_speed; bool _slaved; bool in_set_state; samplepos_t playback_sample; diff --git a/libs/ardour/ardour/disk_reader.h b/libs/ardour/ardour/disk_reader.h index a7a74027f0..47812f54c8 100644 --- a/libs/ardour/ardour/disk_reader.h +++ b/libs/ardour/ardour/disk_reader.h @@ -97,8 +97,17 @@ public: static void set_midi_readahead_samples (samplecnt_t samples_ahead) { midi_readahead = samples_ahead; } - static void set_no_disk_output (bool yn); - static bool no_disk_output() { return _no_disk_output; } + /* inc/dec variants MUST be called as part of the process call tree, before any + disk readers are invoked. We use it when the session needs the + transport (and thus effective read position for DiskReaders) to keep + advancing as part of syncing up with a transport master, but we + don't want any actual disk output yet because we are still not + synced. + */ + + static void inc_no_disk_output () { g_atomic_int_inc (&_no_disk_output); } + static void dec_no_disk_output(); + static bool no_disk_output () { return g_atomic_int_get (&_no_disk_output); } protected: friend class Track; @@ -156,7 +165,7 @@ private: static samplecnt_t _chunk_samples; static samplecnt_t midi_readahead; - static bool _no_disk_output; + static gint _no_disk_output; int audio_read (PBD::PlaybackBuffer<Sample>*, Sample* sum_buffer, diff --git a/libs/ardour/ardour/export_status.h b/libs/ardour/ardour/export_status.h index 0fb4e39a78..7f36e60e06 100644 --- a/libs/ardour/ardour/export_status.h +++ b/libs/ardour/ardour/export_status.h @@ -54,8 +54,8 @@ class LIBARDOUR_API ExportStatus { } Glib::Threads::Mutex& lock () { return _run_lock; } - PBD::Signal0<void> Finished; - void finish (); + PBD::Signal1<void,TransportRequestSource> Finished; + void finish (TransportRequestSource); void cleanup (); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index d2d0b39dbd..337b88a8e8 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -88,7 +88,7 @@ #include "ardour/presentation_info.h" #include "ardour/route.h" #include "ardour/route_graph.h" - +#include "ardour/transport_api.h" class XMLTree; class XMLNode; @@ -167,6 +167,7 @@ class Source; class Speakers; class TempoMap; class TransportMaster; +struct TransportFSM; class Track; class UI_TransportMaster; class VCAManager; @@ -186,7 +187,7 @@ private: }; /** Ardour Session */ -class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionList, public SessionEventManager +class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionList, public SessionEventManager, public TransportAPI { private: @@ -457,7 +458,8 @@ public: void adjust_capture_buffering(); bool global_locate_pending() const { return _global_locate_pending; } - bool locate_pending() const { return static_cast<bool>(post_transport_work()&PostTransportLocate); } + bool locate_pending() const; + bool declick_in_progress () const; bool transport_locked () const; int wipe (); @@ -752,8 +754,8 @@ public: return 0; } double transport_speed() const { return _count_in_samples > 0 ? 0. : _transport_speed; } - bool transport_stopped() const { return _transport_speed == 0.0; } - bool transport_rolling() const { return _transport_speed != 0.0 && _count_in_samples == 0 && _remaining_latency_preroll == 0; } + bool transport_stopped() const; + bool transport_rolling() const; bool silent () { return _silent; } @@ -1237,6 +1239,16 @@ protected: friend class Route; void update_latency_compensation (bool force = false); + /* transport API */ + + void locate (samplepos_t, bool with_roll, bool with_flush, bool with_loop=false, bool force=false, bool with_mmc=true); + void stop_transport (bool abort = false, bool clear_state = false); + void start_transport (); + void butler_completed_transport_work (); + void post_locate (); + void schedule_butler_for_transport_work (); + bool should_roll_after_locate () const; + private: int create (const std::string& mix_template, BusProfile*); void destroy (); @@ -1262,7 +1274,6 @@ private: samplecnt_t _base_sample_rate; // sample-rate of the session at creation time, "native" SR samplecnt_t _nominal_sample_rate; // overridden by audioengine setting samplecnt_t _current_sample_rate; // this includes video pullup offset - int transport_sub_state; mutable gint _record_status; samplepos_t _transport_sample; gint _seek_counter; @@ -1357,7 +1368,7 @@ private: int pre_export (); int stop_audio_export (); - void finalize_audio_export (); + void finalize_audio_export (TransportRequestSource trs); void finalize_export_internal (bool stop_freewheel); bool _pre_export_mmc_enabled; @@ -1436,11 +1447,11 @@ private: Butler* _butler; + boost::shared_ptr<TransportFSM> _transport_fsm; + static const PostTransportWork ProcessCannotProceedMask = PostTransportWork ( - PostTransportReverse| PostTransportAudition| - PostTransportStop| PostTransportClearSubstate); gint _post_transport_work; /* accessed only atomic ops */ @@ -1671,19 +1682,16 @@ private: int start_midi_thread (); bool should_ignore_transport_request (TransportRequestSource, TransportRequestType) const; - bool declick_in_progress () const; void set_play_loop (bool yn, double speed); void unset_play_loop (); void overwrite_some_buffers (Track *); void flush_all_inserts (); int micro_locate (samplecnt_t distance); - void locate (samplepos_t, bool with_roll, bool with_flush, bool with_loop=false, bool force=false, bool with_mmc=true); - void start_locate (samplepos_t, bool with_roll, bool with_flush, bool for_loop_enabled=false, bool force=false); + + void do_locate (samplepos_t, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc); void force_locate (samplepos_t sample, bool with_roll = false); void set_transport_speed (double speed, samplepos_t destination_sample, bool abort = false, bool clear_state = false, bool as_default = false); - void stop_transport (bool abort = false, bool clear_state = false); - void start_transport (); void realtime_stop (bool abort, bool clear_state); void realtime_locate (); void non_realtime_start_scrub (); @@ -1691,8 +1699,8 @@ private: void non_realtime_locate (); void non_realtime_stop (bool abort, int entry_request_count, bool& finished); void non_realtime_overwrite (int entry_request_count, bool& finished); - void post_transport (); void engine_halted (); + void engine_running (); void xrun_recovery (); void set_track_loop (bool); bool select_playhead_priority_target (samplepos_t&); diff --git a/libs/ardour/ardour/session_event.h b/libs/ardour/ardour/session_event.h index bb85624305..98fe6838f5 100644 --- a/libs/ardour/ardour/session_event.h +++ b/libs/ardour/ardour/session_event.h @@ -63,7 +63,6 @@ public: /* only one of each of these events can be queued at any one time */ - StopOnce, AutoLoop, }; diff --git a/libs/ardour/ardour/transport_api.h b/libs/ardour/ardour/transport_api.h new file mode 100644 index 0000000000..8ca7aeb074 --- /dev/null +++ b/libs/ardour/ardour/transport_api.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 Paul Davis <paul@linuxaudiosystems.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _ardour_transport_api_h_ +#define _ardour_transport_api_h_ + +#include "ardour/types.h" +#include "ardour/libardour_visibility.h" + +namespace ARDOUR +{ + +class LIBARDOUR_API TransportAPI +{ + public: + TransportAPI() {} + virtual ~TransportAPI() {} + + private: + friend struct TransportFSM; + + virtual void locate (samplepos_t, bool with_roll, bool with_flush, bool with_loop=false, bool force=false, bool with_mmc=true) = 0; + virtual void stop_transport (bool abort = false, bool clear_state = false) = 0; + virtual void start_transport () = 0; + virtual void butler_completed_transport_work () = 0; + virtual void schedule_butler_for_transport_work () = 0; + virtual bool should_roll_after_locate () const = 0; +}; + +} /* end namespace ARDOUR */ + +#endif diff --git a/libs/ardour/ardour/transport_fsm.h b/libs/ardour/ardour/transport_fsm.h new file mode 100644 index 0000000000..6a920eef4d --- /dev/null +++ b/libs/ardour/ardour/transport_fsm.h @@ -0,0 +1,213 @@ +#ifndef _ardour_transport_fsm_h_ +#define _ardour_transport_fsm_h_ + +#include <boost/weak_ptr.hpp> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/back/tools.hpp> +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +#include "pbd/demangle.h" +#include "pbd/stacktrace.h" + +#include "ardour/debug.h" + +/* state machine */ +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace ARDOUR +{ + +class TransportAPI; + +struct TransportFSM : public msm::front::state_machine_def<TransportFSM> +{ + /* events to be delivered to the FSM */ + + struct butler_done {}; + struct butler_required {}; + struct declick_done {}; + struct start_transport {}; + + struct stop_transport { + stop_transport (bool ab = false, bool cl = false) + : abort (ab) + , clear_state (cl) {} + + bool abort; + bool clear_state; + }; + + struct locate { + locate () + : target (0) + , with_roll (false) + , with_flush (false) + , with_loop (false) + , force (false) {} + + locate (samplepos_t target, bool roll, bool flush, bool loop, bool f4c) + : target (target) + , with_roll (roll) + , with_flush (flush) + , with_loop (loop) + , force (f4c) {} + + samplepos_t target; + bool with_roll; + bool with_flush; + bool with_loop; + bool force; + }; + + struct locate_done {}; + + /* Flags */ + + struct DeclickInProgress {}; + struct LocateInProgress {}; + struct IsRolling {}; + struct IsStopped {}; + struct IsWaitingForButler {}; + + typedef msm::active_state_switch_before_transition active_state_switch_policy; + + /* transition actions */ + + void start_playback (start_transport const& p); + void roll_after_locate (locate_done const& p); + void stop_playback (declick_done const& s); + void start_locate (locate const& s); + void start_saved_locate (declick_done const& s); + void interrupt_locate (locate const& s); + void schedule_butler_for_transport_work (butler_required const&); + void save_locate_and_start_declick (locate const &); + void start_declick (stop_transport const &); + + /* guards */ + + bool should_roll_after_locate (locate_done const &); + bool should_not_roll_after_locate (locate_done const & e) { return !should_roll_after_locate (e); } + +#define define_state(State) \ + struct State : public msm::front::state<> \ + { \ + template <class Event,class FSM> void on_entry (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "entering: " # State "\n"); } \ + template <class Event,class FSM> void on_exit (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "leaving: " # State "\n"); } \ + } + +#define define_state_flag(State,Flag) \ + struct State : public msm::front::state<> \ + { \ + template <class Event,class FSM> void on_entry (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "entering: " # State "\n"); } \ + template <class Event,class FSM> void on_exit (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "leaving: " # State "\n"); } \ + typedef mpl::vector1<Flag> flag_list; \ + } + +#define define_state_flag2(State,Flag1,Flag2) \ + struct State : public msm::front::state<> \ + { \ + template <class Event,class FSM> void on_entry (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "entering: " # State "\n"); } \ + template <class Event,class FSM> void on_exit (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "leaving: " # State "\n"); } \ + typedef mpl::vector2<Flag1,Flag2> flag_list; \ + } + + /* FSM states */ + + define_state_flag (WaitingForButler, IsWaitingForButler); + define_state (NotWaitingForButler); + define_state_flag (Stopped,IsStopped); + define_state_flag (Rolling,IsRolling); + define_state_flag (DeclickToLocate,DeclickInProgress); + define_state_flag (WaitingForLocate,LocateInProgress); + define_state_flag (DeclickToStop,DeclickInProgress); + + // Pick a back-end + typedef msm::back::state_machine<TransportFSM> back; + + boost::weak_ptr<back> wp; + + bool locating () { return backend()->is_flag_active<LocateInProgress>(); } + bool locating (declick_done const &) { return locating(); } + bool rolling () { return backend()->is_flag_active<IsRolling>(); } + bool stopped () { return backend()->is_flag_active<IsStopped>(); } + bool waiting_for_butler() { return backend()->is_flag_active<IsWaitingForButler>(); } + bool declick_in_progress() { return backend()->is_flag_active<DeclickInProgress>(); } + + static boost::shared_ptr<back> create(TransportAPI& api) { + + boost::shared_ptr<back> p (new back ()); + + p->wp = p; + p->api = &api; + return p; + } + + boost::shared_ptr<back> backend() { return wp.lock(); } + + template<typename Event> void enqueue (Event const & e) { + backend()->process_event (e); + } + + /* the initial state */ + typedef boost::mpl::vector<Stopped,NotWaitingForButler> initial_state; + + /* transition table */ + typedef TransportFSM T; // makes transition table cleaner + + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +----------------------+----------------+------------------+---------------------+----------------------+ + a_row < Stopped, start_transport, Rolling, &T::start_playback >, + _row < Stopped, stop_transport, Stopped >, + a_row < Stopped, locate, WaitingForLocate, &T::start_locate >, + g_row < WaitingForLocate, locate_done, Stopped, &T::should_not_roll_after_locate >, + _row < Rolling, butler_done, Rolling >, + _row < Rolling, start_transport, Rolling >, + a_row < Rolling, stop_transport, DeclickToStop, &T::start_declick >, + a_row < DeclickToStop, declick_done, Stopped, &T::stop_playback >, + a_row < Rolling, locate, DeclickToLocate, &T::save_locate_and_start_declick >, + a_row < DeclickToLocate, declick_done, WaitingForLocate, &T::start_saved_locate >, + row < WaitingForLocate, locate_done, Rolling, &T::roll_after_locate, &T::should_roll_after_locate >, + a_row < NotWaitingForButler, butler_required, WaitingForButler, &T::schedule_butler_for_transport_work >, + a_row < WaitingForButler, butler_required, WaitingForButler, &T::schedule_butler_for_transport_work >, + _row < WaitingForButler, butler_done, NotWaitingForButler >, + a_row < WaitingForLocate, locate, WaitingForLocate, &T::interrupt_locate >, + a_row < DeclickToLocate, locate, DeclickToLocate, &T::interrupt_locate >, + + // Deferrals + +#define defer(start_state,ev) boost::msm::front::Row<start_state, ev, start_state, boost::msm::front::Defer, boost::msm::front::none > + + defer (DeclickToLocate, start_transport), + defer (DeclickToLocate, stop_transport), + defer (DeclickToStop, start_transport), + defer (WaitingForLocate, start_transport), + defer (WaitingForLocate, stop_transport) + +#undef defer + > {}; + + typedef int activate_deferred_events; + + locate _last_locate; + stop_transport _last_stop; + + TransportAPI* api; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + typedef typename boost::msm::back::recursive_get_transition_table<FSM>::type recursive_stt; + typedef typename boost::msm::back::generate_state_set<recursive_stt>::type all_states; + std::string stateName; + boost::mpl::for_each<all_states,boost::msm::wrap<boost::mpl::placeholders::_1> >(boost::msm::back::get_state_name<recursive_stt>(stateName, state)); + std::cout << "No transition from state: " << PBD::demangle (stateName) << " on event " << typeid(e).name() << std::endl; + } +}; + +} /* end namespace ARDOUR */ + +#endif diff --git a/libs/ardour/ardour/transport_master.h b/libs/ardour/ardour/transport_master.h index 3dcfe4cd8f..98c4f11156 100644 --- a/libs/ardour/ardour/transport_master.h +++ b/libs/ardour/ardour/transport_master.h @@ -346,8 +346,6 @@ 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); - /* this is set at construction, and not changeable later, so it is not * a property */ @@ -358,6 +356,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful { std::string display_name (bool sh/*ort*/ = true) const; virtual void unregister_port (); + void connect_port_using_state (); + virtual void create_port () = 0; protected: SyncSource _type; @@ -385,6 +385,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful { boost::shared_ptr<Port> _port; + XMLNode port_node; + PBD::ScopedConnection port_connection; bool connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn); @@ -452,6 +454,8 @@ class LIBARDOUR_API MTC_TransportMaster : public TimecodeTransportMaster, public std::string position_string() const; std::string delta_string() const; + void create_port (); + private: PBD::ScopedConnectionList port_connections; PBD::ScopedConnection config_connection; @@ -518,6 +522,8 @@ public: std::string position_string() const; std::string delta_string() const; + void create_port (); + private: void parse_ltc(const pframes_t, const Sample* const, const samplecnt_t); void process_ltc(samplepos_t const); @@ -586,6 +592,8 @@ class LIBARDOUR_API MIDIClock_TransportMaster : public TransportMaster, public T float bpm() const { return _bpm; } + void create_port (); + protected: PBD::ScopedConnectionList port_connections; @@ -648,6 +656,8 @@ class LIBARDOUR_API Engine_TransportMaster : public TransportMaster std::string position_string() const; std::string delta_string() const; + void create_port () { } + private: AudioEngine& engine; bool _starting; diff --git a/libs/ardour/ardour/transport_master_manager.h b/libs/ardour/ardour/transport_master_manager.h index a10b014098..e0926f74ec 100644 --- a/libs/ardour/ardour/transport_master_manager.h +++ b/libs/ardour/ardour/transport_master_manager.h @@ -33,6 +33,7 @@ class UI_TransportMaster; class LIBARDOUR_API TransportMasterManager : public boost::noncopyable { public: + static TransportMasterManager& create (); ~TransportMasterManager (); int set_default_configuration (); @@ -82,6 +83,8 @@ class LIBARDOUR_API TransportMasterManager : public boost::noncopyable static const std::string state_node_name; + void reconnect_ports (); + private: TransportMasterManager(); |