blob: 5468f4248d1253fdb8f84d528b7667dca277dc1e [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>
AudioParam Initial Events
</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>
<script src="../resources/audioparam-testing.js"></script>
</head>
<body>
<script id="layout-test-code">
let sampleRate = 48000;
// Number of frames in a rendering quantum.
let quantumFrames = 128;
// Test doesn't need to run for very long.
let renderDuration = 0.2;
let renderFrames = renderDuration * sampleRate;
let automationEndTime = 0.1;
let audit = Audit.createTaskRunner();
// The following tests start a ramp automation without an initial event.
// This should cause an initial event to be added implicitly to give a
// starting point.
audit.define('linear-ramp', (task, should) => {
runTest('Linear ramp', should, {
automationFunction: function(node, value, time) {
node.gain.linearRampToValueAtTime(value, time);
},
referenceFunction: linearRamp,
// Experimentally determined threshold
threshold: {absoluteThreshold: 6.0003e-8},
// The starting value of the gain node
v0: 0.5,
// The target value of the automation
v1: 0,
}).then(() => task.done());
});
audit.define('exponential-ramp', (task, should) => {
runTest('Exponential ramp', should, {
automationFunction: function(node, value, time) {
node.gain.exponentialRampToValueAtTime(value, time);
},
referenceFunction: exponentialRamp,
threshold: {absoluteThreshold: 2.3842e-6},
v0: 0.5,
v1: 2,
}).then(() => task.done());
});
// Same tests as above, but we delay the call to the automation function.
// This is to verify that the we still do the right thing after the
// context has started.
audit.define('delayed-linear-ramp', (task, should) => {
runTest('Delayed linear ramp', should, {
automationFunction: function(node, value, time) {
node.gain.linearRampToValueAtTime(value, time);
},
referenceFunction: linearRamp,
// Experimentally determined threshold
threshold: {absoluteThreshold: 9.87968e-8},
// The starting value of the gain node
v0: 0.5,
// The target value of the automation
v1: 0,
delay: quantumFrames / sampleRate
}).then(() => task.done());
});
audit.define('delayed-exponential-ramp', (task, should) => {
runTest('Delayed exponential ramp', should, {
automationFunction: function(node, value, time) {
node.gain.exponentialRampToValueAtTime(value, time);
},
referenceFunction: exponentialRamp,
// Experimentally determined threshold
threshold: {absoluteThreshold: 1.3948e-5},
// The starting value of the gain node
v0: 0.5,
// The target value of the automation
v1: 2,
delay: quantumFrames / sampleRate
}).then(() => task.done());
});
audit.run();
// Generate the expected waveform for a linear ramp starting from the
// value |v0|, ramping to |v1| at time |endTime|. The time of |v0| is
// assumed to be 0. |nFrames| is how many frames to generate.
function linearRamp(v0, v1, startTime, endTime, nFrames) {
let expected =
createLinearRampArray(startTime, endTime, v0, v1, sampleRate);
let preFiller = new Array(Math.floor(startTime * sampleRate));
let postFiller = new Array(nFrames - Math.ceil(endTime * sampleRate));
preFiller.fill(v0);
return preFiller.concat(expected.concat(postFiller.fill(v1)));
}
// Generate the expected waveform for an exponential ramp starting from
// the value |v0|, ramping to |v1| at time |endTime|. The time of |v0| is
// assumed to be 0. |nFrames| is how many frames to generate.
function exponentialRamp(v0, v1, startTime, endTime, nFrames) {
let expected =
createExponentialRampArray(startTime, endTime, v0, v1, sampleRate);
let preFiller = new Array(Math.floor(startTime * sampleRate));
preFiller.fill(v0);
let postFiller = new Array(nFrames - Math.ceil(endTime * sampleRate));
return preFiller.concat(expected.concat(postFiller.fill(v1)));
}
// Run an automation test. |message| is the message to use for printing
// the results. |options| is a property bag containing the configuration
// of the test including the following:
//
// automationFunction - automation function to use,
// referenceFunction - function generating the expected result
// threshold - comparison threshold
// v0 - starting value
// v1 - end value for automation
function runTest(message, should, options) {
let automationFunction = options.automationFunction;
let referenceFunction = options.referenceFunction;
let threshold = options.threshold;
let v0 = options.v0;
let v1 = options.v1;
let delay = options.delay;
let context = new OfflineAudioContext(1, renderFrames, sampleRate);
// A constant source of amplitude 1.
let source = context.createBufferSource();
source.buffer = createConstantBuffer(context, 1, 1);
source.loop = true;
// Automation is applied to a gain node. Set the initial value of the
// gain in the constructor; using the value setter does an implicit
// setValueAtTime which sets an initial event. This defeats the purpose
// of the test.
let gain = new GainNode(context, {gain: v0});
// Delay start of automation, if requested
if (delay) {
context.suspend(options.delay).then(function() {
automationFunction(gain, v1, automationEndTime);
context.resume();
});
} else {
automationFunction(gain, v1, automationEndTime);
}
source.connect(gain);
gain.connect(context.destination);
source.start();
return context.startRendering().then(function(resultBuffer) {
let result = resultBuffer.getChannelData(0);
let expected = referenceFunction(
v0, v1, delay ? delay : 0, automationEndTime, renderFrames);
should(result, message).beCloseToArray(expected, threshold);
});
}
</script>
</body>
</html>