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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_LauncherClipPlaybackHandle.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
11
12namespace tracktion { inline namespace engine
13{
14
15LauncherClipPlaybackHandle LauncherClipPlaybackHandle::forLooping (BeatRange loopRange,
16 BeatDuration offset)
17{
18 LauncherClipPlaybackHandle h;
19 h.loopRange = loopRange;
20 h.offset = offset;
21 h.isLooping = true;
22
23 assert (! h.loopRange.isEmpty());
24
25 return h;
26}
27
28LauncherClipPlaybackHandle LauncherClipPlaybackHandle::forOneShot (BeatRange clipRange)
29{
30 LauncherClipPlaybackHandle h;
31 h.clipRange = clipRange;
32
33 assert (! h.clipRange.isEmpty());
34
35 return h;
36}
37
38//==============================================================================
40{
41 playStartPosition = p;
42}
43
45{
46 playStartPosition = std::nullopt;
47}
48
50{
51 return playStartPosition;
52}
53
55{
56 if (! playStartPosition)
57 return {};
58
59 const auto s = *playStartPosition;
60
61 if (isLooping)
62 {
63 if (r.getEnd() < s)
64 return {};
65
66 // Case where range is off the start edge
67 if (r.getStart() < s)
68 {
69 const auto duration1 = s - r.getStart();
70 const auto duration2 = r.getEnd() - s;
71
72 SplitBeatRange splitRange;
73 splitRange.range1 = { loopRange.getStart() + offset - duration1, duration1 };
74 splitRange.playing1 = false;
75 splitRange.range2 = { loopRange.getStart() + offset, duration2 };
76 splitRange.playing2 = true;
77
78 return splitRange;
79 }
80
81 // The whole range is within the looping region
82 // Wrap each position to the loop length and then see if the end is
83 // before the start to know it looped
84 {
85 assert (r.getStart () >= s);
86 const auto loopStart = loopRange.getStart();
87 const auto sourceTimelineStart = s - offset;
88 const BeatRange virtualClipTimelineRange (sourceTimelineStart, loopRange.getLength());
89
90 const auto wrappedTimelineStart = virtualClipTimelineRange.getStart()
91 + BeatDuration::fromBeats (std::fmod ((r.getStart() - virtualClipTimelineRange.getStart()).inBeats(),
92 virtualClipTimelineRange.getLength().inBeats()));
93 const auto wrappedTimelineEnd = virtualClipTimelineRange.getStart()
94 + BeatDuration::fromBeats (std::fmod ((r.getEnd() - virtualClipTimelineRange.getStart()).inBeats(),
95 virtualClipTimelineRange.getLength().inBeats()));
96 assert (wrappedTimelineEnd != wrappedTimelineStart);
97 assert (virtualClipTimelineRange.contains (wrappedTimelineStart));
98 assert (virtualClipTimelineRange.contains (wrappedTimelineEnd));
99
100 if (wrappedTimelineEnd > wrappedTimelineStart)
101 {
102 SplitBeatRange splitRange;
103 splitRange.range1 = { wrappedTimelineStart + toDuration (loopStart) - offset,
104 wrappedTimelineEnd + toDuration (loopStart) - offset };
105 splitRange.playing1 = true;
106
107 return splitRange;
108 }
109
110 // Case where the beat range straddles a loop point
111 // The range has to be split in two
112 assert (wrappedTimelineStart > wrappedTimelineEnd);
113 SplitBeatRange splitRange;
114 splitRange.range1 = { wrappedTimelineStart + toDuration (loopStart) - offset,
115 virtualClipTimelineRange.getEnd() + toDuration (loopStart) - offset };
116 splitRange.playing1 = true;
117 splitRange.range2 = { virtualClipTimelineRange.getStart() + toDuration (loopStart) - offset,
118 wrappedTimelineEnd + toDuration (loopStart) - offset };
119 splitRange.playing2 = true;
120
121 return splitRange;
122 }
123 }
124
125 if (const BeatRange validClipRange (s, clipRange.getLength());
126 validClipRange.intersects (r))
127 {
128 const auto clipStart = clipRange.getStart();
129 const auto clipEnd = clipRange.getEnd();
130
131 if (r.getEnd() < s)
132 return {};
133
134 // Case where range is off the start edge
135 if (r.getStart() < s)
136 {
137 const auto duration1 = s - r.getStart();
138 const auto duration2 = r.getEnd() - s;
139
140 SplitBeatRange splitRange;
141 splitRange.range1 = { clipStart - duration1, clipStart };
142 splitRange.playing1 = false;
143 splitRange.range2 = { clipStart, duration2 };
144 splitRange.playing2 = true;
145
146 return splitRange;
147 }
148
149 // Case where range is off the start edge
150 if (r.getEnd() > validClipRange.getEnd())
151 {
152 const auto duration1 = validClipRange.getEnd() - r.getStart();
153 const auto duration2 = r.getEnd() - validClipRange.getEnd();
154
155 SplitBeatRange splitRange;
156 splitRange.range1 = { clipEnd - duration1, clipEnd };
157 splitRange.playing1 = true;
158 splitRange.range2 = { clipEnd, duration2 };
159 splitRange.playing2 = false;
160
161 return splitRange;
162 }
163
164 // Case where range is within the clip range
165 assert (validClipRange.contains (r));
166 const auto positionInClip = r.getStart() - toDuration (s);
167
168 SplitBeatRange splitRange;
169 splitRange.range1 = { positionInClip + toDuration (clipStart), r.getLength() };
170 splitRange.playing1 = true;
171
172 return splitRange;
173 }
174
175 return {};
176}
177
179{
180 if (! playStartPosition)
181 return std::nullopt;
182
183 const auto s = *playStartPosition;
184
185 if (isLooping)
186 {
187 if (p < s)
188 return std::nullopt;
189
190 const auto playedDuration = (p - s) + offset;
191
192 if (playedDuration < offset)
193 return std::nullopt;
194
195 const auto numIterationsPlayed = playedDuration / loopRange.getLength();
196
197 return static_cast<float> (numIterationsPlayed - static_cast<int> (numIterationsPlayed));
198 }
199
200 const BeatRange validRange (s, clipRange.getLength());
201
202 if (validRange.contains (p))
203 return static_cast<float> ((p - s) / validRange.getLength());
204
205 return std::nullopt;
206}
207
208}} // namespace tracktion { inline namespace engine
assert
void start(BeatPosition)
Signifies that the clip has started at a given position.
SplitBeatRange timelineRangeToClipSourceRange(BeatRange) const
Converts a timeline range (e.g.
std::optional< BeatPosition > getStart() const
If the clip has been started, this will return that position.
std::optional< float > getProgress(BeatPosition) const
Returns the progress through a clip's source.
T fmod(T... args)
T is_pointer_v
Represents a position in beats.