blob: 60200b24712c29a92d73b6215b85f734999b8209 [file] [log] [blame]
<!doctype html>
<html>
<head>
<title>Test k-rate AudioParams of PannerNode</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
<script src="/webaudio/resources/audit.js"></script>
<script src="automation-rate-testing.js"></script>
</head>
<body>
<script>
let audit = Audit.createTaskRunner();
// Define a test where we verify that a k-rate audio param produces
// different results from an a-rate audio param for each of the audio
// params of a biquad.
//
// Each entry gives the name of the AudioParam, an initial value to be
// used with setValueAtTime, and a final value to be used with
// linearRampToValueAtTime. (See |doTest| for details as well.)
[{name: 'positionX', initial: 0, final: 1000},
{name: 'positionY', initial: 0, final: 1000},
{name: 'orientationX', initial: 1, final: 10},
{name: 'orientationY', initial: 1, final: 10},
{name: 'orientationZ', initial: 1, final: 10},
].forEach(paramProperty => {
audit.define('Panner k-rate ' + paramProperty.name, (task, should) => {
// Arbitrary sample rate and duration.
let sampleRate = 8000;
let testDuration = 5 * 128 / sampleRate;
let context = new OfflineAudioContext({
numberOfChannels: 3,
sampleRate: sampleRate,
length: testDuration * sampleRate
});
doTest(context, should, {
sourceNodeName: 'ConstantSourceNode',
verifyPieceWiseConstant: true,
nodeName: 'PannerNode',
// Make the source directional so orientation matters, and set some
// defaults for the position and orientation so that we're not on an
// axis where the azimuth and elevation might be constant when
// moving one of the AudioParams.
nodeOptions: {
distanceModel: 'inverse',
coneOuterAngle: 360,
coneInnerAngle: 0,
positionX: 1,
positionY: 1,
positionZ: 1,
orientationX: 0,
orientationY: 1,
orientationZ: 1
},
prefix: `k-rate ${paramProperty.name}`,
// Just set the frequency to k-rate
rateSettings: [
{name: paramProperty.name, value: 'k-rate'},
],
// Automate just the given AudioParam
automations: [{
name: paramProperty.name,
methods: [
{name: 'setValueAtTime', options: [paramProperty.initial, 0]}, {
name: 'linearRampToValueAtTime',
options: [paramProperty.final, testDuration]
}
]
}]
}).then(() => task.done());
});
});
// Test k-rate automation of the listener. The intial and final
// automation values are pretty arbitrary, except that they should be such
// that the panner and listener produces non-constant output.
[{name: 'positionX', initial: [1, 0], final: [1000, 1]},
{name: 'positionY', initial: [1, 0], final: [1000, 1]},
{name: 'positionZ', initial: [1, 0], final: [1000, 1]},
{name: 'forwardX', initial: [-1, 0], final: [1, 1]},
{name: 'forwardY', initial: [-1, 0], final: [1, 1]},
{name: 'forwardZ', initial: [-1, 0], final: [1, 1]},
{name: 'upX', initial: [-1, 0], final: [1000, 1]},
{name: 'upY', initial: [-1, 0], final: [1000, 1]},
{name: 'upZ', initial: [-1, 0], final: [1000, 1]},
].forEach(paramProperty => {
audit.define(
'Listener k-rate ' + paramProperty.name, (task, should) => {
// Arbitrary sample rate and duration.
let sampleRate = 8000;
let testDuration = 5 * 128 / sampleRate;
let context = new OfflineAudioContext({
numberOfChannels: 1,
sampleRate: sampleRate,
length: testDuration * sampleRate
});
doListenerTest(context, should, {
param: paramProperty.name,
initial: paramProperty.initial,
final: paramProperty.final
}).then(() => task.done());
});
});
audit.run();
function doListenerTest(context, should, options) {
let src = new ConstantSourceNode(context);
let panner = new PannerNode(context, {
distanceModel: 'inverse',
coneOuterAngle: 360,
coneInnerAngle: 10,
positionX: 10,
positionY: 10,
positionZ: 10,
orientationX: 1,
orientationY: 1,
orientationZ: 1
});
src.connect(panner).connect(context.destination);
src.start();
let listener = context.listener;
// Set listener properties to "random" values so that motion on one of
// the attributes actually changes things relative to the panner
// location. And the up and forward directions should have a simple
// relationship between them.
listener.positionX.value = -1;
listener.positionY.value = 1;
listener.positionZ.value = -1;
listener.forwardX.value = -1;
listener.forwardY.value = 1;
listener.forwardZ.value = -1;
// Make the up vector not parallel or perpendicular to the forward and
// position vectors so that automations of the up vector produce
// noticeable differences.
listener.upX.value = 1;
listener.upY.value = 1;
listener.upZ.value = 2;
let audioParam = listener[options.param];
audioParam.automationRate = 'k-rate';
let prefix = `Listener ${options.param}`;
should(audioParam.automationRate, prefix + '.automationRate')
.beEqualTo('k-rate');
should(() => {
audioParam.setValueAtTime(...options.initial);
}, prefix + `.setValueAtTime(${options.initial})`).notThrow();
should(() => {
audioParam.linearRampToValueAtTime(...options.final);
}, prefix + `.linearRampToValueAtTime(${options.final})`).notThrow();
return context.startRendering().then(renderedBuffer => {
let prefix = `Listener k-rate ${options.param}: `;
let output = renderedBuffer.getChannelData(0);
// Sanity check that the output isn't constant.
should(output, prefix + `Output`).notBeConstantValueOf(output[0]);
// Verify that the output is constant over each render quantum
for (let k = 0; k < output.length; k += 128) {
should(
output.slice(k, k + 128), prefix + `Output [${k}, ${k + 127}]`)
.beConstantValueOf(output[k]);
}
});
}
</script>
</body>
</html>