<!DOCTYPE html>
<html>
<head>
<title>
Test AnalyserNode Downmixing
</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>
<script src="../resources/fft.js"></script>
<script src="../resources/realtimeanalyser-testing.js"></script>
</head>
<body>
<script id="layout-test-code">
let sampleRate = 44100;
let renderFrames = 2048;
let audit = Audit.createTaskRunner();
let testConfigs = [
{channelCount: 1, message: 'mono', floatRelError: 6.3283e-8},
{channelCount: 2, message: 'stereo', floatRelError: 1.1681e-7},
{channelCount: 4, message: 'quad', floatRelError: 4.9793e-7},
{channelCount: 6, message: '5.1', floatRelError: 2.0215e-7},
{channelCount: 3, message: '3-channel', floatRelError: 6.3283e-8}
];
// Create tasks for each entry in testConfigs
for (k in testConfigs) {
audit.define(testConfigs[k].message, (function(config) {
return function(task, should) {
runTest(config, should).then(() => task.done());
};
})(testConfigs[k]));
}
audit.run();
// Test downmixing of the AnalyserNode time data. We use the downmixing
// that automatically happens in the destination node to generate the
// reference data which is compared to the data that the Analyser node has
// captured.
function runTest(options, should) {
// Context MUST have exactly one channel so that we downmix the source
// to mono to generate the reference.
let context = new OfflineAudioContext(1, renderFrames, sampleRate);
let channels = options.channelCount || 1;
let source = context.createBufferSource();
// The signals in each channel. Doesn't matter much what is in here, but
// it's best if the values aren't linearly increasing so that the
// average of the values isn't one of the values (in case the
// implementation does something silly). Only need to support up to 6
// channels.
let bufferValues = [1, 2, 3, 4, 5, 6].map(function(x) {
return x * x
});
;
source.buffer = createConstantBuffer(
context, renderFrames, bufferValues.slice(0, channels));
let analyser = context.createAnalyser();
analyser.smoothingTimeConstant = 0;
analyser.fftSize = 256;
// Run analyser as an automatic pull node. Do NOT connect to the
// destination. We don't want the output of the analyser to mix in with
// the source that is also directly connected to the destination.
source.connect(analyser);
source.connect(context.destination);
let timeData = new Float32Array(analyser.fftSize);
let freqData = new Float32Array(analyser.frequencyBinCount);
let suspendFrame = analyser.fftSize;
context.suspend(suspendFrame / context.sampleRate)
.then(function() {
analyser.getFloatTimeDomainData(timeData);
analyser.getFloatFrequencyData(freqData);
})
.then(context.resume.bind(context));
source.start();
return context.startRendering().then(function(renderedBuffer) {
// Verify the time domain data is correct.
let prefix = 'Analyser downmix ' + options.message + ' to mono'
should(timeData, prefix + ' time data')
.beEqualToArray(renderedBuffer.getChannelData(0).subarray(
0, analyser.fftSize));
let expectedTimeData =
renderedBuffer.getChannelData(0).subarray(0, analyser.fftSize);
let fftOrder = Math.floor(Math.log2(analyser.fftSize));
let expectedFreqData =
computeFFTMagnitude(expectedTimeData, fftOrder).map(linearToDb);
compareFloatFreq(
prefix + ' freq data', freqData, expectedFreqData, should, {
precision: 6,
floatRelError: options.floatRelError,
});
});
}
</script>
</body>
</html>