Bug 1313058 - Fix SetValueCurveAtTime interpolation; r=padenot
authorDan Minor <dminor@mozilla.com>
Wed, 26 Oct 2016 10:33:20 -0400
changeset 347615 9bb46ea431c62578546c50f6b4610f3f7118bec3
parent 347502 fa679b6eced2ad9a0850d69fd4bbd3f448921ddf
child 347616 f450a6c50f9e320a92feb3410c910137328cce4a
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1313058
milestone52.0a1
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
dom/media/webaudio/AudioEventTimeline.cpp
dom/media/webaudio/compiledtest/TestAudioEventTimeline.cpp
dom/media/webaudio/test/test_audioParamSetCurveAtTime.html
dom/media/webaudio/test/test_audioParamSetCurveAtTimeTwice.html
--- 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();