7f6728da07b780ee9ad7b582dcd0b27885b6dc0f
[ardour.git] / libs / ardour / ardour / transport_fsm.h
1 #ifndef _ardour_transport_fsm_h_
2 #define _ardour_transport_fsm_h_
3
4 #include <list>
5 #include <queue>
6
7 #include <boost/intrusive/list.hpp>
8 #include <boost/optional.hpp>
9
10 #include <string>
11 #include <utility>
12 #include <iostream>
13
14 #include "pbd/demangle.h"
15 #include "pbd/stacktrace.h"
16
17 #include "ardour/debug.h"
18 #include "ardour/types.h"
19
20 namespace ARDOUR
21 {
22
23 class TransportAPI;
24
25 struct TransportFSM
26 {
27         /* All code related to this object is expected to be run synchronously
28          * and single-threaded from the process callback. It can be re-entrant
29          * if handling one transport state change queues another state change,
30          * but that is handled explicitly (see the @param processing member and
31          * its usage).
32          */
33
34   public:
35         enum EventType {
36                 ButlerDone,
37                 ButlerRequired,
38                 DeclickDone,
39                 StartTransport,
40                 StopTransport,
41                 Locate,
42                 LocateDone
43         };
44
45         struct Event : public boost::intrusive::list_base_hook<> {
46                 EventType type;
47                 /* for stop */
48                 bool abort;
49                 bool clear_state;
50                 /* for locate */
51                 LocateTransportDisposition ltd;
52                 bool with_flush;
53                 samplepos_t target;
54                 bool for_loop_end;
55                 bool force;
56
57                 Event (EventType t)
58                         : type (t)
59                         , abort (false)
60                         , clear_state (false)
61                         , ltd (MustStop)
62                         , with_flush (false)
63                         , target (0)
64                         , for_loop_end (false)
65                         , force (false)
66                 {
67                         assert (t != StopTransport);
68                         assert (t != Locate);
69                 }
70                 Event (EventType t, bool ab, bool cl)
71                         : type (t)
72                         , abort (ab)
73                         , clear_state (cl)
74                         , ltd (MustStop)
75                         , with_flush (false)
76                         , target (0)
77                         , for_loop_end (false)
78                         , force (false)
79                 {
80                         assert (t == StopTransport);
81                 }
82                 Event (EventType t, samplepos_t pos, LocateTransportDisposition l, bool fl, bool lp, bool f4c)
83                         : type (t)
84                         , abort (false)
85                         , clear_state (false)
86                         , ltd (l)
87                         , with_flush (fl)
88                         , target (pos)
89                         , for_loop_end (lp)
90                         , force (f4c)
91                 {
92                         assert (t == Locate);
93                 }
94
95                 void* operator new (size_t);
96                 void  operator delete (void *ptr, size_t /*size*/);
97
98                 static void init_pool ();
99
100           private:
101                 static Pool* pool;
102
103         };
104
105         TransportFSM (TransportAPI& tapi);
106
107         void start () {
108                 init ();
109         }
110
111         void stop () {
112                 /* should we do anything here? this method is modelled on the
113                    boost::msm design, but its not clear that we ever need to
114                    do anything like this.
115                 */
116         }
117
118         enum MotionState {
119                 Stopped,
120                 Rolling,
121                 DeclickToStop,
122                 DeclickToLocate,
123                 WaitingForLocate
124         };
125
126         enum ButlerState {
127                 NotWaitingForButler,
128                 WaitingForButler
129         };
130
131         std::string current_state () const;
132
133   private:
134         MotionState _motion_state;
135         ButlerState _butler_state;
136
137         void init();
138
139         /* transition actions */
140
141         void schedule_butler_for_transport_work () const;
142         void start_playback ();
143         void stop_playback (Event const &);
144         void start_locate_after_declick () const;
145         void locate_for_loop (Event const &);
146         void roll_after_locate () const;
147         void start_locate_while_stopped (Event const &) const;
148         void interrupt_locate (Event const &) const;
149         void start_declick_for_locate (Event const &);
150
151         /* guards */
152
153         bool should_roll_after_locate () const;
154         bool should_not_roll_after_locate ()  const { return !should_roll_after_locate (); }
155
156   public:
157         bool locating () const           { return _motion_state == WaitingForLocate; }
158         bool rolling () const            { return _motion_state == Rolling; }
159         bool stopped () const            { return _motion_state == Stopped; }
160         bool stopping () const           { return _motion_state == DeclickToStop; }
161         bool waiting_for_butler() const  { return _butler_state == WaitingForButler; }
162         bool declick_in_progress() const { return _motion_state == DeclickToLocate || _motion_state == DeclickToStop; }
163
164         void enqueue (Event* ev);
165
166   private:
167
168         void transition (MotionState ms);
169         void transition (ButlerState bs);
170
171         void process_events ();
172         bool process_event (Event&, bool was_deferred, bool& deferred);
173
174         Event _last_locate;
175
176         TransportAPI* api;
177         typedef boost::intrusive::list<Event> EventList;
178         EventList queued_events;
179         EventList deferred_events;
180         int processing;
181         mutable boost::optional<bool> current_roll_after_locate_status;
182
183         void defer (Event& ev);
184         void bad_transition (Event const &);
185         void set_roll_after (bool) const;
186         bool compute_should_roll (LocateTransportDisposition) const;
187 };
188
189 } /* end namespace ARDOUR */
190
191 #endif