2 * Copyright (C) 2019 Robin Gareus <robin@gareus.org>
3 * Copyright (C) 2019 Paul Davis <paul@linuxaudiosystems.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "pbd/error.h"
25 #include "ardour/debug.h"
26 #include "ardour/session.h"
27 #include "ardour/transport_fsm.h"
29 using namespace ARDOUR;
32 Pool* TransportFSM::Event::pool = 0;
35 TransportFSM::Event::init_pool ()
37 pool = new Pool (X_("Events"), sizeof (Event), 128);
41 TransportFSM::Event::operator new (size_t)
47 TransportFSM::Event::operator delete (void *ptr, size_t /*size*/)
49 return pool->release (ptr);
52 TransportFSM::TransportFSM (TransportAPI& tapi)
53 : _last_locate (Locate)
54 , _last_stop (StopTransport)
64 _motion_state = Stopped;
65 _butler_state = NotWaitingForButler;
69 TransportFSM::process_events ()
73 while (!queued_events.empty()) {
75 MotionState oms = _motion_state;
76 ButlerState obs = _butler_state;
78 if (process_event (queued_events.front())) { /* event processed successfully */
80 if (oms != _motion_state || obs != _butler_state) {
82 /* state changed, so now check deferred events
83 * to see if they can be processed now
86 if (!deferred_events.empty() ){
87 DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("processing %1 deferred events\n", deferred_events.size()));
89 for (EventList::iterator e = deferred_events.begin(); e != deferred_events.end(); ) {
90 Event* deferred_ev = &(*e);
91 if (process_event (*e)) { /* event processed, remove from deferred */
92 e = deferred_events.erase (e);
102 Event* ev = &queued_events.front();
103 queued_events.pop_front ();
110 /* This is the transition table from the original boost::msm
111 * implementation of this FSM. It is more easily readable and
112 * consultable. Please keep it updated as the FSM changes.
114 * Here's a hint about how to read each line of this table:
116 * "if the current state is Start and event Event arrives, new state is Next and we execute Action()"
120 * "if the current state is Start and event Event arrives, new state is Next and we execute Action() ***IF*** Guard() returns true"
122 * This new implementation, however, does not use metaprogramming to achieve all this,
123 * but just uses a large-ish switch() block.
128 Start Event Next Action Guard
129 +----------------------+----------------+------------------+---------------------+---------------------------------+
130 a_row < Stopped, start_transport, Rolling, &T::start_playback >,
131 _row < Stopped, stop_transport, Stopped >,
132 a_row < Stopped, locate, WaitingForLocate, &T::start_locate >,
133 g_row < WaitingForLocate, locate_done, Stopped, &T::should_not_roll_after_locate >,
134 _row < Rolling, butler_done, Rolling >,
135 _row < Rolling, start_transport, Rolling >,
136 a_row < Rolling, stop_transport, DeclickToStop, &T::start_declick >,
137 a_row < DeclickToStop, declick_done, Stopped, &T::stop_playback >,
138 a_row < Rolling, locate, DeclickToLocate, &T::save_locate_and_start_declick >,
139 a_row < DeclickToLocate, declick_done, WaitingForLocate, &T::start_saved_locate >,
140 row < WaitingForLocate, locate_done, Rolling, &T::roll_after_locate, &T::should_roll_after_locate >,
141 a_row < NotWaitingForButler, butler_required, WaitingForButler, &T::schedule_butler_for_transport_work >,
142 a_row < WaitingForButler, butler_required, WaitingForButler, &T::schedule_butler_for_transport_work >,
143 _row < WaitingForButler, butler_done, NotWaitingForButler >,
144 a_row < WaitingForLocate, locate, WaitingForLocate, &T::interrupt_locate >,
145 a_row < DeclickToLocate, locate, DeclickToLocate, &T::interrupt_locate >,
149 #define defer(start_state,ev) boost::msm::front::Row<start_state, ev, start_state, boost::msm::front::Defer, boost::msm::front::none >
151 defer (DeclickToLocate, start_transport),
152 defer (DeclickToLocate, stop_transport),
153 defer (DeclickToStop, start_transport),
154 defer (WaitingForLocate, start_transport),
155 defer (WaitingForLocate, stop_transport)
161 TransportFSM::current_state () const
164 s << enum_2_string (_motion_state) << '/' << enum_2_string (_butler_state);
169 TransportFSM::bad_transition (Event const & ev)
171 error << "bad transition, current state = " << current_state() << " event = " << enum_2_string (ev.type) << endmsg;
172 std::cerr << "bad transition, current state = " << current_state() << " event = " << enum_2_string (ev.type) << std::endl;
176 TransportFSM::process_event (Event& ev)
178 DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("process %1\n", enum_2_string (ev.type)));
183 switch (_motion_state) {
185 transition (Rolling);
190 case DeclickToLocate:
191 case WaitingForLocate:
198 bad_transition (ev); return false;
204 switch (_motion_state) {
206 transition (DeclickToStop);
211 case DeclickToLocate:
212 case WaitingForLocate:
216 bad_transition (ev); return false;
222 switch (_motion_state) {
224 transition (WaitingForLocate);
228 transition (DeclickToLocate);
229 save_locate_and_start_declick (ev);
231 case WaitingForLocate:
232 case DeclickToLocate:
233 interrupt_locate (ev);
236 bad_transition (ev); return false;
241 switch (_motion_state) {
242 case WaitingForLocate:
243 if (should_not_roll_after_locate()) {
244 transition (Stopped);
246 transition (Rolling);
247 roll_after_locate ();
251 bad_transition (ev); return false;
256 switch (_motion_state) {
257 case DeclickToLocate:
258 transition (WaitingForLocate);
259 start_saved_locate ();
262 transition (Stopped);
266 bad_transition (ev); return false;
271 switch (_butler_state) {
272 case NotWaitingForButler:
273 transition (WaitingForButler);
274 schedule_butler_for_transport_work ();
276 case WaitingForButler:
277 schedule_butler_for_transport_work ();
280 bad_transition (ev); return false;
285 switch (_butler_state) {
286 case WaitingForButler:
287 transition (NotWaitingForButler);
290 bad_transition (ev); return false;
298 /* transition actions */
301 TransportFSM::start_playback ()
303 DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_playback\n");
304 api->start_transport();
308 TransportFSM::start_declick (Event const & s)
310 assert (s.type == StopTransport);
311 DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_declick\n");
316 TransportFSM::stop_playback ()
318 DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::stop_playback\n");
319 api->stop_transport (_last_stop.abort, _last_stop.clear_state);
323 TransportFSM::save_locate_and_start_declick (Event const & l)
325 assert (l.type == Locate);
326 DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::save_locate_and_stop\n");
328 _last_stop = Event (StopTransport, false, false);
332 TransportFSM::start_locate (Event const & l)
334 assert (l.type == Locate);
335 DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_locate\n");
336 api->locate (l.target, l.with_roll, l.with_flush, l.with_loop, l.force);
340 TransportFSM::start_saved_locate ()
342 DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_save\n");
343 api->locate (_last_locate.target, _last_locate.with_roll, _last_locate.with_flush, _last_locate.with_loop, _last_locate.force);
347 TransportFSM::interrupt_locate (Event const & l)
349 assert (l.type == Locate);
350 DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::interrupt\n");
351 /* maintain original "with-roll" choice of initial locate, even though
352 * we are interrupting the locate to start a new one.
354 api->locate (l.target, _last_locate.with_roll, l.with_flush, l.with_loop, l.force);
358 TransportFSM::schedule_butler_for_transport_work ()
360 api->schedule_butler_for_transport_work ();
364 TransportFSM::should_roll_after_locate ()
366 bool ret = api->should_roll_after_locate ();
367 DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("tfsm::should_roll_after_locate() ? %1\n", ret));
372 TransportFSM::roll_after_locate ()
374 DEBUG_TRACE (DEBUG::TFSMEvents, "rolling after locate\n");
375 api->start_transport ();
379 TransportFSM::defer (Event& ev)
381 DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("Defer %1 during %2\n", enum_2_string (ev.type), current_state()));
382 deferred_events.push_back (ev);
386 TransportFSM::transition (MotionState ms)
388 DEBUG_TRACE (DEBUG::TFSMState, string_compose ("Leave %1, enter %2\n", enum_2_string (_motion_state), enum_2_string (ms)));
393 TransportFSM::transition (ButlerState bs)
395 DEBUG_TRACE (DEBUG::TFSMState, string_compose ("Leave %1, enter %2\n", enum_2_string (_butler_state), enum_2_string (bs)));