blob: 8e7ed164602df40da596fce5e088c7138b12a681 [file] [log] [blame]
<!doctype html>
<html>
<head>
<script src="../resources/js-test.js"></script>
<script src="resources/compatibility.js"></script>
<script src="resources/audio-testing.js"></script>
<script src="resources/panner-formulas.js"></script>
<title>Test Basic PannerNode with Automation Position Properties</title>
</head>
<body>
<script>
description("Test Basic PannerNode with Automation Position Properties.");
window.jsTestIsAsync = true;
var 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.
var renderFrames = 256;
var renderDuration = renderFrames / sampleRate;
var 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.
var testConfig = [{
setter: "positionX",
}, {
setter: "positionY",
}, {
setter: "positionZ",
}];
// Create tests for the panner position setters. Both mono and steroe sources are tested.
for (var k = 0; k < testConfig.length; ++k) {
var config = testConfig[k];
// Function to create the test to define the test.
var tester = function (config, channelCount) {
return function (done) {
var nodes = createGraph(channelCount);
var {context, source, panner} = nodes;
var message = channelCount == 1 ? "Mono" : "Stereo";
message += " panner." + config.setter;
testPositionSetter({
nodes: nodes,
pannerSetter: panner[config.setter],
message: message
}).then(done);
}
}
audit.defineTask("Stereo panner." + config.setter, tester(config, 2));
audit.defineTask("Mono panner." + config.setter, tester(config, 1));
}
// Create tests for the listener position setters. Both mono and steroe sources are tested.
for (var k = 0; k < testConfig.length; ++k) {
var config = testConfig[k];
// Function to create the test to define the test.
var tester = function (config, channelCount) {
return function (done) {
var nodes = createGraph(channelCount);
var {context, source, panner} = nodes;
var 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({
nodes: nodes,
pannerSetter: context.listener[config.setter],
message: message
}).then(done);
}
}
audit.defineTask("Stereo listener." + config.setter, tester(config, 2));
audit.defineTask("Mono listener." + config.setter, tester(config, 1));
}
// Test setPosition method.
audit.defineTask("setPosition", function (done) {
var {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.
var suspendFrame = 128;
context.suspend(suspendFrame / sampleRate).then(function () {
panner.setPosition(-100, 2000, 8000);
}).then(context.resume.bind(context));
context.startRendering().then(function (resultBuffer) {
verifyPannerOutputChanged(resultBuffer, {message: "setPosition", suspendFrame: suspendFrame});
}).then(done);
});
audit.defineTask("orientation setter", function (done) {
var {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.
var 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(resultBuffer, {message: "panner.orientation{XYZ}", suspendFrame: suspendFrame});
}).then(done);
});
audit.defineTask("forward setter", function (done) {
var {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.
var 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(resultBuffer, {message: "listener.forward{XYZ}", suspendFrame: suspendFrame});
}).then(done);
});
audit.defineTask("up setter", function (done) {
var {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.
var 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(resultBuffer, {message: "listener.up{XYZ}", suspendFrame: suspendFrame});
}).then(done);
});
audit.defineTask("finish", function (done) {
finishJSTest();
done();
});
audit.runTasks();
function createGraph(channelCount) {
var context = new OfflineAudioContext(2, renderFrames, sampleRate);
var panner = context.createPanner();
var 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(options) {
var {nodes, pannerSetter, message} = options;
var {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).
var suspendFrame = 128;
context.suspend(suspendFrame / sampleRate).then(function () {
pannerSetter.value = 10000;
}).then(context.resume.bind(context));
return context.startRendering().then(function (resultBuffer) {
verifyPannerOutputChanged(resultBuffer, {message: message, suspendFrame: suspendFrame});
});
}
function verifyPannerOutputChanged(resultBuffer, options) {
var {message, suspendFrame} = options;
// Verify that the first part of output is constant. (Doesn't matter what.)
var success = true;
var data0 = resultBuffer.getChannelData(0);
var data1 = resultBuffer.getChannelData(1);
var middle = "[0, " + suspendFrame + ") ";
success = Should(message + ".value frame " + middle + "channel 0", data0.slice(0, suspendFrame))
.beConstantValueOf(data0[0]) && success;
success = Should(message + ".value frame " + middle + "channel 1", data1.slice(0, suspendFrame))
.beConstantValueOf(data1[0]) && success;
// The rest after suspendTime should be constant and different from the first part.
middle = "[" + suspendFrame + ", " + renderFrames + ") ";
success = Should(message + ".value frame " + middle + "channel 0",
data0.slice(suspendFrame))
.beConstantValueOf(data0[suspendFrame]) && success;
success = Should(message + ".value frame " + middle + "channel 0",
data1.slice(suspendFrame))
.beConstantValueOf(data1[suspendFrame]) && success;
success = Should("Output at frame " + suspendFrame + " channel 0", data0[suspendFrame])
.notBeEqualTo(data0[0]) && success;
success = Should("Output at frame " + suspendFrame + " channel 1", data1[suspendFrame])
.notBeEqualTo(data1[0]) && success;
var prefix = "Directly setting " + message + ".value";
if (success)
testPassed(prefix + " worked.\n");
else
testFailed(prefix + " failed.\n");
}
</script>
</body>
</html>