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/convolution-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/convolution-testing.js')
-rw-r--r-- | testing/web-platform/tests/webaudio/resources/convolution-testing.js | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/resources/convolution-testing.js b/testing/web-platform/tests/webaudio/resources/convolution-testing.js new file mode 100644 index 0000000000..c976f86c78 --- /dev/null +++ b/testing/web-platform/tests/webaudio/resources/convolution-testing.js @@ -0,0 +1,168 @@ +let sampleRate = 44100.0; + +let renderLengthSeconds = 8; +let pulseLengthSeconds = 1; +let pulseLengthFrames = pulseLengthSeconds * sampleRate; + +function createSquarePulseBuffer(context, sampleFrameLength) { + let audioBuffer = + context.createBuffer(1, sampleFrameLength, context.sampleRate); + + let n = audioBuffer.length; + let data = audioBuffer.getChannelData(0); + + for (let i = 0; i < n; ++i) + data[i] = 1; + + return audioBuffer; +} + +// The triangle buffer holds the expected result of the convolution. +// It linearly ramps up from 0 to its maximum value (at the center) +// then linearly ramps down to 0. The center value corresponds to the +// point where the two square pulses overlap the most. +function createTrianglePulseBuffer(context, sampleFrameLength) { + let audioBuffer = + context.createBuffer(1, sampleFrameLength, context.sampleRate); + + let n = audioBuffer.length; + let halfLength = n / 2; + let data = audioBuffer.getChannelData(0); + + for (let i = 0; i < halfLength; ++i) + data[i] = i + 1; + + for (let i = halfLength; i < n; ++i) + data[i] = n - i - 1; + + return audioBuffer; +} + +function log10(x) { + return Math.log(x) / Math.LN10; +} + +function linearToDecibel(x) { + return 20 * log10(x); +} + +// Verify that the rendered result is very close to the reference +// triangular pulse. +function checkTriangularPulse(rendered, reference, should) { + let match = true; + let maxDelta = 0; + let valueAtMaxDelta = 0; + let maxDeltaIndex = 0; + + for (let i = 0; i < reference.length; ++i) { + let diff = rendered[i] - reference[i]; + let x = Math.abs(diff); + if (x > maxDelta) { + maxDelta = x; + valueAtMaxDelta = reference[i]; + maxDeltaIndex = i; + } + } + + // allowedDeviationFraction was determined experimentally. It + // is the threshold of the relative error at the maximum + // difference between the true triangular pulse and the + // rendered pulse. + let allowedDeviationDecibels = -124.41; + let maxDeviationDecibels = linearToDecibel(maxDelta / valueAtMaxDelta); + + should( + maxDeviationDecibels, + 'Deviation (in dB) of triangular portion of convolution') + .beLessThanOrEqualTo(allowedDeviationDecibels); + + return match; +} + +// Verify that the rendered data is close to zero for the first part +// of the tail. +function checkTail1(data, reference, breakpoint, should) { + let isZero = true; + let tail1Max = 0; + + for (let i = reference.length; i < reference.length + breakpoint; ++i) { + let mag = Math.abs(data[i]); + if (mag > tail1Max) { + tail1Max = mag; + } + } + + // Let's find the peak of the reference (even though we know a + // priori what it is). + let refMax = 0; + for (let i = 0; i < reference.length; ++i) { + refMax = Math.max(refMax, Math.abs(reference[i])); + } + + // This threshold is experimentally determined by examining the + // value of tail1MaxDecibels. + let threshold1 = -129.7; + + let tail1MaxDecibels = linearToDecibel(tail1Max / refMax); + should(tail1MaxDecibels, 'Deviation in first part of tail of convolutions') + .beLessThanOrEqualTo(threshold1); + + return isZero; +} + +// Verify that the second part of the tail of the convolution is +// exactly zero. +function checkTail2(data, reference, breakpoint, should) { + let isZero = true; + let tail2Max = 0; + // For the second part of the tail, the maximum value should be + // exactly zero. + let threshold2 = 0; + for (let i = reference.length + breakpoint; i < data.length; ++i) { + if (Math.abs(data[i]) > 0) { + isZero = false; + break; + } + } + + should(isZero, 'Rendered signal after tail of convolution is silent') + .beTrue(); + + return isZero; +} + +function checkConvolvedResult(renderedBuffer, trianglePulse, should) { + let referenceData = trianglePulse.getChannelData(0); + let renderedData = renderedBuffer.getChannelData(0); + + let success = true; + + // Verify the triangular pulse is actually triangular. + + success = + success && checkTriangularPulse(renderedData, referenceData, should); + + // Make sure that portion after convolved portion is totally + // silent. But round-off prevents this from being completely + // true. At the end of the triangle, it should be close to + // zero. If we go farther out, it should be even closer and + // eventually zero. + + // For the tail of the convolution (where the result would be + // theoretically zero), we partition the tail into two + // parts. The first is the at the beginning of the tail, + // where we tolerate a small but non-zero value. The second part is + // farther along the tail where the result should be zero. + + // breakpoint is the point dividing the first two tail parts + // we're looking at. Experimentally determined. + let breakpoint = 12800; + + success = + success && checkTail1(renderedData, referenceData, breakpoint, should); + + success = + success && checkTail2(renderedData, referenceData, breakpoint, should); + + should(success, 'Test signal convolved').message('correctly', 'incorrectly'); +} |