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 
16 #include "ardour/debug.h"
17 #include "ardour/types.h"
18 
19 namespace ARDOUR
20 {
21 
22 class TransportAPI;
23 
24 struct TransportFSM
25 {
26 	/* All code related to this object is expected to be run synchronously
27 	 * and single-threaded from the process callback. It can be re-entrant
28 	 * if handling one transport state change queues another state change,
29 	 * but that is handled explicitly (see the @param processing member and
30 	 * its usage).
31 	 */
32 
33   public:
34 	enum EventType {
35 		ButlerDone,
36 		ButlerRequired,
37 		DeclickDone,
38 		StartTransport,
39 		StopTransport,
40 		Locate,
41 		LocateDone,
42 		SetSpeed,
43 	};
44 
45 	struct Event : public boost::intrusive::list_base_hook<> {
46 		EventType type;
47 		/* for stop and speed */
48 		bool abort_capture;
49 		bool clear_state;
50 		/* for locate */
51 		LocateTransportDisposition ltd;
52 		samplepos_t target;
53 		bool for_loop_end;
54 		bool force;
55 		/* for SetSpeed */
56 		double speed;
57 		bool   as_default;
58 
EventTransportFSM::Event59 		Event (EventType t)
60 			: type (t)
61 			, abort_capture (false)
62 			, clear_state (false)
63 			, ltd (MustStop)
64 			, target (0)
65 			, for_loop_end (false)
66 			, force (false)
67 		{
68 			assert (t != StopTransport);
69 			assert (t != Locate);
70 		}
EventTransportFSM::Event71 		Event (EventType t, bool ab, bool cl)
72 			: type (t)
73 			, abort_capture (ab)
74 			, clear_state (cl)
75 			, ltd (MustStop)
76 			, target (0)
77 			, for_loop_end (false)
78 			, force (false)
79 		{
80 			assert (t == StopTransport);
81 		}
EventTransportFSM::Event82 		Event (EventType t, samplepos_t pos, LocateTransportDisposition l, bool lp, bool f4c)
83 			: type (t)
84 			, abort_capture (false)
85 			, clear_state (false)
86 			, ltd (l)
87 			, target (pos)
88 			, for_loop_end (lp)
89 			, force (f4c)
90 		{
91 			assert (t == Locate);
92 		}
93 		/* here we drop the event type as the first argument in order
94 		   disambiguate from the StopTransport case above (compiler can
95 		   cast double-to-bool and complains. C++11 would allow "=
96 		   delete" as an alternate fix, but this is fine.
97 		*/
EventTransportFSM::Event98 		Event (double sp, bool ad)
99 			: type (SetSpeed)
100 			, speed (sp)
101 			, as_default (ad)
102 		{
103 		}
104 
105 		void* operator new (size_t);
106 		void  operator delete (void *ptr, size_t /*size*/);
107 
108 		static void init_pool ();
109 
110           private:
111 		static Pool* pool;
112 
113 	};
114 
115 	TransportFSM (TransportAPI& tapi);
116 
startTransportFSM117 	void start () {
118 		init ();
119 	}
120 
stopTransportFSM121 	void stop () {
122 		/* should we do anything here? this method is modelled on the
123 		   boost::msm design, but its not clear that we ever need to
124 		   do anything like this.
125 		*/
126 	}
127 
128 	enum MotionState {
129 		Stopped,
130 		Rolling,
131 		DeclickToStop,
132 		DeclickToLocate,
133 		WaitingForLocate
134 	};
135 
136 	enum ButlerState {
137 		NotWaitingForButler,
138 		WaitingForButler
139 	};
140 
141 	enum DirectionState {
142 		Forwards,
143 		Backwards,
144 		Reversing,
145 	};
146 
147 	std::string current_state () const;
148 
transport_speedTransportFSM149 	double transport_speed() const { return _transport_speed; }
default_speedTransportFSM150 	double default_speed() const { return _default_speed; }
151 
152   private:
153 	MotionState _motion_state;
154 	ButlerState _butler_state;
155 	DirectionState _direction_state;
156 	double _transport_speed;
157 
158 	void init();
159 
160 	/* transition actions */
161 
162 	void schedule_butler_for_transport_work () const;
163 	void start_playback ();
164 	void stop_playback (Event const &);
165 	void start_locate_after_declick ();
166 	void locate_for_loop (Event const &);
167 	void roll_after_locate () const;
168 	void start_locate_while_stopped (Event const &) const;
169 	void interrupt_locate (Event const &) const;
170 	void start_declick_for_locate (Event const &);
171 	bool set_speed (Event const &);
172 
173 	/* guards */
174 
175 	bool should_roll_after_locate () const;
should_not_roll_after_locateTransportFSM176 	bool should_not_roll_after_locate ()  const { return !should_roll_after_locate (); }
177 
178   public:
locatingTransportFSM179 	bool locating () const           { return _motion_state == WaitingForLocate; }
rollingTransportFSM180 	bool rolling () const            { return _motion_state == Rolling; }
stoppedTransportFSM181 	bool stopped () const            { return _motion_state == Stopped; }
stoppingTransportFSM182 	bool stopping () const           { return _motion_state == DeclickToStop; }
waiting_for_butlerTransportFSM183 	bool waiting_for_butler() const  { return _butler_state == WaitingForButler; }
declick_in_progressTransportFSM184 	bool declick_in_progress() const { return _motion_state == DeclickToLocate || _motion_state == DeclickToStop; }
declicking_for_locateTransportFSM185 	bool declicking_for_locate() const { return _motion_state == DeclickToLocate; }
forwardsTransportFSM186 	bool forwards() const             { return _direction_state == Forwards; }
backwardsTransportFSM187 	bool backwards() const             { return _direction_state == Backwards; }
reversingTransportFSM188 	bool reversing() const             { return _direction_state == Reversing; }
189 	bool will_roll_fowards() const;
190 
191 	void enqueue (Event* ev);
192 
193   private:
194 
195 	void transition (MotionState);
196 	void transition (ButlerState);
197 	void transition (DirectionState);
198 
199 	void process_events ();
200 	bool process_event (Event&, bool was_deferred, bool& deferred);
201 
202 	mutable Event _last_locate;
203 
204 	TransportAPI* api;
205 	typedef boost::intrusive::list<Event> EventList;
206 	EventList queued_events;
207 	EventList deferred_events;
208 	int processing;
209 	mutable boost::optional<bool> current_roll_after_locate_status;
210 	mutable double most_recently_requested_speed;
211 	mutable double _default_speed;
212 	int _reverse_after_declick;
213 
214 	void defer (Event& ev);
215 	void bad_transition (Event const &);
216 	void set_roll_after (bool) const;
217 	bool compute_should_roll (LocateTransportDisposition) const;
218 	int  compute_transport_speed () const;
219 	bool  maybe_reset_speed ();
220 };
221 
222 } /* end namespace ARDOUR */
223 
224 #endif
225