blob: 3700bfa8ce806a57a4df6c050a9d299d9274edea [file] [log] [blame]
<!doctype html>
<html>
<head>
<title>
Test Sub-Sample Accurate Stitching of ABSNs
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
<script src="/webaudio/resources/audit.js"></script>
</head>
<body>
<script>
let audit = Audit.createTaskRunner();
audit.define(
{
label: 'buffer-stitching-1',
description: 'Subsample buffer stitching, same rates'
},
(task, should) => {
const sampleRate = 44100;
const bufferRate = 44100;
const bufferLength = 30;
// Experimentally determined thresholds. DO NOT relax these values
// to far from these values to make the tests pass.
const errorThreshold = 9.0957e-5;
const snrThreshold = 85.580;
// Informative message
should(sampleRate, 'Test 1: context.sampleRate')
.beEqualTo(sampleRate);
testBufferStitching(sampleRate, bufferRate, bufferLength)
.then(resultBuffer => {
const actual = resultBuffer.getChannelData(0);
const expected = resultBuffer.getChannelData(1);
should(
actual,
`Stitched sine-wave buffers at sample rate ${bufferRate}`)
.beCloseToArray(
expected, {absoluteThreshold: errorThreshold});
const SNR = 10 * Math.log10(computeSNR(actual, expected));
should(SNR, `SNR (${SNR} dB)`)
.beGreaterThanOrEqualTo(snrThreshold);
})
.then(() => task.done());
});
audit.define(
{
label: 'buffer-stitching-2',
description: 'Subsample buffer stitching, different rates'
},
(task, should) => {
const sampleRate = 44100;
const bufferRate = 43800;
const bufferLength = 30;
// Experimentally determined thresholds. DO NOT relax these values
// to far from these values to make the tests pass.
const errorThreshold = 3.8986e-3;
const snrThreshold = 65.737;
// Informative message
should(sampleRate, 'Test 2: context.sampleRate')
.beEqualTo(sampleRate);
testBufferStitching(sampleRate, bufferRate, bufferLength)
.then(resultBuffer => {
const actual = resultBuffer.getChannelData(0);
const expected = resultBuffer.getChannelData(1);
should(
actual,
`Stitched sine-wave buffers at sample rate ${bufferRate}`)
.beCloseToArray(
expected, {absoluteThreshold: errorThreshold});
const SNR = 10 * Math.log10(computeSNR(actual, expected));
should(SNR, `SNR (${SNR} dB)`)
.beGreaterThanOrEqualTo(snrThreshold);
})
.then(() => task.done());
});
audit.run();
// Create graph to test stitching of consecutive ABSNs. The context rate
// is |sampleRate|, and the buffers have a fixed length of |bufferLength|
// and rate of |bufferRate|. The |bufferRate| should not be too different
// from |sampleRate| because of interpolation of the buffer to the context
// rate.
function testBufferStitching(sampleRate, bufferRate, bufferLength) {
// The context for testing. Channel 0 contains the output from
// stitching all the buffers together, and channel 1 contains the
// expected output.
const context = new OfflineAudioContext(
{numberOfChannels: 2, length: sampleRate, sampleRate: sampleRate});
const merger = new ChannelMergerNode(
context, {numberOfInputs: context.destination.channelCount});
merger.connect(context.destination);
// The reference is a sine wave at 440 Hz.
const ref = new OscillatorNode(context, {frequency: 440, type: 'sine'});
ref.connect(merger, 0, 1);
ref.start();
// The test signal is a bunch of short AudioBufferSources containing
// bits of a sine wave.
let waveSignal = new Float32Array(context.length);
const omega = 2 * Math.PI / bufferRate * ref.frequency.value;
for (let k = 0; k < context.length; ++k) {
waveSignal[k] = Math.sin(omega * k);
}
// Slice the sine wave into many little buffers to be assigned to ABSNs
// that are started at the appropriate times to produce a final sine
// wave.
for (let k = 0; k < context.length; k += bufferLength) {
const buffer =
new AudioBuffer({length: bufferLength, sampleRate: bufferRate});
buffer.copyToChannel(waveSignal.slice(k, k + bufferLength), 0);
const src = new AudioBufferSourceNode(context, {buffer: buffer});
src.connect(merger, 0, 0);
src.start(k / bufferRate);
}
return context.startRendering();
}
</script>
</body>
</html>