Bug 1313058 - Fix SetValueCurveAtTime interpolation; r=padenot
This interpolates over aCurveLength - 1 steps rather than over
aCurveLength steps as was done before.
Previously we would reach the final value on the curve before
the end of the specified duration. For example, on the curve
[1.0, 0.0] with duration 1000, we would interpolate from 1.0 to
0.0 by time 500 rather than time 1000. With these changes, we
don't reach 0.0 until time 1000, as expected.
This also updates TestSpecExample in TestAudioEventTimeline.cpp
to match the curve in the latest spec.
MozReview-Commit-ID: Cgs8csbRUMh
--- a/dom/media/webaudio/AudioEventTimeline.cpp
+++ b/dom/media/webaudio/AudioEventTimeline.cpp
@@ -32,21 +32,22 @@ static float ExtractValueFromCurve(doubl
if (t >= startTime + duration) {
// After the duration, return the last curve value
return aCurve[aCurveLength - 1];
}
double ratio = std::max((t - startTime) / duration, 0.0);
if (ratio >= 1.0) {
return aCurve[aCurveLength - 1];
}
- uint32_t current = uint32_t(aCurveLength * ratio);
+ uint32_t current = uint32_t(floor((aCurveLength - 1) * ratio));
uint32_t next = current + 1;
+ double step = duration / double(aCurveLength - 1);
if (next < aCurveLength) {
- double t0 = double(current) / double(aCurveLength) * duration ;
- double t1 = double(next) / double(aCurveLength) * duration ;
+ double t0 = current * step;
+ double t1 = next * step;
return LinearInterpolate(t0, aCurve[current], t1, aCurve[next], t - startTime);
} else {
return aCurve[current];
}
}
namespace mozilla {
namespace dom {
--- a/dom/media/webaudio/compiledtest/TestAudioEventTimeline.cpp
+++ b/dom/media/webaudio/compiledtest/TestAudioEventTimeline.cpp
@@ -97,17 +97,21 @@ typedef AudioEventTimeline Timeline;
void TestSpecExample()
{
// First, run the basic tests
Timeline timeline(10.0f);
is(timeline.Value(), 10.0f, "Correct default value returned");
ErrorResultMock rv;
- float curve[] = { -1.0f, 0.0f, 1.0f };
+ uint32_t curveLength = 44100;
+ float* curve = new float[curveLength];
+ for (uint32_t i = 0; i < curveLength; ++i) {
+ curve[i] = sin(M_PI * i / float(curveLength));
+ }
// This test is copied from the example in the Web Audio spec
const double t0 = 0.0,
t1 = 0.1,
t2 = 0.2,
t3 = 0.3,
t4 = 0.4,
t5 = 0.6,
@@ -122,37 +126,38 @@ void TestSpecExample()
timeline.LinearRampToValueAtTime(1.0f, t3, rv);
is(rv, NS_OK, "LinearRampToValueAtTime succeeded");
timeline.LinearRampToValueAtTime(0.15f, t4, rv);
is(rv, NS_OK, "LinearRampToValueAtTime succeeded");
timeline.ExponentialRampToValueAtTime(0.75f, t5, rv);
is(rv, NS_OK, "ExponentialRampToValueAtTime succeeded");
timeline.ExponentialRampToValueAtTime(0.05f, t6, rv);
is(rv, NS_OK, "ExponentialRampToValueAtTime succeeded");
- timeline.SetValueCurveAtTime(curve, ArrayLength(curve), t6, t7 - t6, rv);
+ timeline.SetValueCurveAtTime(curve, curveLength, t6, t7 - t6, rv);
is(rv, NS_OK, "SetValueCurveAtTime succeeded");
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.05f / 0.75f, 0.5f)), "Correct value");
- is(timeline.GetValueAtTime(0.7), -1.0f, "Correct value");
- is(timeline.GetValueAtTime(0.8), 0.0f, "Correct value");
- is(timeline.GetValueAtTime(0.9), 1.0f, "Correct value");
- is(timeline.GetValueAtTime(1.0), 1.0f, "Correct value");
+ is(timeline.GetValueAtTime(0.7), 0.0f, "Correct value");
+ is(timeline.GetValueAtTime(0.85), 1.0f, "Correct value");
+ is(timeline.GetValueAtTime(1.0), curve[curveLength - 1], "Correct value");
+
+ delete[] curve;
}
void TestInvalidEvents()
{
static_assert(numeric_limits<float>::has_quiet_NaN, "Platform must have a quiet NaN");
const float NaN = numeric_limits<float>::quiet_NaN();
const float Infinity = numeric_limits<float>::infinity();
Timeline timeline(10.0f);
--- a/dom/media/webaudio/test/test_audioParamSetCurveAtTime.html
+++ b/dom/media/webaudio/test/test_audioParamSetCurveAtTime.html
@@ -25,23 +25,24 @@ var gTest = {
source.start(0);
return gain;
},
createExpectedBuffers: function(context) {
this.duration = 1024 / context.sampleRate;
this.curve = new Float32Array([1.0, 0.5, 0.75, 0.25]);
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
var data = expectedBuffer.getChannelData(0);
+ var step = 1024 / 3;
for (var i = 0; i < 2048; ++i) {
- if (i < 256) {
- data[i] = 1.0 - 0.5*i/256;
- } else if (i < 512) {
- data[i] = 0.5 + 0.25*(i - 256)/256;
- } else if (i < 768) {
- data[i] = 0.75 - 0.5*(i - 512)/256;
+ if (i < step) {
+ data[i] = 1.0 - 0.5*i/step;
+ } else if (i < 2*step) {
+ data[i] = 0.5 + 0.25*(i - step)/step;
+ } else if (i < 3*step) {
+ data[i] = 0.75 - 0.5*(i - 2*step)/step;
} else {
data[i] = 0.25;
}
}
return expectedBuffer;
},
};
--- a/dom/media/webaudio/test/test_audioParamSetCurveAtTimeTwice.html
+++ b/dom/media/webaudio/test/test_audioParamSetCurveAtTimeTwice.html
@@ -42,25 +42,23 @@ var gTest = {
createExpectedBuffers: function(context) {
this.duration = 1024 / context.sampleRate;
this.curve = new Float32Array(100);
for (var i = 0; i < 100; ++i) {
this.curve[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
}
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
for (var i = 0; i < 2048; ++i) {
- var t = i / context.sampleRate;
- var current = Math.min(99, Math.floor(100 * Math.min(1.0, (t - T0) / this.duration)));
+ step = 1024.0/99.0;
+ var current = Math.floor(i / step);
var next = current + 1;
if (next < this.curve.length) {
- var t0 = current / this.curve.length * this.duration;
- var t1 = next / this.curve.length * this.duration;
- expectedBuffer.getChannelData(0)[i] = linearInterpolate(t0, this.curve[current], t1, this.curve[next], t);
+ expectedBuffer.getChannelData(0)[i] = linearInterpolate(current*step, this.curve[current], next*step, this.curve[next], i);
} else {
- expectedBuffer.getChannelData(0)[i] = this.curve[current];
+ expectedBuffer.getChannelData(0)[i] = this.curve[99];
}
}
return expectedBuffer;
},
};
runTest();