| <!DOCTYPE html> |
| <html> |
| <head> |
| <title> |
| Test AnalyserNode getFloatTimeDomainData |
| </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> |
| </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(); |
| |
| // Test that getFloatTimeDomainData handles short and long vectors |
| // correctly. |
| audit.define('short and long vector', (task, should) => { |
| let fftSize = 32; |
| let graphInfo = createGraph(fftSize); |
| let context = graphInfo.context; |
| let analyser = graphInfo.analyser; |
| let signalBuffer = graphInfo.signalBuffer; |
| let signal = signalBuffer.getChannelData(0); |
| |
| let success = true; |
| let sampleFrame = 128; |
| |
| context.suspend(sampleFrame / sampleRate) |
| .then(function() { |
| let shortData = new Float32Array(8); |
| // Initialize the array to Infinity to represent uninitialize |
| // data. |
| shortData.fill(Infinity); |
| |
| analyser.getFloatTimeDomainData(shortData); |
| |
| // The short array should be filled with the expected data, with |
| // no errors thrown. |
| |
| let expected = |
| signal.subarray(sampleFrame - fftSize, sampleFrame); |
| should(shortData, shortData.length + '-element time domain data') |
| .beEqualToArray(expected.subarray(0, shortData.length)); |
| |
| let longData = new Float32Array(2 * fftSize); |
| // Initialize the array to Infinity to represent uninitialize |
| // data. |
| longData.fill(Infinity); |
| |
| analyser.getFloatTimeDomainData(longData); |
| |
| // The long array should filled with the expected data but the |
| // extra elements should be untouched. |
| should( |
| longData.subarray(0, fftSize), |
| 'longData.subarray(0, ' + fftSize + ')') |
| .beEqualToArray(expected); |
| |
| should( |
| longData.subarray(fftSize), |
| 'Unfilled elements longData.subarray(' + fftSize + ')') |
| .beConstantValueOf(Infinity); |
| }) |
| .then(context.resume.bind(context)); |
| |
| context.startRendering().then(() => task.done()); |
| }); |
| |
| let success = true; |
| |
| // Generate tests for all valid FFT sizes for an AnalyserNode. |
| for (let k = 5; k < 16; ++k) { |
| let fftSize = Math.pow(2, k); |
| (function(n) { |
| // We grab a sample at (roughly) half the rendering duration. |
| audit.define('fftSize ' + n, (task, should) => { |
| runTest(n, renderDuration / 2, should).then(() => task.done()); |
| }); |
| })(fftSize); |
| } |
| |
| // Special case for a large size, but the sampling point is early. The |
| // initial part of the buffer should be filled with zeroes. |
| |
| audit.define('initial zeroes', (task, should) => { |
| // Somewhat arbitrary size for the analyser. It should be greater than |
| // one rendering quantum. |
| let fftSize = 2048; |
| let graphInfo = createGraph(fftSize); |
| let context = graphInfo.context; |
| let analyser = graphInfo.analyser; |
| let signalBuffer = graphInfo.signalBuffer; |
| |
| let data = new Float32Array(fftSize); |
| |
| success = true; |
| // Suspend every rendering quantum and examine the analyser data. |
| for (let k = 128; k <= fftSize; k += 128) { |
| context.suspend(k / sampleRate) |
| .then(function() { |
| analyser.getFloatTimeDomainData(data); |
| let sampleFrame = context.currentTime * sampleRate; |
| |
| // Verify that the last k frames are not zero, but the first |
| // fftSize - k frames are. |
| let prefix = |
| 'At frame ' + (sampleFrame - 1) + ': data.subarray'; |
| if (sampleFrame < fftSize) { |
| should( |
| data.subarray(0, fftSize - sampleFrame), |
| prefix + '(0, ' + (fftSize - sampleFrame) + ')') |
| .beConstantValueOf(0) && |
| success; |
| } |
| |
| let signal = signalBuffer.getChannelData(0); |
| should( |
| data.subarray(fftSize - sampleFrame, fftSize), |
| prefix + '(' + (fftSize - sampleFrame) + ', ' + fftSize + |
| ')') |
| .beEqualToArray(signal.subarray(0, sampleFrame)) && |
| success; |
| }) |
| .then(context.resume.bind(context)); |
| } |
| |
| context.startRendering().then(() => task.done()); |
| }); |
| |
| audit.run(); |
| |
| // Run test of an AnalyserNode with fftSize of |fftSize|, and with the |
| // data from the node being requested at time |sampletime|. The result |
| // from the analyser node is compared against the expected data. The |
| // result of startRendering() is returned. |
| function runTest(fftSize, sampleTime, should) { |
| let graphInfo = createGraph(fftSize); |
| let context = graphInfo.context; |
| let analyser = graphInfo.analyser; |
| let signalBuffer = graphInfo.signalBuffer; |
| |
| // Grab the data at the requested time. |
| context.suspend(sampleTime) |
| .then(function() { |
| let lastFrame = Math.floor(context.currentTime * sampleRate); |
| |
| // Grab the time domain data from the analyzer and compare against |
| // the expected result. |
| let actualFloatData = new Float32Array(fftSize); |
| analyser.getFloatTimeDomainData(actualFloatData); |
| |
| // Compare against the expected result. |
| let signal = signalBuffer.getChannelData(0); |
| let message = |
| actualFloatData.length + '-point analyser time domain data'; |
| should(actualFloatData, message) |
| .beEqualToArray(signal.subarray( |
| lastFrame - actualFloatData.length, lastFrame)) && |
| success; |
| }) |
| .then(context.resume.bind(context)); |
| |
| return context.startRendering(); |
| } |
| |
| // Create the audio graph with an AnalyserNode with fftSize |fftSize|. A |
| // simple integer-valued linear ramp is the source so we can easily verify |
| // the results. A dictionary consisting of the context, the analyser |
| // node, and the signal is returned. |
| function createGraph(fftSize) { |
| let context = new OfflineAudioContext(1, renderFrames, sampleRate); |
| |
| let src = context.createBufferSource(); |
| |
| // Use a simple linear ramp as the source. For simplicity of inspecting |
| // results, the ramp starts at 1 with an increment of 1. |
| let signalBuffer = |
| context.createBuffer(1, renderFrames, context.sampleRate); |
| let data = signalBuffer.getChannelData(0); |
| for (let k = 0; k < data.length; ++k) { |
| data[k] = k + 1; |
| } |
| |
| src.buffer = signalBuffer; |
| |
| let analyser = context.createAnalyser(); |
| analyser.fftSize = fftSize; |
| |
| src.connect(analyser); |
| analyser.connect(context.destination); |
| src.start(); |
| |
| return { |
| context: context, |
| analyser: analyser, |
| signalBuffer: signalBuffer |
| }; |
| } |
| </script> |
| </body> |
| </html> |