| 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);
|
| }
|
|
|