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
|
<!DOCTYPE html>
<html>
<head>
<title>
realtimeanalyser-fft-scaling.html
</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>
<div id="description"></div>
<div id="console"></div>
<script id="layout-test-code">
let audit = Audit.createTaskRunner();
// The number of analysers. We have analysers from size for each of the
// possible sizes of 2^5 to 2^15 for a total of 11.
let numberOfAnalysers = 11;
let sampleRate = 44100;
let 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.
let oscFrequency = nyquistFrequency / 16;
// The actual peak values from each analyser. Useful for examining the
// actual peak values.
let 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.
let peakThreshold = [
-14.43, -13.56, -13.56, -13.56, -13.56, -13.56, -13.56, -13.56, -13.56,
-13.56, -13.56
];
function checkResult(order, analyser, should) {
return function() {
let index = order - 5;
let fftSize = 1 << order;
let fftData = new Float32Array(fftSize);
analyser.getFloatFrequencyData(fftData);
// Compute the frequency bin that should contain the peak.
let expectedBin =
analyser.frequencyBinCount * (oscFrequency / nyquistFrequency);
// Find the actual bin by finding the bin containing the peak.
let actualBin = 0;
peakValue[index] = -1000;
for (k = 0; k < analyser.frequencyBinCount; ++k) {
if (fftData[k] > peakValue[index]) {
actualBin = k;
peakValue[index] = fftData[k];
}
}
should(actualBin, (1 << order) + '-point FFT peak position')
.beEqualTo(expectedBin);
should(
peakValue[index], (1 << order) + '-point FFT peak value in dBFS')
.beGreaterThanOrEqualTo(peakThreshold[index]);
}
}
audit.define(
{
label: 'FFT scaling tests',
description: 'Test Scaling of FFT in AnalyserNode'
},
async function(task, should) {
let tests = [];
for (let k = 5; k <= 15; ++k)
await runTest(k, should);
task.done();
});
function runTest(order, should) {
let context = new OfflineAudioContext(1, 1 << order, sampleRate);
// Use a sine wave oscillator as the reference source signal.
let osc = context.createOscillator();
osc.type = 'sine';
osc.frequency.value = oscFrequency;
osc.connect(context.destination);
let analyser = context.createAnalyser();
// No smoothing to simplify the analysis of the result.
analyser.smoothingTimeConstant = 0;
analyser.fftSize = 1 << order;
osc.connect(analyser);
osc.start();
return context.startRendering().then(() => {
checkResult(order, analyser, should)();
});
}
audit.run();
</script>
</body>
</html>
|