Coverage Report

Created: 2018-09-25 14:53

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