LCOV - code coverage report
Current view: top level - dom/media/webaudio - AudioEventTimeline.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 157 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 29 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             : #ifndef AudioEventTimeline_h_
       8             : #define AudioEventTimeline_h_
       9             : 
      10             : #include <algorithm>
      11             : #include "mozilla/Assertions.h"
      12             : #include "mozilla/FloatingPoint.h"
      13             : #include "mozilla/PodOperations.h"
      14             : 
      15             : #include "MainThreadUtils.h"
      16             : #include "nsTArray.h"
      17             : #include "math.h"
      18             : #include "WebAudioUtils.h"
      19             : 
      20             : namespace mozilla {
      21             : 
      22             : class MediaStream;
      23             : 
      24             : namespace dom {
      25             : 
      26             : struct AudioTimelineEvent final
      27             : {
      28             :   enum Type : uint32_t
      29             :   {
      30             :     SetValue,
      31             :     SetValueAtTime,
      32             :     LinearRamp,
      33             :     ExponentialRamp,
      34             :     SetTarget,
      35             :     SetValueCurve,
      36             :     Stream,
      37             :     Cancel
      38             :   };
      39             : 
      40           0 :   AudioTimelineEvent(Type aType, double aTime, float aValue, double aTimeConstant = 0.0,
      41             :                      double aDuration = 0.0, const float* aCurve = nullptr,
      42             :                      uint32_t aCurveLength = 0)
      43           0 :     : mType(aType)
      44             :     , mCurve(nullptr)
      45             :     , mTimeConstant(aTimeConstant)
      46             :     , mDuration(aDuration)
      47             : #ifdef DEBUG
      48           0 :     , mTimeIsInTicks(false)
      49             : #endif
      50             :   {
      51           0 :     mTime = aTime;
      52           0 :     if (aType == AudioTimelineEvent::SetValueCurve) {
      53           0 :       SetCurveParams(aCurve, aCurveLength);
      54             :     } else {
      55           0 :       mValue = aValue;
      56             :     }
      57           0 :   }
      58             : 
      59           0 :   explicit AudioTimelineEvent(MediaStream* aStream)
      60           0 :     : mType(Stream)
      61             :     , mCurve(nullptr)
      62             :     , mStream(aStream)
      63             :     , mTimeConstant(0.0)
      64             :     , mDuration(0.0)
      65             : #ifdef DEBUG
      66           0 :     , mTimeIsInTicks(false)
      67             : #endif
      68             :   {
      69           0 :   }
      70             : 
      71           0 :   AudioTimelineEvent(const AudioTimelineEvent& rhs)
      72           0 :   {
      73           0 :     PodCopy(this, &rhs, 1);
      74             : 
      75           0 :     if (rhs.mType == AudioTimelineEvent::SetValueCurve) {
      76           0 :       SetCurveParams(rhs.mCurve, rhs.mCurveLength);
      77           0 :     } else if (rhs.mType == AudioTimelineEvent::Stream) {
      78           0 :       new (&mStream) decltype(mStream)(rhs.mStream);
      79             :     }
      80           0 :   }
      81             : 
      82           0 :   ~AudioTimelineEvent()
      83           0 :   {
      84           0 :     if (mType == AudioTimelineEvent::SetValueCurve) {
      85           0 :       delete[] mCurve;
      86             :     }
      87           0 :   }
      88             : 
      89             :   template <class TimeType>
      90             :   TimeType Time() const;
      91             : 
      92           0 :   void SetTimeInTicks(int64_t aTimeInTicks)
      93             :   {
      94           0 :     mTimeInTicks = aTimeInTicks;
      95             : #ifdef DEBUG
      96           0 :     mTimeIsInTicks = true;
      97             : #endif
      98           0 :   }
      99             : 
     100           0 :   void SetCurveParams(const float* aCurve, uint32_t aCurveLength) {
     101           0 :     mCurveLength = aCurveLength;
     102           0 :     if (aCurveLength) {
     103           0 :       mCurve = new float[aCurveLength];
     104           0 :       PodCopy(mCurve, aCurve, aCurveLength);
     105             :     } else {
     106           0 :       mCurve = nullptr;
     107             :     }
     108           0 :   }
     109             : 
     110             :   Type mType;
     111             :   union {
     112             :     float mValue;
     113             :     uint32_t mCurveLength;
     114             :   };
     115             :   // mCurve contains a buffer of SetValueCurve samples.  We sample the
     116             :   // values in the buffer depending on how far along we are in time.
     117             :   // If we're at time T and the event has started as time T0 and has a
     118             :   // duration of D, we sample the buffer at floor(mCurveLength*(T-T0)/D)
     119             :   // if T<T0+D, and just take the last sample in the buffer otherwise.
     120             :   float* mCurve;
     121             :   RefPtr<MediaStream> mStream;
     122             :   double mTimeConstant;
     123             :   double mDuration;
     124             : #ifdef DEBUG
     125             :   bool mTimeIsInTicks;
     126             : #endif
     127             : 
     128             : private:
     129             :   // This member is accessed using the `Time` method, for safety.
     130             :   //
     131             :   // The time for an event can either be in absolute value or in ticks.
     132             :   // Initially the time of the event is always in absolute value.
     133             :   // In order to convert it to ticks, call SetTimeInTicks.  Once this
     134             :   // method has been called for an event, the time cannot be converted
     135             :   // back to absolute value.
     136             :   union {
     137             :     double mTime;
     138             :     int64_t mTimeInTicks;
     139             :   };
     140             : };
     141             : 
     142             : template <>
     143           0 : inline double AudioTimelineEvent::Time<double>() const
     144             : {
     145           0 :   MOZ_ASSERT(!mTimeIsInTicks);
     146           0 :   return mTime;
     147             : }
     148             : 
     149             : template <>
     150           0 : inline int64_t AudioTimelineEvent::Time<int64_t>() const
     151             : {
     152           0 :   MOZ_ASSERT(!NS_IsMainThread());
     153           0 :   MOZ_ASSERT(mTimeIsInTicks);
     154           0 :   return mTimeInTicks;
     155             : }
     156             : 
     157             : /**
     158             :  * Some methods in this class will be instantiated with different ErrorResult
     159             :  * template arguments for testing and production code.
     160             :  *
     161             :  * ErrorResult is a type which satisfies the following:
     162             :  *  - Implements a Throw() method taking an nsresult argument, representing an error code.
     163             :  */
     164           0 : class AudioEventTimeline
     165             : {
     166             : public:
     167           0 :   explicit AudioEventTimeline(float aDefaultValue)
     168           0 :     : mValue(aDefaultValue),
     169             :       mComputedValue(aDefaultValue),
     170           0 :       mLastComputedValue(aDefaultValue)
     171           0 :   { }
     172             : 
     173             :   template <class ErrorResult>
     174           0 :   bool ValidateEvent(AudioTimelineEvent& aEvent, ErrorResult& aRv)
     175             :   {
     176           0 :     MOZ_ASSERT(NS_IsMainThread());
     177             : 
     178           0 :     auto TimeOf = [](const AudioTimelineEvent& aEvent) -> double {
     179           0 :       return aEvent.template Time<double>();
     180             :     };
     181             : 
     182             :     // Validate the event itself
     183           0 :     if (!WebAudioUtils::IsTimeValid(TimeOf(aEvent)) ||
     184           0 :         !WebAudioUtils::IsTimeValid(aEvent.mTimeConstant)) {
     185           0 :       aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     186           0 :       return false;
     187             :     }
     188             : 
     189           0 :     if (aEvent.mType == AudioTimelineEvent::SetValueCurve) {
     190           0 :       if (!aEvent.mCurve || !aEvent.mCurveLength) {
     191           0 :         aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     192           0 :         return false;
     193             :       }
     194           0 :       for (uint32_t i = 0; i < aEvent.mCurveLength; ++i) {
     195           0 :         if (!IsValid(aEvent.mCurve[i])) {
     196           0 :           aRv.Throw(NS_ERROR_TYPE_ERR);
     197           0 :           return false;
     198             :         }
     199             :       }
     200             :     }
     201             : 
     202           0 :     bool timeAndValueValid = IsValid(aEvent.mValue) &&
     203           0 :                              IsValid(aEvent.mDuration);
     204           0 :     if (!timeAndValueValid) {
     205           0 :       aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     206           0 :       return false;
     207             :     }
     208             : 
     209             :     // Make sure that non-curve events don't fall within the duration of a
     210             :     // curve event.
     211           0 :     for (unsigned i = 0; i < mEvents.Length(); ++i) {
     212           0 :       if (mEvents[i].mType == AudioTimelineEvent::SetValueCurve &&
     213           0 :           !(aEvent.mType == AudioTimelineEvent::SetValueCurve &&
     214           0 :             TimeOf(aEvent) == TimeOf(mEvents[i])) &&
     215           0 :           TimeOf(mEvents[i]) <= TimeOf(aEvent) &&
     216           0 :           TimeOf(mEvents[i]) + mEvents[i].mDuration >= TimeOf(aEvent)) {
     217           0 :         aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     218           0 :         return false;
     219             :       }
     220             :     }
     221             : 
     222             :     // Make sure that curve events don't fall in a range which includes other
     223             :     // events.
     224           0 :     if (aEvent.mType == AudioTimelineEvent::SetValueCurve) {
     225           0 :       for (unsigned i = 0; i < mEvents.Length(); ++i) {
     226             :         // In case we have two curve at the same time
     227           0 :         if (mEvents[i].mType == AudioTimelineEvent::SetValueCurve &&
     228           0 :             TimeOf(mEvents[i]) == TimeOf(aEvent)) {
     229           0 :           continue;
     230             :         }
     231           0 :         if (TimeOf(mEvents[i]) > TimeOf(aEvent) &&
     232           0 :             TimeOf(mEvents[i]) < TimeOf(aEvent) + aEvent.mDuration) {
     233           0 :           aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     234           0 :           return false;
     235             :         }
     236             :       }
     237             :     }
     238             : 
     239             :     // Make sure that invalid values are not used for exponential curves
     240           0 :     if (aEvent.mType == AudioTimelineEvent::ExponentialRamp) {
     241           0 :       if (aEvent.mValue <= 0.f) {
     242           0 :         aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     243           0 :         return false;
     244             :       }
     245           0 :       const AudioTimelineEvent* previousEvent = GetPreviousEvent(TimeOf(aEvent));
     246           0 :       if (previousEvent) {
     247           0 :         if (previousEvent->mValue <= 0.f) {
     248           0 :           aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     249           0 :           return false;
     250             :         }
     251             :       } else {
     252           0 :         if (mValue <= 0.f) {
     253           0 :           aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     254           0 :           return false;
     255             :         }
     256             :       }
     257             :     }
     258           0 :     return true;
     259             :   }
     260             : 
     261             :   template<typename TimeType>
     262           0 :   void InsertEvent(const AudioTimelineEvent& aEvent)
     263             :   {
     264           0 :     for (unsigned i = 0; i < mEvents.Length(); ++i) {
     265           0 :       if (aEvent.template Time<TimeType>() == mEvents[i].template Time<TimeType>()) {
     266           0 :         if (aEvent.mType == mEvents[i].mType) {
     267             :           // If times and types are equal, replace the event
     268           0 :           mEvents.ReplaceElementAt(i, aEvent);
     269             :         } else {
     270             :           // Otherwise, place the element after the last event of another type
     271           0 :           do {
     272           0 :             ++i;
     273           0 :           } while (i < mEvents.Length() &&
     274           0 :                    aEvent.mType != mEvents[i].mType &&
     275           0 :                    aEvent.template Time<TimeType>() == mEvents[i].template Time<TimeType>());
     276           0 :           mEvents.InsertElementAt(i, aEvent);
     277             :         }
     278           0 :         return;
     279             :       }
     280             :       // Otherwise, place the event right after the latest existing event
     281           0 :       if (aEvent.template Time<TimeType>() < mEvents[i].template Time<TimeType>()) {
     282           0 :         mEvents.InsertElementAt(i, aEvent);
     283           0 :         return;
     284             :       }
     285             :     }
     286             : 
     287             :     // If we couldn't find a place for the event, just append it to the list
     288           0 :     mEvents.AppendElement(aEvent);
     289             :   }
     290             : 
     291           0 :   bool HasSimpleValue() const
     292             :   {
     293           0 :     return mEvents.IsEmpty();
     294             :   }
     295             : 
     296           0 :   float GetValue() const
     297             :   {
     298             :     // This method should only be called if HasSimpleValue() returns true
     299           0 :     MOZ_ASSERT(HasSimpleValue());
     300           0 :     return mValue;
     301             :   }
     302             : 
     303           0 :   float Value() const
     304             :   {
     305             :     // TODO: Return the current value based on the timeline of the AudioContext
     306           0 :     return mValue;
     307             :   }
     308             : 
     309           0 :   void SetValue(float aValue)
     310             :   {
     311             :     // Silently don't change anything if there are any events
     312           0 :     if (mEvents.IsEmpty()) {
     313           0 :       mLastComputedValue = mComputedValue = mValue = aValue;
     314             :     }
     315           0 :   }
     316             : 
     317             :   template <class ErrorResult>
     318             :   void SetValueAtTime(float aValue, double aStartTime, ErrorResult& aRv)
     319             :   {
     320             :     AudioTimelineEvent event(AudioTimelineEvent::SetValueAtTime, aStartTime, aValue);
     321             : 
     322             :     if (ValidateEvent(event, aRv)) {
     323             :       InsertEvent<double>(event);
     324             :     }
     325             :   }
     326             : 
     327             :   template <class ErrorResult>
     328             :   void LinearRampToValueAtTime(float aValue, double aEndTime, ErrorResult& aRv)
     329             :   {
     330             :     AudioTimelineEvent event(AudioTimelineEvent::LinearRamp, aEndTime, aValue);
     331             : 
     332             :     if (ValidateEvent(event, aRv)) {
     333             :       InsertEvent<double>(event);
     334             :     }
     335             :   }
     336             : 
     337             :   template <class ErrorResult>
     338             :   void ExponentialRampToValueAtTime(float aValue, double aEndTime, ErrorResult& aRv)
     339             :   {
     340             :     AudioTimelineEvent event(AudioTimelineEvent::ExponentialRamp, aEndTime, aValue);
     341             : 
     342             :     if (ValidateEvent(event, aRv)) {
     343             :       InsertEvent<double>(event);
     344             :     }
     345             :   }
     346             : 
     347             :   template <class ErrorResult>
     348             :   void SetTargetAtTime(float aTarget, double aStartTime, double aTimeConstant, ErrorResult& aRv)
     349             :   {
     350             :     AudioTimelineEvent event(AudioTimelineEvent::SetTarget, aStartTime, aTarget, aTimeConstant);
     351             : 
     352             :     if (ValidateEvent(event, aRv)) {
     353             :       InsertEvent<double>(event);
     354             :     }
     355             :   }
     356             : 
     357             :   template <class ErrorResult>
     358             :   void SetValueCurveAtTime(const float* aValues, uint32_t aValuesLength, double aStartTime, double aDuration, ErrorResult& aRv)
     359             :   {
     360             :     AudioTimelineEvent event(AudioTimelineEvent::SetValueCurve, aStartTime, 0.0f, 0.0f, aDuration, aValues, aValuesLength);
     361             :     if (ValidateEvent(event, aRv)) {
     362             :       InsertEvent<double>(event);
     363             :     }
     364             :   }
     365             : 
     366             :   template<typename TimeType>
     367           0 :   void CancelScheduledValues(TimeType aStartTime)
     368             :   {
     369           0 :     for (unsigned i = 0; i < mEvents.Length(); ++i) {
     370           0 :       if (mEvents[i].template Time<TimeType>() >= aStartTime) {
     371             : #ifdef DEBUG
     372             :         // Sanity check: the array should be sorted, so all of the following
     373             :         // events should have a time greater than aStartTime too.
     374           0 :         for (unsigned j = i + 1; j < mEvents.Length(); ++j) {
     375           0 :           MOZ_ASSERT(mEvents[j].template Time<TimeType>() >= aStartTime);
     376             :         }
     377             : #endif
     378           0 :         mEvents.TruncateLength(i);
     379           0 :         break;
     380             :       }
     381             :     }
     382           0 :   }
     383             : 
     384             :   void CancelAllEvents()
     385             :   {
     386             :     mEvents.Clear();
     387             :   }
     388             : 
     389           0 :   static bool TimesEqual(int64_t aLhs, int64_t aRhs)
     390             :   {
     391           0 :     return aLhs == aRhs;
     392             :   }
     393             : 
     394             :   // Since we are going to accumulate error by adding 0.01 multiple time in a
     395             :   // loop, we want to fuzz the equality check in GetValueAtTime.
     396           0 :   static bool TimesEqual(double aLhs, double aRhs)
     397             :   {
     398           0 :     const float kEpsilon = 0.0000000001f;
     399           0 :     return fabs(aLhs - aRhs) < kEpsilon;
     400             :   }
     401             : 
     402             :   template<class TimeType>
     403           0 :   float GetValueAtTime(TimeType aTime)
     404             :   {
     405             :     float result;
     406           0 :     GetValuesAtTimeHelper(aTime, &result, 1);
     407           0 :     return result;
     408             :   }
     409             : 
     410             :   template<class TimeType>
     411           0 :   void GetValuesAtTime(TimeType aTime, float* aBuffer, const size_t aSize)
     412             :   {
     413           0 :     MOZ_ASSERT(aBuffer);
     414           0 :     GetValuesAtTimeHelper(aTime, aBuffer, aSize);
     415           0 :   }
     416             : 
     417             :   // Return the number of events scheduled
     418             :   uint32_t GetEventCount() const
     419             :   {
     420             :     return mEvents.Length();
     421             :   }
     422             : 
     423             :   template<class TimeType>
     424           0 :   void CleanupEventsOlderThan(TimeType aTime)
     425             :   {
     426           0 :     while (mEvents.Length() > 1 &&
     427           0 :         aTime > mEvents[1].template Time<TimeType>()) {
     428             : 
     429           0 :       if (mEvents[1].mType == AudioTimelineEvent::SetTarget) {
     430           0 :         mLastComputedValue = GetValuesAtTimeHelperInternal(
     431           0 :                                 mEvents[1].template Time<TimeType>(),
     432           0 :                                 &mEvents[0], nullptr);
     433             :       }
     434             : 
     435           0 :       mEvents.RemoveElementAt(0);
     436             :     }
     437           0 :   }
     438             : 
     439             : private:
     440             :   template<class TimeType>
     441             :   void GetValuesAtTimeHelper(TimeType aTime, float* aBuffer, const size_t aSize);
     442             : 
     443             :   template<class TimeType>
     444             :   float GetValueAtTimeOfEvent(const AudioTimelineEvent* aNext);
     445             : 
     446             :   template<class TimeType>
     447             :   float GetValuesAtTimeHelperInternal(TimeType aTime,
     448             :                                       const AudioTimelineEvent* aPrevious,
     449             :                                       const AudioTimelineEvent* aNext);
     450             : 
     451             :   const AudioTimelineEvent* GetPreviousEvent(double aTime) const;
     452             : 
     453           0 :   static bool IsValid(double value)
     454             :   {
     455           0 :     return mozilla::IsFinite(value);
     456             :   }
     457             : 
     458             :   // This is a sorted array of the events in the timeline.  Queries of this
     459             :   // data structure should probably be more frequent than modifications to it,
     460             :   // and that is the reason why we're using a simple array as the data structure.
     461             :   // We can optimize this in the future if the performance of the array ends up
     462             :   // being a bottleneck.
     463             :   nsTArray<AudioTimelineEvent> mEvents;
     464             :   float mValue;
     465             :   // This is the value of this AudioParam we computed at the last tick.
     466             :   float mComputedValue;
     467             :   // This is the value of this AudioParam at the last tick of the previous event.
     468             :   float mLastComputedValue;
     469             : };
     470             : 
     471             : } // namespace dom
     472             : } // namespace mozilla
     473             : 
     474             : #endif

Generated by: LCOV version 1.13