Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "AudioEventTimeline.h"
8 :
9 : #include "mozilla/ErrorResult.h"
10 :
11 0 : static float LinearInterpolate(double t0, float v0, double t1, float v1, double t)
12 : {
13 0 : return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
14 : }
15 :
16 0 : static float ExponentialInterpolate(double t0, float v0, double t1, float v1, double t)
17 : {
18 0 : return v0 * powf(v1 / v0, (t - t0) / (t1 - t0));
19 : }
20 :
21 0 : static float ExponentialApproach(double t0, double v0, float v1, double timeConstant, double t)
22 : {
23 0 : if (!mozilla::dom::WebAudioUtils::FuzzyEqual(timeConstant, 0.0)) {
24 0 : return v1 + (v0 - v1) * expf(-(t - t0) / timeConstant);
25 : } else {
26 0 : return v1;
27 : }
28 : }
29 :
30 0 : static float ExtractValueFromCurve(double startTime, float* aCurve, uint32_t aCurveLength, double duration, double t)
31 : {
32 0 : if (t >= startTime + duration) {
33 : // After the duration, return the last curve value
34 0 : return aCurve[aCurveLength - 1];
35 : }
36 0 : double ratio = std::max((t - startTime) / duration, 0.0);
37 0 : if (ratio >= 1.0) {
38 0 : return aCurve[aCurveLength - 1];
39 : }
40 0 : uint32_t current = uint32_t(floor((aCurveLength - 1) * ratio));
41 0 : uint32_t next = current + 1;
42 0 : double step = duration / double(aCurveLength - 1);
43 0 : if (next < aCurveLength) {
44 0 : double t0 = current * step;
45 0 : double t1 = next * step;
46 0 : return LinearInterpolate(t0, aCurve[current], t1, aCurve[next], t - startTime);
47 : } else {
48 0 : return aCurve[current];
49 : }
50 : }
51 :
52 : namespace mozilla {
53 : namespace dom {
54 :
55 : // This method computes the AudioParam value at a given time based on the event timeline
56 : template<class TimeType> void
57 0 : AudioEventTimeline::GetValuesAtTimeHelper(TimeType aTime, float* aBuffer,
58 : const size_t aSize)
59 : {
60 0 : MOZ_ASSERT(aBuffer);
61 0 : MOZ_ASSERT(aSize);
62 :
63 0 : auto TimeOf = [](const AudioTimelineEvent& aEvent) -> TimeType {
64 0 : return aEvent.template Time<TimeType>();
65 : };
66 :
67 0 : size_t eventIndex = 0;
68 0 : const AudioTimelineEvent* previous = nullptr;
69 :
70 : // Let's remove old events except the last one: we need it to calculate some curves.
71 0 : CleanupEventsOlderThan(aTime);
72 :
73 0 : for (size_t bufferIndex = 0; bufferIndex < aSize; ++bufferIndex, ++aTime) {
74 :
75 0 : bool timeMatchesEventIndex = false;
76 : const AudioTimelineEvent* next;
77 0 : for (; ; ++eventIndex) {
78 :
79 0 : if (eventIndex >= mEvents.Length()) {
80 0 : next = nullptr;
81 0 : break;
82 : }
83 :
84 0 : next = &mEvents[eventIndex];
85 0 : if (aTime < TimeOf(*next)) {
86 0 : break;
87 : }
88 :
89 : #ifdef DEBUG
90 0 : MOZ_ASSERT(next->mType == AudioTimelineEvent::SetValueAtTime ||
91 : next->mType == AudioTimelineEvent::SetTarget ||
92 : next->mType == AudioTimelineEvent::LinearRamp ||
93 : next->mType == AudioTimelineEvent::ExponentialRamp ||
94 : next->mType == AudioTimelineEvent::SetValueCurve);
95 : #endif
96 :
97 0 : if (TimesEqual(aTime, TimeOf(*next))) {
98 0 : mLastComputedValue = mComputedValue;
99 : // Find the last event with the same time
100 0 : while (eventIndex < mEvents.Length() - 1 &&
101 0 : TimesEqual(aTime, TimeOf(mEvents[eventIndex + 1]))) {
102 0 : mLastComputedValue = GetValueAtTimeOfEvent<TimeType>(&mEvents[eventIndex]);
103 0 : ++eventIndex;
104 : }
105 :
106 0 : timeMatchesEventIndex = true;
107 0 : break;
108 : }
109 :
110 0 : previous = next;
111 : }
112 :
113 0 : if (timeMatchesEventIndex) {
114 : // The time matches one of the events exactly.
115 0 : MOZ_ASSERT(TimesEqual(aTime, TimeOf(mEvents[eventIndex])));
116 0 : mComputedValue = GetValueAtTimeOfEvent<TimeType>(&mEvents[eventIndex]);
117 : } else {
118 0 : mComputedValue = GetValuesAtTimeHelperInternal(aTime, previous, next);
119 : }
120 :
121 0 : aBuffer[bufferIndex] = mComputedValue;
122 : }
123 0 : }
124 : template void
125 : AudioEventTimeline::GetValuesAtTimeHelper(double aTime, float* aBuffer,
126 : const size_t aSize);
127 : template void
128 : AudioEventTimeline::GetValuesAtTimeHelper(int64_t aTime, float* aBuffer,
129 : const size_t aSize);
130 :
131 : template<class TimeType> float
132 0 : AudioEventTimeline::GetValueAtTimeOfEvent(const AudioTimelineEvent* aNext)
133 : {
134 0 : TimeType time = aNext->template Time<TimeType>();
135 0 : switch (aNext->mType) {
136 : case AudioTimelineEvent::SetTarget:
137 : // SetTarget nodes can be handled no matter what their next node is
138 : // (if they have one).
139 : // Follow the curve, without regard to the next event, starting at
140 : // the last value of the last event.
141 0 : return ExponentialApproach(time,
142 0 : mLastComputedValue, aNext->mValue,
143 0 : aNext->mTimeConstant, time);
144 : break;
145 : case AudioTimelineEvent::SetValueCurve:
146 : // SetValueCurve events can be handled no matter what their event
147 : // node is (if they have one)
148 0 : return ExtractValueFromCurve(time,
149 0 : aNext->mCurve,
150 0 : aNext->mCurveLength,
151 0 : aNext->mDuration, time);
152 : break;
153 : default:
154 : // For other event types
155 0 : return aNext->mValue;
156 : }
157 : }
158 :
159 : template<class TimeType> float
160 0 : AudioEventTimeline::GetValuesAtTimeHelperInternal(TimeType aTime,
161 : const AudioTimelineEvent* aPrevious,
162 : const AudioTimelineEvent* aNext)
163 : {
164 : // If the requested time is before all of the existing events
165 0 : if (!aPrevious) {
166 0 : return mValue;
167 : }
168 :
169 0 : auto TimeOf = [](const AudioTimelineEvent* aEvent) -> TimeType {
170 0 : return aEvent->template Time<TimeType>();
171 : };
172 :
173 : // SetTarget nodes can be handled no matter what their next node is (if
174 : // they have one)
175 0 : if (aPrevious->mType == AudioTimelineEvent::SetTarget) {
176 0 : return ExponentialApproach(TimeOf(aPrevious),
177 0 : mLastComputedValue, aPrevious->mValue,
178 0 : aPrevious->mTimeConstant, aTime);
179 : }
180 :
181 : // SetValueCurve events can be handled no matter what their next node is
182 : // (if they have one)
183 0 : if (aPrevious->mType == AudioTimelineEvent::SetValueCurve) {
184 0 : return ExtractValueFromCurve(TimeOf(aPrevious),
185 0 : aPrevious->mCurve, aPrevious->mCurveLength,
186 0 : aPrevious->mDuration, aTime);
187 : }
188 :
189 : // If the requested time is after all of the existing events
190 0 : if (!aNext) {
191 0 : switch (aPrevious->mType) {
192 : case AudioTimelineEvent::SetValueAtTime:
193 : case AudioTimelineEvent::LinearRamp:
194 : case AudioTimelineEvent::ExponentialRamp:
195 : // The value will be constant after the last event
196 0 : return aPrevious->mValue;
197 : case AudioTimelineEvent::SetValueCurve:
198 0 : return ExtractValueFromCurve(TimeOf(aPrevious),
199 0 : aPrevious->mCurve, aPrevious->mCurveLength,
200 0 : aPrevious->mDuration, aTime);
201 : case AudioTimelineEvent::SetTarget:
202 0 : MOZ_FALLTHROUGH_ASSERT("AudioTimelineEvent::SetTarget");
203 : case AudioTimelineEvent::SetValue:
204 : case AudioTimelineEvent::Cancel:
205 : case AudioTimelineEvent::Stream:
206 0 : MOZ_ASSERT(false, "Should have been handled earlier.");
207 : }
208 0 : MOZ_ASSERT(false, "unreached");
209 : }
210 :
211 : // Finally, handle the case where we have both a previous and a next event
212 :
213 : // First, handle the case where our range ends up in a ramp event
214 0 : switch (aNext->mType) {
215 : case AudioTimelineEvent::LinearRamp:
216 0 : return LinearInterpolate(TimeOf(aPrevious),
217 0 : aPrevious->mValue,
218 0 : TimeOf(aNext),
219 0 : aNext->mValue, aTime);
220 :
221 : case AudioTimelineEvent::ExponentialRamp:
222 0 : return ExponentialInterpolate(TimeOf(aPrevious),
223 0 : aPrevious->mValue,
224 0 : TimeOf(aNext),
225 0 : aNext->mValue, aTime);
226 :
227 : case AudioTimelineEvent::SetValueAtTime:
228 : case AudioTimelineEvent::SetTarget:
229 : case AudioTimelineEvent::SetValueCurve:
230 0 : break;
231 : case AudioTimelineEvent::SetValue:
232 : case AudioTimelineEvent::Cancel:
233 : case AudioTimelineEvent::Stream:
234 0 : MOZ_ASSERT(false, "Should have been handled earlier.");
235 : }
236 :
237 : // Now handle all other cases
238 0 : switch (aPrevious->mType) {
239 : case AudioTimelineEvent::SetValueAtTime:
240 : case AudioTimelineEvent::LinearRamp:
241 : case AudioTimelineEvent::ExponentialRamp:
242 : // If the next event type is neither linear or exponential ramp, the
243 : // value is constant.
244 0 : return aPrevious->mValue;
245 : case AudioTimelineEvent::SetValueCurve:
246 0 : return ExtractValueFromCurve(TimeOf(aPrevious),
247 0 : aPrevious->mCurve, aPrevious->mCurveLength,
248 0 : aPrevious->mDuration, aTime);
249 : case AudioTimelineEvent::SetTarget:
250 0 : MOZ_FALLTHROUGH_ASSERT("AudioTimelineEvent::SetTarget");
251 : case AudioTimelineEvent::SetValue:
252 : case AudioTimelineEvent::Cancel:
253 : case AudioTimelineEvent::Stream:
254 0 : MOZ_ASSERT(false, "Should have been handled earlier.");
255 : }
256 :
257 0 : MOZ_ASSERT(false, "unreached");
258 : return 0.0f;
259 : }
260 : template float
261 : AudioEventTimeline::GetValuesAtTimeHelperInternal(double aTime,
262 : const AudioTimelineEvent* aPrevious,
263 : const AudioTimelineEvent* aNext);
264 : template float
265 : AudioEventTimeline::GetValuesAtTimeHelperInternal(int64_t aTime,
266 : const AudioTimelineEvent* aPrevious,
267 : const AudioTimelineEvent* aNext);
268 :
269 : const AudioTimelineEvent*
270 0 : AudioEventTimeline::GetPreviousEvent(double aTime) const
271 : {
272 0 : const AudioTimelineEvent* previous = nullptr;
273 0 : const AudioTimelineEvent* next = nullptr;
274 :
275 0 : auto TimeOf = [](const AudioTimelineEvent& aEvent) -> double {
276 0 : return aEvent.template Time<double>();
277 : };
278 :
279 0 : bool bailOut = false;
280 0 : for (unsigned i = 0; !bailOut && i < mEvents.Length(); ++i) {
281 0 : switch (mEvents[i].mType) {
282 : case AudioTimelineEvent::SetValueAtTime:
283 : case AudioTimelineEvent::SetTarget:
284 : case AudioTimelineEvent::LinearRamp:
285 : case AudioTimelineEvent::ExponentialRamp:
286 : case AudioTimelineEvent::SetValueCurve:
287 0 : if (aTime == TimeOf(mEvents[i])) {
288 : // Find the last event with the same time
289 0 : do {
290 0 : ++i;
291 0 : } while (i < mEvents.Length() &&
292 0 : aTime == TimeOf(mEvents[i]));
293 0 : return &mEvents[i - 1];
294 : }
295 0 : previous = next;
296 0 : next = &mEvents[i];
297 0 : if (aTime < TimeOf(mEvents[i])) {
298 0 : bailOut = true;
299 : }
300 0 : break;
301 : default:
302 0 : MOZ_ASSERT(false, "unreached");
303 : }
304 : }
305 : // Handle the case where the time is past all of the events
306 0 : if (!bailOut) {
307 0 : previous = next;
308 : }
309 :
310 0 : return previous;
311 : }
312 :
313 : } // namespace dom
314 : } // namespace mozilla
315 :
|