Bug 1651830 [wpt PR 24541] - Add some basic Oscillator tests, a=testonly
authorRaymond Toy <rtoy@chromium.org>
Fri, 17 Jul 2020 11:14:49 +0000
changeset 541060 17cb2fe2abe2e502bc5593e0f9e3265a53cac3be
parent 541059 047384b455a0ee165039f854d80b796922b05e01
child 541061 f2d81c219006dad087c6b185fe6b21b9dac9ce44
push id37613
push userbtara@mozilla.com
push dateSat, 18 Jul 2020 09:26:25 +0000
treeherdermozilla-central@08cd64cdbc3b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1651830, 24541, 1013118, 2290693, 787297
milestone80.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 1651830 [wpt PR 24541] - Add some basic Oscillator tests, a=testonly Automatic update from web-platform-tests Add some basic Oscillator tests Test the output of the OscillatorNode for a sine wave at various frequencies (including a negative frequency) against the exact mathematical output. The low frequencies are intended to test Chrome's interpolation algorithm. Since we use sine waves or custom waves with a very few known coefficients (without normalization), we know what the exact output should be. Bug: 1013118 Change-Id: Ie68ef5a8e6979aab70d0a4d4c24ee31810599f56 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2290693 Reviewed-by: Hongchan Choi <hongchan@chromium.org> Commit-Queue: Raymond Toy <rtoy@chromium.org> Cr-Commit-Position: refs/heads/master@{#787297} -- wpt-commits: 980dc0c7625921634185a5369c05506196102b53 wpt-pr: 24541
testing/web-platform/tests/webaudio/the-audio-api/the-oscillatornode-interface/osc-basic-waveform.html
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-oscillatornode-interface/osc-basic-waveform.html
@@ -0,0 +1,229 @@
+<!doctype html>
+<html>
+  <head>
+    <title>
+      Test Basic Oscillator Sine Wave Test
+    </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>
+      // Don't change the sample rate.  The tests below depend on this sample
+      // rate to cover all the cases in Chrome's implementation.  But the tests
+      // are general and apply to any browser.
+      const sampleRate = 44100;
+
+      // Only need a few samples for testing, so just use two renders.
+      const durationFrames = 2 * RENDER_QUANTUM_FRAMES;
+
+      let audit = Audit.createTaskRunner();
+
+      // The following tests verify that the oscillator produces the same
+      // results as the mathematical oscillators.  We choose sine wave and a
+      // custom wave because we know they're bandlimited and won't change with
+      // the frequency.
+      //
+      // The tests for 1 and 2 Hz are intended to test Chrome's interpolation
+      // algorithm, but are still generally applicable to any browser.
+
+      audit.define(
+          {label: 'Test 0', description: 'Sine wave: 100 Hz'},
+          async (task, should) => {
+            let context = new OfflineAudioContext(
+                {length: durationFrames, sampleRate: sampleRate});
+
+            const freqHz = 100;
+
+            let src =
+                new OscillatorNode(context, {type: 'sine', frequency: freqHz});
+            src.connect(context.destination);
+
+            src.start();
+
+            let renderedBuffer = await context.startRendering();
+            checkResult(should, renderedBuffer, context, {
+              freqHz: freqHz,
+              a1: 0,
+              b1: 1,
+              prefix: 'Sine',
+              threshold: 1.8045e-6,
+              snrThreshold: 118.91
+            });
+            task.done();
+          });
+
+      audit.define(
+          {label: 'Test 1', description: 'Sine wave: -100 Hz'},
+          async (task, should) => {
+            let context = new OfflineAudioContext(
+                {length: durationFrames, sampleRate: sampleRate});
+
+            const freqHz = -100;
+
+            let src =
+                new OscillatorNode(context, {type: 'sine', frequency: freqHz});
+            src.connect(context.destination);
+
+            src.start();
+
+            let renderedBuffer = await context.startRendering();
+            checkResult(should, renderedBuffer, context, {
+              freqHz: freqHz,
+              a1: 0,
+              b1: 1,
+              prefix: 'Sine',
+              threshold: 4.1724e-7,
+              snrThreshold: 130.95
+            });
+            task.done();
+          });
+
+      audit.define(
+          {label: 'Test 2', description: 'Sine wave: 2 Hz'},
+          async (task, should) => {
+            let context = new OfflineAudioContext(
+                {length: durationFrames, sampleRate: sampleRate});
+
+            const freqHz = 2;
+
+            let src =
+                new OscillatorNode(context, {type: 'sine', frequency: freqHz});
+            src.connect(context.destination);
+
+            src.start();
+
+            let renderedBuffer = await context.startRendering();
+            checkResult(should, renderedBuffer, context, {
+              freqHz: freqHz,
+              a1: 0,
+              b1: 1,
+              prefix: 'Sine',
+              threshold: 1.4516e-7,
+              snrThreshold: 119.93
+            });
+            task.done();
+          });
+
+      audit.define(
+          {label: 'Test 3', description: 'Sine wave: 1 Hz'},
+          async (task, should) => {
+            let context = new OfflineAudioContext(
+                {length: durationFrames, sampleRate: sampleRate});
+
+            const freqHz = 1;
+
+            let src =
+                new OscillatorNode(context, {type: 'sine', frequency: freqHz});
+            src.connect(context.destination);
+
+            src.start();
+
+            let renderedBuffer = await context.startRendering();
+            checkResult(should, renderedBuffer, context, {
+              freqHz: freqHz,
+              a1: 0,
+              b1: 1,
+              prefix: 'Sine',
+              threshold: 1.4157e-7,
+              snrThreshold: 112.22
+            });
+            task.done();
+          });
+
+      audit.define(
+          {label: 'Test 4', description: 'Custom wave: 100 Hz'},
+          async (task, should) => {
+            let context = new OfflineAudioContext(
+                {length: durationFrames, sampleRate: sampleRate});
+
+            const freqHz = 100;
+
+            let wave = new PeriodicWave(
+                context,
+                {real: [0, 1], imag: [0, 1], disableNormalization: true});
+            let src = new OscillatorNode(
+                context,
+                {type: 'custom', frequency: freqHz, periodicWave: wave});
+            src.connect(context.destination);
+
+            src.start();
+
+            let renderedBuffer = await context.startRendering();
+            checkResult(should, renderedBuffer, context, {
+              freqHz: freqHz,
+              a1: 1,
+              b1: 1,
+              prefix: 'Custom',
+              threshold: 1.8478e-6,
+              snrThreshold: 122.43
+            });
+            task.done();
+          });
+
+      audit.define(
+          {label: 'Test 5', description: 'Custom wave: 1 Hz'},
+          async (task, should) => {
+            let context = new OfflineAudioContext(
+                {length: durationFrames, sampleRate: sampleRate});
+
+            const freqHz = 1;
+
+            let wave = new PeriodicWave(
+                context,
+                {real: [0, 1], imag: [0, 1], disableNormalization: true});
+            let src = new OscillatorNode(
+                context,
+                {type: 'custom', frequency: freqHz, periodicWave: wave});
+            src.connect(context.destination);
+
+            src.start();
+
+            let renderedBuffer = await context.startRendering();
+            checkResult(should, renderedBuffer, context, {
+              freqHz: freqHz,
+              a1: 1,
+              b1: 1,
+              prefix: 'Custom',
+              threshold: 4.7684e-7,
+              snrThreshold: 138.76
+            });
+            task.done();
+          });
+
+      audit.run();
+
+      function waveForm(context, freqHz, a1, b1, nsamples) {
+        let buffer =
+            new AudioBuffer({length: nsamples, sampleRate: context.sampleRate});
+        let signal = buffer.getChannelData(0);
+        const omega = 2 * Math.PI * freqHz / context.sampleRate;
+        for (let k = 0; k < nsamples; ++k) {
+          signal[k] = a1 * Math.cos(omega * k) + b1 * Math.sin(omega * k);
+        }
+
+        return buffer;
+      }
+
+      function checkResult(should, renderedBuffer, context, options) {
+        let {freqHz, a1, b1, prefix, threshold, snrThreshold} = options;
+
+        let actual = renderedBuffer.getChannelData(0);
+
+        let expected =
+            waveForm(context, freqHz, a1, b1, actual.length).getChannelData(0);
+
+        should(actual, `${prefix}: ${freqHz} Hz`).beCloseToArray(expected, {
+          absoluteThreshold: threshold
+        });
+
+        let snr = 10 * Math.log10(computeSNR(actual, expected));
+
+        should(snr, `${prefix}: SNR (db)`).beGreaterThanOrEqualTo(snrThreshold);
+      }
+    </script>
+  </body>
+</html>