mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-10 16:46:35 +01:00
replace boost::msm - based FSM for transport with one written in "plain C++"
Still need to use boost::intrusive to managed qeued/deferred containers
This commit is contained in:
parent
e698a1b2fa
commit
61afcb8e2b
9 changed files with 489 additions and 224 deletions
|
|
@ -1447,7 +1447,7 @@ private:
|
||||||
|
|
||||||
Butler* _butler;
|
Butler* _butler;
|
||||||
|
|
||||||
boost::shared_ptr<TransportFSM> _transport_fsm;
|
TransportFSM* _transport_fsm;
|
||||||
|
|
||||||
static const PostTransportWork ProcessCannotProceedMask =
|
static const PostTransportWork ProcessCannotProceedMask =
|
||||||
PostTransportWork (
|
PostTransportWork (
|
||||||
|
|
|
||||||
|
|
@ -1,219 +1,169 @@
|
||||||
#ifndef _ardour_transport_fsm_h_
|
#ifndef _ardour_transport_fsm_h_
|
||||||
#define _ardour_transport_fsm_h_
|
#define _ardour_transport_fsm_h_
|
||||||
|
|
||||||
#ifdef nil
|
#include <list>
|
||||||
#undef nil
|
#include <queue>
|
||||||
#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 "pbd/demangle.h"
|
#include "pbd/demangle.h"
|
||||||
#include "pbd/stacktrace.h"
|
#include "pbd/stacktrace.h"
|
||||||
|
|
||||||
#include "ardour/debug.h"
|
#include "ardour/debug.h"
|
||||||
|
#include "ardour/types.h"
|
||||||
/* state machine */
|
|
||||||
namespace msm = boost::msm;
|
|
||||||
namespace mpl = boost::mpl;
|
|
||||||
|
|
||||||
namespace ARDOUR
|
namespace ARDOUR
|
||||||
{
|
{
|
||||||
|
|
||||||
class TransportAPI;
|
class TransportAPI;
|
||||||
|
|
||||||
struct TransportFSM : public msm::front::state_machine_def<TransportFSM>
|
struct TransportFSM
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
/* events to be delivered to the FSM */
|
/* events to be delivered to the FSM */
|
||||||
|
|
||||||
struct butler_done {};
|
enum EventType {
|
||||||
struct butler_required {};
|
ButlerDone,
|
||||||
struct declick_done {};
|
ButlerRequired,
|
||||||
struct start_transport {};
|
DeclickDone,
|
||||||
|
StartTransport,
|
||||||
struct stop_transport {
|
StopTransport,
|
||||||
stop_transport (bool ab = false, bool cl = false)
|
Locate,
|
||||||
: abort (ab)
|
LocateDone
|
||||||
, clear_state (cl) {}
|
|
||||||
|
|
||||||
bool abort;
|
|
||||||
bool clear_state;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct locate {
|
struct FSMEvent {
|
||||||
locate ()
|
EventType type;
|
||||||
: target (0)
|
union {
|
||||||
, with_roll (false)
|
bool abort; /* for stop */
|
||||||
, with_flush (false)
|
bool with_roll; /* for locate */
|
||||||
, with_loop (false)
|
};
|
||||||
, force (false) {}
|
union {
|
||||||
|
bool clear_state; /* for stop */
|
||||||
locate (samplepos_t target, bool roll, bool flush, bool loop, bool f4c)
|
bool with_flush; /* for locate */
|
||||||
: target (target)
|
};
|
||||||
, with_roll (roll)
|
/* for locate */
|
||||||
, with_flush (flush)
|
|
||||||
, with_loop (loop)
|
|
||||||
, force (f4c) {}
|
|
||||||
|
|
||||||
samplepos_t target;
|
samplepos_t target;
|
||||||
bool with_roll;
|
|
||||||
bool with_flush;
|
|
||||||
bool with_loop;
|
bool with_loop;
|
||||||
bool force;
|
bool force;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct locate_done {};
|
TransportFSM (TransportAPI& tapi);
|
||||||
|
|
||||||
/* Flags */
|
void start () {
|
||||||
|
init ();
|
||||||
|
}
|
||||||
|
|
||||||
struct DeclickInProgress {};
|
void stop () {
|
||||||
struct LocateInProgress {};
|
/* should we do anything here? this method is modelled on the
|
||||||
struct IsRolling {};
|
boost::msm design, but its not clear that we ever need to
|
||||||
struct IsStopped {};
|
do anything like this.
|
||||||
struct IsWaitingForButler {};
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
typedef msm::active_state_switch_before_transition active_state_switch_policy;
|
enum MotionState {
|
||||||
|
Stopped,
|
||||||
|
Rolling,
|
||||||
|
DeclickToStop,
|
||||||
|
DeclickToLocate,
|
||||||
|
WaitingForLocate
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ButlerState {
|
||||||
|
NotWaitingForButler,
|
||||||
|
WaitingForButler
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string current_state () const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MotionState _motion_state;
|
||||||
|
ButlerState _butler_state;
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
/* transition actions */
|
/* transition actions */
|
||||||
|
|
||||||
void start_playback (start_transport const& p);
|
void schedule_butler_for_transport_work ();
|
||||||
void roll_after_locate (locate_done const& p);
|
void start_playback ();
|
||||||
void stop_playback (declick_done const& s);
|
void stop_playback ();
|
||||||
void start_locate (locate const& s);
|
void start_saved_locate ();
|
||||||
void start_saved_locate (declick_done const& s);
|
void roll_after_locate ();
|
||||||
void interrupt_locate (locate const& s);
|
void start_locate (FSMEvent const *);
|
||||||
void schedule_butler_for_transport_work (butler_required const&);
|
void interrupt_locate (FSMEvent const *);
|
||||||
void save_locate_and_start_declick (locate const &);
|
void save_locate_and_start_declick (FSMEvent const *);
|
||||||
void start_declick (stop_transport const &);
|
void start_declick (FSMEvent const *);
|
||||||
|
|
||||||
/* guards */
|
/* guards */
|
||||||
|
|
||||||
bool should_roll_after_locate (locate_done const &);
|
bool should_roll_after_locate ();
|
||||||
bool should_not_roll_after_locate (locate_done const & e) { return !should_roll_after_locate (e); }
|
bool should_not_roll_after_locate () { return !should_roll_after_locate (); }
|
||||||
|
|
||||||
#define define_state(State) \
|
public:
|
||||||
struct State : public msm::front::state<> \
|
bool locating () { return _motion_state == WaitingForLocate; }
|
||||||
{ \
|
bool rolling () { return _motion_state == Rolling; }
|
||||||
template <class Event,class FSM> void on_entry (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "entering: " # State "\n"); } \
|
bool stopped () { return _motion_state == Stopped; }
|
||||||
template <class Event,class FSM> void on_exit (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "leaving: " # State "\n"); } \
|
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 ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define define_state_flag(State,Flag) \
|
private:
|
||||||
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) \
|
void transition (MotionState ms);
|
||||||
struct State : public msm::front::state<> \
|
void transition (ButlerState bs);
|
||||||
{ \
|
|
||||||
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 */
|
void process_events ();
|
||||||
|
bool process_event (FSMEvent *);
|
||||||
|
|
||||||
define_state_flag (WaitingForButler, IsWaitingForButler);
|
FSMEvent _last_locate;
|
||||||
define_state (NotWaitingForButler);
|
FSMEvent _last_stop;
|
||||||
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;
|
TransportAPI* api;
|
||||||
|
std::queue<FSMEvent*> queued_events;
|
||||||
|
std::list<FSMEvent*> deferred_events;
|
||||||
|
int processing;
|
||||||
|
|
||||||
// Replaces the default no-transition response.
|
void defer (FSMEvent* ev);
|
||||||
template <class FSM,class Event>
|
void bad_transition (FSMEvent const *);
|
||||||
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 */
|
} /* end namespace ARDOUR */
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
#include "ardour/source.h"
|
#include "ardour/source.h"
|
||||||
#include "ardour/tempo.h"
|
#include "ardour/tempo.h"
|
||||||
#include "ardour/track.h"
|
#include "ardour/track.h"
|
||||||
|
#include "ardour/transport_fsm.h"
|
||||||
#include "ardour/transport_master.h"
|
#include "ardour/transport_master.h"
|
||||||
#include "ardour/types.h"
|
#include "ardour/types.h"
|
||||||
|
|
||||||
|
|
@ -151,6 +152,9 @@ setup_enum_writer ()
|
||||||
PresentationInfo::Flag _PresentationInfo_Flag;
|
PresentationInfo::Flag _PresentationInfo_Flag;
|
||||||
MusicalMode::Type mode;
|
MusicalMode::Type mode;
|
||||||
MidiPortFlags _MidiPortFlags;
|
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(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()
|
#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, Persian);
|
||||||
REGISTER_CLASS_ENUM (MusicalMode, Algerian);
|
REGISTER_CLASS_ENUM (MusicalMode, Algerian);
|
||||||
REGISTER (mode);
|
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 */
|
} /* namespace ARDOUR */
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@
|
||||||
#include "ardour/runtime_functions.h"
|
#include "ardour/runtime_functions.h"
|
||||||
#include "ardour/session_event.h"
|
#include "ardour/session_event.h"
|
||||||
#include "ardour/source_factory.h"
|
#include "ardour/source_factory.h"
|
||||||
|
#include "ardour/transport_fsm.h"
|
||||||
#include "ardour/transport_master_manager.h"
|
#include "ardour/transport_master_manager.h"
|
||||||
#ifdef LV2_SUPPORT
|
#ifdef LV2_SUPPORT
|
||||||
#include "ardour/uri_map.h"
|
#include "ardour/uri_map.h"
|
||||||
|
|
@ -467,6 +468,7 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SessionEvent::init_event_pool ();
|
SessionEvent::init_event_pool ();
|
||||||
|
TransportFSM::FSMEvent::init_pool ();
|
||||||
|
|
||||||
Operations::make_operations_quarks ();
|
Operations::make_operations_quarks ();
|
||||||
SessionObject::make_property_quarks ();
|
SessionObject::make_property_quarks ();
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ Session::Session (AudioEngine &eng,
|
||||||
, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
|
, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
|
||||||
, _n_lua_scripts (0)
|
, _n_lua_scripts (0)
|
||||||
, _butler (new Butler (*this))
|
, _butler (new Butler (*this))
|
||||||
, _transport_fsm (TransportFSM::create (*this))
|
, _transport_fsm (new TransportFSM (*this))
|
||||||
, _post_transport_work (0)
|
, _post_transport_work (0)
|
||||||
, _locations (new Locations (*this))
|
, _locations (new Locations (*this))
|
||||||
, _ignore_skips_updates (false)
|
, _ignore_skips_updates (false)
|
||||||
|
|
@ -609,7 +609,7 @@ Session::immediately_post_engine ()
|
||||||
|
|
||||||
/* Restart transport FSM */
|
/* Restart transport FSM */
|
||||||
|
|
||||||
_transport_fsm->backend()->start ();
|
_transport_fsm->start ();
|
||||||
|
|
||||||
/* every time we reconnect, do stuff ... */
|
/* every time we reconnect, do stuff ... */
|
||||||
|
|
||||||
|
|
@ -884,8 +884,7 @@ Session::destroy ()
|
||||||
delete _selection;
|
delete _selection;
|
||||||
_selection = 0;
|
_selection = 0;
|
||||||
|
|
||||||
_transport_fsm->backend()->stop ();
|
_transport_fsm->stop ();
|
||||||
_transport_fsm.reset ();
|
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
|
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,10 @@ using namespace ARDOUR;
|
||||||
using namespace PBD;
|
using namespace PBD;
|
||||||
using namespace std;
|
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.
|
/** Called by the audio engine when there is work to be done with JACK.
|
||||||
* @param nframes Number of samples to process.
|
* @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()) {
|
if (!one_or_more_routes_declicking && declick_in_progress()) {
|
||||||
/* end of the declick has been reached by all routes */
|
/* end of the declick has been reached by all routes */
|
||||||
TFSM_EVENT (TransportFSM::declick_done());
|
TFSM_EVENT (TransportFSM::DeclickDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
_engine.main_thread()->drop_buffers ();
|
_engine.main_thread()->drop_buffers ();
|
||||||
|
|
@ -241,7 +244,7 @@ Session::process_routes (pframes_t nframes, bool& need_butler)
|
||||||
bool b = false;
|
bool b = false;
|
||||||
|
|
||||||
if ((ret = (*i)->roll (nframes, start_sample, end_sample, b)) < 0) {
|
if ((ret = (*i)->roll (nframes, start_sample, end_sample, b)) < 0) {
|
||||||
TFSM_EVENT (TransportFSM::stop_transport (false, false));
|
TFSM_STOP (false, false);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -851,7 +854,7 @@ Session::process_event (SessionEvent* ev)
|
||||||
/* roll after locate, do not flush, set "with loop"
|
/* roll after locate, do not flush, set "with loop"
|
||||||
true only if we are seamless looping
|
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;
|
remove = false;
|
||||||
del = false;
|
del = false;
|
||||||
|
|
@ -859,19 +862,19 @@ Session::process_event (SessionEvent* ev)
|
||||||
|
|
||||||
case SessionEvent::Locate:
|
case SessionEvent::Locate:
|
||||||
/* args: do not roll after locate, do flush, not with loop, force */
|
/* 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;
|
_send_timecode_update = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SessionEvent::LocateRoll:
|
case SessionEvent::LocateRoll:
|
||||||
/* args: roll after locate, do flush, not with loop, force */
|
/* 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;
|
_send_timecode_update = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SessionEvent::Skip:
|
case SessionEvent::Skip:
|
||||||
if (Config->get_skip_playback()) {
|
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;
|
_send_timecode_update = true;
|
||||||
}
|
}
|
||||||
remove = false;
|
remove = false;
|
||||||
|
|
@ -912,14 +915,14 @@ Session::process_event (SessionEvent* ev)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SessionEvent::RangeStop:
|
case SessionEvent::RangeStop:
|
||||||
TFSM_EVENT (TransportFSM::stop_transport (ev->yes_or_no, false));
|
TFSM_STOP (ev->yes_or_no, false);
|
||||||
remove = false;
|
remove = false;
|
||||||
del = false;
|
del = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SessionEvent::RangeLocate:
|
case SessionEvent::RangeLocate:
|
||||||
/* args: roll after locate, do flush, not with loop */
|
/* 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;
|
remove = false;
|
||||||
del = false;
|
del = false;
|
||||||
break;
|
break;
|
||||||
|
|
@ -1109,7 +1112,7 @@ Session::follow_transport_master (pframes_t nframes)
|
||||||
DiskReader::inc_no_disk_output ();
|
DiskReader::inc_no_disk_output ();
|
||||||
if (!_transport_fsm->locating()) {
|
if (!_transport_fsm->locating()) {
|
||||||
DEBUG_TRACE (DEBUG::Slave, string_compose ("request locate to master position %1\n", slave_transport_sample));
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1117,12 +1120,12 @@ Session::follow_transport_master (pframes_t nframes)
|
||||||
if (slave_speed != 0.0) {
|
if (slave_speed != 0.0) {
|
||||||
if (_transport_speed == 0.0f) {
|
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));
|
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 {
|
} else {
|
||||||
if (_transport_speed != 0.0f) {
|
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));
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ using namespace std;
|
||||||
using namespace ARDOUR;
|
using namespace ARDOUR;
|
||||||
using namespace PBD;
|
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*/
|
/* BBT TIME*/
|
||||||
|
|
||||||
|
|
@ -217,7 +217,7 @@ Session::backend_sync_callback (TransportState state, samplepos_t pos)
|
||||||
case TransportRolling:
|
case TransportRolling:
|
||||||
// cerr << "SYNC: rolling slave = " << slave << endl;
|
// cerr << "SYNC: rolling slave = " << slave << endl;
|
||||||
if (slave) {
|
if (slave) {
|
||||||
TFSM_EVENT (TransportFSM::start_transport());
|
TFSM_EVENT (TransportFSM::StartTransport);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,9 @@ using namespace PBD;
|
||||||
#endif
|
#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)
|
* REALTIME ACTIONS (to be called on state transitions)
|
||||||
|
|
@ -159,7 +161,7 @@ Session::realtime_stop (bool abort, bool clear_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (todo) {
|
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);
|
set_transport_speed (1.0, 0, false);
|
||||||
}
|
}
|
||||||
loop_changing = false;
|
loop_changing = false;
|
||||||
TFSM_EVENT (TransportFSM::locate_done());
|
TFSM_EVENT (TransportFSM::LocateDone);
|
||||||
Located (); /* EMIT SIGNAL */
|
Located (); /* EMIT SIGNAL */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -382,9 +384,9 @@ Session::do_locate (samplepos_t target_sample, bool with_roll, bool with_flush,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (need_butler) {
|
if (need_butler) {
|
||||||
TFSM_EVENT (TransportFSM::butler_required());
|
TFSM_EVENT (TransportFSM::ButlerRequired);
|
||||||
} else {
|
} else {
|
||||||
TFSM_EVENT (TransportFSM::locate_done());
|
TFSM_EVENT (TransportFSM::LocateDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
loop_changing = false;
|
loop_changing = false;
|
||||||
|
|
@ -484,7 +486,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
|
||||||
_requested_return_sample = destination_sample;
|
_requested_return_sample = destination_sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
TFSM_EVENT (TransportFSM::stop_transport (abort, false));
|
TFSM_STOP (abort, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (transport_stopped() && speed == 1.0) {
|
} 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 ();
|
_engine.transport_start ();
|
||||||
_count_in_once = false;
|
_count_in_once = false;
|
||||||
} else {
|
} else {
|
||||||
TFSM_EVENT (TransportFSM::start_transport());
|
TFSM_EVENT (TransportFSM::StartTransport);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -576,7 +578,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
|
||||||
|
|
||||||
if (todo) {
|
if (todo) {
|
||||||
add_post_transport_work (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));
|
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) {
|
if (ptw & PostTransportLocate) {
|
||||||
post_locate ();
|
post_locate ();
|
||||||
TFSM_EVENT (TransportFSM::locate_done());
|
TFSM_EVENT (TransportFSM::LocateDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptw & PostTransportAdjustPlaybackBuffering) {
|
if (ptw & PostTransportAdjustPlaybackBuffering) {
|
||||||
|
|
@ -763,7 +765,7 @@ Session::butler_completed_transport_work ()
|
||||||
set_post_transport_work (PostTransportWork (0));
|
set_post_transport_work (PostTransportWork (0));
|
||||||
|
|
||||||
if (_transport_fsm->waiting_for_butler()) {
|
if (_transport_fsm->waiting_for_butler()) {
|
||||||
TFSM_EVENT (TransportFSM::butler_done());
|
TFSM_EVENT (TransportFSM::ButlerDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
DiskReader::dec_no_disk_output ();
|
DiskReader::dec_no_disk_output ();
|
||||||
|
|
@ -785,7 +787,7 @@ Session::maybe_stop (samplepos_t limit)
|
||||||
if (synced_to_engine () && config.get_jack_time_master ()) {
|
if (synced_to_engine () && config.get_jack_time_master ()) {
|
||||||
_engine.transport_stop ();
|
_engine.transport_stop ();
|
||||||
} else if (!synced_to_engine ()) {
|
} else if (!synced_to_engine ()) {
|
||||||
TFSM_EVENT (TransportFSM::stop_transport ());
|
TFSM_EVENT (TransportFSM::StopTransport);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -894,11 +896,11 @@ Session::set_play_loop (bool yn, double speed)
|
||||||
rolling, do not locate to loop start.
|
rolling, do not locate to loop start.
|
||||||
*/
|
*/
|
||||||
if (!transport_rolling() && (speed != 0.0)) {
|
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 {
|
} else {
|
||||||
if (speed != 0.0) {
|
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()) {
|
if (Config->get_seamless_loop()) {
|
||||||
/* likely need to flush track buffers: this will locate us to wherever we are */
|
/* likely need to flush track buffers: this will locate us to wherever we are */
|
||||||
add_post_transport_work (PostTransportLocate);
|
add_post_transport_work (PostTransportLocate);
|
||||||
TFSM_EVENT (TransportFSM::butler_required());
|
TFSM_EVENT (TransportFSM::ButlerRequired);
|
||||||
}
|
}
|
||||||
TransportStateChange (); /* EMIT SIGNAL */
|
TransportStateChange (); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
|
|
@ -1936,7 +1938,7 @@ Session::engine_halted ()
|
||||||
* ::engine_running() (if we ever get there)
|
* ::engine_running() (if we ever get there)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
_transport_fsm->backend()->stop ();
|
_transport_fsm->stop ();
|
||||||
|
|
||||||
/* Synchronously do the realtime part of a transport stop.
|
/* Synchronously do the realtime part of a transport stop.
|
||||||
*
|
*
|
||||||
|
|
@ -1952,7 +1954,7 @@ void
|
||||||
Session::engine_running ()
|
Session::engine_running ()
|
||||||
{
|
{
|
||||||
initialize_latencies ();
|
initialize_latencies ();
|
||||||
_transport_fsm->backend()->start ();
|
_transport_fsm->start ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2019 Robin Gareus <robin@gareus.org>
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* 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.
|
* 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/debug.h"
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
#include "ardour/transport_fsm.h"
|
#include "ardour/transport_fsm.h"
|
||||||
|
|
@ -23,69 +29,327 @@
|
||||||
using namespace ARDOUR;
|
using namespace ARDOUR;
|
||||||
using namespace PBD;
|
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 */
|
/* transition actions */
|
||||||
|
|
||||||
void
|
void
|
||||||
TransportFSM::start_playback (TransportFSM::start_transport const& p)
|
TransportFSM::start_playback ()
|
||||||
{
|
{
|
||||||
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_playback\n");
|
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_playback\n");
|
||||||
api->start_transport();
|
api->start_transport();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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");
|
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_declick\n");
|
||||||
_last_stop = s;
|
_last_stop = *s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TransportFSM::stop_playback (TransportFSM::declick_done const& /*ignored*/)
|
TransportFSM::stop_playback ()
|
||||||
{
|
{
|
||||||
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::stop_playback\n");
|
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::stop_playback\n");
|
||||||
api->stop_transport (_last_stop.abort, _last_stop.clear_state);
|
api->stop_transport (_last_stop.abort, _last_stop.clear_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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");
|
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::save_locate_and_stop\n");
|
||||||
_last_locate = l;
|
_last_locate = *l;
|
||||||
start_declick (stop_transport (false, false));
|
_last_stop = FSMEvent (StopTransport, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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");
|
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
|
void
|
||||||
TransportFSM::start_saved_locate (TransportFSM::declick_done const&)
|
TransportFSM::start_saved_locate ()
|
||||||
{
|
{
|
||||||
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_save\n");
|
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);
|
api->locate (_last_locate.target, _last_locate.with_roll, _last_locate.with_flush, _last_locate.with_loop, _last_locate.force);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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");
|
DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::interrupt\n");
|
||||||
/* maintain original "with-roll" choice of initial locate, even though
|
/* maintain original "with-roll" choice of initial locate, even though
|
||||||
* we are interrupting the locate to start a new one.
|
* 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
|
void
|
||||||
TransportFSM::schedule_butler_for_transport_work (TransportFSM::butler_required const&)
|
TransportFSM::schedule_butler_for_transport_work ()
|
||||||
{
|
{
|
||||||
api->schedule_butler_for_transport_work ();
|
api->schedule_butler_for_transport_work ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TransportFSM::should_roll_after_locate (TransportFSM::locate_done const &)
|
TransportFSM::should_roll_after_locate ()
|
||||||
{
|
{
|
||||||
bool ret = api->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));
|
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
|
void
|
||||||
TransportFSM::roll_after_locate (TransportFSM::locate_done const &)
|
TransportFSM::roll_after_locate ()
|
||||||
{
|
{
|
||||||
DEBUG_TRACE (DEBUG::TFSMEvents, "rolling after locate\n");
|
DEBUG_TRACE (DEBUG::TFSMEvents, "rolling after locate\n");
|
||||||
api->start_transport ();
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue