Commit 80c39e54 authored by Raymond Toy's avatar Raymond Toy Committed by Commit Bot

Handle k-rate AudioParams with input for AudioBufferSourceNode

The node already correctly handles k-rate inputs to the two
AudioParams.  Thus, we just add a test for this.

Bug: 1015760
Test: k-rate-audiobuffersource-connections.html
Change-Id: I5ef616692ba5ba836906c39cf8593624fe782728
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2145701Reviewed-by: default avatarHongchan Choi <hongchan@chromium.org>
Commit-Queue: Raymond Toy <rtoy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758986}
parent f471504b
<!doctype html>
<html>
<head>
<title>k-rate AudioParams with inputs for AudioBufferSourceNode</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
</head>
<body>
<script>
let audit = Audit.createTaskRunner();
// Fairly abitrary sampleRate and somewhat duration
const sampleRate = 8000;
const testDuration = 0.25;
[['playbackRate', [1, 0], [2, testDuration]],
['detune', [-1200, 0], [1200, testDuration]]]
.forEach(param => {
audit.define(
{label: param[0], description: `AudioBufferSource ${param[0]}`},
async (task, should) => {
await doTest(should, {
prefix: task.label,
paramName: param[0],
startValue: param[1],
endValue: param[2]
});
task.done();
});
});
audit.run();
async function doTest(should, options) {
// Test k-rate automation of AudioBufferSourceNode with connected
// input.
//
// A reference source node is created with an automation on the
// selected AudioParam. For simplicity, we just use a linear ramp from
// the minValue to the maxValue of the AudioParam.
//
// The test node has an input signal connected to the AudioParam. This
// input signal is created to match the automation on the reference
// node.
//
// Finally, the output from the two nodes must be identical if k-rate
// inputs are working correctly.
//
// Options parameter is a dictionary with the following required
// members:
// prefix - prefix to use for the messages.
// paramName - Name of the AudioParam to be tested
let {prefix, paramName, startValue, endValue} = options;
let context = new OfflineAudioContext({
numberOfChannels: 2,
sampleRate: sampleRate,
length: testDuration * sampleRate
});
let merger = new ChannelMergerNode(
context, {numberOfInputs: context.destination.channelCount});
merger.connect(context.destination);
// Linear ramp to use for the buffer sources
let ramp = createLinearRampBuffer(context, context.length);
// Create the reference and test nodes.
let refNode;
let tstNode;
const nodeOptions = {buffer: ramp};
should(
() => refNode = new AudioBufferSourceNode(context, nodeOptions),
`${prefix}: refNode = new AudioBufferSourceNode(context, ${
JSON.stringify(nodeOptions)})`)
.notThrow();
should(
() => tstNode = new AudioBufferSourceNode(context, nodeOptions),
`${prefix}: tstNode = new AudioBufferSourceNode(context, ${
JSON.stringify(nodeOptions)})`)
.notThrow();
// Automate the AudioParam of the reference node with a linear ramp
should(
() => refNode[paramName].setValueAtTime(...startValue),
`${prefix}: refNode[${paramName}].setValueAtTime(${
startValue[0]}, ${startValue[1]})`)
.notThrow();
should(
() => refNode[paramName].linearRampToValueAtTime(...endValue),
`${prefix}: refNode[${paramName}].linearRampToValueAtTime(${
endValue[0]}, ${endValue[1]})`)
.notThrow();
// Create the input node and automate it so that it's output when added
// to the intrinsic value of the AudioParam we get the same values as
// the automations on the reference node.
// Compute the start and end values based on the defaultValue of the
// param and the desired startValue and endValue. The input is added to
// the intrinsic value of the AudioParam, so we need to account for
// that.
let mod;
should(
() => mod = new ConstantSourceNode(context, {offset: 0}),
`${prefix}: mod = new ConstantSourceNode(context, {offset: 0})`)
.notThrow();
let modStart = startValue[0] - refNode[paramName].defaultValue;
let modEnd = endValue[0] - refNode[paramName].defaultValue;
should(
() => mod.offset.setValueAtTime(modStart, startValue[1]),
`${prefix}: mod.offset.setValueAtTime(${modStart}, ${
startValue[1]})`)
.notThrow();
should(
() => mod.offset.linearRampToValueAtTime(modEnd, endValue[1]),
`${prefix}: mod.offset.linearRampToValueAtTime(${modEnd}, ${
endValue[1]})`)
.notThrow();
// Connect up everything.
should(
() => mod.connect(tstNode[paramName]),
`${prefix}: mod.connect(tstNode[${paramName}])`)
.notThrow();
refNode.connect(merger, 0, 0);
tstNode.connect(merger, 0, 1);
// Go!
refNode.start();
tstNode.start();
mod.start();
const buffer = await context.startRendering();
let expected = buffer.getChannelData(0);
let actual = buffer.getChannelData(1);
// Quick sanity check that output isn't zero. This means we messed up
// the connections or automations or the buffer source.
should(expected, `Expected k-rate ${paramName} AudioParam with input`)
.notBeConstantValueOf(0);
should(actual, `Actual k-rate ${paramName} AudioParam with input`)
.notBeConstantValueOf(0);
// The expected and actual results must be EXACTLY the same.
should(actual, `k-rate ${paramName} AudioParam with input`)
.beCloseToArray(expected, {absoluteThreshold: 0});
}
</script>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment