| <!doctype html> |
| <html> |
| <head> |
| <title>Test fftSize Changes Resetting AnalyserNode State </title> |
| <script src="../resources/testharness.js"></script> |
| <script src="../resources/testharnessreport.js"></script> |
| <script src="resources/audio-testing.js"></script> |
| </head> |
| |
| <body> |
| <script> |
| // Fairly arbitrary sample rate. |
| var sampleRate = 24000; |
| |
| var audit = Audit.createTaskRunner(); |
| |
| // Verify that setting the fftSize resets the memory for the FFT smoothing |
| // operation. Only a few of the possible variations are tested. |
| |
| audit.defineTask("128->1024", function (taskDone) { |
| testFFTSize({ |
| initialFFTSize: 128, |
| finalFFTSize: 1024, |
| errorThreshold: { |
| relativeThreshold: 1.9095e-6 |
| } |
| }).then(taskDone); |
| }); |
| |
| audit.defineTask("512->256", function (taskDone) { |
| testFFTSize({ |
| initialFFTSize: 512, |
| finalFFTSize: 256, |
| errorThreshold: { |
| relativeThreshold: 1.8166e-6 |
| } |
| }).then(taskDone); |
| }); |
| |
| function testFFTSize(options) { |
| var { |
| initialFFTSize, finalFFTSize, errorThreshold |
| } = options; |
| |
| // The duration is fairly arbitrary as long as it's long enough for the |
| // FFT test. |
| var context = new OfflineAudioContext(1, sampleRate, sampleRate); |
| |
| // Actual source doesn't matter but a sawtooth is a nice waveform with |
| // lots of harmonic content. |
| var osc = context.createOscillator(); |
| osc.type = "sawtooth"; |
| |
| // The analyser under test. |
| var testAnalyser = context.createAnalyser(); |
| testAnalyser.fftSize = initialFFTSize; |
| |
| // The reference analyser. The fftSize is fixed to the desired value, |
| // and we turn off smoothing so that we get the FFT of the current time |
| // data. |
| var refAnalyser = context.createAnalyser(); |
| refAnalyser.fftSize = finalFFTSize; |
| refAnalyser.smoothingTimeConstant = 0; |
| |
| // Setup the graph and start the oscillator. |
| osc.connect(testAnalyser) |
| .connect(context.destination); |
| osc.connect(refAnalyser) |
| .connect(context.destination); |
| |
| osc.start(); |
| |
| // Let the analyser smooth a few FFTs (rather arbitrary, but should be |
| // more than one), then switch the size. |
| |
| var suspendFrame = 4 * initialFFTSize; |
| context.suspend(suspendFrame / context.sampleRate) |
| .then(function () { |
| testAnalyser.fftSize = finalFFTSize; |
| }) |
| .then(context.resume.bind(context)); |
| |
| // Wait some frames and grab the FFT data. This is fairly arbitrary |
| // too, and can be independent of the FFT sizes. |
| suspendFrame += 1024; |
| context.suspend(suspendFrame / context.sampleRate) |
| .then(function () { |
| var testFFT = new Float32Array(testAnalyser.frequencyBinCount); |
| var refFFT = new Float32Array(refAnalyser.frequencyBinCount) |
| var testSignal = new Float32Array(testAnalyser.fftSize); |
| var refSignal = new Float32Array(refAnalyser.fftSize); |
| |
| testAnalyser.getFloatTimeDomainData(testSignal); |
| refAnalyser.getFloatTimeDomainData(refSignal); |
| |
| testAnalyser.getFloatFrequencyData(testFFT); |
| refAnalyser.getFloatFrequencyData(refFFT); |
| |
| // Convert the FFT data from dB to linear |
| testFFT = testFFT.map(x => Math.pow(10, x / 20)); |
| refFFT = refFFT.map(x => Math.pow(10, x / 20)); |
| |
| // The test data has smoothing applied, but the reference doesn't. |
| // Apply the smoothing factor to the reference data. |
| var smoothing = 1 - testAnalyser.smoothingTimeConstant; |
| refFFT = refFFT.map(x => x * smoothing); |
| |
| var success = true; |
| |
| // First a basic sanity check that the time domain signals are |
| // exactly the same for both analysers. |
| success = Should("Time data", testSignal) |
| .beCloseToArray(refSignal, 0) && success; |
| |
| success = Should("Linear FFT data after setting fftSize = " + testAnalyser.fftSize, |
| testFFT) |
| .beCloseToArray(refFFT, errorThreshold) && success; |
| |
| Should("*** Changing fftSize from " + initialFFTSize + " to " + finalFFTSize, success) |
| .summarize( |
| "correctly reset the smoothing state", |
| "did not correctly reset the smoothing state"); |
| }) |
| .then(context.resume.bind(context)); |
| |
| return context.startRendering(); |
| } |
| |
| audit.runTasks(); |
| </script> |
| </body> |
| </html> |