blob: 690b86f7debbc1fd8ab7328e2e1ca8e5db045a6f [file] [log] [blame]
<!doctype html>
<html>
<head>
<title>
Test Reconnection of Tail-Processing Nodes
</title>
<script src="../../imported/w3c/web-platform-tests/resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../resources/audit.js"></script>
<script src="../resources/audit-util.js"></script>
</head>
<body>
<script>
let audit = Audit.createTaskRunner();
audit.define(
{
test: 'Test Compressor',
label: 'Reconnect graph to DynamicsCompressor before end of tail'
},
(task, should) => {
// Arbitrary sample rate
const sampleRate = 16000;
// These constants taken from dyanmics_compress_kernel.{cc,h}.
const preDelayFrames = 1024;
const meteringReleaseTime = 0.325;
const tailTime = 5 * meteringReleaseTime;
const latencyTime = preDelayFrames / sampleRate;
const tailProcessingTime = tailTime + latencyTime;
// The DynamicsCompressorNode has a tail + latency time of a little
// over 1.68 sec, so make the duration longer than that.
const renderDuration = 3;
let context = new OfflineAudioContext(
1, renderDuration * sampleRate, sampleRate);
runTest(context, should, {
testNodeName: 'DynamicsCompressorNode',
nodeOptions: null,
suspendTime: 2,
tailProcessingTime: tailProcessingTime
}).then(() => task.done());
});
audit.define(
{
test: 'Test Biquad',
label: 'Reconnect graph to Biquad before end of tail'
},
(task, should) => {
let renderDuration = 1;
let sampleRate = 16384;
let context = new OfflineAudioContext(
1, renderDuration * sampleRate, sampleRate);
// Taken from BiquadFilter/tail-time-lowpass.html
let filterOptions = {
type: 'lowpass',
Q: 40,
frequency: sampleRate / 4
};
let tailProcessingTime = 2079.4 / context.sampleRate;
runTest(context, should, {
testNodeName: 'BiquadFilterNode',
nodeOptions: filterOptions,
suspendTime: .5,
tailProcessingTime: tailProcessingTime
}).then(() => task.done());
});
// Run a test using the given test node that has a tail time.
// |options| is a dictionary with the following members:
// testNodeName: name of the node (used as a constructor)
// nodeOptiosn: any options needed to create the node with the right
// properties.
// suspendTime: Time at which oscillator should start.
// tailProcessingTime: Length of the tail of the node, in sec.
function runTest(context, should, options) {
// This test basically taken from the repro case in crbug.com/829767
let osc = new OscillatorNode(context);
let g0 = new GainNode(context);
let g1 = new GainNode(context);
let g2 = new GainNode(context);
let g3 = new GainNode(context);
let testNode =
new window[options.testNodeName](context, options.nodeOptions);
osc.connect(g0)
.connect(g1)
.connect(testNode)
.connect(g2)
.connect(g3)
.connect(context.destination);
// Disconnect the rest of the graph from g0. This should place the
// test node on the internal tail processing list.
g0.disconnect();
// Now reconnect g0 to the graph.. This should re-enable the test node
// output and remove it from the tail list.
g0.connect(g1);
// Start the source after the tail time of the test node.
context.suspend(options.suspendTime)
.then(() => {
// Sanity check that we start the oscillator after the tail
// processing time.
should(context.currentTime, 'Oscillator start time')
.beGreaterThanOrEqualTo(options.tailProcessingTime);
osc.start();
})
.then(() => context.resume());
return context.startRendering().then(renderedBuffer => {
// Just check that the output isn't silence. If things are working
// after reconnecting, there should be non-zero output.
should(
renderedBuffer.getChannelData(0),
`Output of ${options.testNodeName}`)
.notBeConstantValueOf(0);
});
}
audit.run();
</script>
</body>
</html>