| <!DOCTYPE html> |
| <html> |
| <head> |
| <title> |
| Test Analyser getFloatFrequencyData and getByteFrequencyData, No |
| Smoothing |
| </title> |
| <script src="../../imported/w3c/web-platform-tests/resources/testharness.js"></script> |
| <script src="../../resources/testharnessreport.js"></script> |
| <script src="../resources/audit-util.js"></script> |
| <script src="../resources/audit.js"></script> |
| <script src="../resources/realtimeanalyser-testing.js"></script> |
| <script src="../resources/fft.js"></script> |
| </head> |
| <body> |
| <script id="layout-test-code"> |
| // Use a power of two to eliminate any round-off in the computation of the |
| // times for context.suspend(). |
| let sampleRate = 32768; |
| |
| // The largest FFT size for the analyser node is 32768. We want to render |
| // longer than this so that we have at least one complete buffer of data |
| // of 32768 samples. |
| let renderFrames = 2 * 32768; |
| let renderDuration = renderFrames / sampleRate; |
| |
| let audit = Audit.createTaskRunner(); |
| |
| // Options for basic tests of the AnalyserNode frequency domain data. The |
| // thresholds are experimentally determined. The threshold for the byte |
| // frequency results could in general be off by 1 depending on very minor |
| // differences in computing the FFT value and converting it to a byte |
| // value (because Math.floor must be used). For the tests that fail, set |
| // |byteThreshold| to 1. Using any threshold larger than this is a |
| // serious error in the implementation of the AnalyserNode FFT. |
| let testConfig = [ |
| { |
| order: 5, |
| // For this order, need to specify a higher minDecibels value for the |
| // analyser because the FFT doesn't get that small. This allows us to |
| // test that (a changed) minDecibels has an effect and that we |
| // properly clip the byte data. |
| minDecibels: -50, |
| floatRelError: 9.6549e-7, |
| }, |
| {order: 6, floatRelError: 1.0084e-5}, |
| {order: 7, floatRelError: 1.1473e-6}, |
| {order: 8, floatRelError: 1.0442e-6}, |
| {order: 9, floatRelError: 2.6427e-5}, |
| {order: 10, floatRelError: 2.9771e-5, byteThreshold: 1}, |
| {order: 11, floatRelError: 1.3456e-5}, |
| {order: 12, floatRelError: 4.6116e-7}, |
| {order: 13, floatRelError: 3.4196e-7}, |
| {order: 14, floatRelError: 1.6257e-7}, |
| {order: 15, floatRelError: 2.0876e-7} |
| ]; |
| |
| // True if all of the basic tests passed. |
| let basicTestsPassed = true; |
| |
| // Generate tests for each entry in testConfig. |
| for (let k = 0; k < testConfig.length; ++k) { |
| let name = testConfig[k].order + '-order FFT'; |
| (function(config) { |
| audit.define(name, (task, should) => { |
| basicFFTTest(should, config).then(() => task.done()); |
| }); |
| })(testConfig[k]); |
| } |
| |
| // Test that smoothing isn't done and we have the expected data, calling |
| // getFloatFrequencyData twice at different times. |
| audit.define('no smoothing', (task, should) => { |
| // Use 128-point FFT for the test. The actual order doesn't matter (but |
| // the error threshold depends on the order). |
| let options = {order: 7, smoothing: 0, floatRelError: 1.5684e-6}; |
| let graph = createGraph(options); |
| let context = graph.context; |
| let analyser = graph.analyser; |
| |
| // Be sure to suspend after the analyser fftSize so we get a full buffer |
| // of data. We will grab the FFT data to prime the pump for smoothing. |
| // We don't need to check the results (because this is tested above in |
| // the basicFFTTests). |
| let suspendFrame = Math.max(128, analyser.fftSize); |
| context.suspend(suspendFrame / sampleRate) |
| .then(function() { |
| // Grab the time and frequency data. But we don't care what |
| // values we get now; we just want to prime the analyser. |
| let freqData = new Float32Array(analyser.frequencyBinCount); |
| |
| // Grab the frequency domain data |
| analyser.getFloatFrequencyData(freqData); |
| }) |
| .then(context.resume.bind(context)); |
| |
| // Grab another set of data after one rendering quantum. We will test |
| // this to make sure smoothing was not done. |
| suspendFrame += 128; |
| context.suspend(suspendFrame / sampleRate) |
| .then(function() { |
| let timeData = new Float32Array(analyser.fftSize); |
| let freqData = new Float32Array(analyser.frequencyBinCount); |
| |
| // Grab the time domain and frequency domain data |
| analyser.getFloatTimeDomainData(timeData); |
| analyser.getFloatFrequencyData(freqData); |
| |
| let expected = |
| computeFFTMagnitude(timeData, options.order).map(linearToDb); |
| let comparison = compareFloatFreq( |
| Math.pow(2, options.order) + '-point float FFT', freqData, |
| expected, should, options); |
| basicTestsPassed = basicTestsPassed && comparison.success; |
| }) |
| .then(context.resume.bind(context)); |
| |
| context.startRendering().then(() => task.done()); |
| }); |
| |
| audit.run(); |
| |
| // Run a simple test of the AnalyserNode's frequency domain data. Both |
| // the float and byte frequency data are tested. The byte tests depend on |
| // the float tests being correct. |
| // |
| // The parameters of the test are given by |options| which is a property |
| // bag consisting of the following: |
| // |
| // order: Order of the FFT to test. |
| // smoothing: smoothing time constant for the analyser. |
| // minDecibels: min decibels value for the analyser. |
| // floatRelError: max allowed relative error for the float FFT data |
| function basicFFTTest(should, options) { |
| let graph = createGraph(options); |
| let context = graph.context; |
| let analyser = graph.analyser; |
| |
| let suspendTime = Math.max(128, analyser.fftSize) / sampleRate; |
| context.suspend(suspendTime) |
| .then(function() { |
| let timeData = new Float32Array(analyser.fftSize); |
| let freqData = new Float32Array(analyser.frequencyBinCount); |
| |
| // Grab the time domain and frequency domain data |
| analyser.getFloatTimeDomainData(timeData); |
| analyser.getFloatFrequencyData(freqData); |
| |
| let expected = |
| computeFFTMagnitude(timeData, options.order).map(linearToDb); |
| let comparison = compareFloatFreq( |
| Math.pow(2, options.order) + '-point float FFT', freqData, |
| expected, should, options); |
| basicTestsPassed = basicTestsPassed && comparison.success; |
| expected = comparison.expected; |
| |
| // For the byte test to be better, check that there are some |
| // samples that are outside the range of minDecibels and |
| // maxDecibels. If there aren't the test should update the |
| // minDecibels and maxDecibels values for the analyser. |
| |
| let minValue = Math.min(...expected); |
| let maxValue = Math.max(...expected); |
| |
| should(minValue, 'Order: ' + options.order + ': Min FFT value') |
| .beLessThanOrEqualTo(analyser.minDecibels); |
| should(maxValue, 'Order: ' + options.order + ': Max FFT value') |
| .beGreaterThanOrEqualTo(analyser.maxDecibels); |
| // Test the byte frequency data. |
| let byteFreqData = new Uint8Array(analyser.frequencyBinCount); |
| analyser.getByteFrequencyData(byteFreqData); |
| |
| // Convert the expected float frequency data to byte data. |
| let expectedByteData = convertFloatToByte( |
| expected, analyser.minDecibels, analyser.maxDecibels); |
| |
| should(byteFreqData, analyser.fftSize + '-point byte FFT') |
| .beCloseToArray( |
| expectedByteData, |
| {absoluteThreshold: options.byteThreshold || 0}); |
| }) |
| .then(context.resume.bind(context)); |
| |
| return context.startRendering(); |
| } |
| </script> |
| </body> |
| </html> |