diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2019-09-19 22:33:43 -0600 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2019-09-19 22:34:18 -0600 |
commit | 61afcb8e2bb7cfa8b1fb8bd2f56c4700679b79c7 (patch) | |
tree | 3ad55dbde8119f98f520ef80a6d3eee2e320cab6 /libs/ardour/transport_fsm.cc | |
parent | e698a1b2faf6758ffcb83dea2dfbeabad23275bd (diff) |
replace boost::msm - based FSM for transport with one written in "plain C++"
Still need to use boost::intrusive to managed qeued/deferred containers
Diffstat (limited to 'libs/ardour/transport_fsm.cc')
-rw-r--r-- | libs/ardour/transport_fsm.cc | 314 |
1 files changed, 299 insertions, 15 deletions
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; +} |