| Index: third_party/WebKit/LayoutTests/webaudio/Oscillator/start-sampling.html
|
| diff --git a/third_party/WebKit/LayoutTests/webaudio/Oscillator/start-sampling.html b/third_party/WebKit/LayoutTests/webaudio/Oscillator/start-sampling.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..61032d0cf304b24e7666bee4117682ea1b0ea88c
|
| --- /dev/null
|
| +++ b/third_party/WebKit/LayoutTests/webaudio/Oscillator/start-sampling.html
|
| @@ -0,0 +1,173 @@
|
| +<!doctype html>
|
| +<html>
|
| + <head>
|
| + <title>Test Sampling of Oscillator Start Times</title>
|
| + <script src="../../resources/testharness.js"></script>
|
| + <script src="../../resources/testharnessreport.js"></script>
|
| + <script src="../resources/audit-util.js"></script>
|
| + <script src="../resources/audit.js"></script>
|
| + </head>
|
| +
|
| + <body>
|
| + <script>
|
| + // Experimentation indicates that this sample rate with a 440 Hz
|
| + // oscillator makes for a large difference in the difference signal if the
|
| + // oscillator start isn't sampled correctly.
|
| + let defaultSampleRate = 24000;
|
| + let renderDuration = 1;
|
| + let renderFrames = renderDuration * defaultSampleRate;
|
| +
|
| + let audit = Audit.createTaskRunner();
|
| +
|
| + audit.define("basic test small", function (task, should) {
|
| + task.describe("Start oscillator slightly past a sample frame")
|
| + testStartSampling(should, 1.25, {
|
| + error: 1.0842e-4,
|
| + snrThreshold: 84.054
|
| + })
|
| + .then(task.done.bind(task));
|
| + });
|
| +
|
| + audit.define("basic test big", function (task, should) {
|
| + task.describe("Start oscillator slightly before a sample frame")
|
| + testStartSampling(should, 1.75, {
|
| + error: 1.0839e-4,
|
| + snrThreshold: 84.056
|
| + })
|
| + .then(task.done.bind(task));
|
| + });
|
| +
|
| + audit.define("diff big offset", function (task, should) {
|
| + task.describe(
|
| + "Test sampling with start offset greater than 1/2 sampling frame"
|
| + );
|
| + // With a sample rate of 24000 Hz, and an oscillator frequency of 440 Hz
|
| + // (the default), a quarter wave delay is 13.636363... frames. This
|
| + // tests the case where the starting time is more than 1/2 frame from
|
| + // the preceding sampling frame. This tests one path of the internal
|
| + // implementation.
|
| + testStartWithGain(should, defaultSampleRate, {
|
| + error: 4.1724e-7,
|
| + snrThreshold: 137.536
|
| + })
|
| + .then(task.done.bind(task));
|
| + });
|
| +
|
| + audit.define("diff small offset", function (task, should) {
|
| + task.describe(
|
| + "Test sampling with start offset less than 1/2 sampling frame");
|
| + // With a sample rate of 48000 Hz, and an oscillator frequency of 440 Hz
|
| + // (the default), a quarter wave delay is 27.2727... frames. This tests
|
| + // the case where the starting time is less than 1/2 frame from the
|
| + // preceding sampling frame. This tests one path of the internal
|
| + // implementation.
|
| + testStartWithGain(should, 48000, {
|
| + error: 4.1724e-7,
|
| + snrThreshold: 137.536
|
| + })
|
| + .then(task.done.bind(task));
|
| + });
|
| +
|
| + function testStartSampling(should, startFrame, thresholds) {
|
| + // Start the oscillator in the middle of a sample frame and compare
|
| + // against the theoretical result.
|
| + let context = new OfflineAudioContext(1, renderFrames,
|
| + defaultSampleRate);
|
| + let osc = context.createOscillator();
|
| + osc.connect(context.destination);
|
| + osc.start(startFrame / context.sampleRate);
|
| +
|
| + return context.startRendering().then(function (result) {
|
| + let actual = result.getChannelData(0);
|
| + let expected = new Array(actual.length);
|
| + expected.fill(0);
|
| +
|
| + // The expected curve is
|
| + //
|
| + // sin(2*pi*f*(t-t0))
|
| + //
|
| + // where f is the oscillator frequency and t0 is the start time.
|
| + let actualStart = Math.ceil(startFrame);
|
| + let omega = 2 * Math.PI * osc.frequency.value / context.sampleRate;
|
| + for (let k = actualStart; k < actual.length; ++k) {
|
| + expected[k] = Math.sin(omega * (k - startFrame));
|
| + }
|
| +
|
| + should(actual, "Oscillator.start(" + startFrame + " frames)")
|
| + .beCloseToArray(expected, {
|
| + absoluteThreshold: thresholds.error
|
| + });
|
| + let snr = 10 * Math.log10(computeSNR(actual, expected));
|
| + should(snr, "SNR (dB)")
|
| + .beGreaterThanOrEqualTo(thresholds.snrThreshold);
|
| + })
|
| + }
|
| +
|
| + function testStartWithGain(should, sampleRate, thresholds) {
|
| + // Test consists of starting a cosine wave with a quarter wavelength
|
| + // delay and comparing that with a sine wave that has the initial
|
| + // quarter wavelength zeroed out. These should be equal.
|
| +
|
| + let context = new OfflineAudioContext(3, renderFrames, sampleRate);
|
| + let osc = context.createOscillator();
|
| +
|
| + let merger = context.createChannelMerger(3);
|
| + merger.connect(context.destination);
|
| +
|
| + // Start the cosine oscillator at this time. This means the wave starts
|
| + // at frame 13.636363....
|
| + let quarterWaveTime = (1 / 4) / osc.frequency.value;
|
| +
|
| + // Sine wave oscillator with gain term to zero out the initial quarter
|
| + // wave length of the output.
|
| + let g = context.createGain();
|
| + g.gain.setValueAtTime(0, 0);
|
| + g.gain.setValueAtTime(1, quarterWaveTime);
|
| + osc.connect(g);
|
| + g.connect(merger, 0, 2);
|
| + g.connect(merger, 0, 0);
|
| +
|
| + // Cosine wave oscillator with starting after a quarter wave length.
|
| + let osc2 = context.createOscillator();
|
| + // Creates a cosine wave.
|
| + let wave = context.createPeriodicWave(
|
| + Float32Array.from([0, 1]),
|
| + Float32Array.from([0, 0]));
|
| + osc2.setPeriodicWave(wave);
|
| +
|
| + osc2.connect(merger, 0, 1);
|
| +
|
| + // A gain inverter so subtract the two waveforms.
|
| + let inverter = context.createGain();
|
| + inverter.gain.value = -1;
|
| + osc2.connect(inverter);
|
| + inverter.connect(merger, 0, 0);
|
| +
|
| + osc.start();
|
| + osc2.start(quarterWaveTime);
|
| +
|
| + return context.startRendering().then(function (result) {
|
| + // Channel 0 = diff
|
| + // Channel 1 = osc with start
|
| + // Channel 2 = osc with gain
|
| +
|
| + // Channel 0 should be very close to 0.
|
| + // Channel 1 should match channel 2 very closely.
|
| + let diff = result.getChannelData(0);
|
| + let oscStart = result.getChannelData(1);
|
| + let oscGain = result.getChannelData(2);
|
| + let snr = 10 * Math.log10(computeSNR(oscStart, oscGain));
|
| +
|
| + should(oscStart, "Delayed cosine oscillator at sample rate " + sampleRate)
|
| + .beCloseToArray(oscGain, {
|
| + absoluteThreshold: thresholds.error
|
| + });
|
| + should(snr, "SNR (dB)")
|
| + .beGreaterThanOrEqualTo(thresholds.snrThreshold);
|
| + });
|
| + }
|
| +
|
| + audit.run();
|
| + </script>
|
| + </body>
|
| +</html>
|
|
|