diff options
-rw-r--r-- | libs/ardour/ardour/session.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/transport_fsm.h | 292 | ||||
-rw-r--r-- | libs/ardour/enums.cc | 25 | ||||
-rw-r--r-- | libs/ardour/globals.cc | 2 | ||||
-rw-r--r-- | libs/ardour/session.cc | 7 | ||||
-rw-r--r-- | libs/ardour/session_process.cc | 27 | ||||
-rw-r--r-- | libs/ardour/session_time.cc | 4 | ||||
-rw-r--r-- | libs/ardour/session_transport.cc | 34 | ||||
-rw-r--r-- | libs/ardour/transport_fsm.cc | 314 |
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; +} |