Index: third_party/WebKit/LayoutTests/webaudio/resources/biquad-filters.js |
diff --git a/third_party/WebKit/LayoutTests/webaudio/resources/biquad-testing.js b/third_party/WebKit/LayoutTests/webaudio/resources/biquad-filters.js |
similarity index 60% |
copy from third_party/WebKit/LayoutTests/webaudio/resources/biquad-testing.js |
copy to third_party/WebKit/LayoutTests/webaudio/resources/biquad-filters.js |
index 8d8077ba12d7b037497f3a32918bb849bdfde97b..c2517694218bcc5408b09ed4107dc6d0d121cfce 100644 |
--- a/third_party/WebKit/LayoutTests/webaudio/resources/biquad-testing.js |
+++ b/third_party/WebKit/LayoutTests/webaudio/resources/biquad-filters.js |
@@ -1,33 +1,3 @@ |
-// Globals, to make testing and debugging easier. |
-var context; |
-var filter; |
-var signal; |
-var renderedBuffer; |
-var renderedData; |
- |
-var sampleRate = 44100.0; |
-var pulseLengthFrames = .1 * sampleRate; |
- |
-// Maximum allowed error for the test to succeed. Experimentally determined. |
-var maxAllowedError = 5.9e-8; |
- |
-// This must be large enough so that the filtered result is |
-// essentially zero. See comments for createTestAndRun. |
-var timeStep = .1; |
- |
-// Maximum number of filters we can process (mostly for setting the |
-// render length correctly.) |
-var maxFilters = 5; |
- |
-// How long to render. Must be long enough for all of the filters we |
-// want to test. |
-var renderLengthSeconds = timeStep * (maxFilters + 1) ; |
- |
-var renderLengthSamples = Math.round(renderLengthSeconds * sampleRate); |
- |
-// Number of filters that will be processed. |
-var nFilters; |
- |
// A biquad filter has a z-transform of |
// H(z) = (b0 + b1 / z + b2 / z^2) / (1 + a1 / z + a2 / z^2) |
// |
@@ -156,7 +126,7 @@ function createBandpassFilter(freq, q, gain) { |
// independent of q. |
coef = {b0 : 0, b1 : 0, b2 : 0, a1 : 0, a2 : 0} |
} |
- |
+ |
return coef; |
} |
@@ -169,16 +139,16 @@ function createLowShelfFilter(freq, q, gain) { |
var a1; |
var a2; |
var coef; |
- |
+ |
var S = 1; |
var A = Math.pow(10, gain / 40); |
if (freq == 1) { |
// The filter is just a constant gain |
- coef = {b0 : A * A, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; |
+ coef = {b0 : A * A, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; |
} else if (freq == 0) { |
// The filter is 1 |
- coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; |
+ coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0}; |
} else { |
var w0 = Math.PI * freq; |
var alpha = 1 / 2 * Math.sin(w0) * Math.sqrt((A + 1 / A) * (1 / S - 1) + 2); |
@@ -262,7 +232,7 @@ function createPeakingFilter(freq, q, gain) { |
a0 = 1 + alpha / A; |
a1 = -2 * k; |
a2 = 1 - alpha / A; |
- |
+ |
coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2); |
} else { |
// q = 0, we have a divide by zero problem in the formulas |
@@ -347,30 +317,6 @@ function createAllpassFilter(freq, q, gain) { |
return coef; |
} |
-// Map the filter type name to a function that computes the filter coefficents for the given filter |
-// type. |
-var filterCreatorFunction = {"lowpass": createLowpassFilter, |
- "highpass": createHighpassFilter, |
- "bandpass": createBandpassFilter, |
- "lowshelf": createLowShelfFilter, |
- "highshelf": createHighShelfFilter, |
- "peaking": createPeakingFilter, |
- "notch": createNotchFilter, |
- "allpass": createAllpassFilter}; |
- |
-var filterTypeName = {"lowpass": "Lowpass filter", |
- "highpass": "Highpass filter", |
- "bandpass": "Bandpass filter", |
- "lowshelf": "Lowshelf filter", |
- "highshelf": "Highshelf filter", |
- "peaking": "Peaking filter", |
- "notch": "Notch filter", |
- "allpass": "Allpass filter"}; |
- |
-function createFilter(filterType, freq, q, gain) { |
- return filterCreatorFunction[filterType](freq, q, gain); |
-} |
- |
function filterData(filterCoef, signal, len) { |
var y = new Array(len); |
var b0 = filterCoef.b0; |
@@ -397,152 +343,26 @@ function filterData(filterCoef, signal, len) { |
return y; |
} |
-function createImpulseBuffer(context, length) { |
- var impulse = context.createBuffer(1, length, context.sampleRate); |
- var data = impulse.getChannelData(0); |
- for (var k = 1; k < data.length; ++k) { |
- data[k] = 0; |
- } |
- data[0] = 1; |
- |
- return impulse; |
-} |
- |
- |
-function createTestAndRun(context, filterType, filterParameters) { |
- // To test the filters, we apply a signal (an impulse) to each of |
- // the specified filters, with each signal starting at a different |
- // time. The output of the filters is summed together at the |
- // output. Thus for filter k, the signal input to the filter |
- // starts at time k * timeStep. For this to work well, timeStep |
- // must be large enough for the output of each filter to have |
- // decayed to zero with timeStep seconds. That way the filter |
- // outputs don't interfere with each other. |
- |
- nFilters = Math.min(filterParameters.length, maxFilters); |
- |
- signal = new Array(nFilters); |
- filter = new Array(nFilters); |
- |
- impulse = createImpulseBuffer(context, pulseLengthFrames); |
- |
- // Create all of the signal sources and filters that we need. |
- for (var k = 0; k < nFilters; ++k) { |
- signal[k] = context.createBufferSource(); |
- signal[k].buffer = impulse; |
- |
- filter[k] = context.createBiquadFilter(); |
- filter[k].type = filterType; |
- filter[k].frequency.value = context.sampleRate / 2 * filterParameters[k].cutoff; |
- filter[k].detune.value = (filterParameters[k].detune === undefined) ? 0 : filterParameters[k].detune; |
- filter[k].Q.value = filterParameters[k].q; |
- filter[k].gain.value = filterParameters[k].gain; |
- |
- signal[k].connect(filter[k]); |
- filter[k].connect(context.destination); |
- |
- signal[k].start(timeStep * k); |
- } |
- |
- context.oncomplete = checkFilterResponse(filterType, filterParameters); |
- context.startRendering(); |
-} |
- |
-function addSignal(dest, src, destOffset) { |
- // Add src to dest at the given dest offset. |
- for (var k = destOffset, j = 0; k < dest.length, j < src.length; ++k, ++j) { |
- dest[k] += src[j]; |
- } |
-} |
- |
-function generateReference(filterType, filterParameters) { |
- var result = new Array(renderLengthSamples); |
- var data = new Array(renderLengthSamples); |
- // Initialize the result array and data. |
- for (var k = 0; k < result.length; ++k) { |
- result[k] = 0; |
- data[k] = 0; |
- } |
- // Make data an impulse. |
- data[0] = 1; |
- |
- for (var k = 0; k < nFilters; ++k) { |
- // Filter an impulse |
- var detune = (filterParameters[k].detune === undefined) ? 0 : filterParameters[k].detune; |
- var frequency = filterParameters[k].cutoff * Math.pow(2, detune / 1200); // Apply detune, converting from Cents. |
- |
- var filterCoef = createFilter(filterType, |
- frequency, |
- filterParameters[k].q, |
- filterParameters[k].gain); |
- var y = filterData(filterCoef, data, renderLengthSamples); |
- |
- // Accumulate this filtered data into the final output at the desired offset. |
- addSignal(result, y, timeToSampleFrame(timeStep * k, sampleRate)); |
- } |
- |
- return result; |
-} |
- |
-function checkFilterResponse(filterType, filterParameters) { |
- return function(event) { |
- renderedBuffer = event.renderedBuffer; |
- renderedData = renderedBuffer.getChannelData(0); |
- |
- reference = generateReference(filterType, filterParameters); |
- |
- var len = Math.min(renderedData.length, reference.length); |
- |
- var success = true; |
- |
- // Maximum error between rendered data and expected data |
- var maxError = 0; |
- |
- // Sample offset where the maximum error occurred. |
- var maxPosition = 0; |
- |
- // Number of infinities or NaNs that occurred in the rendered data. |
- var invalidNumberCount = 0; |
- |
- if (nFilters != filterParameters.length) { |
- testFailed("Test wanted " + filterParameters.length + " filters but only " + maxFilters + " allowed."); |
- success = false; |
- } |
+// Map the filter type name to a function that computes the filter coefficents for the given filter |
+// type. |
+var filterCreatorFunction = {"lowpass": createLowpassFilter, |
+ "highpass": createHighpassFilter, |
+ "bandpass": createBandpassFilter, |
+ "lowshelf": createLowShelfFilter, |
+ "highshelf": createHighShelfFilter, |
+ "peaking": createPeakingFilter, |
+ "notch": createNotchFilter, |
+ "allpass": createAllpassFilter}; |
- // Compare the rendered signal with our reference, keeping |
- // track of the maximum difference (and the offset of the max |
- // difference.) Check for bad numbers in the rendered output |
- // too. There shouldn't be any. |
- for (var k = 0; k < len; ++k) { |
- var err = Math.abs(renderedData[k] - reference[k]); |
- if (err > maxError) { |
- maxError = err; |
- maxPosition = k; |
- } |
- if (!isValidNumber(renderedData[k])) { |
- ++invalidNumberCount; |
- } |
- } |
+var filterTypeName = {"lowpass": "Lowpass filter", |
+ "highpass": "Highpass filter", |
+ "bandpass": "Bandpass filter", |
+ "lowshelf": "Lowshelf filter", |
+ "highshelf": "Highshelf filter", |
+ "peaking": "Peaking filter", |
+ "notch": "Notch filter", |
+ "allpass": "Allpass filter"}; |
- if (invalidNumberCount > 0) { |
- testFailed("Rendered output has " + invalidNumberCount + " infinities or NaNs."); |
- success = false; |
- } else { |
- testPassed("Rendered output did not have infinities or NaNs."); |
- } |
- |
- if (maxError <= maxAllowedError) { |
- testPassed(filterTypeName[filterType] + " response is correct."); |
- } else { |
- testFailed(filterTypeName[filterType] + " response is incorrect. Max err = " + maxError + " at " + maxPosition + ". Threshold = " + maxAllowedError); |
- success = false; |
- } |
- |
- if (success) { |
- testPassed("Test signal was correctly filtered."); |
- } else { |
- testFailed("Test signal was not correctly filtered."); |
- } |
- finishJSTest(); |
- } |
+function createFilter(filterType, freq, q, gain) { |
+ return filterCreatorFunction[filterType](freq, q, gain); |
} |