| OLD | NEW |
| 1 <!doctype html> | 1 <!DOCTYPE html> |
| 2 <html> | 2 <html> |
| 3 <head> | 3 <head> |
| 4 <title>Test Clamping of Distance for PannerNode</title> | 4 <title> |
| 5 Test Clamping of Distance for PannerNode |
| 6 </title> |
| 5 <script src="../../resources/testharness.js"></script> | 7 <script src="../../resources/testharness.js"></script> |
| 6 <script src="../../resources/testharnessreport.js"></script> | 8 <script src="../../resources/testharnessreport.js"></script> |
| 7 <script src="../resources/audit-util.js"></script> | 9 <script src="../resources/audit-util.js"></script> |
| 8 <script src="../resources/audit.js"></script> | 10 <script src="../resources/audit.js"></script> |
| 9 </head> | 11 </head> |
| 12 <body> |
| 13 <script id="layout-test-code"> |
| 14 // Arbitrary sample rate and render length. |
| 15 let sampleRate = 48000; |
| 16 let renderFrames = 128; |
| 10 | 17 |
| 11 <body> | 18 let audit = Audit.createTaskRunner(); |
| 12 <script> | |
| 13 // Arbitrary sample rate and render length. | |
| 14 var sampleRate = 48000; | |
| 15 var renderFrames = 128; | |
| 16 | 19 |
| 17 var audit = Audit.createTaskRunner(); | 20 audit.define('ref-distance-error', (task, should) => { |
| 18 | 21 testDistanceLimits(should, {name: 'refDistance', isZeroAllowed: true}); |
| 19 audit.define("ref-distance-error", (task, should) => { | |
| 20 testDistanceLimits(should, {name: "refDistance", isZeroAllowed: true}); | |
| 21 task.done(); | 22 task.done(); |
| 22 }); | 23 }); |
| 23 | 24 |
| 24 audit.define("max-distance-error", (task, should) => { | 25 audit.define('max-distance-error', (task, should) => { |
| 25 testDistanceLimits(should, {name: "maxDistance", isZeroAllowed: false}); | 26 testDistanceLimits(should, {name: 'maxDistance', isZeroAllowed: false}); |
| 26 task.done(); | 27 task.done(); |
| 27 }); | 28 }); |
| 28 | 29 |
| 29 function testDistanceLimits(should, options) { | 30 function testDistanceLimits(should, options) { |
| 30 // Verify that exceptions are thrown for invalid values of refDistance. | 31 // Verify that exceptions are thrown for invalid values of refDistance. |
| 31 var context = new OfflineAudioContext(1, renderFrames, sampleRate); | 32 let context = new OfflineAudioContext(1, renderFrames, sampleRate); |
| 32 | 33 |
| 33 var attrName = options.name; | 34 let attrName = options.name; |
| 34 var prefix = "new PannerNode(c, {" + attrName + ": "; | 35 let prefix = 'new PannerNode(c, {' + attrName + ': '; |
| 35 | 36 |
| 36 should(function () { | 37 should(function() { |
| 37 var nodeOptions = {}; | 38 let nodeOptions = {}; |
| 38 nodeOptions[attrName] = -1; | 39 nodeOptions[attrName] = -1; |
| 39 new PannerNode(context, nodeOptions); | 40 new PannerNode(context, nodeOptions); |
| 40 }, prefix + "-1})").throw("RangeError"); | 41 }, prefix + '-1})').throw('RangeError'); |
| 41 | 42 |
| 42 if (options.isZeroAllowed) { | 43 if (options.isZeroAllowed) { |
| 43 should(function () { | 44 should(function() { |
| 44 var nodeOptions = {}; | 45 let nodeOptions = {}; |
| 45 nodeOptions[attrName] = 0; | 46 nodeOptions[attrName] = 0; |
| 46 new PannerNode(context, nodeOptions); | 47 new PannerNode(context, nodeOptions); |
| 47 }, prefix + "0})").notThrow(); | 48 }, prefix + '0})').notThrow(); |
| 48 } else { | 49 } else { |
| 49 should(function () { | 50 should(function() { |
| 50 var nodeOptions = {}; | 51 let nodeOptions = {}; |
| 51 nodeOptions[attrName] = 0; | 52 nodeOptions[attrName] = 0; |
| 52 new PannerNode(context, nodeOptions); | 53 new PannerNode(context, nodeOptions); |
| 53 }, prefix + "0})").throw("RangeError"); | 54 }, prefix + '0})').throw('RangeError'); |
| 54 } | 55 } |
| 55 | 56 |
| 56 // The smallest representable positive single float. | 57 // The smallest representable positive single float. |
| 57 var leastPositiveDoubleFloat = 4.9406564584124654e-324; | 58 let leastPositiveDoubleFloat = 4.9406564584124654e-324; |
| 58 | 59 |
| 59 should( | 60 should(function() { |
| 60 function () { | 61 let nodeOptions = {}; |
| 61 var nodeOptions = {}; | 62 nodeOptions[attrName] = leastPositiveDoubleFloat; |
| 62 nodeOptions[attrName] = leastPositiveDoubleFloat; | 63 new PannerNode(context, nodeOptions); |
| 63 new PannerNode(context, nodeOptions); | 64 }, prefix + leastPositiveDoubleFloat + '})').notThrow(); |
| 64 }, prefix + leastPositiveDoubleFloat + "})") | |
| 65 .notThrow(); | |
| 66 | 65 |
| 67 prefix = "panner." + attrName + " = "; | 66 prefix = 'panner.' + attrName + ' = '; |
| 68 panner = new PannerNode(context); | 67 panner = new PannerNode(context); |
| 69 should(function () { | 68 should(function() { |
| 70 panner[attrName] = -1; | 69 panner[attrName] = -1; |
| 71 }, prefix + "-1").throw("RangeError"); | 70 }, prefix + '-1').throw('RangeError'); |
| 72 | 71 |
| 73 if (options.isZeroAllowed) { | 72 if (options.isZeroAllowed) { |
| 74 should(function () { | 73 should(function() { |
| 75 panner[attrName] = 0; | 74 panner[attrName] = 0; |
| 76 }, prefix + "0").notThrow(); | 75 }, prefix + '0').notThrow(); |
| 77 } else { | 76 } else { |
| 78 should(function () { | 77 should(function() { |
| 79 panner[attrName] = 0; | 78 panner[attrName] = 0; |
| 80 }, prefix + "0").throw("RangeError"); | 79 }, prefix + '0').throw('RangeError'); |
| 81 } | 80 } |
| 82 | 81 |
| 83 should(function () { | 82 should(function() { |
| 84 panner[attrName] = leastPositiveDoubleFloat; | 83 panner[attrName] = leastPositiveDoubleFloat; |
| 85 }, prefix + leastPositiveDoubleFloat).notThrow(); | 84 }, prefix + leastPositiveDoubleFloat).notThrow(); |
| 86 } | 85 } |
| 87 | 86 |
| 88 audit.define("min-distance", (task, should) => { | 87 audit.define('min-distance', (task, should) => { |
| 89 // Test clamping of panner distance to refDistance for all of the | 88 // Test clamping of panner distance to refDistance for all of the |
| 90 // distance models. The actual distance is arbitrary as long as it's | 89 // distance models. The actual distance is arbitrary as long as it's |
| 91 // less than refDistance. We test default and non-default values for | 90 // less than refDistance. We test default and non-default values for |
| 92 // the panner's refDistance and maxDistance. | 91 // the panner's refDistance and maxDistance. |
| 93 // correctly. | 92 // correctly. |
| 94 Promise.all([ | 93 Promise |
| 95 runTest(should, { | 94 .all([ |
| 96 distance: 0.01, | 95 runTest(should, { |
| 97 distanceModel: "linear", | 96 distance: 0.01, |
| 98 }), | 97 distanceModel: 'linear', |
| 99 runTest(should, { | 98 }), |
| 100 distance: 0.01, | 99 runTest(should, { |
| 101 distanceModel: "exponential", | 100 distance: 0.01, |
| 102 }), | 101 distanceModel: 'exponential', |
| 103 runTest(should, { | 102 }), |
| 104 distance: 0.01, | 103 runTest(should, { |
| 105 distanceModel: "inverse", | 104 distance: 0.01, |
| 106 }), | 105 distanceModel: 'inverse', |
| 107 runTest(should, { | 106 }), |
| 108 distance: 2, | 107 runTest(should, { |
| 109 distanceModel: "linear", | 108 distance: 2, |
| 110 maxDistance: 1000, | 109 distanceModel: 'linear', |
| 111 refDistance: 10, | 110 maxDistance: 1000, |
| 112 }), | 111 refDistance: 10, |
| 113 runTest(should, { | 112 }), |
| 114 distance: 2, | 113 runTest(should, { |
| 115 distanceModel: "exponential", | 114 distance: 2, |
| 116 maxDistance: 1000, | 115 distanceModel: 'exponential', |
| 117 refDistance: 10, | 116 maxDistance: 1000, |
| 118 }), | 117 refDistance: 10, |
| 119 runTest(should, { | 118 }), |
| 120 distance: 2, | 119 runTest(should, { |
| 121 distanceModel: "inverse", | 120 distance: 2, |
| 122 maxDistance: 1000, | 121 distanceModel: 'inverse', |
| 123 refDistance: 10, | 122 maxDistance: 1000, |
| 124 }), | 123 refDistance: 10, |
| 125 ]) | 124 }), |
| 126 .then(() => task.done()); | 125 ]) |
| 126 .then(() => task.done()); |
| 127 }); | 127 }); |
| 128 | 128 |
| 129 audit.define("max-distance", (task, should) => { | 129 audit.define('max-distance', (task, should) => { |
| 130 // Like the "min-distance" task, but for clamping to the max | 130 // Like the "min-distance" task, but for clamping to the max |
| 131 // distance. The actual distance is again arbitrary as long as it is | 131 // distance. The actual distance is again arbitrary as long as it is |
| 132 // greater than maxDistance. | 132 // greater than maxDistance. |
| 133 Promise.all([ | 133 Promise |
| 134 runTest(should, { | 134 .all([ |
| 135 distance: 20000, | 135 runTest(should, { |
| 136 distanceModel: "linear", | 136 distance: 20000, |
| 137 }), | 137 distanceModel: 'linear', |
| 138 runTest(should, { | 138 }), |
| 139 distance: 21000, | 139 runTest(should, { |
| 140 distanceModel: "exponential", | 140 distance: 21000, |
| 141 }), | 141 distanceModel: 'exponential', |
| 142 runTest(should, { | 142 }), |
| 143 distance: 23000, | 143 runTest(should, { |
| 144 distanceModel: "inverse", | 144 distance: 23000, |
| 145 }), | 145 distanceModel: 'inverse', |
| 146 runTest(should, { | 146 }), |
| 147 distance: 5000, | 147 runTest(should, { |
| 148 distanceModel: "linear", | 148 distance: 5000, |
| 149 maxDistance: 1000, | 149 distanceModel: 'linear', |
| 150 refDistance: 10, | 150 maxDistance: 1000, |
| 151 }), | 151 refDistance: 10, |
| 152 runTest(should, { | 152 }), |
| 153 distance: 5000, | 153 runTest(should, { |
| 154 distanceModel: "exponential", | 154 distance: 5000, |
| 155 maxDistance: 1000, | 155 distanceModel: 'exponential', |
| 156 refDistance: 10, | 156 maxDistance: 1000, |
| 157 }), | 157 refDistance: 10, |
| 158 runTest(should, { | 158 }), |
| 159 distance: 5000, | 159 runTest(should, { |
| 160 distanceModel: "inverse", | 160 distance: 5000, |
| 161 maxDistance: 1000, | 161 distanceModel: 'inverse', |
| 162 refDistance: 10, | 162 maxDistance: 1000, |
| 163 }), | 163 refDistance: 10, |
| 164 ]) | 164 }), |
| 165 .then(() => task.done()); | 165 ]) |
| 166 .then(() => task.done()); |
| 166 }); | 167 }); |
| 167 | 168 |
| 168 function runTest(should, options) { | 169 function runTest(should, options) { |
| 169 var context = new OfflineAudioContext(2, renderFrames, sampleRate); | 170 let context = new OfflineAudioContext(2, renderFrames, sampleRate); |
| 170 var src = new OscillatorNode(context, { | 171 let src = new OscillatorNode(context, { |
| 171 type: "sawtooth", | 172 type: 'sawtooth', |
| 172 frequency: 20*440, | 173 frequency: 20 * 440, |
| 173 }); | 174 }); |
| 174 | 175 |
| 175 // Set panner options. Use a non-default rolloffFactor so that the | 176 // Set panner options. Use a non-default rolloffFactor so that the |
| 176 // various distance models look distinctly different. | 177 // various distance models look distinctly different. |
| 177 var pannerOptions = {}; | 178 let pannerOptions = {}; |
| 178 Object.assign(pannerOptions, options, {rolloffFactor: 0.5}); | 179 Object.assign(pannerOptions, options, {rolloffFactor: 0.5}); |
| 179 | 180 |
| 180 var pannerRef = new PannerNode(context, pannerOptions); | 181 let pannerRef = new PannerNode(context, pannerOptions); |
| 181 var pannerTest = new PannerNode(context, pannerOptions); | 182 let pannerTest = new PannerNode(context, pannerOptions); |
| 182 | 183 |
| 183 // Split the panner output so we can grab just one of the output | 184 // Split the panner output so we can grab just one of the output |
| 184 // channels. | 185 // channels. |
| 185 var splitRef = new ChannelSplitterNode(context, {numberOfOutputs: 2}); | 186 let splitRef = new ChannelSplitterNode(context, {numberOfOutputs: 2}); |
| 186 var splitTest = new ChannelSplitterNode(context, {numberOfOutputs: 2}); | 187 let splitTest = new ChannelSplitterNode(context, {numberOfOutputs: 2}); |
| 187 | 188 |
| 188 // Merge the panner outputs back into one stereo stream for the | 189 // Merge the panner outputs back into one stereo stream for the |
| 189 // destination. | 190 // destination. |
| 190 var merger = new ChannelMergerNode(context, {numberOfInputs: 2}); | 191 let merger = new ChannelMergerNode(context, {numberOfInputs: 2}); |
| 191 | 192 |
| 192 src.connect(pannerTest).connect(splitTest).connect(merger, 0, 0); | 193 src.connect(pannerTest).connect(splitTest).connect(merger, 0, 0); |
| 193 src.connect(pannerRef).connect(splitRef).connect(merger, 0, 1); | 194 src.connect(pannerRef).connect(splitRef).connect(merger, 0, 1); |
| 194 | 195 |
| 195 merger.connect(context.destination); | 196 merger.connect(context.destination); |
| 196 | 197 |
| 197 // Move the panner some distance away. Arbitrarily select the x | 198 // Move the panner some distance away. Arbitrarily select the x |
| 198 // direction. For the reference panner, manually clamp the distance. | 199 // direction. For the reference panner, manually clamp the distance. |
| 199 // All models clamp the distance to a minimum of refDistance. Only the | 200 // All models clamp the distance to a minimum of refDistance. Only the |
| 200 // linear model also clamps to a maximum of maxDistance. | 201 // linear model also clamps to a maximum of maxDistance. |
| 201 var xRef = Math.max(options.distance, pannerRef.refDistance); | 202 let xRef = Math.max(options.distance, pannerRef.refDistance); |
| 202 | 203 |
| 203 if (pannerRef.distanceModel === "linear") { | 204 if (pannerRef.distanceModel === 'linear') { |
| 204 xRef = Math.min(xRef, pannerRef.maxDistance); | 205 xRef = Math.min(xRef, pannerRef.maxDistance); |
| 205 } | 206 } |
| 206 | 207 |
| 207 var xTest = options.distance; | 208 let xTest = options.distance; |
| 208 | 209 |
| 209 pannerRef.positionZ.setValueAtTime(xRef, 0); | 210 pannerRef.positionZ.setValueAtTime(xRef, 0); |
| 210 pannerTest.positionZ.setValueAtTime(xTest, 0); | 211 pannerTest.positionZ.setValueAtTime(xTest, 0); |
| 211 | 212 |
| 212 src.start(); | 213 src.start(); |
| 213 | 214 |
| 214 return context.startRendering().then(function (resultBuffer) { | 215 return context.startRendering().then(function(resultBuffer) { |
| 215 var actual = resultBuffer.getChannelData(0); | 216 let actual = resultBuffer.getChannelData(0); |
| 216 var expected = resultBuffer.getChannelData(1); | 217 let expected = resultBuffer.getChannelData(1); |
| 217 | 218 |
| 218 should(xTest < pannerRef.refDistance || xTest > pannerRef.maxDistance, | 219 should( |
| 219 "Model: " + options.distanceModel + ": Distance (" + xTest + | 220 xTest < pannerRef.refDistance || xTest > pannerRef.maxDistance, |
| 220 ") is outside the range [" + pannerRef.refDistance + ", " + | 221 'Model: ' + options.distanceModel + ': Distance (' + xTest + |
| 221 pannerRef.maxDistance + "]") | 222 ') is outside the range [' + pannerRef.refDistance + ', ' + |
| 222 .beEqualTo(true); | 223 pannerRef.maxDistance + ']') |
| 223 should(actual, "Test panner output " + JSON.stringify(options)) | 224 .beEqualTo(true); |
| 224 .beEqualToArray(expected); | 225 should(actual, 'Test panner output ' + JSON.stringify(options)) |
| 226 .beEqualToArray(expected); |
| 225 }); | 227 }); |
| 226 } | 228 } |
| 227 | 229 |
| 228 audit.run(); | 230 audit.run(); |
| 229 </script> | 231 </script> |
| 230 </body> | 232 </body> |
| 231 </html> | 233 </html> |
| OLD | NEW |