summaryrefslogtreecommitdiff
path: root/libs/ardour/ardour/transport_fsm.h
blob: 54ceac230446963685aa863a697d95fa501b8ffd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#ifndef _ardour_transport_fsm_h_
#define _ardour_transport_fsm_h_

#include <list>
#include <queue>

#include <boost/intrusive/list.hpp>
#include <boost/optional.hpp>

#include <string>
#include <utility>
#include <iostream>

#include "pbd/demangle.h"
#include "pbd/stacktrace.h"

#include "ardour/debug.h"
#include "ardour/types.h"

namespace ARDOUR
{

class TransportAPI;

struct TransportFSM
{
	/* All code related to this object is expected to be run synchronously
	 * and single-threaded from the process callback. It can be re-entrant
	 * if handling one transport state change queues another state change,
	 * but that is handled explicitly (see the @param processing member and
	 * its usage).
	 */

  public:
	enum EventType {
		ButlerDone,
		ButlerRequired,
		DeclickDone,
		StartTransport,
		StopTransport,
		Locate,
		LocateDone
	};

	struct Event : public boost::intrusive::list_base_hook<> {
		EventType type;
		union {
			bool abort; /* for stop */
			LocateTransportDisposition ltd; /* for locate */
		};
		union {
			bool clear_state; /* for stop */
			bool with_flush; /* for locate */
		};
		/* for locate */
		samplepos_t target;
		bool for_loop_end;
		bool force;

		Event (EventType t)
			: type (t)
			, ltd (MustStop)
			, with_flush (false)
			, target (0)
			, for_loop_end (false)
			, force (false)
		{}
		Event (EventType t, bool ab, bool cl)
			: type (t)
			, abort (ab)
			, clear_state (cl)
		{
			assert (t == StopTransport);
		}
		Event (EventType t, samplepos_t pos, LocateTransportDisposition l, bool fl, bool lp, bool f4c)
			: type (t)
			, ltd (l)
			, with_flush (fl)
			, target (pos)
			, for_loop_end (lp)
			, force (f4c)
		{
			assert (t == Locate);
		}

		void* operator new (size_t);
		void  operator delete (void *ptr, size_t /*size*/);

		static void init_pool ();

          private:
		static Pool* pool;

	};

	TransportFSM (TransportAPI& tapi);

	void start () {
		init ();
	}

	void stop () {
		/* should we do anything here? this method is modelled on the
		   boost::msm design, but its not clear that we ever need to
		   do anything like this.
		*/
	}

	enum MotionState {
		Stopped,
		Rolling,
		DeclickToStop,
		DeclickToLocate,
		WaitingForLocate
	};

	enum ButlerState {
		NotWaitingForButler,
		WaitingForButler
	};

	std::string current_state () const;

  private:
	MotionState _motion_state;
	ButlerState _butler_state;

	void init();

	/* transition actions */

	void schedule_butler_for_transport_work () const;
	void start_playback ();
	void stop_playback ();
	void start_locate_after_declick () const;
	void locate_for_loop (Event const &);
	void roll_after_locate () const;
	void start_locate_while_stopped (Event const &) const;
	void interrupt_locate (Event const &) const;
	void start_declick_for_locate (Event const &);

	/* guards */

	bool should_roll_after_locate () const;
	bool should_not_roll_after_locate ()  const { return !should_roll_after_locate (); }

  public:
	bool locating () const           { return _motion_state == WaitingForLocate; }
	bool rolling () const            { return _motion_state == Rolling; }
	bool stopped () const            { return _motion_state == Stopped; }
	bool stopping () const           { return _motion_state == DeclickToStop; }
	bool waiting_for_butler() const  { return _butler_state == WaitingForButler; }
	bool declick_in_progress() const { return _motion_state == DeclickToLocate || _motion_state == DeclickToStop; }

	void enqueue (Event* ev);

  private:

	void transition (MotionState ms);
	void transition (ButlerState bs);

	void process_events ();
	bool process_event (Event&, bool was_deferred, bool& deferred);

	Event _last_locate;
	Event _last_stop;

	TransportAPI* api;
	typedef boost::intrusive::list<Event> EventList;
	EventList queued_events;
	EventList deferred_events;
	int processing;
	mutable boost::optional<bool> current_roll_after_locate_status;

	void defer (Event& ev);
	void bad_transition (Event const &);
	void set_roll_after (bool) const;
	bool compute_should_roll (LocateTransportDisposition) const;
};

} /* end namespace ARDOUR */

#endif