1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
<!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>
|