1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
<!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>
|