if a complete refill is called for, DiskReader cannot internal seek
[ardour.git] / libs / ardour / transport_fsm.cc
1 /*
2  * Copyright (C) 2019 Robin Gareus <robin@gareus.org>
3  * Copyright (C) 2019 Paul Davis <paul@linuxaudiosystems.com>
4  *
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.
9  *
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.
14  *
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.
18  */
19
20 #include <sstream>
21
22 #include <boost/none.hpp>
23
24 #include "pbd/error.h"
25 #include "pbd/i18n.h"
26 #include "pbd/pthread_utils.h"
27 #include "pbd/stacktrace.h"
28
29 #include "ardour/debug.h"
30 #include "ardour/session.h"
31 #include "ardour/transport_fsm.h"
32
33 using namespace ARDOUR;
34 using namespace PBD;
35
36 Pool* TransportFSM::Event::pool = 0;
37
38 void
39 TransportFSM::Event::init_pool ()
40 {
41         pool = new Pool (X_("Events"), sizeof (Event), 128);
42 }
43
44 void*
45 TransportFSM::Event::operator new (size_t)
46 {
47         return pool->alloc();
48  }
49
50 void
51 TransportFSM::Event::operator delete (void *ptr, size_t /*size*/)
52 {
53         return pool->release (ptr);
54 }
55
56 TransportFSM::TransportFSM (TransportAPI& tapi)
57         : _last_locate (Locate)
58         , _last_stop (StopTransport)
59         , api (&tapi)
60         , processing (0)
61 {
62         init ();
63 }
64
65 void
66 TransportFSM::init ()
67 {
68         _motion_state = Stopped;
69         _butler_state = NotWaitingForButler;
70         _last_locate.target = max_samplepos;
71 }
72
73 void
74 TransportFSM::process_events ()
75 {
76         processing++;
77
78         while (!queued_events.empty()) {
79
80                 MotionState oms = _motion_state;
81                 ButlerState obs = _butler_state;
82
83                 Event* ev = &queued_events.front();
84                 bool deferred;
85
86                 /* must remove from the queued_events list now, because
87                  * process_event() may defer the event. This will lead to
88                  * insertion into the deferred_events list, and its not possible
89                  * with intrusive lists to be present in two lists at once
90                  * (without additional hooks).
91                  */
92
93                 queued_events.pop_front ();
94
95                 if (process_event (*ev, false, deferred)) { /* event processed successfully */
96
97                         if (oms != _motion_state || obs != _butler_state) {
98
99                                 /* state changed, so now check deferred events
100                                  * to see if they can be processed now
101                                  */
102
103                                 if (!deferred_events.empty() ){
104                                         DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("processing %1 deferred events\n", deferred_events.size()));
105
106                                         for (EventList::iterator e = deferred_events.begin(); e != deferred_events.end(); ) {
107                                                 Event* deferred_ev = &(*e);
108                                                 bool deferred2;
109                                                 if (process_event (*e, true, deferred2)) { /* event processed, remove from deferred */
110                                                         e = deferred_events.erase (e);
111                                                         delete deferred_ev;
112                                                 } else {
113                                                         ++e;
114                                                 }
115                                         }
116                                 }
117                         }
118                 }
119
120                 if (!deferred) {
121                         delete ev;
122                 }
123         }
124
125         processing--;
126 }
127
128 /* This is the transition table from the original boost::msm
129  * implementation of this FSM. It is more easily readable and
130  * consultable. Please keep it updated as the FSM changes.
131  *
132  * Here's a hint about how to read each line of this table:
133  *
134  * "if the current state is Start and event Event arrives, new state is Next and we execute Action()"
135  *
136  * with a variant:
137  *
138  * "if the current state is Start and event Event arrives, new state is Next and we execute Action() ***IF*** Guard() returns true"
139  *
140  * This new implementation, however, does not use metaprogramming to achieve all this,
141  * but just uses a large-ish switch() block.
142  *
143  */
144
145 /*
146         Start                Event            Next               Action                Guard
147       +----------------------+----------------+------------------+---------------------+---------------------------------+
148 a_row < Stopped,             start_transport, Rolling,           &T::start_playback                                      >,
149 _row  < Stopped,             stop_transport,  Stopped                                                                    >,
150 a_row < Stopped,             locate,          WaitingForLocate,  &T::start_locate_while_stopped                          >,
151 g_row < WaitingForLocate,    locate_done,     Stopped,                                  &T::should_not_roll_after_locate >,
152 _row  < Rolling,             butler_done,     Rolling                                                                    >,
153 _row  < Rolling,             start_transport, Rolling                                                                    >,
154 a_row < Rolling,             stop_transport,  DeclickToStop,     &T::start_declick_for_stop                              >,
155 a_row < DeclickToStop,       declick_done,    Stopped,           &T::stop_playback                                       >,
156 a_row < Rolling,             locate,          DeclickToLocate,   &T::start_declick_for_locate                            >,
157 a_row < DeclickToLocate,     declick_done,    WaitingForLocate,  &T::start_locate_after_declick                          >,
158 row   < WaitingForLocate,    locate_done,     Rolling,           &T::roll_after_locate, &T::should_roll_after_locate     >,
159 a_row < NotWaitingForButler, butler_required, WaitingForButler,  &T::schedule_butler_for_transport_work                  >,
160 a_row < WaitingForButler,    butler_required, WaitingForButler,  &T::schedule_butler_for_transport_work                  >,
161 _row  < WaitingForButler,    butler_done,     NotWaitingForButler                                                        >,
162 a_row < WaitingForLocate,    locate,          WaitingForLocate,  &T::interrupt_locate                                    >,
163 a_row < DeclickToLocate,     locate,          DeclickToLocate,   &T::interrupt_locate                                    >,
164
165 // Deferrals
166
167 #define defer(start_state,ev) boost::msm::front::Row<start_state, ev, start_state, boost::msm::front::Defer, boost::msm::front::none >
168
169 defer (DeclickToLocate, start_transport),
170 defer (DeclickToLocate, stop_transport),
171 defer (DeclickToStop, start_transport),
172 defer (WaitingForLocate, start_transport),
173 defer (WaitingForLocate, stop_transport)
174
175 #undef defer
176 */
177
178 std::string
179 TransportFSM::current_state () const
180 {
181         std::stringstream s;
182         s << enum_2_string (_motion_state) << '/' << enum_2_string (_butler_state);
183         return s.str();
184 }
185
186 void
187 TransportFSM::bad_transition (Event const & ev)
188 {
189         error << "bad transition, current state = " << current_state() << " event = " << enum_2_string (ev.type) << endmsg;
190         std::cerr << "bad transition, current state = " << current_state() << " event = " << enum_2_string (ev.type) << std::endl;
191 }
192
193 bool
194 TransportFSM::process_event (Event& ev, bool already_deferred, bool& deferred)
195 {
196         DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("process %1\n", enum_2_string (ev.type)));
197
198         deferred = false;
199
200         switch (ev.type) {
201
202         case StartTransport:
203                 switch (_motion_state) {
204                 case Stopped:
205                         transition (Rolling);
206                         start_playback ();
207                         break;
208                 case Rolling:
209                         break;
210                 case DeclickToLocate:
211                 case WaitingForLocate:
212                         if (!already_deferred) {
213                                 defer (ev);
214                                 deferred = true;
215                         }
216                         break;
217                 case DeclickToStop:
218                         if (!already_deferred) {
219                                 defer (ev);
220                                 deferred = true;
221                         }
222                         break;
223                 default:
224                         bad_transition (ev); return false;
225                         break;
226                 }
227                 break;
228
229         case StopTransport:
230                 switch (_motion_state) {
231                 case Rolling:
232                         transition (DeclickToStop);
233                         start_declick_for_stop (ev);
234                         break;
235                 case Stopped:
236                         break;
237                 case DeclickToLocate:
238                 case WaitingForLocate:
239                         if (!already_deferred) {
240                                 defer (ev);
241                                 deferred = true;
242                         }
243                         break;
244                 default:
245                         bad_transition (ev); return false;
246                         break;
247                 }
248                 break;
249
250         case Locate:
251                 DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("locate, with roll = %1 flush = %2 target = %3 loop %4 force %5\n",
252                                                                 ev.with_roll,
253                                                                 ev.with_flush,
254                                                                 ev.target,
255                                                                 ev.with_loop,
256                                                                 ev.force));
257                 switch (_motion_state) {
258                 case Stopped:
259                         transition (WaitingForLocate);
260                         start_locate_while_stopped (ev);
261                         break;
262                 case Rolling:
263                         if (ev.with_loop) {
264                                 /* no state transitions. Just do a realtime
265                                    locate and continue rolling. Note that
266                                    ev.with_roll is ignored and assumed to be
267                                    true because we're looping.
268                                 */
269                                 locate_for_loop (ev);
270                         } else {
271                                 transition (DeclickToLocate);
272                                 start_declick_for_locate (ev);
273                         }
274                         break;
275                 case WaitingForLocate:
276                 case DeclickToLocate:
277                         interrupt_locate (ev);
278                         break;
279                 default:
280                         bad_transition (ev); return false;
281                 }
282                 break;
283
284         case LocateDone:
285                 switch (_motion_state) {
286                 case WaitingForLocate:
287                         if (should_not_roll_after_locate()) {
288                                 transition (Stopped);
289                                 /* already stopped, nothing to do */
290                         } else {
291                                 transition (Rolling);
292                                 roll_after_locate ();
293                         }
294                         break;
295                 default:
296                         bad_transition (ev); return false;
297                 }
298                 break;
299
300         case DeclickDone:
301                 switch (_motion_state) {
302                 case DeclickToLocate:
303                         transition (WaitingForLocate);
304                         start_locate_after_declick ();
305                         break;
306                 case DeclickToStop:
307                         transition (Stopped);
308                         stop_playback ();
309                         break;
310                 default:
311                         bad_transition (ev); return false;
312                 }
313                 break;
314
315         case ButlerRequired:
316                 switch (_butler_state) {
317                 case NotWaitingForButler:
318                         transition (WaitingForButler);
319                         schedule_butler_for_transport_work ();
320                         break;
321                 case WaitingForButler:
322                         schedule_butler_for_transport_work ();
323                         break;
324                 default:
325                         bad_transition (ev); return false;
326                 }
327                 break;
328
329         case ButlerDone:
330                 switch (_butler_state) {
331                 case WaitingForButler:
332                         transition (NotWaitingForButler);
333                         break;
334                 default:
335                         bad_transition (ev); return false;
336                 }
337                 break;
338         }
339
340         return true;
341 }
342
343 /* transition actions */
344
345 void
346 TransportFSM::start_playback ()
347 {
348         DEBUG_TRACE (DEBUG::TFSMEvents, "start_playback\n");
349
350         _last_locate.target = max_samplepos;
351         current_roll_after_locate_status = boost::none;
352
353         api->start_transport();
354 }
355
356 void
357 TransportFSM::stop_playback ()
358 {
359         DEBUG_TRACE (DEBUG::TFSMEvents, "stop_playback\n");
360
361         _last_locate.target = max_samplepos;
362         current_roll_after_locate_status = boost::none;
363
364         api->stop_transport (_last_stop.abort, _last_stop.clear_state);
365 }
366
367 void
368 TransportFSM::start_declick_for_stop (Event const & s)
369 {
370         assert (s.type == StopTransport);
371         DEBUG_TRACE (DEBUG::TFSMEvents, "start_declick_for_stop\n");
372         _last_stop = s;
373 }
374
375 void
376 TransportFSM::start_declick_for_locate (Event const & l)
377 {
378         assert (l.type == Locate);
379         DEBUG_TRACE (DEBUG::TFSMEvents, "start_declick_for_locate\n");
380         _last_locate = l;
381
382         if (!current_roll_after_locate_status) {
383                 if (l.with_roll) {
384                         if (api->speed() != 0.) {
385                                 current_roll_after_locate_status = true;
386                         } else {
387                                 current_roll_after_locate_status = api->should_roll_after_locate();
388                         }
389                 } else {
390                         current_roll_after_locate_status = (api->speed() != 0.);
391                 }
392         }
393
394         _last_stop = Event (StopTransport, false, false);
395 }
396
397 void
398 TransportFSM::start_locate_while_stopped (Event const & l) const
399 {
400         assert (l.type == Locate);
401         DEBUG_TRACE (DEBUG::TFSMEvents, "start_locate_while_stopped\n");
402
403         current_roll_after_locate_status = api->should_roll_after_locate();
404         api->locate (l.target, current_roll_after_locate_status.get(), l.with_flush, l.with_loop, l.force);
405 }
406
407 void
408 TransportFSM::locate_for_loop (Event const & l)
409 {
410         assert (l.type == Locate);
411         DEBUG_TRACE (DEBUG::TFSMEvents, "locate_for_loop\n");
412         current_roll_after_locate_status = l.with_roll;
413         api->locate (l.target, l.with_roll, l.with_flush, l.with_loop, l.force);
414 }
415
416 void
417 TransportFSM::start_locate_after_declick () const
418 {
419         DEBUG_TRACE (DEBUG::TFSMEvents, "start_locate_after_declick\n");
420         const bool roll = current_roll_after_locate_status ? current_roll_after_locate_status.get() : _last_locate.with_roll;
421         api->locate (_last_locate.target, roll, _last_locate.with_flush, _last_locate.with_loop, _last_locate.force);
422 }
423
424 void
425 TransportFSM::interrupt_locate (Event const & l) const
426 {
427         assert (l.type == Locate);
428         DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("interrupt to %1 versus %2\n", l.target, _last_locate.target));
429
430         /* Because of snapping (e.g. of mouse position) we could be
431          * interrupting an existing locate to the same position. If we go ahead
432          * with this, the code in Session::do_locate() will notice that it's a
433          * repeat position, will do nothing, will queue a "locate_done" event
434          * that will arrive in the next process cycle. But this event may be
435          * processed before the original (real) locate has completed in the
436          * butler thread, and processing it may transition us back to Rolling
437          * before some (or even all) tracks are actually ready.
438          *
439          * So, we must avoid this from happening, and this seems like the
440          * simplest way.
441          */
442
443         if (l.target == _last_locate.target && !l.force) {
444                 return;
445         }
446         /* maintain original "with-roll" choice of initial locate, even though
447          * we are interrupting the locate to start a new one.
448          */
449         api->locate (l.target, false, l.with_flush, l.with_loop, l.force);
450 }
451
452 void
453 TransportFSM::schedule_butler_for_transport_work () const
454 {
455         api->schedule_butler_for_transport_work ();
456 }
457
458 bool
459 TransportFSM::should_roll_after_locate () const
460 {
461         bool roll;
462
463         if (current_roll_after_locate_status) {
464                 roll = current_roll_after_locate_status.get();
465         } else {
466                 roll = api->should_roll_after_locate ();
467         }
468
469         DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("should_roll_after_locate() ? %1\n", roll));
470         return roll;
471 }
472
473 void
474 TransportFSM::roll_after_locate () const
475 {
476         DEBUG_TRACE (DEBUG::TFSMEvents, "rolling after locate\n");
477         current_roll_after_locate_status = boost::none;
478         api->start_transport ();
479 }
480
481 void
482 TransportFSM::defer (Event& ev)
483 {
484         DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("Defer %1 during %2\n", enum_2_string (ev.type), current_state()));
485         deferred_events.push_back (ev);
486 }
487
488 void
489 TransportFSM::transition (MotionState ms)
490 {
491         const MotionState old = _motion_state;
492         _motion_state = ms;
493         DEBUG_TRACE (DEBUG::TFSMState, string_compose ("Leave %1, enter %2\n", enum_2_string (old), current_state()));
494 }
495
496 void
497 TransportFSM::transition (ButlerState bs)
498 {
499         const ButlerState old = _butler_state;
500         _butler_state = bs;
501         DEBUG_TRACE (DEBUG::TFSMState, string_compose ("Leave %1, enter %2\n", enum_2_string (old), current_state()));
502 }
503
504 void
505 TransportFSM::enqueue (Event* ev)
506 {
507         DEBUG_TRACE (DEBUG::TFSMState, string_compose ("queue tfsm event %1\n", enum_2_string (ev->type)));
508         queued_events.push_back (*ev);
509         if (!processing) {
510                 process_events ();
511         }
512 }