Chromium Code Reviews| 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..e7de56ab7e480c714a52f00594553d815b504c66 |
| --- /dev/null |
| +++ b/third_party/WebKit/LayoutTests/webaudio/Oscillator/start-sampling.html |
| @@ -0,0 +1,170 @@ |
| +<!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. |
| + var defaultSampleRate = 24000; |
|
hongchan
2017/01/05 23:48:05
This is a new test, so we should use 'let'.
Raymond Toy
2017/01/10 00:09:48
Done.
|
| + var renderDuration = 1; |
| + var renderFrames = renderDuration * defaultSampleRate; |
| + |
| + var 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.055 |
| + }) |
| + .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.0838e-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"); |
|
hongchan
2017/01/05 23:48:05
Can we wrap this line?
Raymond Toy
2017/01/10 00:09:48
Done.
|
| + // 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"); |
|
hongchan
2017/01/05 23:48:05
ditto.
Raymond Toy
2017/01/10 00:09:48
Done.
|
| + // 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. |
| + var context = new OfflineAudioContext(1, renderFrames, defaultSampleRate); |
|
hongchan
2017/01/05 23:48:05
ditto.
Raymond Toy
2017/01/10 00:09:48
Done.
|
| + var osc = context.createOscillator(); |
| + osc.connect(context.destination); |
| + osc.start(startFrame / context.sampleRate); |
| + |
| + return context.startRendering().then(function (result) { |
| + var actual = result.getChannelData(0); |
| + var 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. |
| + var actualStart = Math.ceil(startFrame); |
| + var omega = 2 * Math.PI * osc.frequency.value / context.sampleRate; |
| + for (var k = actualStart; k < actual.length; ++k) { |
| + expected[k] = Math.sin(omega * (k - startFrame)); |
| + } |
| + |
| + should(actual, "Oscillator.start(" + startFrame + " frames)") |
| + .beCloseToArray(expected, { |
| + absoluteThreshold: thresholds.error |
| + }); |
| + var snr = 10 * Math.log10(computeSNR(actual, expected)); |
| + should(snr, "SNR") |
| + .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. |
| + |
| + var context = new OfflineAudioContext(3, renderFrames, sampleRate); |
| + var osc = context.createOscillator(); |
| + |
| + var merger = context.createChannelMerger(3); |
| + merger.connect(context.destination); |
| + |
| + // Start the cosine oscillator at this time. This means the wave starts |
| + // at frame 13.636363.... |
| + var quarterWaveTime = (1 / 4) / osc.frequency.value; |
| + |
| + // Sine wave oscillator with gain term to zero out the initial quarter |
| + // wave length of the output. |
| + var osc = context.createOscillator(); |
| + var 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. |
| + var osc2 = context.createOscillator(); |
| + // Creates a cosine wave. |
| + var 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. |
| + var 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. |
| + var diff = result.getChannelData(0); |
| + var oscStart = result.getChannelData(1); |
| + var oscGain = result.getChannelData(2); |
| + var snr = 10 * Math.log10(computeSNR(oscStart, oscGain)); |
| + |
| + should(oscStart, "Delayed cosine oscillator at sample rate " + sampleRate) |
| + .beCloseToArray(oscGain, { |
| + absoluteThreshold: thresholds.error |
| + }); |
| + should(snr, "SNR") |
| + .beGreaterThanOrEqualTo(thresholds.snrThreshold); |
| + }); |
| + } |
| + |
| + audit.run(); |
| + </script> |
| + </body> |
| +</html> |