summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html')
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html227
1 files changed, 227 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html b/testing/web-platform/tests/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html
new file mode 100644
index 0000000000..78c1ec6dc2
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html
@@ -0,0 +1,227 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Test Clamping of Distance for PannerNode
+ </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>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ // Arbitrary sample rate and render length.
+ let sampleRate = 48000;
+ let renderFrames = 128;
+
+ let audit = Audit.createTaskRunner();
+
+ audit.define('ref-distance-error', (task, should) => {
+ testDistanceLimits(should, {name: 'refDistance', isZeroAllowed: true});
+ task.done();
+ });
+
+ audit.define('max-distance-error', (task, should) => {
+ testDistanceLimits(should, {name: 'maxDistance', isZeroAllowed: false});
+ task.done();
+ });
+
+ function testDistanceLimits(should, options) {
+ // Verify that exceptions are thrown for invalid values of refDistance.
+ let context = new OfflineAudioContext(1, renderFrames, sampleRate);
+
+ let attrName = options.name;
+ let prefix = 'new PannerNode(c, {' + attrName + ': ';
+
+ should(function() {
+ let nodeOptions = {};
+ nodeOptions[attrName] = -1;
+ new PannerNode(context, nodeOptions);
+ }, prefix + '-1})').throw(RangeError);
+
+ if (options.isZeroAllowed) {
+ should(function() {
+ let nodeOptions = {};
+ nodeOptions[attrName] = 0;
+ new PannerNode(context, nodeOptions);
+ }, prefix + '0})').notThrow();
+ } else {
+ should(function() {
+ let nodeOptions = {};
+ nodeOptions[attrName] = 0;
+ new PannerNode(context, nodeOptions);
+ }, prefix + '0})').throw(RangeError);
+ }
+
+ // The smallest representable positive single float.
+ let leastPositiveDoubleFloat = 4.9406564584124654e-324;
+
+ should(function() {
+ let nodeOptions = {};
+ nodeOptions[attrName] = leastPositiveDoubleFloat;
+ new PannerNode(context, nodeOptions);
+ }, prefix + leastPositiveDoubleFloat + '})').notThrow();
+
+ prefix = 'panner.' + attrName + ' = ';
+ panner = new PannerNode(context);
+ should(function() {
+ panner[attrName] = -1;
+ }, prefix + '-1').throw(RangeError);
+
+ if (options.isZeroAllowed) {
+ should(function() {
+ panner[attrName] = 0;
+ }, prefix + '0').notThrow();
+ } else {
+ should(function() {
+ panner[attrName] = 0;
+ }, prefix + '0').throw(RangeError);
+ }
+
+ should(function() {
+ panner[attrName] = leastPositiveDoubleFloat;
+ }, prefix + leastPositiveDoubleFloat).notThrow();
+ }
+
+ audit.define('min-distance', async (task, should) => {
+ // Test clamping of panner distance to refDistance for all of the
+ // distance models. The actual distance is arbitrary as long as it's
+ // less than refDistance. We test default and non-default values for
+ // the panner's refDistance and maxDistance.
+ // correctly.
+ await runTest(should, {
+ distance: 0.01,
+ distanceModel: 'linear',
+ });
+ await runTest(should, {
+ distance: 0.01,
+ distanceModel: 'exponential',
+ });
+ await runTest(should, {
+ distance: 0.01,
+ distanceModel: 'inverse',
+ });
+ await runTest(should, {
+ distance: 2,
+ distanceModel: 'linear',
+ maxDistance: 1000,
+ refDistance: 10,
+ });
+ await runTest(should, {
+ distance: 2,
+ distanceModel: 'exponential',
+ maxDistance: 1000,
+ refDistance: 10,
+ });
+ await runTest(should, {
+ distance: 2,
+ distanceModel: 'inverse',
+ maxDistance: 1000,
+ refDistance: 10,
+ });
+ task.done();
+ });
+
+ audit.define('max-distance', async (task, should) => {
+ // Like the "min-distance" task, but for clamping to the max
+ // distance. The actual distance is again arbitrary as long as it is
+ // greater than maxDistance.
+ await runTest(should, {
+ distance: 20000,
+ distanceModel: 'linear',
+ });
+ await runTest(should, {
+ distance: 21000,
+ distanceModel: 'exponential',
+ });
+ await runTest(should, {
+ distance: 23000,
+ distanceModel: 'inverse',
+ });
+ await runTest(should, {
+ distance: 5000,
+ distanceModel: 'linear',
+ maxDistance: 1000,
+ refDistance: 10,
+ });
+ await runTest(should, {
+ distance: 5000,
+ distanceModel: 'exponential',
+ maxDistance: 1000,
+ refDistance: 10,
+ });
+ await runTest(should, {
+ distance: 5000,
+ distanceModel: 'inverse',
+ maxDistance: 1000,
+ refDistance: 10,
+ });
+ task.done();
+ });
+
+ function runTest(should, options) {
+ let context = new OfflineAudioContext(2, renderFrames, sampleRate);
+ let src = new OscillatorNode(context, {
+ type: 'sawtooth',
+ frequency: 20 * 440,
+ });
+
+ // Set panner options. Use a non-default rolloffFactor so that the
+ // various distance models look distinctly different.
+ let pannerOptions = {};
+ Object.assign(pannerOptions, options, {rolloffFactor: 0.5});
+
+ let pannerRef = new PannerNode(context, pannerOptions);
+ let pannerTest = new PannerNode(context, pannerOptions);
+
+ // Split the panner output so we can grab just one of the output
+ // channels.
+ let splitRef = new ChannelSplitterNode(context, {numberOfOutputs: 2});
+ let splitTest = new ChannelSplitterNode(context, {numberOfOutputs: 2});
+
+ // Merge the panner outputs back into one stereo stream for the
+ // destination.
+ let merger = new ChannelMergerNode(context, {numberOfInputs: 2});
+
+ src.connect(pannerTest).connect(splitTest).connect(merger, 0, 0);
+ src.connect(pannerRef).connect(splitRef).connect(merger, 0, 1);
+
+ merger.connect(context.destination);
+
+ // Move the panner some distance away. Arbitrarily select the x
+ // direction. For the reference panner, manually clamp the distance.
+ // All models clamp the distance to a minimum of refDistance. Only the
+ // linear model also clamps to a maximum of maxDistance.
+ let xRef = Math.max(options.distance, pannerRef.refDistance);
+
+ if (pannerRef.distanceModel === 'linear') {
+ xRef = Math.min(xRef, pannerRef.maxDistance);
+ }
+
+ let xTest = options.distance;
+
+ pannerRef.positionZ.setValueAtTime(xRef, 0);
+ pannerTest.positionZ.setValueAtTime(xTest, 0);
+
+ src.start();
+
+ return context.startRendering().then(function(resultBuffer) {
+ let actual = resultBuffer.getChannelData(0);
+ let expected = resultBuffer.getChannelData(1);
+
+ should(
+ xTest < pannerRef.refDistance || xTest > pannerRef.maxDistance,
+ 'Model: ' + options.distanceModel + ': Distance (' + xTest +
+ ') is outside the range [' + pannerRef.refDistance + ', ' +
+ pannerRef.maxDistance + ']')
+ .beEqualTo(true);
+ should(actual, 'Test panner output ' + JSON.stringify(options))
+ .beEqualToArray(expected);
+ });
+ }
+
+ audit.run();
+ </script>
+ </body>
+</html>