LCOV - code coverage report
Current view: top level - dom/media/webaudio - AudioEventTimeline.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 130 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 20 0.0 %
Legend: Lines: hit not hit

          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             : 

Generated by: LCOV version 1.13