<!DOCTYPE html>
<!--
Create two sources and play them simultaneously. This tests unity-gain summing of AudioNode inputs.
The result should be some laughing playing at the same time as the drumming.
-->
<html>
<head>
<title>
mixing.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();
let sampleRate = 44100.0;
let lengthInSeconds = 2;
audit.define('test', (task, should) => {
// Create offline audio context.
let context = new OfflineAudioContext(
2, sampleRate * lengthInSeconds, sampleRate);
// Load up audio files and test
Promise
.all([
// This file is stereo
Audit.loadFileFromUrl('resources/hyper-reality/br-jam-loop.wav')
.then(response => {
return context.decodeAudioData(response);
}),
// This file is mono
Audit.loadFileFromUrl('resources/hyper-reality/laughter.wav')
.then(response => {
return context.decodeAudioData(response);
}),
])
.then(audioBuffers => {
// Thresholds are experimentally determined
return runTest(context, audioBuffers, should, [
{snrThreshold: Infinity, errorThreshold: 0},
{snrThreshold: Infinity, errorThreshold: 0}
]);
})
.then(() => task.done());
});
audit.run();
function runTest(context, bufferList, should, testThresholds) {
should(bufferList.length, 'Number of decoded files').beEqualTo(2);
// Create two sources and play them at the same time.
let source1 = context.createBufferSource();
let source2 = context.createBufferSource();
source1.buffer = bufferList[0];
source2.buffer = bufferList[1];
source1.connect(context.destination);
source2.connect(context.destination);
source1.start(0);
source2.start(0);
// Verify the number of channels in each source and the expected result.
should(
bufferList[0].numberOfChannels,
'Number of channels in stereo source')
.beEqualTo(2);
should(
bufferList[1].numberOfChannels, 'Number of channels in mono source')
.beEqualTo(1);
return context.startRendering().then(audioBuffer => {
verifyResult(
audioBuffer, context, bufferList, testThresholds, should);
});
}
function verifyResult(
renderedBuffer, context, bufferList, testThresholds, should) {
// Test only works if we have a stereo result.
should(
renderedBuffer.numberOfChannels,
'Number of channels in rendered output')
.beEqualTo(2);
// Note: the source lengths may not match the context length. Create
// copies of the sources truncated or zero-filled to the rendering
// length.
let stereoSource = new AudioBuffer({
length: renderedBuffer.length,
numberOfChannels: 2,
sampleRate: context.sampleRate
});
stereoSource.copyToChannel(bufferList[0].getChannelData(0), 0);
stereoSource.copyToChannel(bufferList[0].getChannelData(1), 1);
let monoSource = new AudioBuffer({
length: renderedBuffer.length,
numberOfChannels: 1,
sampleRate: context.sampleRate
});
monoSource.copyToChannel(bufferList[1].getChannelData(0), 0);
// Compute the expected result buffer0 is stereo and buffer1 is mono.
// The result should be stereo, with the mono source implicitly upmixed
// to stereo to produce the expected result.
let expectedBuffer = new AudioBuffer({
length: renderedBuffer.length,
numberOfChannels: 2,
sampleRate: context.sampleRate
});
let monoData = monoSource.getChannelData(0);
for (let c = 0; c < expectedBuffer.numberOfChannels; ++c) {
let expectedData = expectedBuffer.getChannelData(c);
let stereoData = stereoSource.getChannelData(c);
for (let k = 0; k < expectedBuffer.length; ++k) {
expectedData[k] = stereoData[k] + monoData[k];
}
}
// Compare the rendered data with the expected data for each channel.
for (let k = 0; k < renderedBuffer.numberOfChannels; ++k) {
let actualData = renderedBuffer.getChannelData(k);
let expectedData = expectedBuffer.getChannelData(k);
let threshold = testThresholds[k];
let snr = 10 * Math.log10(computeSNR(actualData, expectedData));
should(snr, 'SNR for channel ' + k)
.beGreaterThanOrEqualTo(threshold.snrThreshold);
should(actualData, 'Rendered audio').beCloseToArray(expectedData, {
absoluteThreshold: threshold.errorThreshold
});
}
}
</script>
</body>
</html>