blob: 5c3df0e6fdf85f9cf33314c0748e72b515f40e6f [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>
Test Basic PannerNode with Automation Position Properties
</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/panner-formulas.js"></script>
</head>
<body>
<script id="layout-test-code">
let sampleRate = 48000;
// These tests are quite slow, so don't run for many frames. 256 frames
// should be enough to demonstrate that automations are working.
let renderFrames = 256;
let renderDuration = renderFrames / sampleRate;
let audit = Audit.createTaskRunner();
// Array of tests for setting the panner positions. These tests basically
// verify that the position setters for the panner and listener are
// working correctly.
let testConfig = [
{
setter: 'positionX',
},
{
setter: 'positionY',
},
{
setter: 'positionZ',
}
];
// Create tests for the panner position setters. Both mono and steroe
// sources are tested.
for (let k = 0; k < testConfig.length; ++k) {
let config = testConfig[k];
// Function to create the test to define the test.
let tester = function(config, channelCount) {
return (task, should) => {
let nodes = createGraph(channelCount);
let {context, source, panner} = nodes;
let message = channelCount == 1 ? 'Mono' : 'Stereo';
message += ' panner.' + config.setter;
testPositionSetter(should, {
nodes: nodes,
pannerSetter: panner[config.setter],
message: message
}).then(() => task.done());
}
};
audit.define('Stereo panner.' + config.setter, tester(config, 2));
audit.define('Mono panner.' + config.setter, tester(config, 1));
}
// Create tests for the listener position setters. Both mono and steroe
// sources are tested.
for (let k = 0; k < testConfig.length; ++k) {
let config = testConfig[k];
// Function to create the test to define the test.
let tester = function(config, channelCount) {
return (task, should) => {
let nodes = createGraph(channelCount);
let {context, source, panner} = nodes;
let message = channelCount == 1 ? 'Mono' : 'Stereo';
message += ' listener.' + config.setter;
// Some relatively arbitrary (non-default) position for the source
// location.
panner.setPosition(1, 0, 1);
testPositionSetter(should, {
nodes: nodes,
pannerSetter: context.listener[config.setter],
message: message
}).then(() => task.done());
}
};
audit.define('Stereo listener.' + config.setter, tester(config, 2));
audit.define('Mono listener.' + config.setter, tester(config, 1));
}
// Test setPosition method.
audit.define('setPosition', (task, should) => {
let {context, panner, source} = createGraph(2);
// Initialize source position (values don't really matter).
panner.setPosition(1, 1, 1);
// After some (unimportant) time, move the panner to a (any) new
// location.
let suspendFrame = 128;
context.suspend(suspendFrame / sampleRate)
.then(function() {
panner.setPosition(-100, 2000, 8000);
})
.then(context.resume.bind(context));
context.startRendering()
.then(function(resultBuffer) {
verifyPannerOutputChanged(
should, resultBuffer,
{message: 'setPosition', suspendFrame: suspendFrame});
})
.then(() => task.done());
});
audit.define('orientation setter', (task, should) => {
let {context, panner, source} = createGraph(2);
// For orientation to matter, we need to make the source directional,
// and also move away from the listener (because the default location is
// 0,0,0).
panner.setPosition(0, 0, 1);
panner.coneInnerAngle = 0;
panner.coneOuterAngle = 360;
panner.coneOuterGain = .001;
// After some (unimportant) time, change the panner orientation to a new
// orientation. The only constraint is that the orientation changes
// from before.
let suspendFrame = 128;
context.suspend(suspendFrame / sampleRate)
.then(function() {
panner.orientationX.value = -100;
panner.orientationY.value = 2000;
panner.orientationZ.value = 8000;
})
.then(context.resume.bind(context));
context.startRendering()
.then(function(resultBuffer) {
verifyPannerOutputChanged(should, resultBuffer, {
message: 'panner.orientation{XYZ}',
suspendFrame: suspendFrame
});
})
.then(() => task.done());
});
audit.define('forward setter', (task, should) => {
let {context, panner, source} = createGraph(2);
// For orientation to matter, we need to make the source directional,
// and also move away from the listener (because the default location is
// 0,0,0).
panner.setPosition(0, 0, 1);
panner.coneInnerAngle = 0;
panner.coneOuterAngle = 360;
panner.coneOuterGain = .001;
// After some (unimportant) time, change the panner orientation to a new
// orientation. The only constraint is that the orientation changes
// from before.
let suspendFrame = 128;
context.suspend(suspendFrame / sampleRate)
.then(function() {
context.listener.forwardX.value = -100;
context.listener.forwardY.value = 2000;
context.listener.forwardZ.value = 8000;
})
.then(context.resume.bind(context));
context.startRendering()
.then(function(resultBuffer) {
verifyPannerOutputChanged(should, resultBuffer, {
message: 'listener.forward{XYZ}',
suspendFrame: suspendFrame
});
})
.then(() => task.done());
});
audit.define('up setter', (task, should) => {
let {context, panner, source} = createGraph(2);
// For orientation to matter, we need to make the source directional,
// and also move away from the listener (because the default location is
// 0,0,0).
panner.setPosition(0, 0, 1);
panner.coneInnerAngle = 0;
panner.coneOuterAngle = 360;
panner.coneOuterGain = .001;
panner.setPosition(1, 0, 1);
// After some (unimportant) time, change the panner orientation to a new
// orientation. The only constraint is that the orientation changes
// from before.
let suspendFrame = 128;
context.suspend(suspendFrame / sampleRate)
.then(function() {
context.listener.upX.value = 100;
context.listener.upY.value = 100;
context.listener.upZ.value = 100;
;
})
.then(context.resume.bind(context));
context.startRendering()
.then(function(resultBuffer) {
verifyPannerOutputChanged(
should, resultBuffer,
{message: 'listener.up{XYZ}', suspendFrame: suspendFrame});
})
.then(() => task.done());
});
audit.run();
function createGraph(channelCount) {
let context = new OfflineAudioContext(2, renderFrames, sampleRate);
let panner = context.createPanner();
let source = context.createBufferSource();
source.buffer =
createConstantBuffer(context, 1, channelCount == 1 ? 1 : [1, 2]);
source.loop = true;
source.connect(panner);
panner.connect(context.destination);
source.start();
return {context: context, source: source, panner: panner};
}
function testPositionSetter(should, options) {
let {nodes, pannerSetter, message} = options;
let {context, source, panner} = nodes;
// Set panner x position. (Value doesn't matter);
pannerSetter.value = 1;
// Wait a bit and set a new position. (Actual time and position doesn't
// matter).
let suspendFrame = 128;
context.suspend(suspendFrame / sampleRate)
.then(function() {
pannerSetter.value = 10000;
})
.then(context.resume.bind(context));
return context.startRendering().then(function(resultBuffer) {
verifyPannerOutputChanged(
should, resultBuffer,
{message: message, suspendFrame: suspendFrame});
});
}
function verifyPannerOutputChanged(should, resultBuffer, options) {
let {message, suspendFrame} = options;
// Verify that the first part of output is constant. (Doesn't matter
// what.)
let data0 = resultBuffer.getChannelData(0);
let data1 = resultBuffer.getChannelData(1);
let middle = '[0, ' + suspendFrame + ') ';
should(
data0.slice(0, suspendFrame),
message + '.value frame ' + middle + 'channel 0')
.beConstantValueOf(data0[0]);
should(
data1.slice(0, suspendFrame),
message + '.value frame ' + middle + 'channel 1')
.beConstantValueOf(data1[0]);
// The rest after suspendTime should be constant and different from the
// first part.
middle = '[' + suspendFrame + ', ' + renderFrames + ') ';
should(
data0.slice(suspendFrame),
message + '.value frame ' + middle + 'channel 0')
.beConstantValueOf(data0[suspendFrame]);
should(
data1.slice(suspendFrame),
message + '.value frame ' + middle + 'channel 1')
.beConstantValueOf(data1[suspendFrame]);
should(
data0[suspendFrame],
message + ': Output at frame ' + suspendFrame + ' channel 0')
.notBeEqualTo(data0[0]);
should(
data1[suspendFrame],
message + ': Output at frame ' + suspendFrame + ' channel 1')
.notBeEqualTo(data1[0]);
}
</script>
</body>
</html>