diff options
Diffstat (limited to 'dom/media/webaudio/test/blink/convolution-testing.js')
-rw-r--r-- | dom/media/webaudio/test/blink/convolution-testing.js | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/dom/media/webaudio/test/blink/convolution-testing.js b/dom/media/webaudio/test/blink/convolution-testing.js new file mode 100644 index 000000000..98ff0c775 --- /dev/null +++ b/dom/media/webaudio/test/blink/convolution-testing.js @@ -0,0 +1,182 @@ +var sampleRate = 44100.0; + +var renderLengthSeconds = 8; +var pulseLengthSeconds = 1; +var pulseLengthFrames = pulseLengthSeconds * sampleRate; + +function createSquarePulseBuffer(context, sampleFrameLength) { + var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate); + + var n = audioBuffer.length; + var data = audioBuffer.getChannelData(0); + + for (var 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) { + var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate); + + var n = audioBuffer.length; + var halfLength = n / 2; + var data = audioBuffer.getChannelData(0); + + for (var i = 0; i < halfLength; ++i) + data[i] = i + 1; + + for (var 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) { + var match = true; + var maxDelta = 0; + var valueAtMaxDelta = 0; + var maxDeltaIndex = 0; + + for (var i = 0; i < reference.length; ++i) { + var diff = rendered[i] - reference[i]; + var 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. + var allowedDeviationDecibels = -129.4; + var maxDeviationDecibels = linearToDecibel(maxDelta / valueAtMaxDelta); + + if (maxDeviationDecibels <= allowedDeviationDecibels) { + testPassed("Triangular portion of convolution is correct."); + } else { + testFailed("Triangular portion of convolution is not correct. Max deviation = " + maxDeviationDecibels + " dB at " + maxDeltaIndex); + match = false; + } + + return match; +} + +// Verify that the rendered data is close to zero for the first part +// of the tail. +function checkTail1(data, reference, breakpoint) { + var isZero = true; + var tail1Max = 0; + + for (var i = reference.length; i < reference.length + breakpoint; ++i) { + var 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). + var refMax = 0; + for (var 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. + var threshold1 = -129.7; + + var tail1MaxDecibels = linearToDecibel(tail1Max/refMax); + if (tail1MaxDecibels <= threshold1) { + testPassed("First part of tail of convolution is sufficiently small."); + } else { + testFailed("First part of tail of convolution is not sufficiently small: " + tail1MaxDecibels + " dB"); + isZero = false; + } + + return isZero; +} + +// Verify that the second part of the tail of the convolution is +// exactly zero. +function checkTail2(data, reference, breakpoint) { + var isZero = true; + var tail2Max = 0; + // For the second part of the tail, the maximum value should be + // exactly zero. + var threshold2 = 0; + for (var i = reference.length + breakpoint; i < data.length; ++i) { + if (Math.abs(data[i]) > 0) { + isZero = false; + break; + } + } + + if (isZero) { + testPassed("Rendered signal after tail of convolution is silent."); + } else { + testFailed("Rendered signal after tail of convolution should be silent."); + } + + return isZero; +} + +function checkConvolvedResult(trianglePulse) { + return function(event) { + var renderedBuffer = event.renderedBuffer; + + var referenceData = trianglePulse.getChannelData(0); + var renderedData = renderedBuffer.getChannelData(0); + + var success = true; + + // Verify the triangular pulse is actually triangular. + + success = success && checkTriangularPulse(renderedData, referenceData); + + // 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. + var breakpoint = 12800; + + success = success && checkTail1(renderedData, referenceData, breakpoint); + + success = success && checkTail2(renderedData, referenceData, breakpoint); + + if (success) { + testPassed("Test signal was correctly convolved."); + } else { + testFailed("Test signal was not correctly convolved."); + } + + finishJSTest(); + } +} |