86133e77c5605a4f5fda6b1f3f2937402dc77e9a
[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                 union {
48                         bool abort; /* for stop */
49                         LocateTransportDisposition ltd; /* for locate */
50                 };
51                 union {
52                         bool clear_state; /* for stop */
53                         bool with_flush; /* for locate */
54                 };
55                 /* for locate */
56                 samplepos_t target;
57                 bool for_loop_end;
58                 bool force;
59
60                 Event (EventType t)
61                         : type (t)
62                         , ltd (MustStop)
63                         , with_flush (false)
64                         , target (0)
65                         , for_loop_end (false)
66                         , force (false)
67                 {
68                         assert (t != StopTransport);
69                         assert (t != Locate);
70                 }
71                 Event (EventType t, bool ab, bool cl)
72                         : type (t)
73                         , abort (ab)
74                         , clear_state (cl)
75                 {
76                         assert (t == StopTransport);
77                 }
78                 Event (EventType t, samplepos_t pos, LocateTransportDisposition l, bool fl, bool lp, bool f4c)
79                         : type (t)
80                         , ltd (l)
81                         , with_flush (fl)
82                         , target (pos)
83                         , for_loop_end (lp)
84                         , force (f4c)
85                 {
86                         assert (t == Locate);
87                 }
88
89                 void* operator new (size_t);
90                 void  operator delete (void *ptr, size_t /*size*/);
91
92                 static void init_pool ();
93
94           private:
95                 static Pool* pool;
96
97         };
98
99         TransportFSM (TransportAPI& tapi);
100
101         void start () {
102                 init ();
103         }
104
105         void stop () {
106                 /* should we do anything here? this method is modelled on the
107                    boost::msm design, but its not clear that we ever need to
108                    do anything like this.
109                 */
110         }
111
112         enum MotionState {
113                 Stopped,
114                 Rolling,
115                 DeclickToStop,
116                 DeclickToLocate,
117                 WaitingForLocate
118         };
119
120         enum ButlerState {
121                 NotWaitingForButler,
122                 WaitingForButler
123         };
124
125         std::string current_state () const;
126
127   private:
128         MotionState _motion_state;
129         ButlerState _butler_state;
130
131         void init();
132
133         /* transition actions */
134
135         void schedule_butler_for_transport_work () const;
136         void start_playback ();
137         void stop_playback (Event const &);
138         void start_locate_after_declick () const;
139         void locate_for_loop (Event const &);
140         void roll_after_locate () const;
141         void start_locate_while_stopped (Event const &) const;
142         void interrupt_locate (Event const &) const;
143         void start_declick_for_locate (Event const &);
144
145         /* guards */
146
147         bool should_roll_after_locate () const;
148         bool should_not_roll_after_locate ()  const { return !should_roll_after_locate (); }
149
150   public:
151         bool locating () const           { return _motion_state == WaitingForLocate; }
152         bool rolling () const            { return _motion_state == Rolling; }
153         bool stopped () const            { return _motion_state == Stopped; }
154         bool stopping () const           { return _motion_state == DeclickToStop; }
155         bool waiting_for_butler() const  { return _butler_state == WaitingForButler; }
156         bool declick_in_progress() const { return _motion_state == DeclickToLocate || _motion_state == DeclickToStop; }
157
158         void enqueue (Event* ev);
159
160   private:
161
162         void transition (MotionState ms);
163         void transition (ButlerState bs);
164
165         void process_events ();
166         bool process_event (Event&, bool was_deferred, bool& deferred);
167
168         Event _last_locate;
169
170         TransportAPI* api;
171         typedef boost::intrusive::list<Event> EventList;
172         EventList queued_events;
173         EventList deferred_events;
174         int processing;
175         mutable boost::optional<bool> current_roll_after_locate_status;
176
177         void defer (Event& ev);
178         void bad_transition (Event const &);
179         void set_roll_after (bool) const;
180         bool compute_should_roll (LocateTransportDisposition) const;
181 };
182
183 } /* end namespace ARDOUR */
184
185 #endif