Title: [109662] trunk/LayoutTests
Revision
109662
Author
[email protected]
Date
2012-03-03 10:55:49 -0800 (Sat, 03 Mar 2012)

Log Message

Biquad getFrequencyResponse needs a layout test.
https://bugs.webkit.org/show_bug.cgi?id=79503

Add simple test for getFrequencyResponse.

Patch by Raymond Toy <[email protected]> on 2012-03-03
Reviewed by Chris Rogers.

* webaudio/biquad-getFrequencyResponse-expected.txt: Added.
* webaudio/biquad-getFrequencyResponse.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (109661 => 109662)


--- trunk/LayoutTests/ChangeLog	2012-03-03 18:49:50 UTC (rev 109661)
+++ trunk/LayoutTests/ChangeLog	2012-03-03 18:55:49 UTC (rev 109662)
@@ -1,3 +1,15 @@
+2012-03-03  Raymond Toy  <[email protected]>
+
+        Biquad getFrequencyResponse needs a layout test.
+        https://bugs.webkit.org/show_bug.cgi?id=79503
+
+        Add simple test for getFrequencyResponse.
+        
+        Reviewed by Chris Rogers.
+
+        * webaudio/biquad-getFrequencyResponse-expected.txt: Added.
+        * webaudio/biquad-getFrequencyResponse.html: Added.
+
 2012-03-03  Sheriff Bot  <[email protected]>
 
         Unreviewed, rolling out r109343.

Added: trunk/LayoutTests/webaudio/biquad-getFrequencyResponse-expected.txt (0 => 109662)


--- trunk/LayoutTests/webaudio/biquad-getFrequencyResponse-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webaudio/biquad-getFrequencyResponse-expected.txt	2012-03-03 18:55:49 UTC (rev 109662)
@@ -0,0 +1,11 @@
+Test Biquad getFrequencyResponse() functionality.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS Magnitude response within acceptable threshold.
+PASS Phase response within acceptable threshold.
+PASS Peaking filter frequency response was correct.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/webaudio/biquad-getFrequencyResponse.html (0 => 109662)


--- trunk/LayoutTests/webaudio/biquad-getFrequencyResponse.html	                        (rev 0)
+++ trunk/LayoutTests/webaudio/biquad-getFrequencyResponse.html	2012-03-03 18:55:49 UTC (rev 109662)
@@ -0,0 +1,314 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<script src=""
+<script src=""
+<script src=""
+</head>
+
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<script>
+description("Test Biquad getFrequencyResponse() functionality.");
+
+// Test the frequency response of a biquad filter.  We compute the frequency response for a simple
+// peaking biquad filter and compare it with the expected frequency response.  The actual filter
+// used doesn't matter since we're testing getFrequencyResponse and not the actual filter output.
+// The filters are extensively tested in other biquad tests.
+
+var context;
+
+// The biquad filter node.
+var filter;
+
+// The magnitude response of the biquad filter.
+var magResponse;
+
+// The phase response of the biquad filter.
+var phaseResponse;
+
+// Number of frequency samples to take.
+var numberOfFrequencies = 1000;
+
+// The filter parameters.
+var filterCutoff = 1000; // Hz.
+var filterQ = 1;
+var filterGain = 5; // Decibels.
+
+// The maximum allowed error in the magnitude response.
+var maxAllowedMagError = 5.7e-7;
+
+// The maximum allowed error in the phase response.
+var maxAllowedPhaseError = 4.7e-8;
+
+// The magnitudes and phases of the reference frequency response.
+var magResponse;
+var phaseResponse;
+      
+// The magnitudes and phases of the reference frequency response.
+var expectedMagnitudes;
+var expectedPhases;
+
+// Convert frequency in Hz to a normalized frequency between 0 to 1 with 1 corresponding to the
+// Nyquist frequency.
+function normalizedFrequency(freqHz, sampleRate)
+{
+    var nyquist = sampleRate / 2;
+    return freqHz / nyquist;
+}
+
+// Get the filter response at a (normalized) frequency |f| for the filter with coefficients |coef|.
+function getResponseAt(coef, f)
+{
+    var b0 = coef.b0;
+    var b1 = coef.b1;
+    var b2 = coef.b2;
+    var a1 = coef.a1;
+    var a2 = coef.a2;
+
+    // H(z) = (b0 + b1 / z + b2 / z^2) / (1 + a1 / z + a2 / z^2)
+    //
+    // Compute H(exp(i * pi * f)).  No native complex numbers in _javascript_, so break H(exp(i * pi * // f))
+    // in to the real and imaginary parts of the numerator and denominator.  Let omega = pi * f.
+    // Then the numerator is
+    //
+    // b0 + b1 * cos(omega) + b2 * cos(2 * omega) - i * (b1 * sin(omega) + b2 * sin(2 * omega))
+    //
+    // and the denominator is
+    //
+    // 1 + a1 * cos(omega) + a2 * cos(2 * omega) - i * (a1 * sin(omega) + a2 * sin(2 * omega))
+    //
+    // Compute the magnitude and phase from the real and imaginary parts.
+
+    var omega = Math.PI * f;
+    var numeratorReal = b0 + b1 * Math.cos(omega) + b2 * Math.cos(2 * omega);
+    var numeratorImag = -(b1 * Math.sin(omega) + b2 * Math.sin(2 * omega));
+    var denominatorReal = 1 + a1 * Math.cos(omega) + a2 * Math.cos(2 * omega);
+    var denominatorImag = -(a1 * Math.sin(omega) + a2 * Math.sin(2 * omega));
+
+    var magnitude = Math.sqrt((numeratorReal * numeratorReal + numeratorImag * numeratorImag)
+                              / (denominatorReal * denominatorReal + denominatorImag * denominatorImag));
+    var phase = Math.atan2(numeratorImag, numeratorReal) - Math.atan2(denominatorImag, denominatorReal);
+    
+    if (phase >= Math.PI) {
+        phase -= 2 * Math.PI;
+    } else if (phase <= -Math.PI) {
+        phase += 2 * Math.PI;
+    }
+   
+    return {magnitude : magnitude, phase : phase}; 
+}
+
+// Compute the reference frequency response for the biquad filter |filter| at the frequency samples
+// given by |frequencies|.
+function frequencyResponseReference(filter, frequencies)
+{
+    var sampleRate = filter.context.sampleRate;
+    var normalizedFreq = normalizedFrequency(filter.frequency.value, sampleRate);
+    var filterCoefficients = createFilter(filter.type, normalizedFreq, filter.Q.value, filter.gain.value);
+
+    var magnitudes = [];
+    var phases = [];
+
+    for (var k = 0; k < frequencies.length; ++k) {
+        var response = getResponseAt(filterCoefficients, normalizedFrequency(frequencies[k], sampleRate));
+        magnitudes.push(response.magnitude);
+        phases.push(response.phase);
+    }
+
+    return {magnitudes : magnitudes, phases : phases};
+}
+
+// Compute a set of linearly spaced frequencies.
+function createFrequencies(nFrequencies, sampleRate)
+{
+    var frequencies = new Float32Array(nFrequencies);
+    var nyquist = sampleRate / 2;
+    var freqDelta = nyquist / nFrequencies;
+
+    for (var k = 0; k < nFrequencies; ++k) {
+        frequencies[k] = k * freqDelta;
+    }
+
+    return frequencies;
+}
+
+function linearToDecibels(x)
+{
+    if (x) {
+        return 20 * Math.log(x) / Math.LN10;
+    } else {
+        return -1000;
+    }
+}
+
+// Look through the array and find any NaN or infinity. Returns the index of the first occurence or
+// -1 if none.
+function findBadNumber(signal)
+{
+    for (var k = 0; k < signal.length; ++k) {
+        if (!isValidNumber(signal[k])) {
+           return k;
+        }
+    }
+    return -1;
+}
+
+// Compute absolute value of the difference between phase angles, taking into account the wrapping
+// of phases.
+function absolutePhaseDifference(x, y)
+{
+    var diff = Math.abs(x - y);
+    
+    if (diff > Math.PI) {
+        diff = 2 * Math.PI - diff;
+    }
+    return diff;
+}
+
+// Compare the frequency response with our expected response.
+function compareResponses(filter, frequencies, magResponse, phaseResponse)
+{
+    var expectedResponse = frequencyResponseReference(filter, frequencies);
+
+    expectedMagnitudes = expectedResponse.magnitudes;
+    expectedPhases = expectedResponse.phases;
+
+    var n = magResponse.length;
+    var success = true;
+    var badResponse = false;
+
+    var maxMagError = -1;
+    var maxMagErrorIndex = -1;
+
+    var k;
+    var hasBadNumber;
+
+    hasBadNumber = findBadNumber(magResponse);
+    if (hasBadNumber >= 0) {
+        testFailed("Magnitude response has NaN or infinity at " + hasBadNumber);
+        success = false;
+        badResponse = true;
+    }
+
+    hasBadNumber = findBadNumber(phaseResponse);
+    if (hasBadNumber >= 0) {
+        testFailed("Phase response has NaN or infinity at " + hasBadNumber);
+        success = false;
+        badResponse = true;
+    }
+
+    // These aren't testing the implementation itself.  Instead, these are sanity checks on the
+    // reference.  Failure here does not imply an error in the implementation.
+    hasBadNumber = findBadNumber(expectedMagnitudes);
+    if (hasBadNumber >= 0) {
+        testFailed("Expected magnitude response has NaN or infinity at " + hasBadNumber);
+        success = false;
+        badResponse = true;
+    }
+
+    hasBadNumber = findBadNumber(expectedPhases);
+    if (hasBadNumber >= 0) {
+        testFailed("Expected phase response has NaN or infinity at " + hasBadNumber);
+        success = false;
+        badResponse = true;
+    }
+
+    // If we found a NaN or infinity, the following tests aren't very helpful, especially for NaN.
+    // We run them anyway, after printing a warning message.
+
+    if (badResponse) {
+        testFailed("NaN or infinity in the actual or expected results makes the following test results suspect.");
+        success = false;
+    }
+
+    for (k = 0; k < n; ++k) {
+        var error = Math.abs(linearToDecibels(magResponse[k]) - linearToDecibels(expectedMagnitudes[k]));
+        if (error > maxMagError) {
+            maxMagError = error;
+            maxMagErrorIndex = k;
+        }
+    }
+
+    if (maxMagError > maxAllowedMagError) {
+        var message = "Magnitude error (" + maxMagError + " dB)";
+        message += " exceeded threshold at " + frequencies[maxMagErrorIndex];
+        message += " Hz.  Actual: " + linearToDecibels(magResponse[maxMagErrorIndex]);
+        message += " dB, expected: " + linearToDecibels(expectedMagnitudes[maxMagErrorIndex]) + " dB.";
+        testFailed(message);
+        success = false;
+    } else {
+        testPassed("Magnitude response within acceptable threshold.");
+    }
+
+    var maxPhaseError = -1;
+    var maxPhaseErrorIndex = -1;
+
+    for (k = 0; k < n; ++k) {
+        var error = absolutePhaseDifference(phaseResponse[k], expectedPhases[k]);
+        if (error > maxPhaseError) {
+            maxPhaseError = error;
+            maxPhaseErrorIndex = k;
+        }
+    }
+
+    if (maxPhaseError > maxAllowedPhaseError) {
+        var message = "Phase error (radians) (" + maxPhaseError;
+        message += ") exceeded threshold at " + frequencies[maxPhaseErrorIndex];
+        message += " Hz.  Actual: " + phaseResponse[maxPhaseErrorIndex];
+        message += " expected: " + expectedPhases[maxPhaseErrorIndex];
+        testFailed(message);
+        success = false;
+    } else {
+        testPassed("Phase response within acceptable threshold.");
+    }
+
+
+    return success;
+}
+
+function runTest()
+{
+    if (window.layoutTestController) {
+        layoutTestController.dumpAsText();
+        layoutTestController.waitUntilDone();
+    }
+
+    window.jsTestIsAsync = true;
+
+    context = new webkitAudioContext();
+    
+    filter = context.createBiquadFilter();
+
+    // Arbitrarily test a peaking filter, but any kind of filter can be tested.
+    filter.type = filter.PEAKING;
+    filter.frequency.value = filterCutoff;
+    filter.Q.value = filterQ;
+    filter.gain.value = filterGain;
+
+    var frequencies = createFrequencies(numberOfFrequencies, context.sampleRate);
+    magResponse = new Float32Array(numberOfFrequencies);
+    phaseResponse = new Float32Array(numberOfFrequencies);
+
+    filter.getFrequencyResponse(frequencies, magResponse, phaseResponse);
+    var success = compareResponses(filter, frequencies, magResponse, phaseResponse);
+
+    if (success) {
+        testPassed(filterTypeName[filter.type] + " frequency response was correct.");
+    } else {
+        testFailed(filterTypeName[filter.type] + " frequency response was incorrect.");
+    }
+
+    finishJSTest();
+}
+
+runTest();
+successfullyParsed = true;
+
+</script>
+
+<script src=""
+</body>
+</html>
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to