<!DOCTYPE html>
<html>
<head>
<title>
audiobuffer-resample.html
</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../resources/audit-util.js"></script>
<script src="../resources/audit.js"></script>
</head>
<body>
<script id="layout-test-code">
let audit = Audit.createTaskRunner();
// These are global to make debugging a little easier.
let context;
let buffer;
let source;
let renderedData;
let trueData;
let signalEnergy;
let noiseEnergy;
let maxError;
// Context sample rate.
let sampleRate = 48000;
// The sample rate of the buffer.
let bufferRate = 3000;
// The audio buffer is a sine wave of this frequency.
let toneFrequency = 440;
// How long test is
let lengthInSeconds = 0.5;
// The maximum allowed peak error between the actual and true output. This
// value was experimentally determined for the given bufferRate.
let peakThreshold = 0.11;
// The minimum SNR allowed between the actual and true output.
let snrThreshold = 22.35;
// Generate a sine wave in an AudioBuffer using the given |freq|. The
// AudioBuffer has the sample rate of |rate|.
function createSineBuffer(context, freq, rate) {
let buf = context.createBuffer(1, lengthInSeconds * rate, rate);
let omega = 2 * Math.PI * freq / rate;
let signal = buf.getChannelData(0);
let length = signal.length;
for (let k = 0; k < length; ++k)
signal[k] = Math.sin(omega * k);
return buf;
}
// Check the output against the expected output.
function checkResult(buffer, should) {
renderedData = buffer.getChannelData(0);
let length = renderedData.length;
// Generate a reference sine wave at the context rate
let trueReference =
createSineBuffer(context, toneFrequency, context.sampleRate);
trueData = trueReference.getChannelData(0);
// To compare the actual output against the reference, we compute the
// peak error and the SNR between the two.
signalEnergy = 0;
noiseEnergy = 0;
maxError = -1;
let success = true;
// Compute the peak error and the SNR.
for (let k = 0; k < length / 2; ++k) {
let diff = renderedData[k] - trueData[k];
noiseEnergy += diff * diff;
signalEnergy += trueData[k] * trueData[k];
if (Math.abs(diff) > maxError)
maxError = Math.abs(diff);
}
let snr;
if (noiseEnergy == 0)
snr = 1000;
else
snr = 10 * Math.log10(signalEnergy / noiseEnergy);
should(maxError, 'Peak error between actual and reference data')
.beLessThan(peakThreshold);
should(snr, 'SNR').beGreaterThan(snrThreshold);
}
audit.define('Test resampling of an AudioBuffer', function(task, should) {
context = new OfflineAudioContext(
1, lengthInSeconds * sampleRate, sampleRate);
// Create a sine wave in a buffer that's different from the context rate
// to test resampling.
buffer = createSineBuffer(context, toneFrequency, bufferRate);
source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.start();
context.startRendering()
.then(function(buffer) {
checkResult(buffer, should);
})
.then(task.done.bind(task));
});
audit.run();
</script>
</body>
</html>