blob: 1cf41812a3950e6f0132704a2e394d027a15e3c0 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>
Test AnalyserNode getFloatTimeDomainData
</title>
<script src="../../imported/w3c/web-platform-tests/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">
// Use a power of two to eliminate any round-off in the computation of the
// times for context.suspend().
let sampleRate = 32768;
// The largest FFT size for the analyser node is 32768. We want to render
// longer than this so that we have at least one complete buffer of data
// of 32768 samples.
let renderFrames = 2 * 32768;
let renderDuration = renderFrames / sampleRate;
let audit = Audit.createTaskRunner();
// Test that getFloatTimeDomainData handles short and long vectors
// correctly.
audit.define('short and long vector', (task, should) => {
let fftSize = 32;
let graphInfo = createGraph(fftSize);
let context = graphInfo.context;
let analyser = graphInfo.analyser;
let signalBuffer = graphInfo.signalBuffer;
let signal = signalBuffer.getChannelData(0);
let success = true;
let sampleFrame = 128;
context.suspend(sampleFrame / sampleRate)
.then(function() {
let shortData = new Float32Array(8);
// Initialize the array to Infinity to represent uninitialize
// data.
shortData.fill(Infinity);
analyser.getFloatTimeDomainData(shortData);
// The short array should be filled with the expected data, with
// no errors thrown.
let expected =
signal.subarray(sampleFrame - fftSize, sampleFrame);
should(shortData, shortData.length + '-element time domain data')
.beEqualToArray(expected.subarray(0, shortData.length));
let longData = new Float32Array(2 * fftSize);
// Initialize the array to Infinity to represent uninitialize
// data.
longData.fill(Infinity);
analyser.getFloatTimeDomainData(longData);
// The long array should filled with the expected data but the
// extra elements should be untouched.
should(
longData.subarray(0, fftSize),
'longData.subarray(0, ' + fftSize + ')')
.beEqualToArray(expected);
should(
longData.subarray(fftSize),
'Unfilled elements longData.subarray(' + fftSize + ')')
.beConstantValueOf(Infinity);
})
.then(context.resume.bind(context));
context.startRendering().then(() => task.done());
});
let success = true;
// Generate tests for all valid FFT sizes for an AnalyserNode.
for (let k = 5; k < 16; ++k) {
let fftSize = Math.pow(2, k);
(function(n) {
// We grab a sample at (roughly) half the rendering duration.
audit.define('fftSize ' + n, (task, should) => {
runTest(n, renderDuration / 2, should).then(() => task.done());
});
})(fftSize);
}
// Special case for a large size, but the sampling point is early. The
// initial part of the buffer should be filled with zeroes.
audit.define('initial zeroes', (task, should) => {
// Somewhat arbitrary size for the analyser. It should be greater than
// one rendering quantum.
let fftSize = 2048;
let graphInfo = createGraph(fftSize);
let context = graphInfo.context;
let analyser = graphInfo.analyser;
let signalBuffer = graphInfo.signalBuffer;
let data = new Float32Array(fftSize);
success = true;
// Suspend every rendering quantum and examine the analyser data.
for (let k = 128; k <= fftSize; k += 128) {
context.suspend(k / sampleRate)
.then(function() {
analyser.getFloatTimeDomainData(data);
let sampleFrame = context.currentTime * sampleRate;
// Verify that the last k frames are not zero, but the first
// fftSize - k frames are.
let prefix =
'At frame ' + (sampleFrame - 1) + ': data.subarray';
if (sampleFrame < fftSize) {
should(
data.subarray(0, fftSize - sampleFrame),
prefix + '(0, ' + (fftSize - sampleFrame) + ')')
.beConstantValueOf(0) &&
success;
}
let signal = signalBuffer.getChannelData(0);
should(
data.subarray(fftSize - sampleFrame, fftSize),
prefix + '(' + (fftSize - sampleFrame) + ', ' + fftSize +
')')
.beEqualToArray(signal.subarray(0, sampleFrame)) &&
success;
})
.then(context.resume.bind(context));
}
context.startRendering().then(() => task.done());
});
audit.run();
// Run test of an AnalyserNode with fftSize of |fftSize|, and with the
// data from the node being requested at time |sampletime|. The result
// from the analyser node is compared against the expected data. The
// result of startRendering() is returned.
function runTest(fftSize, sampleTime, should) {
let graphInfo = createGraph(fftSize);
let context = graphInfo.context;
let analyser = graphInfo.analyser;
let signalBuffer = graphInfo.signalBuffer;
// Grab the data at the requested time.
context.suspend(sampleTime)
.then(function() {
let lastFrame = Math.floor(context.currentTime * sampleRate);
// Grab the time domain data from the analyzer and compare against
// the expected result.
let actualFloatData = new Float32Array(fftSize);
analyser.getFloatTimeDomainData(actualFloatData);
// Compare against the expected result.
let signal = signalBuffer.getChannelData(0);
let message =
actualFloatData.length + '-point analyser time domain data';
should(actualFloatData, message)
.beEqualToArray(signal.subarray(
lastFrame - actualFloatData.length, lastFrame)) &&
success;
})
.then(context.resume.bind(context));
return context.startRendering();
}
// Create the audio graph with an AnalyserNode with fftSize |fftSize|. A
// simple integer-valued linear ramp is the source so we can easily verify
// the results. A dictionary consisting of the context, the analyser
// node, and the signal is returned.
function createGraph(fftSize) {
let context = new OfflineAudioContext(1, renderFrames, sampleRate);
let src = context.createBufferSource();
// Use a simple linear ramp as the source. For simplicity of inspecting
// results, the ramp starts at 1 with an increment of 1.
let signalBuffer =
context.createBuffer(1, renderFrames, context.sampleRate);
let data = signalBuffer.getChannelData(0);
for (let k = 0; k < data.length; ++k) {
data[k] = k + 1;
}
src.buffer = signalBuffer;
let analyser = context.createAnalyser();
analyser.fftSize = fftSize;
src.connect(analyser);
analyser.connect(context.destination);
src.start();
return {
context: context,
analyser: analyser,
signalBuffer: signalBuffer
};
}
</script>
</body>
</html>