mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-12 14:37:50 +00:00
7af104bee8
--HG-- extra : rebase_source : b829991957f0349a6c2541fcc99d4214e701a2ae
183 lines
5.7 KiB
JavaScript
183 lines
5.7 KiB
JavaScript
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();
|
|
}
|
|
}
|