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