realtimeanalyser-fft-scaling.html (3637B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title> 5 realtimeanalyser-fft-scaling.html 6 </title> 7 <script src="/resources/testharness.js"></script> 8 <script src="/resources/testharnessreport.js"></script> 9 </head> 10 <body> 11 <div id="description"></div> 12 <div id="console"></div> 13 <script> 14 // The number of analysers. We have analysers from size for each of the 15 // possible sizes of 2^5 to 2^15 for a total of 11. 16 const numberOfAnalysers = 11; 17 const sampleRate = 44100; 18 const nyquistFrequency = sampleRate / 2; 19 20 // Frequency of the sine wave test signal. Should be high enough so that 21 // we get at least one full cycle for the 32-point FFT. This should also 22 // be such that the frequency should be exactly in one of the FFT bins for 23 // each of the possible FFT sizes. 24 const oscFrequency = nyquistFrequency / 16; 25 26 // The actual peak values from each analyser. Useful for examining the 27 // actual peak values. 28 const peakValue = new Array(numberOfAnalysers); 29 30 // For a 0dBFS sine wave, we would expect the FFT magnitude to be 0dB as 31 // well, but the analyzer node applies a Blackman window (to smooth the 32 // estimate). This reduces the energy of the signal so the FFT peak is 33 // less than 0dB. The threshold value given here was determined 34 // experimentally. 35 // 36 // See https://code.google.com/p/chromium/issues/detail?id=341596. 37 const peakThreshold = [ 38 -14.43, -13.56, -13.56, -13.56, -13.56, -13.56, -13.56, -13.56, -13.56, 39 -13.56, -13.56, 40 ]; 41 42 function checkResult(order, analyser) { 43 const index = order - 5; 44 const fftSize = 1 << order; 45 46 // We only need frequencyBinCount bins. 47 const fftData = new Float32Array(fftSize); 48 analyser.getFloatFrequencyData(fftData); 49 50 // Compute the frequency bin that should contain the peak. 51 const expectedBin = 52 analyser.frequencyBinCount * (oscFrequency / nyquistFrequency); 53 54 // Find the actual bin by finding the bin containing the peak. 55 let actualBin = 0; 56 peakValue[index] = -1000; 57 for (let k = 0; k < analyser.frequencyBinCount; ++k) { 58 if (fftData[k] > peakValue[index]) { 59 actualBin = k; 60 peakValue[index] = fftData[k]; 61 } 62 } 63 64 assert_equals( 65 actualBin, expectedBin, 66 `${(fftSize)}-point FFT peak position`); 67 assert_greater_than_equal( 68 peakValue[index], peakThreshold[index], 69 `${fftSize}-point FFT peak value in dBFS`); 70 } 71 72 // Run the tests for FFT sizes 2^5 through 2^15. 73 function runTest(order) { 74 const context = new OfflineAudioContext(1, 1 << order, sampleRate); 75 const fftSize = 1 << order; 76 77 // Use a sine wave oscillator as the reference source signal. 78 const osc = new OscillatorNode( 79 context, {type: 'sine', frequency: oscFrequency}); 80 81 // No smoothing to simplify the analysis of the result. 82 const analyser = new AnalyserNode(context, { 83 fftSize: fftSize, 84 smoothingTimeConstant: 0, 85 }); 86 87 osc.connect(context.destination); 88 89 osc.connect(analyser); 90 91 osc.start(); 92 93 return context.startRendering().then(() => { 94 checkResult(order, analyser); 95 }); 96 } 97 98 promise_test(async t => { 99 for (let k = 5; k <= 15; ++k) { 100 await runTest(k); 101 } 102 }, 'FFT scaling tests — Test Scaling of FFT in AnalyserNode'); 103 </script> 104 </body> 105 </html>