tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_LaunchHandle.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
14LaunchHandle::State::State() = default;
15
16//==============================================================================
17LaunchHandle::PlayState LaunchHandle::getPlayingStatus() const
18{
19 return getState().status;
20}
21
23{
24 return getState().nextStatus;
25}
26
28{
29 return getState().nextEventTime;
30}
31
33{
34 auto s = getState();
35
36 s.nextStatus = QueueState::playQueued;
37 s.nextEventTime = pos;
38
39 setState (std::move (s));
40}
41
43{
44 auto s = getState();
45
46 if (s.status == PlayState::stopped)
47 {
48 if (s.nextStatus == QueueState::playQueued)
49 {
50 s.nextStatus = {};
51 s.nextEventTime = {};
52 setState (std::move (s));
53 }
54
55 return;
56 }
57
58 s.nextStatus = QueueState::stopQueued;
59 s.nextEventTime = pos;
60
61 setState (std::move (s));
62}
63
65{
66 return getState().playedRange;
67}
68
70{
71 return getState().playedMonotonicRange;
72}
73
74std::optional<BeatRange> LaunchHandle::getLastPlayedRange() const
75{
76 return getState().lastPlayedRange;
77}
78
80{
81 auto s = getState();
82
83 const auto blockMonotonicBeatRange = getMonotonicBeatRange (syncRange);
84 const auto duration = blockMonotonicBeatRange.v.getLength();
85
86 // Playhead isn't moving so just apply stop queued states straight away
87 if (duration == 0_bd)
88 {
89 SplitStatus splitStatus;
90
91 if (s.nextStatus == QueueState::stopQueued)
92 {
93 splitStatus.playing1 = false;
94
95 if (s.playedRange)
96 s.lastPlayedRange = s.playedRange;
97
98 s.playedRange = std::nullopt;
99 s.playedMonotonicRange = std::nullopt;
100 s.status = PlayState::stopped;
101 s.nextStatus = std::nullopt;
102
103 setState (std::move (s));
104 }
105
106 return splitStatus;
107 }
108
109 SplitStatus splitStatus;
110
111 auto continuePlayingOrStopped = [&]
112 {
113 splitStatus.playing1 = s.status == PlayState::playing;
114 splitStatus.range1 = getBeatRange (syncRange);
115 splitStatus.playStartTime1 = s.playedRange ? std::optional (s.playedRange->getStart())
116 : std::nullopt;
117
118 if (s.playedRange)
119 s.playedRange = withEndExtended (*s.playedRange, duration);
120
121 if (s.playedMonotonicRange)
122 s.playedMonotonicRange = MonotonicBeatRange { withEndExtended (s.playedMonotonicRange->v, duration) };
123 };
124
125 // Check if we need to change state
126 if (s.nextStatus)
127 {
128 switch (*s.nextStatus)
129 {
130 case QueueState::playQueued:
131 {
132 if (s.nextEventTime)
133 {
134 if (s.nextEventTime->v <= blockMonotonicBeatRange.v.getStart())
135 {
136 splitStatus.playing1 = true;
137 splitStatus.range1 = getBeatRange (syncRange);
138 splitStatus.playStartTime1 = splitStatus.range1.getStart();
139
140 if (s.playedRange)
141 s.lastPlayedRange = s.playedRange;
142
143 const auto numBeatsSinceLaunch = blockMonotonicBeatRange.v.getEnd() - s.nextEventTime->v;
144
145 s.playedRange = BeatRange::endingAt (splitStatus.range1.getEnd(), numBeatsSinceLaunch);
146 s.playedMonotonicRange = MonotonicBeatRange { BeatRange::endingAt (blockMonotonicBeatRange.v.getEnd(), numBeatsSinceLaunch) };
147 s.status = PlayState::playing;
148 s.nextStatus = std::nullopt;
149 }
150 else if (blockMonotonicBeatRange.v.contains (s.nextEventTime->v))
151 {
152 const auto blockEditBeatRange = getBeatRange (syncRange);
153
154 splitStatus.playing1 = s.status == PlayState::playing;
155 splitStatus.range1 = blockEditBeatRange.withLength (s.nextEventTime->v - blockMonotonicBeatRange.v.getStart());
156 splitStatus.playStartTime1 = s.playedRange ? std::optional (s.playedRange->getStart()) : std::nullopt;
157 splitStatus.playing2 = true;
158 splitStatus.range2 = { splitStatus.range1.getEnd(), blockEditBeatRange.getEnd() };
159 splitStatus.playStartTime2 = splitStatus.range2.getStart();
160 splitStatus.isSplit = true;
161
162 assert(! splitStatus.range1.isEmpty());
163 assert(! splitStatus.range2.isEmpty());
164
165 if (s.playedRange)
166 s.lastPlayedRange = s.playedRange;
167
168 s.playedRange = { splitStatus.range2 };
169 s.playedMonotonicRange = MonotonicBeatRange { BeatRange (s.nextEventTime->v, splitStatus.range2.getLength()) };
170 s.status = PlayState::playing;
171 s.nextEventTime = std::nullopt;
172 s.nextStatus = std::nullopt;
173 }
174 else
175 {
176 continuePlayingOrStopped();
177 }
178 }
179 else
180 {
181 const auto blockEditBeatRange = getBeatRange (syncRange);
182
183 splitStatus.playing1 = true;
184 splitStatus.range1 = blockEditBeatRange;
185 splitStatus.playStartTime1 = blockEditBeatRange.getStart();
186
187 s.playedRange = blockEditBeatRange;
188 s.playedMonotonicRange = getMonotonicBeatRange (syncRange);
189 s.status = PlayState::playing;
190 s.nextStatus = std::nullopt;
191 }
192
193 break;
194 }
195 case QueueState::stopQueued:
196 {
197 if (s.status == PlayState::stopped)
198 {
199 splitStatus.playing1 = false;
200 splitStatus.range1 = getBeatRange (syncRange);
201 splitStatus.playStartTime1 = std::nullopt;
202
203 s.playedRange = std::nullopt;
204 s.playedMonotonicRange = std::nullopt;
205 s.nextStatus = std::nullopt;
206 s.nextEventTime = std::nullopt;
207 }
208 else if (s.nextEventTime)
209 {
210 if (s.nextEventTime->v <= blockMonotonicBeatRange.v.getStart())
211 {
212 splitStatus.playing1 = false;
213 splitStatus.range1 = getBeatRange (syncRange);
214 splitStatus.playStartTime1 = std::nullopt;
215
216 if (s.playedRange)
217 s.lastPlayedRange = s.playedRange;
218
219 s.playedRange = std::nullopt;
220 s.playedMonotonicRange = std::nullopt;
221 s.status = PlayState::stopped;
222 s.nextStatus = std::nullopt;
223 }
224 else if (blockMonotonicBeatRange.v.contains (s.nextEventTime->v))
225 {
226 const auto blockEditBeatRange = getBeatRange (syncRange);
227 const auto playingDuration = s.nextEventTime->v - blockMonotonicBeatRange.v.getStart();
228
229 splitStatus.playing1 = s.status == PlayState::playing;
230 splitStatus.range1 = blockEditBeatRange.withLength (playingDuration);
231 splitStatus.playStartTime1 = s.playedRange ? std::optional (s.playedRange->getStart()) : std::nullopt;
232 splitStatus.playing2 = false;
233 splitStatus.range2 = { splitStatus.range1.getEnd(), blockEditBeatRange.getEnd() };
234 splitStatus.playStartTime1 = std::nullopt;
235 splitStatus.isSplit = true;
236
237 assert(! splitStatus.range1.isEmpty());
238 assert(! splitStatus.range2.isEmpty());
239
240 if (s.playedRange)
241 s.lastPlayedRange = withEndExtended (*s.playedRange, playingDuration);
242
243 s.playedRange = std::nullopt;
244 s.playedMonotonicRange = std::nullopt;
245 s.status = PlayState::stopped;
246 s.nextEventTime = std::nullopt;
247 s.nextStatus = std::nullopt;
248 }
249 else
250 {
251 continuePlayingOrStopped();
252 }
253 }
254 else
255 {
256 const auto blockEditBeatRange = getBeatRange (syncRange);
257
258 splitStatus.playing1 = false;
259 splitStatus.range1 = blockEditBeatRange;
260 splitStatus.playStartTime1 = std::nullopt;
261
262 if (s.playedRange)
263 s.lastPlayedRange = s.playedRange;
264
265 s.playedRange = std::nullopt;
266 s.playedMonotonicRange = std::nullopt;
267 s.status = PlayState::stopped;
268 s.nextStatus = std::nullopt;
269 }
270
271 break;
272 }
273 };
274 }
275 else
276 {
277 continuePlayingOrStopped();
278 }
279
280 setState (std::move (s));
281
282 return splitStatus;
283}
284
285}} // namespace tracktion { inline namespace engine
assert
std::optional< QueueState > getQueuedStatus() const
Returns the current queue state.
PlayState getPlayingStatus() const
Returns the current playback state.
std::optional< MonotonicBeatRange > getPlayedMonotonicRange() const
Returns the monotonic beat range this has been playing for.
std::optional< BeatRange > getPlayedRange() const
Returns the Edit beat range this has been playing for.
void stop(std::optional< MonotonicBeat >)
Stop playing, optionally at a given beat position.
std::optional< MonotonicBeat > getQueuedEventPosition() const
Returns the current queued event time.
void play(std::optional< MonotonicBeat >)
Start playing, optionally at a given beat position.
SplitStatus advance(const SyncRange &)
Advance the state.
Represents two beat ranges where the play state can be different in each.
T is_pointer_v