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> |