Index: third_party/WebKit/LayoutTests/webaudio/BiquadFilter/test-tail-time.js |
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/test-tail-time.js b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/test-tail-time.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..94f1449e69eaa81191a216458a9ab03a39c6ccc7 |
--- /dev/null |
+++ b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/test-tail-time.js |
@@ -0,0 +1,90 @@ |
+function testTailTime(should, context, options) { |
+ let src = new ConstantSourceNode(context, {offset: 1}); |
+ let f = new BiquadFilterNode(context, options.filterOptions); |
+ |
+ src.connect(f).connect(context.destination); |
+ src.start(); |
+ src.stop(1 / context.sampleRate); |
+ |
+ let expectedTailFrame = computeTailFrame(f); |
+ |
+ // The internal Biquad time computation limits he tail time to a |
+ // maximum of 30 sec. We need to limit the computed tail frame to |
+ // that limit as well. |
+ expectedTailFrame = Math.min(expectedTailFrame, 30 * context.sampleRate); |
+ |
+ return context.startRendering().then(renderedBuffer => { |
+ let s = renderedBuffer.getChannelData(0); |
+ let prefix = options.prefix + ': Biquad(' + |
+ JSON.stringify(options.filterOptions) + ')'; |
+ |
+ // Round actual tail frame to a render boundary |
+ let quantumIndex = Math.floor(expectedTailFrame / RENDER_QUANTUM_FRAMES); |
+ let expectedTailBoundary = RENDER_QUANTUM_FRAMES * quantumIndex; |
+ |
+ // Find the actual tail frame. That is, the last point where the |
+ // output is not zero. |
+ let actualTailFrame; |
+ |
+ for (actualTailFrame = s.length; actualTailFrame > 0; --actualTailFrame) { |
+ if (Math.abs(s[actualTailFrame - 1]) > 0) |
+ break; |
+ } |
+ |
+ should(actualTailFrame, `${prefix}: Actual Tail Frame ${actualTailFrame}`) |
+ .beGreaterThanOrEqualTo(expectedTailFrame); |
+ |
+ // Verify each render quanta is not identically zero up to the |
+ // boundary. |
+ for (let k = 0; k <= quantumIndex; ++k) { |
+ let firstFrame = RENDER_QUANTUM_FRAMES * k; |
+ let lastFrame = firstFrame + RENDER_QUANTUM_FRAMES - 1; |
+ should( |
+ s.slice(firstFrame, lastFrame + 1), |
+ `${prefix}: output[${firstFrame}:${lastFrame}]`) |
+ .notBeConstantValueOf(0); |
+ } |
+ // The frames after the tail should be zero. Because the |
+ // implementation uses approximations to simplify the |
+ // computations, the nodes tail time may be greater than the real |
+ // impulse response tail. Thus, we just verify that the output |
+ // over the tail is less than the tail threshold value. |
+ let zero = new Float32Array(s.length); |
+ should( |
+ s.slice(expectedTailBoundary + RENDER_QUANTUM_FRAMES + 256), |
+ prefix + ': output[' + |
+ (expectedTailBoundary + RENDER_QUANTUM_FRAMES + 256) + ':]') |
+ .beCloseToArray( |
+ zero.slice(expectedTailBoundary + RENDER_QUANTUM_FRAMES + 256), |
+ {absoluteThreshold: options.threshold || 0}); |
+ }) |
+} |
+ |
+function computeTailFrame(filterNode) { |
+ // Compute the impuluse response for the filter |filterNode| by |
+ // filtering the impulse directly ourself. |
+ let coef = createFilter( |
+ filterNode.type, |
+ filterNode.frequency.value / filterNode.context.sampleRate * 2, |
+ filterNode.Q.value, filterNode.gain.value); |
+ |
+ let impulse = new Float32Array(filterNode.context.length); |
+ impulse[0] = 1; |
+ |
+ let filtered = filterData(coef, impulse, impulse.length); |
+ |
+ // Compute the magnitude and find out where the imuplse is small enough. |
+ let tailFrame = 0; |
+ if (Math.abs(filtered[filtered.length - 1]) >= 1 / 32768) { |
+ tailFrame = filtered.length - 1; |
+ } else { |
+ for (let k = filtered.length - 1; k >= 0; --k) { |
+ if (Math.abs(filtered[k]) >= 1 / 32768) { |
+ tailFrame = k + 1; |
+ break; |
+ } |
+ } |
+ } |
+ |
+ return tailFrame; |
+} |