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