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