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

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

Issue 720293002: Replace oscillator tests with more robust tests (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Refactor according to review Created 6 years, 1 month 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/webaudio/oscillator-triangle-expected.wav ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: LayoutTests/webaudio/resources/oscillator-testing.js
diff --git a/LayoutTests/webaudio/resources/oscillator-testing.js b/LayoutTests/webaudio/resources/oscillator-testing.js
index 68e6b9fe14c809f9601f6ba9e7fdad25e8452de0..131910244740a37489c6c21a1148b0ad9cb712db 100644
--- a/LayoutTests/webaudio/resources/oscillator-testing.js
+++ b/LayoutTests/webaudio/resources/oscillator-testing.js
@@ -9,6 +9,31 @@
//
// QUESTION: Why does the very end of the generated signal appear to get slightly weaker?
// ANSWER: This is an artifact of the algorithm to avoid aliasing.
+//
+// QUESTION: Since the tests compare the actual result with an expected reference file, how are the
+// reference files created?
+// ANSWER: Create an html with the following contents in the webaudio directory. Then run a layout
+// test on this file. A new file names "<file>-actual.wav" is created that contains the new result
+// that can be used as the new expected reference file. Replace the "sine" below with the
+// oscillator type that you want to use.
+//
+// <!doctype html>
+// <html>
+// <head>
+// <script src="resources/compatibility.js"></script>
+// <script src="resources/buffer-loader.js"></script>
+// <script src="../resources/js-test.js"></script>
+// <script src="resources/oscillator-testing.js"></script>
+// <script src="resources/audio-testing.js"></script>
+// </head>
+// <body>
+// <script>
+// OscillatorTestingUtils.createNewReference("sine");
+// </script>
+// </body>
+// </html>
+
+OscillatorTestingUtils = (function () {
var sampleRate = 44100.0;
var nyquist = 0.5 * sampleRate;
@@ -17,13 +42,47 @@ var lowFrequency = 10;
var highFrequency = nyquist + 2000; // go slightly higher than nyquist to make sure we generate silence there
var context = 0;
+// Scaling factor for converting the 16-bit WAV data to float (and vice-versa).
+var waveScaleFactor = 32768;
+
+// Thresholds for verifying the test passes. The thresholds are experimentally determined. The
+// default values here will cause the test to fail, which is useful for determining new thresholds,
+// if needed.
+
+// SNR must be greater than this to pass the test.
+// Q: Why is the SNR threshold not infinity?
+// A: The reference result is a 16-bit WAV file, so it won't compare exactly with the
+// floating point result.
+var thresholdSNR = 10000;
+
+// Max diff must be less than this to pass the test.
+var thresholdDiff = 0;
+
+// Count the number of differences between the expected and actual result. The tests passes
+// if the count is less than this threshold.
+var thresholdDiffCount = 0;
+
+// Mostly for debugging
+
+// An AudioBuffer for the reference (expected) result.
+var reference = 0;
+
+// The actual rendered data produced by the test.
+var renderedData = 0;
+
+// Signal power of the reference
+var signalPower = 0;
+
+// Noise power of the difference between the reference and actual result.
+var noisePower = 0;
+
function generateExponentialOscillatorSweep(context, oscillatorType) {
var osc = context.createOscillator();
if (oscillatorType == "custom") {
// Create a simple waveform with three Fourier coefficients.
// Note the first values are expected to be zero (DC for coeffA and Nyquist for coeffB).
var coeffA = new Float32Array([0, 1, 0.5]);
- var coeffB = new Float32Array([0, 0, 0]);
+ var coeffB = new Float32Array([0, 0, 0]);
var wave = context.createPeriodicWave(coeffA, coeffB);
osc.setPeriodicWave(wave);
} else {
@@ -38,7 +97,117 @@ function generateExponentialOscillatorSweep(context, oscillatorType) {
osc.start(0);
- var nyquist = 0.5 * sampleRate;
osc.frequency.setValueAtTime(10, 0);
osc.frequency.exponentialRampToValueAtTime(highFrequency, lengthInSeconds);
}
+
+function calculateSNR(sPower, nPower)
+{
+ if (nPower == 0 && sPower > 0) {
+ return 1000;
+ }
+ return 10 * Math.log10(sPower / nPower);
+}
+
+function loadReferenceAndRunTest(oscType) {
+ var bufferLoader = new BufferLoader(
+ context,
+ [ "oscillator-" + oscType + "-expected.wav" ],
+ function (bufferList) {
+ reference = bufferList[0].getChannelData(0);
+ generateExponentialOscillatorSweep(context, oscType);
+ context.oncomplete = checkResult;
+ context.startRendering();
+ });
+
+ bufferLoader.load();
+}
+
+function checkResult (event) {
+ renderedData = event.renderedBuffer.getChannelData(0);
+ // Compute signal to noise ratio between the result and the reference. Also keep track
+ // of the max difference (and position).
+
+ var maxError = -1;
+ var errorPosition = -1;
+ var diffCount = 0;
+
+ for (var k = 0; k < renderedData.length; ++k) {
+ var diff = renderedData[k] - reference[k];
+ noisePower += diff * diff;
+ signalPower += reference[k] * reference[k];
+ if (Math.abs(diff) > maxError) {
+ maxError = Math.abs(diff);
+ errorPosition = k;
+ }
+ // The reference file is a 16-bit WAV file, so we will almost never get an exact match
+ // between it and the actual floating-point result.
+ if (diff > 1/waveScaleFactor) {
+ diffCount++;
+ }
+ }
+
+ var snr = calculateSNR(signalPower, noisePower);
+ if (snr >= thresholdSNR) {
+ testPassed("Exceeded SNR threshold of " + thresholdSNR + " dB");
+ } else {
+ testFailed("Expected SNR of " + thresholdSNR + " dB, but actual SNR is " + snr + " dB");
+ }
+
+ if (maxError <= thresholdDiff) {
+ testPassed("Maximum difference below threshold of "
+ + (thresholdDiff * waveScaleFactor) + " ulp (16-bits)");
+ } else {
+ testFailed("Maximum difference of " + (maxError * waveScaleFactor) + " at "
+ + errorPosition + " exceeded threshold of " + (thresholdDiff * waveScaleFactor)
+ + " ulp (16-bits)");
+ }
+
+ if (diffCount <= thresholdDiffCount) {
+ testPassed("Number of differences between actual and expected result is less than " + thresholdDiffCount);
+ } else {
+ testFailed(diffCount + " differences found but expected no more than " + thresholdDiffCount);
+ }
+
+ finishJSTest();
+}
+
+function setThresholds(thresholds) {
+ thresholdSNR = thresholds.snr;
+ thresholdDiff = thresholds.maxDiff / waveScaleFactor;
+ thresholdDiffCount = thresholds.diffCount;
+}
+
+function runTest(oscType) {
+ window.jsTestIsAsync = true;
+ context = new OfflineAudioContext(1, sampleRate * lengthInSeconds, sampleRate);
+ loadReferenceAndRunTest(oscType);
+}
+
+function createNewReference(oscType) {
+ if (!window.testRunner)
+ return;
+
+ context = new OfflineAudioContext(1, sampleRate * lengthInSeconds, sampleRate);
+ generateExponentialOscillatorSweep(context, oscType);
+
+ context.oncomplete = finishAudioTest;
+ context.startRendering();
+
+ testRunner.waitUntilDone();
+}
+
+return {
+ sampleRate: sampleRate,
+ lengthInSeconds: lengthInSeconds,
+ thresholdSNR: thresholdSNR,
+ thresholdDiff: thresholdDiff,
+ thresholdDiffCount: thresholdDiffCount,
+ waveScaleFactor: waveScaleFactor,
+ setThresholds: setThresholds,
+ loadReferenceAndRunTest: loadReferenceAndRunTest,
+ runTest: runTest,
+ createNewReference: createNewReference,
+};
+
+}());
« no previous file with comments | « LayoutTests/webaudio/oscillator-triangle-expected.wav ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698