tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

panner-distance-clamping.html (7701B)


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