<!DOCTYPE html>
<html>
<head>
<title>
Test Processing Of AudioParams of Disconnected AudioNodes
</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../resources/audit.js"></script>
</head>
<body>
<script id="layout-test-code">
// Arbitrary sample rate.
let sampleRate = 8000;
// Arbitrary number of frames for the test, except it must be greater than
// |automationEndFrame|.
let renderFrames = 1024;
// The linear ramp automation ends at this frame. Arbitrary, except it
// must be strictly less than the render length.
let automationEndFrame = 128;
let audit = Audit.createTaskRunner();
// There should be a test for every single node which has an AudioParam.
// Source nodes are not included in this because the AudioParams for the
// source nodes only process when the node has been started.
audit.define('BiquadFilterNode', (task, should) => {
let nodeName = 'BiquadFilterNode'
Promise
.all([
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'Q',
initialValue: 2,
rampFinalValue: 5
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'detune',
initialValue: 1,
rampFinalValue: 0.5
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'frequency',
initialValue: 1000,
rampFinalValue: 100
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'gain',
initialValue: -3,
rampFinalValue: 3
}),
])
.then(() => task.done());
});
audit.define('DelayNode', (task, should) => {
testParamAutomation(should, {
nodeName: 'DelayNode',
paramName: 'delayTime',
initialValue: 0.25,
rampFinalValue: 0.5
}).then(() => task.done());
});
audit.define('DynamicsCompressorNode', (task, should) => {
let nodeName = 'DynamicsCompressorNode';
Promise
.all([
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'attack',
initialValue: 0.1,
rampFinalValue: 0.5
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'knee',
initialValue: 0,
rampFinalValue: 25
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'ratio',
initialValue: 1,
rampFinalValue: 15
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'release',
initialValue: 0,
rampFinalValue: 0.75
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'threshold',
initialValue: -50,
rampFinalValue: -10
})
])
.then(() => task.done());
});
audit.define('GainNode', (task, should) => {
testParamAutomation(should, {
nodeName: 'GainNode',
paramName: 'gain',
initialValue: 1,
rampFinalValue: 0.5
}).then(() => task.done());
});
audit.define('PannerNode', (task, should) => {
let nodeName = 'PannerNode';
Promise
.all([
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'positionX',
initialValue: 0.1,
rampFinalValue: 0.5
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'positionY',
initialValue: 2,
rampFinalValue: 30
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'positionZ',
initialValue: 1,
rampFinalValue: 15
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'orientationX',
initialValue: 0.1,
rampFinalValue: 0.5
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'orientationY',
initialValue: 2,
rampFinalValue: 30
}),
testParamAutomation(should, {
nodeName: nodeName,
paramName: 'orientationZ',
initialValue: 1,
rampFinalValue: 15
}),
])
.then(() => task.done());
});
audit.define('StereoPannerNode', (task, should) => {
testParamAutomation(should, {
nodeName: 'StereoPannerNode',
paramName: 'pan',
initialValue: 1,
rampFinalValue: 0.5
}).then(() => task.done());
});
audit.define('AudioListener', (task, should) => {
Promise
.all([
testAudioListener(should, {
paramName: 'positionX',
initialValue: 1,
rampFinalValue: 100
}),
testAudioListener(should, {
paramName: 'positionY',
initialValue: 1,
rampFinalValue: 200
}),
testAudioListener(should, {
paramName: 'positionZ',
initialValue: 1,
rampFinalValue: 300
}),
testAudioListener(should, {
paramName: 'forwardX',
initialValue: 1,
rampFinalValue: -100
}),
testAudioListener(should, {
paramName: 'forwardY',
initialValue: 1,
rampFinalValue: -200
}),
testAudioListener(should, {
paramName: 'forwardZ',
initialValue: 1,
rampFinalValue: -300
}),
testAudioListener(
should,
{paramName: 'upX', initialValue: 1, rampFinalValue: 99}),
testAudioListener(
should,
{paramName: 'upY', initialValue: 1, rampFinalValue: 42}),
testAudioListener(
should,
{paramName: 'upZ', initialValue: 1, rampFinalValue: 137}),
])
.then(() => task.done());
});
// Run test of automation processing. |options| is a dictionary that
// describes the node to test, the AudioParams to be tested and the values
// for the AudioParam. The members of the dictionary are:
//
// nodeName - name of the node (constructor name)
// paramName - name of the AudioParam to be tested
// initialValue - starting value for linear ramp
// rampFinalValue - finale value for linear ramp
//
// The test is considered to have succeeded if the |.value| of the
// AudioParam is the final value.
//
// A simple graph is created containing the node to be tested, connected
// to the destination. A linear ramp automation is scheduled for the
// specified AudioParam, starting and ending at the values given.
//
// Processing is started and after some number of frames, the |.value| of
// the AudioParam is obtained and compared against the final value. These
// should match exactly.
function testParamAutomation(should, options) {
let context = new OfflineAudioContext(1, renderFrames, sampleRate);
// Create the node to be tested
let node = new window[options.nodeName](context);
node.connect(context.destination);
// A linear ramp starting at frame 0 to frame |automationEndFrame| is
// used for the test. This value is fairly arbitrary, but it should be
// less than the total render frames.
node[options.paramName].setValueAtTime(options.initialValue, 0);
node[options.paramName].linearRampToValueAtTime(
options.rampFinalValue, automationEndFrame / context.sampleRate);
return context.startRendering().then(function(resultBuffer) {
// Sanity check: the given ramp final value must not be the default
// value, otherwise we can't easily tell if the automation was
// actually run.
should(
options.rampFinalValue,
options.nodeName + '.' + options.paramName + ' ramp final value')
.notBeEqualTo(node[options.paramName].defaultValue);
// The actual AudioParam value should be the rampe final value.
should(
node[options.paramName].value,
options.nodeName + '.' + options.paramName + '.value')
.beEqualTo(options.rampFinalValue);
});
}
function testAudioListener(should, options) {
let context = new OfflineAudioContext(1, renderFrames, sampleRate);
// Create the node to be tested
let node = new PannerNode(context);
node.connect(context.destination);
// A linear ramp starting at frame 0 to frame |automationEndFrame| is
// used for the test. This value is fairly arbitrary, but it should be
// less than the total render frames.
context.listener[options.paramName].setValueAtTime(
options.initialValue, 0);
context.listener[options.paramName].linearRampToValueAtTime(
options.rampFinalValue, automationEndFrame / context.sampleRate);
return context.startRendering().then(function(resultBuffer) {
// Sanity check: the given ramp final value must not be the default
// value, otherwise we can't easily tell if the automation was
// actually run.
should(
options.rampFinalValue,
'AudioListener.' + options.paramName + ' ramp final value')
.notBeEqualTo(context.listener[options.paramName].defaultValue);
// The actual AudioParam value should be the rampe final value.
should(
context.listener[options.paramName].value,
'AudioListener.' + options.paramName + '.value')
.beEqualTo(options.rampFinalValue);
});
}
audit.run();
</script>
</body>
</html>