Bug 1526599 [wpt PR 15215] - Use oversampling to compute frame number, a=testonly
authorRaymond Toy <rtoy@chromium.org>
Mon, 18 Feb 2019 19:25:17 +0000
changeset 519178 aefbbbff52203d20509585e73dda0bdf879b0b40
parent 519177 3cd20f04f2e8fe498b74173fb3e2d467ecbcae54
child 519179 bd2ca52435730fcc809e2b6d77cc31e4a95713da
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1526599, 15215, 876917, 1446869, 629697
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1526599 [wpt PR 15215] - Use oversampling to compute frame number, a=testonly Automatic update from web-platform-tests Use oversampling to compute frame number When computing the frame number, round-off can happen such that the resulting frame number is not the same as the original. To work around this, use oversampling to compute the oversampled frame number. Round that to the nearest frame, and then convert that back to the desired integer frame number at the original sample rate. This won't always work, but should make the round-off errors much less likely. Bug: 876917 Test: the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html Change-Id: Id3658a986490d2aec07a382497bc24a4d99e416c Reviewed-on: https://chromium-review.googlesource.com/c/1446869 Reviewed-by: Hongchan Choi <hongchan@chromium.org> Commit-Queue: Raymond Toy <rtoy@chromium.org> Cr-Commit-Position: refs/heads/master@{#629697} -- wpt-commits: 5a6917c5cb8ea8602c53fd927fe3c208bb99b463 wpt-pr: 15215
testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/resources/sub-sample-scheduling.html
testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html
testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-scheduling.html
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html
@@ -0,0 +1,133 @@
+<!doctype html>
+<html>
+  <head>
+    <title>
+      Test Sub-Sample Accurate Stitching of ABSNs
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
+  </head>
+  <body>
+    <script>
+      let audit = Audit.createTaskRunner();
+
+      audit.define(
+          {
+            label: 'buffer-stitching-1',
+            description: 'Subsample buffer stitching, same rates'
+          },
+          (task, should) => {
+            const sampleRate = 44100;
+            const bufferRate = 44100;
+            const bufferLength = 30;
+
+            // Experimentally determined thresholds.  DO NOT relax these values
+            // to far from these values to make the tests pass.
+            const errorThreshold = 9.0957e-5;
+            const snrThreshold = 85.586;
+
+            // Informative message
+            should(sampleRate, 'Test 1: context.sampleRate')
+                .beEqualTo(sampleRate);
+            testBufferStitching(sampleRate, bufferRate, bufferLength)
+                .then(resultBuffer => {
+                  const actual = resultBuffer.getChannelData(0);
+                  const expected = resultBuffer.getChannelData(1);
+                  should(
+                      actual,
+                      `Stitched sine-wave buffers at sample rate ${bufferRate}`)
+                      .beCloseToArray(
+                          expected, {absoluteThreshold: errorThreshold});
+                  const SNR = 10 * Math.log10(computeSNR(actual, expected));
+                  should(SNR, `SNR (${SNR} dB)`)
+                      .beGreaterThanOrEqualTo(snrThreshold);
+                })
+                .then(() => task.done());
+          });
+
+      audit.define(
+          {
+            label: 'buffer-stitching-2',
+            description: 'Subsample buffer stitching, different rates'
+          },
+          (task, should) => {
+            const sampleRate = 44100;
+            const bufferRate = 43800;
+            const bufferLength = 30;
+
+            // Experimentally determined thresholds.  DO NOT relax these values
+            // to far from these values to make the tests pass.
+            const errorThreshold = 3.8986e-3;
+            const snrThreshold = 65.737;
+
+            // Informative message
+            should(sampleRate, 'Test 2: context.sampleRate')
+                .beEqualTo(sampleRate);
+            testBufferStitching(sampleRate, bufferRate, bufferLength)
+                .then(resultBuffer => {
+                  const actual = resultBuffer.getChannelData(0);
+                  const expected = resultBuffer.getChannelData(1);
+                  should(
+                      actual,
+                      `Stitched sine-wave buffers at sample rate ${bufferRate}`)
+                      .beCloseToArray(
+                          expected, {absoluteThreshold: errorThreshold});
+                  const SNR = 10 * Math.log10(computeSNR(actual, expected));
+                  should(SNR, `SNR (${SNR} dB)`)
+                      .beGreaterThanOrEqualTo(snrThreshold);
+                })
+                .then(() => task.done());
+          });
+
+      audit.run();
+
+      // Create graph to test stitching of consecutive ABSNs.  The context rate
+      // is |sampleRate|, and the buffers have a fixed length of |bufferLength|
+      // and rate of |bufferRate|.  The |bufferRate| should not be too different
+      // from |sampleRate| because of interpolation of the buffer to the context
+      // rate.
+      function testBufferStitching(sampleRate, bufferRate, bufferLength) {
+        // The context for testing.  Channel 0 contains the output from
+        // stitching all the buffers together, and channel 1 contains the
+        // expected output.
+        const context = new OfflineAudioContext(
+            {numberOfChannels: 2, length: sampleRate, sampleRate: sampleRate});
+
+        const merger = new ChannelMergerNode(
+            context, {numberOfInputs: context.destination.channelCount});
+
+        merger.connect(context.destination);
+
+        // The reference is a sine wave at 440 Hz.
+        const ref = new OscillatorNode(context, {frequency: 440, type: 'sine'});
+        ref.connect(merger, 0, 1);
+        ref.start();
+
+        // The test signal is a bunch of short AudioBufferSources containing
+        // bits of a sine wave.
+        let waveSignal = new Float32Array(context.length);
+        const omega = 2 * Math.PI / bufferRate * ref.frequency.value;
+        for (let k = 0; k < context.length; ++k) {
+          waveSignal[k] = Math.sin(omega * k);
+        }
+
+        // Slice the sine wave into many little buffers to be assigned to ABSNs
+        // that are started at the appropriate times to produce a final sine
+        // wave.
+        for (let k = 0; k < context.length; k += bufferLength) {
+          const buffer =
+              new AudioBuffer({length: bufferLength, sampleRate: bufferRate});
+          buffer.copyToChannel(waveSignal.slice(k, k + bufferLength), 0);
+
+          const src = new AudioBufferSourceNode(context, {buffer: buffer});
+          src.connect(merger, 0, 0);
+          src.start(k / bufferRate);
+        }
+
+        return context.startRendering();
+      }
+    </script>
+  </body>
+</html>
rename from testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/resources/sub-sample-scheduling.html
rename to testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-scheduling.html