blob: d65650a72b8dde4f2ae7440748cdc5fa44da27a9 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>
Basic GainNode Functionality
</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../resources/audit-util.js"></script>
<script src="../resources/audit.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).
let audit = Audit.createTaskRunner();
let sampleRate = 44100.0;
let bufferDurationSeconds = 0.125;
let numberOfNotes = 11;
let noteSpacing = bufferDurationSeconds +
0.020; // leave 20ms of silence between each "note"
let lengthInSeconds = numberOfNotes * noteSpacing;
let context = 0;
let sinWaveBuffer = 0;
// 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(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(time, gain, merger) {
let source = context.createBufferSource();
source.buffer = sinWaveBuffer;
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);
}
audit.define(
{label: 'create context', description: 'Create context for test'},
function(task, should) {
// Create offline audio context.
context = new OfflineAudioContext(
4, sampleRate * lengthInSeconds, sampleRate);
task.done();
});
audit.define(
{label: 'test', description: 'GainNode functionality'},
function(task, should) {
let merger = new ChannelMergerNode(
context, {numberOfInputs: context.destination.channelCount});
merger.connect(context.destination);
// Create a buffer for a short "note".
sinWaveBuffer = createSinWaveBuffer(bufferDurationSeconds, 880.0);
let startTimes = [];
let gainValues = [];
// Render 11 notes, starting at a gain of 1.0, decreasing in gain by
// 0.1. The last note will be of gain 0.0, so shouldn't be
// perceptible in the rendered output.
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(time, gain, merger);
}
context.startRendering()
.then(buffer => {
let actual0 = buffer.getChannelData(0);
let actual1 = buffer.getChannelData(1);
let reference0 = buffer.getChannelData(2);
let reference1 = buffer.getChannelData(3);
// It's ok to a frame too long since the sine pulses are
// followed by silence.
let bufferDurationFrames =
Math.ceil(bufferDurationSeconds * context.sampleRate);
// Apply the gains to the reference signal.
for (let k = 0; k < startTimes.length; ++k) {
// It's ok to be a frame early because the sine pulses are
// preceded by silence.
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;
}
}
// Verify the channels are clsoe to the reference.
should(actual0, 'Left output from gain node')
.beCloseToArray(
reference0, {relativeThreshold: 1.1908e-7});
should(actual1, 'Right output from gain node')
.beCloseToArray(
reference1, {relativeThreshold: 1.1908e-7});
// Test the SNR too for both channels.
let snr0 = 10 * Math.log10(computeSNR(actual0, reference0));
let snr1 = 10 * Math.log10(computeSNR(actual1, reference1));
should(snr0, 'Left SNR (in dB)')
.beGreaterThanOrEqualTo(148.69);
should(snr1, 'Right SNR (in dB)')
.beGreaterThanOrEqualTo(148.69);
})
.then(() => task.done());
;
});
audit.run();
</script>
</body>
</html>