Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(280)

Unified Diff: LayoutTests/webaudio/resources/waveshaper-testing.js

Issue 15619003: Add support for WaveShaperNode.oversample (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: change TestExpectations in middle of file Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « LayoutTests/TestExpectations ('k') | LayoutTests/webaudio/waveshaper-oversample-2x.html » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: LayoutTests/webaudio/resources/waveshaper-testing.js
diff --git a/LayoutTests/webaudio/resources/waveshaper-testing.js b/LayoutTests/webaudio/resources/waveshaper-testing.js
new file mode 100644
index 0000000000000000000000000000000000000000..4f90e750da38c54c5b1cadb1a1e3ecc257e6b6c2
--- /dev/null
+++ b/LayoutTests/webaudio/resources/waveshaper-testing.js
@@ -0,0 +1,175 @@
+var context;
+var lengthInSeconds = 2;
+
+// Skip this many frames before comparing against reference to allow
+// a steady-state to be reached in the up-sampling filters.
+var filterStabilizeSkipFrames = 2048;
+
+var numberOfCurveFrames = 65536;
+var waveShapingCurve;
+
+var waveshaper;
+
+// FIXME: test at more frequencies.
+// When using the up-sampling filters (2x, 4x) any significant aliasing components
+// should be at very high frequencies near Nyquist. These tests could be improved
+// to allow for a higher acceptable amount of aliasing near Nyquist, but then
+// become more stringent for lower frequencies.
+
+// These test parameters are set in runWaveShaperOversamplingTest().
+var sampleRate;
+var nyquist;
+var oversample;
+var fundamentalFrequency;
+var acceptableAliasingThresholdDecibels;
+
+var kScale = 0.25;
+
+// Chebyshev Polynomials.
+// Given an input sinusoid, returns an output sinusoid of the given frequency multiple.
+function T0(x) { return 1; }
+function T1(x) { return x; }
+function T2(x) { return 2*x*x - 1; }
+function T3(x) { return 4*x*x*x - 3*x; }
+function T4(x) { return 8*x*x*x*x - 8*x*x + 1; }
+
+function generateWaveShapingCurve() {
+ var n = 65536;
+ var n2 = n / 2;
+ var curve = new Float32Array(n);
+
+ // The shaping curve uses Chebyshev Polynomial such that an input sinusoid
+ // at frequency f will generate an output of four sinusoids of frequencies:
+ // f, 2*f, 3*f, 4*f
+ // each of which is scaled.
+ for (var i = 0; i < n; ++i) {
+ var x = (i - n2) / n2;
+ var y = kScale * (T1(x) + T2(x) + T3(x) + T4(x));
+ curve[i] = y;
+ }
+
+ return curve;
+}
+
+function checkShapedCurve(event) {
+ var buffer = event.renderedBuffer;
+
+ var outputData = buffer.getChannelData(0);
+ var n = buffer.length;
+
+ // The WaveShaperNode will have a processing latency if oversampling is used,
+ // so we should account for it.
+
+ // FIXME: .latency should be exposed as an attribute of the node
+ // var waveShaperLatencyFrames = waveshaper.latency * sampleRate;
+ // But for now we'll use the hard-coded values corresponding to the actual latencies:
+ var waveShaperLatencyFrames = 0;
+ if (oversample == "2x")
+ waveShaperLatencyFrames = 128;
+ else if (oversample == "4x")
+ waveShaperLatencyFrames = 192;
+
+ var worstDeltaInDecibels = -1000;
+
+ for (var i = waveShaperLatencyFrames; i < n; ++i) {
+ var actual = outputData[i];
+
+ // Account for the expected processing latency.
+ var j = i - waveShaperLatencyFrames;
+
+ // Compute reference sinusoids.
+ var phaseInc = 2 * Math.PI * fundamentalFrequency / sampleRate;
+
+ // Generate an idealized reference based on the four generated frequencies truncated
+ // to the Nyquist rate. Ideally, we'd like the waveshaper's oversampling to perfectly
+ // remove all frequencies above Nyquist to avoid aliasing. In reality the oversampling filters are not
+ // quite perfect, so there will be a (hopefully small) amount of aliasing. We should
+ // be close to the ideal.
+ var reference = 0;
+
+ // Sum in fundamental frequency.
+ if (fundamentalFrequency < nyquist)
+ reference += Math.sin(phaseInc * j);
+
+ // Note that the phase of each of the expected generated harmonics is different.
+ if (fundamentalFrequency * 2 < nyquist)
+ reference += -Math.cos(phaseInc * j * 2);
+ if (fundamentalFrequency * 3 < nyquist)
+ reference += -Math.sin(phaseInc * j * 3);
+ if (fundamentalFrequency * 4 < nyquist)
+ reference += Math.cos(phaseInc * j * 4);
+
+ // Scale the reference the same as the waveshaping curve itself.
+ reference *= kScale;
+
+ var delta = Math.abs(actual - reference);
+ var deltaInDecibels = delta > 0 ? 20 * Math.log(delta)/Math.log(10) : -200;
+
+ if (j >= filterStabilizeSkipFrames) {
+ if (deltaInDecibels > worstDeltaInDecibels) {
+ worstDeltaInDecibels = deltaInDecibels;
+ }
+ }
+ }
+
+ // console.log("worstDeltaInDecibels: " + worstDeltaInDecibels);
+
+ var success = worstDeltaInDecibels < acceptableAliasingThresholdDecibels;
+
+ if (success) {
+ testPassed(oversample + " WaveShaperNode oversampling within acceptable tolerance.");
+ } else {
+ testFailed(oversample + " WaveShaperNode oversampling not within acceptable tolerance. Error = " + worstDeltaInDecibels + " dBFS");
+ }
+
+ finishJSTest();
+}
+
+function createImpulseBuffer(context, sampleFrameLength) {
+ var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
+ var n = audioBuffer.length;
+ var dataL = audioBuffer.getChannelData(0);
+
+ for (var k = 0; k < n; ++k)
+ dataL[k] = 0;
+
+ dataL[0] = 1;
+
+ return audioBuffer;
+}
+
+function runWaveShaperOversamplingTest(testParams) {
+ sampleRate = testParams.sampleRate;
+ nyquist = 0.5 * sampleRate;
+ oversample = testParams.oversample;
+ fundamentalFrequency = testParams.fundamentalFrequency;
+ acceptableAliasingThresholdDecibels = testParams.acceptableAliasingThresholdDecibels;
+
+ if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+ }
+
+ window.jsTestIsAsync = true;
+
+ // Create offline audio context.
+ var numberOfRenderFrames = sampleRate * lengthInSeconds;
+ context = new webkitOfflineAudioContext(1, numberOfRenderFrames, sampleRate);
+
+ // source -> waveshaper -> destination
+ var source = context.createBufferSource();
+ source.buffer = createToneBuffer(context, fundamentalFrequency, lengthInSeconds, 1);
+
+ // Apply a non-linear distortion curve.
+ waveshaper = context.createWaveShaper();
+ waveshaper.curve = generateWaveShapingCurve();
+ waveshaper.oversample = oversample;
+
+ source.connect(waveshaper);
+ waveshaper.connect(context.destination);
+
+ source.start(0);
+
+ context.oncomplete = checkShapedCurve;
+ context.startRendering();
+}
« no previous file with comments | « LayoutTests/TestExpectations ('k') | LayoutTests/webaudio/waveshaper-oversample-2x.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698