Bug 836076 - Part 5: Provide an API for converting event times to tick values; r=roc

This commit is contained in:
Ehsan Akhgari 2013-03-01 17:06:03 -05:00
parent 030416b20c
commit 61bbd92228
2 changed files with 92 additions and 45 deletions

View File

@ -33,6 +33,9 @@ struct AudioTimelineEvent {
: mType(aType)
, mTimeConstant(aTimeConstant)
, mDuration(aDuration)
#ifdef DEBUG
, mTimeIsInTicks(false)
#endif
{
if (aType == AudioTimelineEvent::SetValueCurve) {
mCurve = aCurve;
@ -51,17 +54,39 @@ struct AudioTimelineEvent {
IsValid(mDuration);
}
template <class TimeType>
TimeType Time() const;
void SetTimeInTicks(int64_t aTimeInTicks)
{
mTimeInTicks = aTimeInTicks;
#ifdef DEBUG
mTimeIsInTicks = true;
#endif
}
Type mType;
union {
float mValue;
uint32_t mCurveLength;
};
union {
double mTime;
// The time for an event can either be in absolute value or in ticks.
// Initially the time of the event is always in absolute value.
// In order to convert it to ticks, call SetTimeInTicks. Once this
// method has been called for an event, the time cannot be converted
// back to absolute value.
union {
double mTime;
int64_t mTimeInTicks;
};
float* mCurve;
};
double mTimeConstant;
double mDuration;
#ifdef DEBUG
bool mTimeIsInTicks;
#endif
private:
static bool IsValid(double value)
@ -70,6 +95,20 @@ private:
}
};
template <>
inline double AudioTimelineEvent::Time<double>() const
{
MOZ_ASSERT(!mTimeIsInTicks);
return mTime;
}
template <>
inline int64_t AudioTimelineEvent::Time<int64_t>() const
{
MOZ_ASSERT(mTimeIsInTicks);
return mTimeInTicks;
}
/**
* This class will be instantiated with different template arguments for testing and
* production code.
@ -151,7 +190,8 @@ public:
}
// This method computes the AudioParam value at a given time based on the event timeline
float GetValueAtTime(double aTime) const
template<class TimeType>
float GetValueAtTime(TimeType aTime) const
{
const AudioTimelineEvent* previous = nullptr;
const AudioTimelineEvent* next = nullptr;
@ -163,17 +203,17 @@ public:
case AudioTimelineEvent::SetTarget:
case AudioTimelineEvent::LinearRamp:
case AudioTimelineEvent::ExponentialRamp:
if (aTime == mEvents[i].mTime) {
if (aTime == mEvents[i].template Time<TimeType>()) {
// Find the last event with the same time
do {
++i;
} while (i < mEvents.Length() &&
aTime == mEvents[i].mTime);
aTime == mEvents[i].template Time<TimeType>());
return mEvents[i - 1].mValue;
}
previous = next;
next = &mEvents[i];
if (aTime < mEvents[i].mTime) {
if (aTime < mEvents[i].template Time<TimeType>()) {
bailOut = true;
}
break;
@ -204,10 +244,10 @@ public:
return mValue;
case AudioTimelineEvent::LinearRamp:
// Use t=0 as T0 and v=defaultValue as V0
return LinearInterpolate(0.0, mValue, next->mTime, next->mValue, aTime);
return LinearInterpolate(0.0, mValue, next->template Time<TimeType>(), next->mValue, aTime);
case AudioTimelineEvent::ExponentialRamp:
// Use t=0 as T0 and v=defaultValue as V0
return ExponentialInterpolate(0.0, mValue, next->mTime, next->mValue, aTime);
return ExponentialInterpolate(0.0, mValue, next->template Time<TimeType>(), next->mValue, aTime);
case AudioTimelineEvent::SetValueCurve:
// TODO: implement
return 0.0f;
@ -218,7 +258,7 @@ public:
// SetTarget nodes can be handled no matter what their next node is (if they have one)
if (previous->mType == AudioTimelineEvent::SetTarget) {
// Follow the curve, without regard to the next node
return ExponentialApproach(previous->mTime, mValue, previous->mValue,
return ExponentialApproach(previous->template Time<TimeType>(), mValue, previous->mValue,
previous->mTimeConstant, aTime);
}
@ -244,9 +284,9 @@ public:
// First, handle the case where our range ends up in a ramp event
switch (next->mType) {
case AudioTimelineEvent::LinearRamp:
return LinearInterpolate(previous->mTime, previous->mValue, next->mTime, next->mValue, aTime);
return LinearInterpolate(previous->template Time<TimeType>(), previous->mValue, next->template Time<TimeType>(), next->mValue, aTime);
case AudioTimelineEvent::ExponentialRamp:
return ExponentialInterpolate(previous->mTime, previous->mValue, next->mTime, next->mValue, aTime);
return ExponentialInterpolate(previous->template Time<TimeType>(), previous->mValue, next->template Time<TimeType>(), next->mValue, aTime);
case AudioTimelineEvent::SetValue:
case AudioTimelineEvent::SetTarget:
case AudioTimelineEvent::SetValueCurve:
@ -293,6 +333,13 @@ public:
return v1 + (v0 - v1) * expf(-(t - t0) / timeConstant);
}
void ConvertEventTimesToTicks(int64_t (*aConvertor)(double aTime, void* aClosure), void* aClosure)
{
for (unsigned i = 0; i < mEvents.Length(); ++i) {
mEvents[i].SetTimeInTicks(aConvertor(mEvents[i].template Time<double>(), aClosure));
}
}
private:
const AudioTimelineEvent* GetPreviousEvent(double aTime) const
{

View File

@ -91,14 +91,14 @@ void TestSpecExample()
ErrorResultMock rv;
// This test is copied from the example in the Web Audio spec
const float t0 = 0.0,
t1 = 0.1,
t2 = 0.2,
t3 = 0.3,
t4 = 0.4,
t5 = 0.6,
t6 = 0.7/*,
t7 = 1.0*/;
const double t0 = 0.0,
t1 = 0.1,
t2 = 0.2,
t3 = 0.3,
t4 = 0.4,
t5 = 0.6,
t6 = 0.7/*,
t7 = 1.0*/;
timeline.SetValueAtTime(0.2f, t0, rv);
is(rv, NS_OK, "SetValueAtTime succeeded");
timeline.SetValueAtTime(0.3f, t1, rv);
@ -115,22 +115,22 @@ void TestSpecExample()
is(rv, NS_OK, "ExponentialRampToValueAtTime succeeded");
// TODO: Add the SetValueCurveAtTime test
is(timeline.GetValueAtTime(0.0f), 0.2f, "Correct value");
is(timeline.GetValueAtTime(0.05f), 0.2f, "Correct value");
is(timeline.GetValueAtTime(0.1f), 0.3f, "Correct value");
is(timeline.GetValueAtTime(0.15f), 0.3f, "Correct value");
is(timeline.GetValueAtTime(0.2f), 0.4f, "Correct value");
is(timeline.GetValueAtTime(0.25f), (0.4f + 1.0f) / 2, "Correct value");
is(timeline.GetValueAtTime(0.3f), 1.0f, "Correct value");
is(timeline.GetValueAtTime(0.35f), (1.0f + 0.15f) / 2, "Correct value");
is(timeline.GetValueAtTime(0.4f), 0.15f, "Correct value");
is(timeline.GetValueAtTime(0.45f), (0.15f * powf(0.75f / 0.15f, 0.05f / 0.2f)), "Correct value");
is(timeline.GetValueAtTime(0.5f), (0.15f * powf(0.75f / 0.15f, 0.5f)), "Correct value");
is(timeline.GetValueAtTime(0.55f), (0.15f * powf(0.75f / 0.15f, 0.15f / 0.2f)), "Correct value");
is(timeline.GetValueAtTime(0.6f), 0.75f, "Correct value");
is(timeline.GetValueAtTime(0.65f), (0.75f * powf(0.05 / 0.75f, 0.5f)), "Correct value");
is(timeline.GetValueAtTime(0.7f), 0.05f, "Correct value");
is(timeline.GetValueAtTime(1.0f), 0.05f, "Correct value");
is(timeline.GetValueAtTime(0.0), 0.2f, "Correct value");
is(timeline.GetValueAtTime(0.05), 0.2f, "Correct value");
is(timeline.GetValueAtTime(0.1), 0.3f, "Correct value");
is(timeline.GetValueAtTime(0.15), 0.3f, "Correct value");
is(timeline.GetValueAtTime(0.2), 0.4f, "Correct value");
is(timeline.GetValueAtTime(0.25), (0.4f + 1.0f) / 2, "Correct value");
is(timeline.GetValueAtTime(0.3), 1.0f, "Correct value");
is(timeline.GetValueAtTime(0.35), (1.0f + 0.15f) / 2, "Correct value");
is(timeline.GetValueAtTime(0.4), 0.15f, "Correct value");
is(timeline.GetValueAtTime(0.45), (0.15f * powf(0.75f / 0.15f, 0.05f / 0.2f)), "Correct value");
is(timeline.GetValueAtTime(0.5), (0.15f * powf(0.75f / 0.15f, 0.5f)), "Correct value");
is(timeline.GetValueAtTime(0.55), (0.15f * powf(0.75f / 0.15f, 0.15f / 0.2f)), "Correct value");
is(timeline.GetValueAtTime(0.6), 0.75f, "Correct value");
is(timeline.GetValueAtTime(0.65), (0.75f * powf(0.05 / 0.75f, 0.5f)), "Correct value");
is(timeline.GetValueAtTime(0.7), 0.05f, "Correct value");
is(timeline.GetValueAtTime(1.0), 0.05f, "Correct value");
}
void TestInvalidEvents()
@ -189,11 +189,11 @@ void TestEventReplacement()
timeline.SetValueAtTime(20.0f, 0.1, rv);
is(rv, NS_OK, "Event scheduling should be successful");
is(timeline.GetEventCount(), 1u, "Event should be replaced");
is(timeline.GetValueAtTime(0.1f), 20.0f, "The first event should be overwritten");
is(timeline.GetValueAtTime(0.1), 20.0f, "The first event should be overwritten");
timeline.LinearRampToValueAtTime(30.0f, 0.1, rv);
is(rv, NS_OK, "Event scheduling should be successful");
is(timeline.GetEventCount(), 2u, "Different event type should be appended");
is(timeline.GetValueAtTime(0.1f), 30.0f, "The first event should be overwritten");
is(timeline.GetValueAtTime(0.1), 30.0f, "The first event should be overwritten");
}
void TestEventRemoval()
@ -222,7 +222,7 @@ void TestBeforeFirstEvent()
ErrorResultMock rv;
timeline.SetValueAtTime(20.0f, 1.0, rv);
is(timeline.GetValueAtTime(0.5f), 10.0f, "Retrun the default value before the first event");
is(timeline.GetValueAtTime(0.5), 10.0f, "Retrun the default value before the first event");
}
void TestAfterLastValueEvent()
@ -232,7 +232,7 @@ void TestAfterLastValueEvent()
ErrorResultMock rv;
timeline.SetValueAtTime(20.0f, 1.0, rv);
is(timeline.GetValueAtTime(1.5f), 20.0f, "Return the last value after the last SetValue event");
is(timeline.GetValueAtTime(1.5), 20.0f, "Return the last value after the last SetValue event");
}
void TestAfterLastTargetValueEvent()
@ -242,7 +242,7 @@ void TestAfterLastTargetValueEvent()
ErrorResultMock rv;
timeline.SetTargetAtTime(20.0f, 1.0, 5.0, rv);
is(timeline.GetValueAtTime(10.f), (20.f + (10.f - 20.f) * expf(-9.0f / 5.0f)), "Return the value after the last SetTarget event based on the curve");
is(timeline.GetValueAtTime(10.), (20.f + (10.f - 20.f) * expf(-9.0f / 5.0f)), "Return the value after the last SetTarget event based on the curve");
}
void TestAfterLastTargetValueEventWithValueSet()
@ -253,7 +253,7 @@ void TestAfterLastTargetValueEventWithValueSet()
timeline.SetValue(50.f);
timeline.SetTargetAtTime(20.0f, 1.0, 5.0, rv);
is(timeline.GetValueAtTime(10.f), (20.f + (50.f - 20.f) * expf(-9.0f / 5.0f)), "Return the value after SetValue and the last SetTarget event based on the curve");
is(timeline.GetValueAtTime(10.), (20.f + (50.f - 20.f) * expf(-9.0f / 5.0f)), "Return the value after SetValue and the last SetTarget event based on the curve");
}
void TestValue()
@ -279,7 +279,7 @@ void TestLinearRampAtZero()
ErrorResultMock rv;
timeline.LinearRampToValueAtTime(20.0f, 0.0, rv);
is(timeline.GetValueAtTime(0.0f), 20.0f, "Should get the correct value when t0 == t1 == 0");
is(timeline.GetValueAtTime(0.0), 20.0f, "Should get the correct value when t0 == t1 == 0");
}
void TestExponentialRampAtZero()
@ -289,7 +289,7 @@ void TestExponentialRampAtZero()
ErrorResultMock rv;
timeline.ExponentialRampToValueAtTime(20.0f, 0.0, rv);
is(timeline.GetValueAtTime(0.0f), 20.0f, "Should get the correct value when t0 == t1 == 0");
is(timeline.GetValueAtTime(0.0), 20.0f, "Should get the correct value when t0 == t1 == 0");
}
void TestLinearRampAtSameTime()
@ -300,7 +300,7 @@ void TestLinearRampAtSameTime()
timeline.SetValueAtTime(5.0f, 1.0, rv);
timeline.LinearRampToValueAtTime(20.0f, 1.0, rv);
is(timeline.GetValueAtTime(1.0f), 20.0f, "Should get the correct value when t0 == t1");
is(timeline.GetValueAtTime(1.0), 20.0f, "Should get the correct value when t0 == t1");
}
void TestExponentialRampAtSameTime()
@ -311,7 +311,7 @@ void TestExponentialRampAtSameTime()
timeline.SetValueAtTime(5.0f, 1.0, rv);
timeline.ExponentialRampToValueAtTime(20.0f, 1.0, rv);
is(timeline.GetValueAtTime(1.0f), 20.0f, "Should get the correct value when t0 == t1");
is(timeline.GetValueAtTime(1.0), 20.0f, "Should get the correct value when t0 == t1");
}
void TestSetTargetZeroTimeConstant()
@ -321,7 +321,7 @@ void TestSetTargetZeroTimeConstant()
ErrorResultMock rv;
timeline.SetTargetAtTime(20.0f, 1.0, 0.0, rv);
is(timeline.GetValueAtTime(10.f), 20.f, "Should get the correct value with timeConstant == 0");
is(timeline.GetValueAtTime(10.), 20.f, "Should get the correct value with timeConstant == 0");
}
void TestExponentialInvalidPreviousZeroValue()