blob: 73dd517f07d2e5db6ee28d99ad74cff7b3737a9b [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<script src="../resources/js-test.js"></script>
<script src="resources/compatibility.js"></script>
<script src="resources/audio-testing.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script>
description("Test scaling of FFT data for AnalyserNode");
// The number of analysers. We have analysers from size for each of the possible sizes of 32,
// 64, 128, 256, 512, 1024 and 2048 for a total of 7.
var numberOfAnalysers = 7;
var sampleRate = 44100;
var nyquistFrequency = sampleRate / 2;
// Frequency of the sine wave test signal. Should be high enough so that we get at least one
// full cycle for the 32-point FFT. This should also be such that the frequency should be
// exactly in one of the FFT bins for each of the possible FFT sizes.
var oscFrequency = nyquistFrequency/16;
// The actual peak values from each analyser. Useful for examining the results in Chrome.
var peakValue = new Array(numberOfAnalysers);
// For a 0dBFS sine wave, we would expect the FFT magnitude to be 0dB as well, but the
// analyzer node applies a Blackman window (to smooth the estimate). This reduces the energy
// of the signal so the FFT peak is less than 0dB. The threshold value given here was
// determined experimentally.
//
// See https://code.google.com/p/chromium/issues/detail?id=341596.
var peakThreshold = [-14.43, -13.56, -13.56, -13.56, -13.56, -13.56, -13.56];
var allTestsPassed = true;
function checkResult(order, analyser) {
return function () {
var index = order - 5;
var fftSize = 1 << order;
var fftData = new Float32Array(fftSize);
analyser.getFloatFrequencyData(fftData);
// Compute the frequency bin that should contain the peak.
var expectedBin = analyser.frequencyBinCount * (oscFrequency / nyquistFrequency);
// Find the actual bin by finding the bin containing the peak.
var actualBin = 0;
peakValue[index] = -1000;
for (k = 0; k < analyser.frequencyBinCount; ++k) {
if (fftData[k] > peakValue[index]) {
actualBin = k;
peakValue[index] = fftData[k];
}
}
var success = true;
if (actualBin == expectedBin) {
testPassed("Actual FFT peak in the expected position (" + expectedBin + ").");
} else {
success = false;
testFailed("Actual FFT peak (" + actualBin + ") differs from expected (" + expectedBin + ").");
}
if (peakValue[index] >= peakThreshold[index]) {
testPassed("Peak value is near " + peakThreshold[index] + " dBFS as expected.");
} else {
success = false;
testFailed("Peak value of " + peakValue[index]
+ " is incorrect. (Expected approximately "
+ peakThreshold[index] + ").");
}
if (success) {
testPassed("Analyser correctly scaled FFT data of size " + fftSize);
} else {
testFailed("Analyser incorrectly scaled FFT data of size " + fftSize);
}
allTestsPassed = allTestsPassed && success;
if (fftSize == 2048) {
if (allTestsPassed) {
testPassed("All Analyser tests passed.");
} else {
testFailed("At least one Analyser test failed.");
}
finishJSTest();
}
}
}
function runTests() {
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}
window.jsTestIsAsync = true;
// Test each analyser size from order 5 (size 32) to 11 (size 2048).
for (order = 5; order < 12; ++order) {
// Create a new offline context for each analyser test with the number of samples
// exactly equal to the fft size. This ensures that the analyser node gets the
// expected data from the oscillator.
var context = new OfflineAudioContext(1, 1 << order, sampleRate);
// Use a sine wave oscillator as the reference source signal.
var osc = context.createOscillator();
osc.type = "sine";
osc.frequency.value = oscFrequency;
osc.connect(context.destination);
var analyser = context.createAnalyser();
// No smoothing to simplify the analysis of the result.
analyser.smoothingTimeConstant = 0;
analyser.fftSize = 1 << order;
osc.connect(analyser);
osc.start();
context.oncomplete = checkResult(order, analyser);
context.startRendering();
}
}
runTests();
successfullyParsed = true;
</script>
</body>
</html>