diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/webaudio/resources/biquad-testing.js | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webaudio/resources/biquad-testing.js')
-rw-r--r-- | testing/web-platform/tests/webaudio/resources/biquad-testing.js | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/resources/biquad-testing.js b/testing/web-platform/tests/webaudio/resources/biquad-testing.js new file mode 100644 index 0000000000..7f90a1f72b --- /dev/null +++ b/testing/web-platform/tests/webaudio/resources/biquad-testing.js @@ -0,0 +1,172 @@ +// Globals, to make testing and debugging easier. +let context; +let filter; +let signal; +let renderedBuffer; +let renderedData; + +// Use a power of two to eliminate round-off in converting frame to time +let sampleRate = 32768; +let pulseLengthFrames = .1 * sampleRate; + +// Maximum allowed error for the test to succeed. Experimentally determined. +let maxAllowedError = 5.9e-8; + +// This must be large enough so that the filtered result is essentially zero. +// See comments for createTestAndRun. This must be a whole number of frames. +let timeStep = Math.ceil(.1 * sampleRate) / sampleRate; + +// Maximum number of filters we can process (mostly for setting the +// render length correctly.) +let maxFilters = 5; + +// How long to render. Must be long enough for all of the filters we +// want to test. +let renderLengthSeconds = timeStep * (maxFilters + 1); + +let renderLengthSamples = Math.round(renderLengthSeconds * sampleRate); + +// Number of filters that will be processed. +let nFilters; + +function createImpulseBuffer(context, length) { + let impulse = context.createBuffer(1, length, context.sampleRate); + let data = impulse.getChannelData(0); + for (let k = 1; k < data.length; ++k) { + data[k] = 0; + } + data[0] = 1; + + return impulse; +} + + +function createTestAndRun(context, filterType, testParameters) { + // 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. + + let filterParameters = testParameters.filterParameters; + 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 (let 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); + } + + return context.startRendering().then(buffer => { + checkFilterResponse(buffer, filterType, testParameters); + }); +} + +function addSignal(dest, src, destOffset) { + // Add src to dest at the given dest offset. + for (let k = destOffset, j = 0; k < dest.length, j < src.length; ++k, ++j) { + dest[k] += src[j]; + } +} + +function generateReference(filterType, filterParameters) { + let result = new Array(renderLengthSamples); + let data = new Array(renderLengthSamples); + // Initialize the result array and data. + for (let k = 0; k < result.length; ++k) { + result[k] = 0; + data[k] = 0; + } + // Make data an impulse. + data[0] = 1; + + for (let k = 0; k < nFilters; ++k) { + // Filter an impulse + let detune = (filterParameters[k].detune === undefined) ? + 0 : + filterParameters[k].detune; + let frequency = filterParameters[k].cutoff * + Math.pow(2, detune / 1200); // Apply detune, converting from Cents. + + let filterCoef = createFilter( + filterType, frequency, filterParameters[k].q, filterParameters[k].gain); + let 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(renderedBuffer, filterType, testParameters) { + let filterParameters = testParameters.filterParameters; + let maxAllowedError = testParameters.threshold; + let should = testParameters.should; + + renderedData = renderedBuffer.getChannelData(0); + + reference = generateReference(filterType, filterParameters); + + let len = Math.min(renderedData.length, reference.length); + + let success = true; + + // Maximum error between rendered data and expected data + let maxError = 0; + + // Sample offset where the maximum error occurred. + let maxPosition = 0; + + // Number of infinities or NaNs that occurred in the rendered data. + let invalidNumberCount = 0; + + should(nFilters, 'Number of filters tested') + .beEqualTo(filterParameters.length); + + // 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 (let k = 0; k < len; ++k) { + let err = Math.abs(renderedData[k] - reference[k]); + if (err > maxError) { + maxError = err; + maxPosition = k; + } + if (!isValidNumber(renderedData[k])) { + ++invalidNumberCount; + } + } + + should( + invalidNumberCount, 'Number of non-finite values in the rendered output') + .beEqualTo(0); + + should(maxError, 'Max error in ' + filterTypeName[filterType] + ' response') + .beLessThanOrEqualTo(maxAllowedError); +} |