Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- This WPT test may be referenced by the following Test IDs:
- /webaudio/the-audio-api/the-gainnode-interface/gain.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<html>
<head>
<title>
Basic GainNode Functionality
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
</head>
<body>
<script id="layout-test-code">
// Tests that GainNode is properly scaling the gain. We'll render 11
// notes, starting at a gain of 1.0, decreasing in gain by 0.1. The 11th
// note will be of gain 0.0, so it should be silent (at the end in the
// rendered output).
// Use a power of two to eliminate any round-off when converting frame to
// time.
const sampleRate = 32768;
// Make sure the buffer duration and spacing are all exact frame lengths
// so that the note spacing is also on frame boundaries to eliminate
// sub-sample accurate start of a ABSN.
const bufferDurationSeconds = Math.floor(0.125 * sampleRate) / sampleRate;
const numberOfNotes = 11;
// Leave about 20ms of silence, being sure this is an exact frame
// duration.
const noteSilence = Math.floor(0.020 * sampleRate) / sampleRate;
const noteSpacing = bufferDurationSeconds + noteSilence;
const lengthInSeconds = numberOfNotes * noteSpacing;
// Create a stereo AudioBuffer of duration |lengthInSeconds| consisting of
// a pure sine wave with the given |frequency|. Both channels contain the
// same data.
function createSinWaveBuffer(context, lengthInSeconds, frequency) {
let audioBuffer =
context.createBuffer(2, lengthInSeconds * sampleRate, sampleRate);
let n = audioBuffer.length;
let channelL = audioBuffer.getChannelData(0);
let channelR = audioBuffer.getChannelData(1);
for (let i = 0; i < n; ++i) {
channelL[i] = Math.sin(frequency * 2.0 * Math.PI * i / sampleRate);
channelR[i] = channelL[i];
}
return audioBuffer;
}
function playNote(context, time, gain, buffer, merger) {
let source = context.createBufferSource();
source.buffer = buffer;
let gainNode = context.createGain();
gainNode.gain.value = gain;
let sourceSplitter = context.createChannelSplitter(2);
let gainSplitter = context.createChannelSplitter(2);
// Split the stereo channels from the source output and the gain output
// and merge them into the desired channels of the merger.
source.connect(gainNode).connect(gainSplitter);
source.connect(sourceSplitter);
gainSplitter.connect(merger, 0, 0);
gainSplitter.connect(merger, 1, 1);
sourceSplitter.connect(merger, 0, 2);
sourceSplitter.connect(merger, 1, 3);
source.start(time);
}
promise_test(async t => {
let context = new OfflineAudioContext(
4, sampleRate * lengthInSeconds, sampleRate);
let merger = new ChannelMergerNode(
context, {numberOfInputs: context.destination.channelCount});
merger.connect(context.destination);
let sinWaveBuffer = createSinWaveBuffer(
context, bufferDurationSeconds, 880.0);
let startTimes = [];
let gainValues = [];
for (let i = 0; i < numberOfNotes; ++i) {
let time = i * noteSpacing;
let gain = 1.0 - i / (numberOfNotes - 1);
startTimes.push(time);
gainValues.push(gain);
playNote(context, time, gain, sinWaveBuffer, merger);
}
let buffer = await context.startRendering();
let actual0 = buffer.getChannelData(0);
let actual1 = buffer.getChannelData(1);
let reference0 = buffer.getChannelData(2);
let reference1 = buffer.getChannelData(3);
let bufferDurationFrames =
Math.ceil(bufferDurationSeconds * context.sampleRate);
for (let k = 0; k < startTimes.length; ++k) {
let startFrame = Math.floor(startTimes[k] * context.sampleRate);
let gain = gainValues[k];
for (let n = 0; n < bufferDurationFrames; ++n) {
reference0[startFrame + n] *= gain;
reference1[startFrame + n] *= gain;
}
}
const tolerance = 1.1877e-7;
assert_array_approx_equals(actual0, reference0, tolerance,
'Left output from gain node should match scaled reference');
assert_array_approx_equals(actual1, reference1, tolerance,
'Right output from gain node should match scaled reference');
let snr0 = 10 * Math.log10(computeSNR(actual0, reference0));
let snr1 = 10 * Math.log10(computeSNR(actual1, reference1));
assert_greater_than_equal(
snr0, 148.71, 'Left SNR (in dB) must be ≥ 148.71');
assert_greater_than_equal(
snr1, 148.71, 'Right SNR (in dB) must be ≥ 148.71');
}, 'GainNode should scale gains properly across notes');
</script>
</body>
</html>