summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/ardour/ardour/session.h2
-rw-r--r--libs/ardour/ardour/transport_fsm.h292
-rw-r--r--libs/ardour/enums.cc25
-rw-r--r--libs/ardour/globals.cc2
-rw-r--r--libs/ardour/session.cc7
-rw-r--r--libs/ardour/session_process.cc27
-rw-r--r--libs/ardour/session_time.cc4
-rw-r--r--libs/ardour/session_transport.cc34
-rw-r--r--libs/ardour/transport_fsm.cc314
9 files changed, 486 insertions, 221 deletions
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index 337b88a8e8..70b5bc4896 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -1447,7 +1447,7 @@ private:
Butler* _butler;
- boost::shared_ptr<TransportFSM> _transport_fsm;
+ TransportFSM* _transport_fsm;
static const PostTransportWork ProcessCannotProceedMask =
PostTransportWork (
diff --git a/libs/ardour/ardour/transport_fsm.h b/libs/ardour/ardour/transport_fsm.h
index f175f8161a..efc5bb161a 100644
--- a/libs/ardour/ardour/transport_fsm.h
+++ b/libs/ardour/ardour/transport_fsm.h
@@ -1,219 +1,169 @@
#ifndef _ardour_transport_fsm_h_
#define _ardour_transport_fsm_h_
-#ifdef nil
-#undef nil
-#endif
-
-#ifndef BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
-#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
-#endif
-
-#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 <list>
+#include <queue>
#include "pbd/demangle.h"
#include "pbd/stacktrace.h"
#include "ardour/debug.h"
-
-/* state machine */
-namespace msm = boost::msm;
-namespace mpl = boost::mpl;
+#include "ardour/types.h"
namespace ARDOUR
{
class TransportAPI;
-struct TransportFSM : public msm::front::state_machine_def<TransportFSM>
+struct TransportFSM
{
+ public:
/* 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;
+ enum EventType {
+ ButlerDone,
+ ButlerRequired,
+ DeclickDone,
+ StartTransport,
+ StopTransport,
+ Locate,
+ LocateDone
};
- 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) {}
-
+ struct FSMEvent {
+ EventType type;
+ union {
+ bool abort; /* for stop */
+ bool with_roll; /* for locate */
+ };
+ union {
+ bool clear_state; /* for stop */
+ bool with_flush; /* for locate */
+ };
+ /* for locate */
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 &);
+ FSMEvent (EventType t)
+ : type (t)
+ , with_roll (false)
+ , with_flush (false)
+ , target (0)
+ , with_loop (false)
+ , force (false)
+ {}
+ FSMEvent (EventType t, bool ab, bool cl)
+ : type (t)
+ , abort (ab)
+ , clear_state (cl)
+ {
+ assert (t == StopTransport);
+ }
+ FSMEvent (EventType t, samplepos_t pos, bool r, bool fl, bool lp, bool f4c)
+ : type (t)
+ , with_roll (r)
+ , with_flush (fl)
+ , target (pos)
+ , with_loop (lp)
+ , force (f4c)
+ {
+ assert (t == Locate);
+ }
+
+ void* operator new (size_t);
+ void operator delete (void *ptr, size_t /*size*/);
+
+ static void init_pool ();
+
+ private:
+ static Pool* pool;
- /* 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); }
+ TransportFSM (TransportAPI& tapi);
-#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"); } \
+ void start () {
+ init ();
}
-#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; \
+ void stop () {
+ /* should we do anything here? this method is modelled on the
+ boost::msm design, but its not clear that we ever need to
+ do anything like this.
+ */
}
-#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; \
- }
+ enum MotionState {
+ Stopped,
+ Rolling,
+ DeclickToStop,
+ DeclickToLocate,
+ WaitingForLocate
+ };
- /* FSM states */
+ enum ButlerState {
+ NotWaitingForButler,
+ WaitingForButler
+ };
- 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);
+ std::string current_state () const;
- // Pick a back-end
- typedef msm::back::state_machine<TransportFSM> back;
+ private:
+ MotionState _motion_state;
+ ButlerState _butler_state;
- boost::weak_ptr<back> wp;
+ void init();
- 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>(); }
+ /* transition actions */
- static boost::shared_ptr<back> create(TransportAPI& api) {
+ void schedule_butler_for_transport_work ();
+ void start_playback ();
+ void stop_playback ();
+ void start_saved_locate ();
+ void roll_after_locate ();
+ void start_locate (FSMEvent const *);
+ void interrupt_locate (FSMEvent const *);
+ void save_locate_and_start_declick (FSMEvent const *);
+ void start_declick (FSMEvent const *);
- boost::shared_ptr<back> p (new back ());
+ /* guards */
- p->wp = p;
- p->api = &api;
- return p;
+ bool should_roll_after_locate ();
+ bool should_not_roll_after_locate () { return !should_roll_after_locate (); }
+
+ public:
+ bool locating () { return _motion_state == WaitingForLocate; }
+ bool rolling () { return _motion_state == Rolling; }
+ bool stopped () { return _motion_state == Stopped; }
+ bool waiting_for_butler() { return _butler_state == WaitingForButler; }
+ bool declick_in_progress() { return _motion_state == DeclickToLocate || _motion_state == DeclickToStop; }
+
+ void enqueue (FSMEvent* ev) {
+ queued_events.push (ev);
+ if (!processing) {
+ process_events ();
+ }
}
- boost::shared_ptr<back> backend() { return wp.lock(); }
+ private:
- template<typename Event> void enqueue (Event const & e) {
- backend()->process_event (e);
- }
+ void transition (MotionState ms);
+ void transition (ButlerState bs);
- /* 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;
+ void process_events ();
+ bool process_event (FSMEvent *);
+
+ FSMEvent _last_locate;
+ FSMEvent _last_stop;
TransportAPI* api;
+ std::queue<FSMEvent*> queued_events;
+ std::list<FSMEvent*> deferred_events;
+ int processing;
- // 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;
- }
+ void defer (FSMEvent* ev);
+ void bad_transition (FSMEvent const *);
};
} /* end namespace ARDOUR */
diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc
index 2665a14d94..903861fc02 100644
--- a/libs/ardour/enums.cc
+++ b/libs/ardour/enums.cc
@@ -47,6 +47,7 @@
#include "ardour/source.h"
#include "ardour/tempo.h"
#include "ardour/track.h"
+#include "ardour/transport_fsm.h"
#include "ardour/transport_master.h"
#include "ardour/types.h"
@@ -151,6 +152,9 @@ setup_enum_writer ()
PresentationInfo::Flag _PresentationInfo_Flag;
MusicalMode::Type mode;
MidiPortFlags _MidiPortFlags;
+ TransportFSM::EventType _TransportFSM_EventType;
+ TransportFSM::MotionState _TransportFSM_MotionState;
+ TransportFSM::ButlerState _TransportFSM_ButlerState;
#define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
#define REGISTER_BITS(e) enum_writer.register_bits (typeid(e).name(), i, s); i.clear(); s.clear()
@@ -793,6 +797,27 @@ setup_enum_writer ()
REGISTER_CLASS_ENUM (MusicalMode, Persian);
REGISTER_CLASS_ENUM (MusicalMode, Algerian);
REGISTER (mode);
+
+ REGISTER_CLASS_ENUM (TransportFSM, ButlerDone);
+ REGISTER_CLASS_ENUM (TransportFSM, ButlerRequired);
+ REGISTER_CLASS_ENUM (TransportFSM, DeclickDone);
+ REGISTER_CLASS_ENUM (TransportFSM, StartTransport);
+ REGISTER_CLASS_ENUM (TransportFSM, StopTransport);
+ REGISTER_CLASS_ENUM (TransportFSM, Locate);
+ REGISTER_CLASS_ENUM (TransportFSM, LocateDone);
+ REGISTER (_TransportFSM_EventType);
+
+ REGISTER_CLASS_ENUM (TransportFSM, Stopped);
+ REGISTER_CLASS_ENUM (TransportFSM, Rolling);
+ REGISTER_CLASS_ENUM (TransportFSM, DeclickToStop);
+ REGISTER_CLASS_ENUM (TransportFSM, DeclickToLocate);
+ REGISTER_CLASS_ENUM (TransportFSM, WaitingForLocate);
+ REGISTER (_TransportFSM_MotionState);
+
+
+ REGISTER_CLASS_ENUM (TransportFSM, NotWaitingForButler);
+ REGISTER_CLASS_ENUM (TransportFSM, WaitingForButler);
+ REGISTER (_TransportFSM_ButlerState);
}
} /* namespace ARDOUR */
diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc
index cbb7e4da0e..2a47fd9dea 100644
--- a/libs/ardour/globals.cc
+++ b/libs/ardour/globals.cc
@@ -125,6 +125,7 @@
#include "ardour/runtime_functions.h"
#include "ardour/session_event.h"
#include "ardour/source_factory.h"
+#include "ardour/transport_fsm.h"
#include "ardour/transport_master_manager.h"
#ifdef LV2_SUPPORT
#include "ardour/uri_map.h"
@@ -467,6 +468,7 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
#endif
SessionEvent::init_event_pool ();
+ TransportFSM::FSMEvent::init_pool ();
Operations::make_operations_quarks ();
SessionObject::make_property_quarks ();
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index f96f59e516..a042dbf4f3 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -251,7 +251,7 @@ Session::Session (AudioEngine &eng,
, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
, _n_lua_scripts (0)
, _butler (new Butler (*this))
- , _transport_fsm (TransportFSM::create (*this))
+ , _transport_fsm (new TransportFSM (*this))
, _post_transport_work (0)
, _locations (new Locations (*this))
, _ignore_skips_updates (false)
@@ -609,7 +609,7 @@ Session::immediately_post_engine ()
/* Restart transport FSM */
- _transport_fsm->backend()->start ();
+ _transport_fsm->start ();
/* every time we reconnect, do stuff ... */
@@ -884,8 +884,7 @@ Session::destroy ()
delete _selection;
_selection = 0;
- _transport_fsm->backend()->stop ();
- _transport_fsm.reset ();
+ _transport_fsm->stop ();
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc
index 7661115463..fbcf4c648e 100644
--- a/libs/ardour/session_process.cc
+++ b/libs/ardour/session_process.cc
@@ -59,7 +59,10 @@ using namespace ARDOUR;
using namespace PBD;
using namespace std;
-#define TFSM_EVENT(ev) { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("TFSM(%1)\n", typeid(ev).name())); _transport_fsm->enqueue (ev); }
+#define TFSM_EVENT(evtype) { _transport_fsm->enqueue (new TransportFSM::FSMEvent (evtype)); }
+#define TFSM_STOP(abort,clear) { _transport_fsm->enqueue (new TransportFSM::FSMEvent (TransportFSM::StopTransport,abort,clear)); }
+#define TFSM_LOCATE(target,roll,flush,loop,force) { _transport_fsm->enqueue (new TransportFSM::FSMEvent (TransportFSM::Locate,target,roll,flush,loop,force)); }
+
/** Called by the audio engine when there is work to be done with JACK.
* @param nframes Number of samples to process.
@@ -127,7 +130,7 @@ Session::process (pframes_t nframes)
if (!one_or_more_routes_declicking && declick_in_progress()) {
/* end of the declick has been reached by all routes */
- TFSM_EVENT (TransportFSM::declick_done());
+ TFSM_EVENT (TransportFSM::DeclickDone);
}
_engine.main_thread()->drop_buffers ();
@@ -241,7 +244,7 @@ Session::process_routes (pframes_t nframes, bool& need_butler)
bool b = false;
if ((ret = (*i)->roll (nframes, start_sample, end_sample, b)) < 0) {
- TFSM_EVENT (TransportFSM::stop_transport (false, false));
+ TFSM_STOP (false, false);
return -1;
}
@@ -851,7 +854,7 @@ Session::process_event (SessionEvent* ev)
/* roll after locate, do not flush, set "with loop"
true only if we are seamless looping
*/
- TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, false, Config->get_seamless_loop(), false));
+ TFSM_LOCATE (ev->target_sample, true, false, Config->get_seamless_loop(), false);
}
remove = false;
del = false;
@@ -859,19 +862,19 @@ Session::process_event (SessionEvent* ev)
case SessionEvent::Locate:
/* args: do not roll after locate, do flush, not with loop, force */
- TFSM_EVENT (TransportFSM::locate (ev->target_sample, false, true, false, ev->yes_or_no));
+ TFSM_LOCATE (ev->target_sample, false, true, false, ev->yes_or_no);
_send_timecode_update = true;
break;
case SessionEvent::LocateRoll:
/* args: roll after locate, do flush, not with loop, force */
- TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, true, false, ev->yes_or_no));
+ TFSM_LOCATE (ev->target_sample, true, true, false, ev->yes_or_no);
_send_timecode_update = true;
break;
case SessionEvent::Skip:
if (Config->get_skip_playback()) {
- TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, true, false, false));
+ TFSM_LOCATE (ev->target_sample, true, true, false, false);
_send_timecode_update = true;
}
remove = false;
@@ -912,14 +915,14 @@ Session::process_event (SessionEvent* ev)
break;
case SessionEvent::RangeStop:
- TFSM_EVENT (TransportFSM::stop_transport (ev->yes_or_no, false));
+ TFSM_STOP (ev->yes_or_no, false);
remove = false;
del = false;
break;
case SessionEvent::RangeLocate:
/* args: roll after locate, do flush, not with loop */
- TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, true, false, false));
+ TFSM_LOCATE (ev->target_sample, true, true, false, false);
remove = false;
del = false;
break;
@@ -1109,7 +1112,7 @@ Session::follow_transport_master (pframes_t nframes)
DiskReader::inc_no_disk_output ();
if (!_transport_fsm->locating()) {
DEBUG_TRACE (DEBUG::Slave, string_compose ("request locate to master position %1\n", slave_transport_sample));
- TFSM_EVENT (TransportFSM::locate (slave_transport_sample, true, true, false, false));
+ TFSM_LOCATE (slave_transport_sample, true, true, false, false);
}
return true;
}
@@ -1117,12 +1120,12 @@ Session::follow_transport_master (pframes_t nframes)
if (slave_speed != 0.0) {
if (_transport_speed == 0.0f) {
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave starts transport: %1 sample %2 tf %3\n", slave_speed, slave_transport_sample, _transport_sample));
- TFSM_EVENT (TransportFSM::start_transport ());
+ TFSM_EVENT (TransportFSM::StartTransport);
}
} else {
if (_transport_speed != 0.0f) {
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave stops transport: %1 sample %2 tf %3\n", slave_speed, slave_transport_sample, _transport_sample));
- TFSM_EVENT (TransportFSM::stop_transport (false, false));
+ TFSM_STOP (false, false);
}
}
diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc
index 674ea7b67b..185a86bf4c 100644
--- a/libs/ardour/session_time.cc
+++ b/libs/ardour/session_time.cc
@@ -43,7 +43,7 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
-#define TFSM_EVENT(ev) { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("TFSM(%1)\n", typeid(ev).name())); _transport_fsm->enqueue (ev); }
+#define TFSM_EVENT(evtype) { _transport_fsm->enqueue (new TransportFSM::FSMEvent (evtype)); }
/* BBT TIME*/
@@ -217,7 +217,7 @@ Session::backend_sync_callback (TransportState state, samplepos_t pos)
case TransportRolling:
// cerr << "SYNC: rolling slave = " << slave << endl;
if (slave) {
- TFSM_EVENT (TransportFSM::start_transport());
+ TFSM_EVENT (TransportFSM::StartTransport);
}
break;
diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc
index 2f5325b82e..e9f6ed6bbe 100644
--- a/libs/ardour/session_transport.cc
+++ b/libs/ardour/session_transport.cc
@@ -82,7 +82,9 @@ using namespace PBD;
#endif
-#define TFSM_EVENT(ev) { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("TFSM(%1)\n", typeid(ev).name())); _transport_fsm->enqueue (ev); }
+#define TFSM_EVENT(evtype) { _transport_fsm->enqueue (new TransportFSM::FSMEvent (evtype)); }
+#define TFSM_STOP(abort,clear) { _transport_fsm->enqueue (new TransportFSM::FSMEvent (TransportFSM::StopTransport,abort,clear)); }
+#define TFSM_LOCATE(target,roll,flush,loop,force) { _transport_fsm->enqueue (new TransportFSM::FSMEvent (TransportFSM::Locate,target,roll,flush,loop,force)); }
/* *****************************************************************************
* REALTIME ACTIONS (to be called on state transitions)
@@ -159,7 +161,7 @@ Session::realtime_stop (bool abort, bool clear_state)
}
if (todo) {
- TFSM_EVENT (TransportFSM::butler_required());
+ TFSM_EVENT (TransportFSM::ButlerRequired);
}
}
@@ -239,7 +241,7 @@ Session::do_locate (samplepos_t target_sample, bool with_roll, bool with_flush,
set_transport_speed (1.0, 0, false);
}
loop_changing = false;
- TFSM_EVENT (TransportFSM::locate_done());
+ TFSM_EVENT (TransportFSM::LocateDone);
Located (); /* EMIT SIGNAL */
return;
}
@@ -382,9 +384,9 @@ Session::do_locate (samplepos_t target_sample, bool with_roll, bool with_flush,
}
if (need_butler) {
- TFSM_EVENT (TransportFSM::butler_required());
+ TFSM_EVENT (TransportFSM::ButlerRequired);
} else {
- TFSM_EVENT (TransportFSM::locate_done());
+ TFSM_EVENT (TransportFSM::LocateDone);
}
loop_changing = false;
@@ -484,7 +486,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
_requested_return_sample = destination_sample;
}
- TFSM_EVENT (TransportFSM::stop_transport (abort, false));
+ TFSM_STOP (abort, false);
}
} else if (transport_stopped() && speed == 1.0) {
@@ -521,7 +523,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
_engine.transport_start ();
_count_in_once = false;
} else {
- TFSM_EVENT (TransportFSM::start_transport());
+ TFSM_EVENT (TransportFSM::StartTransport);
}
} else {
@@ -576,7 +578,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
if (todo) {
add_post_transport_work (todo);
- TFSM_EVENT (TransportFSM::butler_required());
+ TFSM_EVENT (TransportFSM::ButlerRequired);
}
DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC3 with speed = %1\n", _transport_speed));
@@ -748,7 +750,7 @@ Session::butler_completed_transport_work ()
if (ptw & PostTransportLocate) {
post_locate ();
- TFSM_EVENT (TransportFSM::locate_done());
+ TFSM_EVENT (TransportFSM::LocateDone);
}
if (ptw & PostTransportAdjustPlaybackBuffering) {
@@ -763,7 +765,7 @@ Session::butler_completed_transport_work ()
set_post_transport_work (PostTransportWork (0));
if (_transport_fsm->waiting_for_butler()) {
- TFSM_EVENT (TransportFSM::butler_done());
+ TFSM_EVENT (TransportFSM::ButlerDone);
}
DiskReader::dec_no_disk_output ();
@@ -785,7 +787,7 @@ Session::maybe_stop (samplepos_t limit)
if (synced_to_engine () && config.get_jack_time_master ()) {
_engine.transport_stop ();
} else if (!synced_to_engine ()) {
- TFSM_EVENT (TransportFSM::stop_transport ());
+ TFSM_EVENT (TransportFSM::StopTransport);
}
return true;
}
@@ -894,11 +896,11 @@ Session::set_play_loop (bool yn, double speed)
rolling, do not locate to loop start.
*/
if (!transport_rolling() && (speed != 0.0)) {
- TFSM_EVENT (TransportFSM::locate (loc->start(), true, true, false, true));
+ TFSM_LOCATE (loc->start(), true, true, false, true);
}
} else {
if (speed != 0.0) {
- TFSM_EVENT (TransportFSM::locate (loc->start(), true, true, false, true));
+ TFSM_LOCATE (loc->start(), true, true, false, true);
}
}
}
@@ -1779,7 +1781,7 @@ Session::unset_play_loop ()
if (Config->get_seamless_loop()) {
/* likely need to flush track buffers: this will locate us to wherever we are */
add_post_transport_work (PostTransportLocate);
- TFSM_EVENT (TransportFSM::butler_required());
+ TFSM_EVENT (TransportFSM::ButlerRequired);
}
TransportStateChange (); /* EMIT SIGNAL */
}
@@ -1936,7 +1938,7 @@ Session::engine_halted ()
* ::engine_running() (if we ever get there)
*/
- _transport_fsm->backend()->stop ();
+ _transport_fsm->stop ();
/* Synchronously do the realtime part of a transport stop.
*
@@ -1952,7 +1954,7 @@ void
Session::engine_running ()
{
initialize_latencies ();
- _transport_fsm->backend()->start ();
+ _transport_fsm->start ();
}
void
diff --git a/libs/ardour/transport_fsm.cc b/libs/ardour/transport_fsm.cc
index ea2be0eb3f..0528f95304 100644
--- a/libs/ardour/transport_fsm.cc
+++ b/libs/ardour/transport_fsm.cc
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2019 Robin Gareus <robin@gareus.org>
+ * 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
@@ -16,6 +17,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <sstream>
+
+#include "pbd/error.h"
+#include "pbd/i18n.h"
+
#include "ardour/debug.h"
#include "ardour/session.h"
#include "ardour/transport_fsm.h"
@@ -23,69 +29,327 @@
using namespace ARDOUR;
using namespace PBD;
+Pool* TransportFSM::FSMEvent::pool = 0;
+
+void
+TransportFSM::FSMEvent::init_pool ()
+{
+ pool = new Pool (X_("FSMEvents"), sizeof (FSMEvent), 128);
+}
+
+void*
+TransportFSM::FSMEvent::operator new (size_t)
+{
+ return pool->alloc();
+}
+
+void
+TransportFSM::FSMEvent::operator delete (void *ptr, size_t /*size*/)
+{
+ return pool->release (ptr);
+}
+
+TransportFSM::TransportFSM (TransportAPI& tapi)
+ : _last_locate (Locate)
+ , _last_stop (StopTransport)
+ , api (&tapi)
+ , processing (0)
+{
+ init ();
+}
+
+void
+TransportFSM::init ()
+{
+ _motion_state = Stopped;
+ _butler_state = NotWaitingForButler;
+}
+
+void
+TransportFSM::process_events ()
+{
+ processing++;
+
+ while (!queued_events.empty()) {
+ FSMEvent* ev = queued_events.front();
+ queued_events.pop ();
+
+ MotionState oms = _motion_state;
+ ButlerState obs = _butler_state;
+
+ if (process_event (ev)) { /* event processed successfully */
+
+ if (oms != _motion_state || obs != _butler_state) {
+
+ /* state changed, so now check deferred events
+ * to see if they can be processed now
+ */
+
+ if (!deferred_events.empty() ){
+ DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("processing %1 deferred events\n", deferred_events.size()));
+
+ for (std::list<FSMEvent*>::iterator e = deferred_events.begin(); e != deferred_events.end(); ) {
+ FSMEvent* deferred_ev = *e;
+ if (process_event (deferred_ev)) { /* event processed, remove from deferred */
+ e = deferred_events.erase (e);
+ delete deferred_ev;
+ } else {
+ ++e;
+ }
+ }
+ }
+ }
+ }
+
+ delete ev;
+ }
+
+ processing--;
+}
+
+/* This is the transition table from the original boost::msm
+ * implementation of this FSM. It is more easily readable and
+ * consultable. Please keep it updated as the FSM changes.
+ */
+
+/*
+ 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
+*/
+
+std::string
+TransportFSM::current_state () const
+{
+ std::stringstream s;
+ s << enum_2_string (_motion_state) << '/' << enum_2_string (_butler_state);
+ return s.str();
+}
+
+void
+TransportFSM::bad_transition (FSMEvent const * ev)
+{
+ error << "bad transition, current state = " << current_state() << " event = " << enum_2_string (ev->type) << endmsg;
+ std::cerr << "bad transition, current state = " << current_state() << " event = " << enum_2_string (ev->type) << std::endl;
+}
+
+bool
+TransportFSM::process_event (FSMEvent* ev)
+{
+ DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("process %1\n", enum_2_string (ev->type)));
+
+ switch (ev->type) {
+
+ case StartTransport:
+ switch (_motion_state) {
+ case Stopped:
+ transition (Rolling);
+ start_playback ();
+ break;
+ case Rolling:
+ break;
+ case DeclickToLocate:
+ case WaitingForLocate:
+ defer (ev);
+ break;
+ case DeclickToStop:
+ defer (ev);
+ break;
+ default:
+ bad_transition (ev); return false;
+ break;
+ }
+ break;
+
+ case StopTransport:
+ switch (_motion_state) {
+ case Rolling:
+ transition (DeclickToStop);
+ start_declick (ev);
+ break;
+ case Stopped:
+ break;
+ case DeclickToLocate:
+ case WaitingForLocate:
+ defer (ev);
+ break;
+ default:
+ bad_transition (ev); return false;
+ break;
+ }
+ break;
+
+ case Locate:
+ switch (_motion_state) {
+ case Stopped:
+ transition (WaitingForLocate);
+ start_locate (ev);
+ break;
+ case Rolling:
+ transition (DeclickToLocate);
+ save_locate_and_start_declick (ev);
+ break;
+ case WaitingForLocate:
+ case DeclickToLocate:
+ interrupt_locate (ev);
+ break;
+ default:
+ bad_transition (ev); return false;
+ }
+ break;
+
+ case LocateDone:
+ switch (_motion_state) {
+ case WaitingForLocate:
+ if (should_not_roll_after_locate()) {
+ transition (Stopped);
+ } else {
+ transition (Rolling);
+ roll_after_locate ();
+ }
+ break;
+ default:
+ bad_transition (ev); return false;
+ }
+ break;
+
+ case DeclickDone:
+ switch (_motion_state) {
+ case DeclickToLocate:
+ transition (WaitingForLocate);
+ start_saved_locate ();
+ break;
+ case DeclickToStop:
+ transition (Stopped);
+ stop_playback ();
+ break;
+ default:
+ bad_transition (ev); return false;
+ }
+ break;
+
+ case ButlerRequired:
+ switch (_butler_state) {
+ case NotWaitingForButler:
+ transition (WaitingForButler);
+ schedule_butler_for_transport_work ();
+ break;
+ case WaitingForButler:
+ schedule_butler_for_transport_work ();
+ break;
+ default:
+ bad_transition (ev); return false;
+ }
+ break;
+
+ case ButlerDone:
+ switch (_butler_state) {
+ case WaitingForButler:
+ transition (NotWaitingForButler);
+ break;
+ default:
+ bad_transition (ev); return false;
+ }
+ break;
+ }
+
+ return true;
+}
+
/* transition actions */
void
-TransportFSM::start_playback (TransportFSM::start_transport const& p)
+TransportFSM::start_playback ()
{
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_playback\n");
api->start_transport();
}
void
-TransportFSM::start_declick (TransportFSM::stop_transport const &s)
+TransportFSM::start_declick (FSMEvent const * s)
{
+ assert (s->type == StopTransport);
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_declick\n");
- _last_stop = s;
+ _last_stop = *s;
}
void
-TransportFSM::stop_playback (TransportFSM::declick_done const& /*ignored*/)
+TransportFSM::stop_playback ()
{
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::stop_playback\n");
api->stop_transport (_last_stop.abort, _last_stop.clear_state);
}
void
-TransportFSM::save_locate_and_start_declick (TransportFSM::locate const & l)
+TransportFSM::save_locate_and_start_declick (FSMEvent const * l)
{
+ assert (l->type == Locate);
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::save_locate_and_stop\n");
- _last_locate = l;
- start_declick (stop_transport (false, false));
+ _last_locate = *l;
+ _last_stop = FSMEvent (StopTransport, false, false);
}
void
-TransportFSM::start_locate (TransportFSM::locate const& l)
+TransportFSM::start_locate (FSMEvent const * l)
{
+ assert (l->type == Locate);
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_locate\n");
- api->locate (l.target, l.with_roll, l.with_flush, l.with_loop, l.force);
+ api->locate (l->target, l->with_roll, l->with_flush, l->with_loop, l->force);
}
void
-TransportFSM::start_saved_locate (TransportFSM::declick_done const&)
+TransportFSM::start_saved_locate ()
{
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_save\n");
api->locate (_last_locate.target, _last_locate.with_roll, _last_locate.with_flush, _last_locate.with_loop, _last_locate.force);
}
void
-TransportFSM::interrupt_locate (TransportFSM::locate const& l)
+TransportFSM::interrupt_locate (FSMEvent const * l)
{
+ assert (l->type == Locate);
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::interrupt\n");
/* maintain original "with-roll" choice of initial locate, even though
* we are interrupting the locate to start a new one.
*/
- api->locate (l.target, _last_locate.with_roll, l.with_flush, l.with_loop, l.force);
+ api->locate (l->target, _last_locate.with_roll, l->with_flush, l->with_loop, l->force);
}
void
-TransportFSM::schedule_butler_for_transport_work (TransportFSM::butler_required const&)
+TransportFSM::schedule_butler_for_transport_work ()
{
api->schedule_butler_for_transport_work ();
}
bool
-TransportFSM::should_roll_after_locate (TransportFSM::locate_done const &)
+TransportFSM::should_roll_after_locate ()
{
bool ret = api->should_roll_after_locate ();
DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("tfsm::should_roll_after_locate() ? %1\n", ret));
@@ -93,9 +357,29 @@ TransportFSM::should_roll_after_locate (TransportFSM::locate_done const &)
}
void
-TransportFSM::roll_after_locate (TransportFSM::locate_done const &)
+TransportFSM::roll_after_locate ()
{
DEBUG_TRACE (DEBUG::TFSMEvents, "rolling after locate\n");
api->start_transport ();
}
+void
+TransportFSM::defer (FSMEvent* ev)
+{
+ DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("Defer %1 during %2\n", enum_2_string (ev->type), current_state()));
+ deferred_events.push_back (ev);
+}
+
+void
+TransportFSM::transition (MotionState ms)
+{
+ DEBUG_TRACE (DEBUG::TFSMState, string_compose ("Leave %1, enter %2\n", enum_2_string (_motion_state), enum_2_string (ms)));
+ _motion_state = ms;
+}
+
+void
+TransportFSM::transition (ButlerState bs)
+{
+ DEBUG_TRACE (DEBUG::TFSMState, string_compose ("Leave %1, enter %2\n", enum_2_string (_butler_state), enum_2_string (bs)));
+ _butler_state = bs;
+}