298 lines
10 KiB
HTML
298 lines
10 KiB
HTML
<!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>
|