OLD | NEW |
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
2 <html> | 2 <html> |
3 <head> | 3 <head> |
4 <script src="../resources/js-test.js"></script> | 4 <script src="../resources/js-test.js"></script> |
5 <script src="resources/compatibility.js"></script> | 5 <script src="resources/compatibility.js"></script> |
6 <script src="resources/audio-testing.js"></script> | 6 <script src="resources/audio-testing.js"></script> |
7 </head> | 7 </head> |
8 | 8 |
9 <body> | 9 <body> |
10 <div id="description"></div> | 10 <div id="description"></div> |
11 <div id="console"></div> | 11 <div id="console"></div> |
12 | 12 |
13 <script> | 13 <script> |
14 description("Test scaling of FFT data for AnalyserNode"); | 14 description("Test scaling of FFT data for AnalyserNode"); |
15 | 15 |
16 // The number of analysers. We have analysers from size for each of the po
ssible sizes of 32, | 16 // The number of analysers. We have analysers from size for each of the po
ssible sizes of 32, |
17 // 64, 128, 256, 512, 1024 and 2048. | 17 // 64, 128, 256, 512, 1024 and 2048 for a total of 7. |
18 var numberOfAnalysers = 7; | 18 var numberOfAnalysers = 7; |
19 var sampleRate = 44100; | 19 var sampleRate = 44100; |
20 var context; | 20 var nyquistFrequency = sampleRate / 2; |
21 var osc; | 21 |
22 var oscFrequency = sampleRate/32; | 22 // Frequency of the sine wave test signal. Should be high enough so that
we get at least one |
23 var analysers = new Array(7); | 23 // full cycle for the 32-point FFT. This should also be such that the fre
quency should be |
24 var peakValue = new Array(7); | 24 // exactly in one of the FFT bins for each of the possible FFT sizes. |
| 25 var oscFrequency = nyquistFrequency/16; |
| 26 |
| 27 // The actual peak values from each analyser. Useful for examining the re
sults in Chrome. |
| 28 var peakValue = new Array(numberOfAnalysers); |
25 | 29 |
26 // For a 0dBFS sine wave, we would expect the FFT magnitude to be 0dB as w
ell, but the | 30 // For a 0dBFS sine wave, we would expect the FFT magnitude to be 0dB as w
ell, but the |
27 // analyzer node applies a Blackman window (to smooth the estimate). This
reduces the energy | 31 // analyzer node applies a Blackman window (to smooth the estimate). This
reduces the energy |
28 // of the signal so the FFT peak is less than 0dB. The threshold value gi
ven here was | 32 // of the signal so the FFT peak is less than 0dB. The threshold value gi
ven here was |
29 // determined experimentally. | 33 // determined experimentally. |
30 // | 34 // |
31 // See https://code.google.com/p/chromium/issues/detail?id=341596. | 35 // See https://code.google.com/p/chromium/issues/detail?id=341596. |
32 var peakThreshold = [-14.43, -13.56, -13.56, -13.56, -13.56, -13.56, -13.5
6]; | 36 var peakThreshold = [-14.43, -13.56, -13.56, -13.56, -13.56, -13.56, -13.5
6]; |
33 | 37 |
34 function checkResult() { | 38 var allTestsPassed = true; |
35 » var allTestsPassed = true; | |
36 | 39 |
37 » for (n = 0; n < analysers.length; ++n) { | 40 function checkResult(order, analyser) { |
38 » // Grab the FFT data from each analyser. | 41 return function () { |
39 » var fftSize = analysers[n].fftSize; | 42 var index = order - 5; |
40 » var fftData = new Float32Array(fftSize); | 43 var fftSize = 1 << order; |
41 » analysers[n].getFloatFrequencyData(fftData); | 44 var fftData = new Float32Array(fftSize); |
| 45 analyser.getFloatFrequencyData(fftData); |
42 | 46 |
43 » // Compute the frequency bin that should contain the peak. | 47 // Compute the frequency bin that should contain the peak. |
44 » var expectedBin = fftSize * (oscFrequency / sampleRate); | 48 var expectedBin = analyser.frequencyBinCount * (oscFrequency / nyq
uistFrequency); |
45 | 49 |
46 » // Find the actual bin by finding the bin containing the peak. | 50 // Find the actual bin by finding the bin containing the peak. |
47 » var actualBin = 0; | 51 var actualBin = 0; |
48 » peakValue[n] = -1000; | 52 peakValue[index] = -1000; |
49 » for (k = 0; k < analysers[n].frequencyBinCount; ++k) { | 53 for (k = 0; k < analyser.frequencyBinCount; ++k) { |
50 » » if (fftData[k] > peakValue[n]) { | 54 if (fftData[k] > peakValue[index]) { |
51 » » actualBin = k; | 55 actualBin = k; |
52 » » peakValue[n] = fftData[k]; | 56 peakValue[index] = fftData[k]; |
53 » » } | 57 } |
54 » } | 58 } |
55 | 59 |
56 » var success = true; | 60 var success = true; |
57 | 61 |
58 » if (actualBin == expectedBin) { | 62 if (actualBin == expectedBin) { |
59 » » testPassed("Actual FFT peak in the expected position (" + expe
ctedBin + ")"); | 63 testPassed("Actual FFT peak in the expected position (" + expe
ctedBin + ")."); |
60 » } else { | 64 } else { |
61 » » success = false; | 65 success = false; |
62 » » testFailed("Actual FFT peak (" + actualBin + ") differs from e
xpected (" + expectedBin + ")"); | 66 testFailed("Actual FFT peak (" + actualBin + ") differs from e
xpected (" + expectedBin + ")."); |
63 » } | 67 } |
64 | 68 |
65 » if (peakValue[n] >= peakThreshold[n]) { | 69 if (peakValue[index] >= peakThreshold[index]) { |
66 » » testPassed("Peak value is near 0 dBFS as expected"); | 70 testPassed("Peak value is near " + peakThreshold[index] + " dB
FS as expected."); |
67 » } else { | 71 } else { |
68 » » success = false; | 72 success = false; |
69 » » testFailed("Peak value of " + peakValue[n] | 73 testFailed("Peak value of " + peakValue[index] |
70 + " is incorrect. (Expected approximately " | 74 + " is incorrect. (Expected approximately " |
71 + peakThreshold[n] + ")"); | 75 + peakThreshold[index] + ")."); |
72 » } | 76 } |
73 | 77 |
74 » if (success) { | 78 if (success) { |
75 » » testPassed("Analyser correctly scaled FFT data of size " + fft
Size); | 79 testPassed("Analyser correctly scaled FFT data of size " + fft
Size); |
76 » } else { | 80 } else { |
77 » » testFailed("Analyser incorrectly scaled FFT data of size " + f
ftSize); | 81 testFailed("Analyser incorrectly scaled FFT data of size " + f
ftSize); |
78 » } | 82 } |
79 » allTestsPassed = allTestsPassed && success; | 83 allTestsPassed = allTestsPassed && success; |
80 » } | |
81 | 84 |
82 » if (allTestsPassed) { | 85 if (fftSize == 2048) { |
83 » testPassed("All Analyser tests passed."); | 86 if (allTestsPassed) { |
84 » } else { | 87 testPassed("All Analyser tests passed."); |
85 » testFailed("At least one Analyser test failed."); | 88 } else { |
86 » } | 89 testFailed("At least one Analyser test failed."); |
| 90 } |
87 | 91 |
88 » finishJSTest(); | 92 finishJSTest(); |
| 93 } |
| 94 } |
89 } | 95 } |
90 | 96 |
91 function runTests() { | 97 function runTests() { |
92 if (window.testRunner) { | 98 if (window.testRunner) { |
93 testRunner.dumpAsText(); | 99 testRunner.dumpAsText(); |
94 testRunner.waitUntilDone(); | 100 testRunner.waitUntilDone(); |
95 } | 101 } |
96 | 102 |
97 window.jsTestIsAsync = true; | 103 window.jsTestIsAsync = true; |
98 | 104 |
99 context = new OfflineAudioContext(1, 2048, sampleRate); | 105 // Test each analyser size from order 5 (size 32) to 11 (size 2048). |
| 106 for (order = 5; order < 12; ++order) { |
| 107 // Create a new offline context for each analyser test with the nu
mber of samples |
| 108 // exactly equal to the fft size. This ensures that the analyser
node gets the |
| 109 // expected data from the oscillator. |
| 110 var context = new OfflineAudioContext(1, 1 << order, sampleRate); |
| 111 // Use a sine wave oscillator as the reference source signal. |
| 112 var osc = context.createOscillator(); |
| 113 osc.type = "sine"; |
| 114 osc.frequency.value = oscFrequency; |
| 115 osc.connect(context.destination); |
100 | 116 |
101 // Use a sine wave oscillator as the reference source signal. | 117 var analyser = context.createAnalyser(); |
102 osc = context.createOscillator(); | 118 // No smoothing to simplify the analysis of the result. |
103 osc.type = "sine"; | 119 analyser.smoothingTimeConstant = 0; |
104 osc.frequency.value = oscFrequency; | 120 analyser.fftSize = 1 << order; |
105 osc.connect(context.destination); | 121 osc.connect(analyser); |
106 | 122 |
107 // Create an analyser node for each of the possible valid sizes. | 123 osc.start(); |
108 for (order = 5; order < 12; ++order) { | 124 context.oncomplete = checkResult(order, analyser); |
109 » analysers[order - 5] = context.createAnalyser(); | 125 context.startRendering(); |
110 // No smoothing so between frames to simplify testing. | |
111 » analysers[order - 5].smoothingTimeConstant = 0; | |
112 » analysers[order - 5].fftSize = 1 << order; | |
113 osc.connect(analysers[order - 5]); | |
114 } | 126 } |
115 | |
116 osc.start(); | |
117 context.oncomplete = checkResult; | |
118 context.startRendering(); | |
119 } | 127 } |
120 | 128 |
121 runTests(); | 129 runTests(); |
122 successfullyParsed = true; | 130 successfullyParsed = true; |
123 </script> | 131 </script> |
124 </body> | 132 </body> |
125 </html> | 133 </html> |
OLD | NEW |