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