Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!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>