summaryrefslogtreecommitdiff
path: root/libs/ardour/ardour
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2019-09-17 18:26:03 -0600
committerPaul Davis <paul@linuxaudiosystems.com>2019-09-17 18:26:03 -0600
commitbd229936ec2cb30fe3360f2d83ffc7197fd6fb97 (patch)
tree84e41e048d76541d5d491ccefe8c7feaa14f4d5c /libs/ardour/ardour
parentfc3e7623e7a4c7fdeebf84c4d47eaf04d9bc744d (diff)
add finite state machine to control/manage transport state
Diffstat (limited to 'libs/ardour/ardour')
-rw-r--r--libs/ardour/ardour/debug.h4
-rw-r--r--libs/ardour/ardour/disk_io.h2
-rw-r--r--libs/ardour/ardour/disk_reader.h15
-rw-r--r--libs/ardour/ardour/export_status.h4
-rw-r--r--libs/ardour/ardour/session.h38
-rw-r--r--libs/ardour/ardour/session_event.h1
-rw-r--r--libs/ardour/ardour/transport_api.h47
-rw-r--r--libs/ardour/ardour/transport_fsm.h213
-rw-r--r--libs/ardour/ardour/transport_master.h14
-rw-r--r--libs/ardour/ardour/transport_master_manager.h3
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();