summaryrefslogtreecommitdiff
path: root/libs/ardour/transport_fsm.cc
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2019-09-19 22:33:43 -0600
committerPaul Davis <paul@linuxaudiosystems.com>2019-09-19 22:34:18 -0600
commit61afcb8e2bb7cfa8b1fb8bd2f56c4700679b79c7 (patch)
tree3ad55dbde8119f98f520ef80a6d3eee2e320cab6 /libs/ardour/transport_fsm.cc
parente698a1b2faf6758ffcb83dea2dfbeabad23275bd (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.cc314
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;
+}