tor-browser

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

audioparam-method-chaining.html (5347B)


      1 <!DOCTYPE html>
      2 <html>
      3  <head>
      4    <title>AudioParam Method Chaining</title>
      5    <script src="/resources/testharness.js"></script>
      6    <script src="/resources/testharnessreport.js"></script>
      7    <script src="/webaudio/resources/audit-util.js"></script>
      8    <script src="/webaudio/resources/audioparam-testing.js"></script>
      9  </head>
     10  <body>
     11    <script>
     12      const sampleRate = 8000;
     13 
     14      // Create a dummy array for setValueCurveAtTime method.
     15      const curveArray = new Float32Array([5.0, 6.0]);
     16 
     17      // Method and argument combinations to test method chaining
     18      const methodDictionary = [
     19        {name: 'setValueAtTime', args: [1.0, 0.0]},
     20        {name: 'linearRampToValueAtTime', args: [2.0, 1.0]},
     21        {name: 'exponentialRampToValueAtTime', args: [3.0, 2.0]},
     22        {name: 'setTargetAtTime', args: [4.0, 2.0, 0.5]},
     23        {name: 'setValueCurveAtTime', args: [curveArray, 5.0, 1.0]},
     24        {name: 'cancelScheduledValues', args: [6.0]}
     25      ];
     26 
     27      test(() => {
     28        const context = new AudioContext();
     29        methodDictionary.forEach(({name, args}) => {
     30          const sourceParam = context.createGain().gain;
     31          const returnParam = sourceParam[name](...args);
     32          assert_equals(
     33            returnParam, sourceParam,
     34            `AudioParam.${name}() should return same AudioParam for chaining`
     35          );
     36        });
     37      }, 'AudioParam: Each method returns the same object to allow chaining');
     38 
     39      // Task: test method chaining with invalid operation.
     40      promise_test(async () => {
     41        const context = new OfflineAudioContext(1, sampleRate, sampleRate);
     42 
     43        const osc = context.createOscillator();
     44        const amp1 = context.createGain();
     45        const amp2 = context.createGain();
     46 
     47        osc.connect(amp1);
     48        osc.connect(amp2);
     49        amp1.connect(context.destination);
     50        amp2.connect(context.destination);
     51 
     52        // The first operation fails with an exception, thus the second one
     53        // should not have effect on the parameter value. Instead, it should
     54        // maintain the default value of 1.0.
     55        assert_throws_js(
     56          RangeError,
     57          () => amp1.gain.setValueAtTime(0.25, -1.0)
     58              .linearRampToValueAtTime(2.0, 1.0),
     59          'Chained call should throw if setValueAtTime() called ' +
     60              'with a negative end time'
     61        );
     62 
     63        // The first operation succeeds but the second fails due to zero target
     64        // value for the exponential ramp. Thus only the first should have
     65        // effect on the parameter value, setting the value to 0.5.
     66        assert_throws_js(
     67          RangeError,
     68          () => amp2.gain.setValueAtTime(0.5, 0.0)
     69              .exponentialRampToValueAtTime(0.0, 1.0),
     70          'Chained call should throw if exponentialRampToValueAtTime() ' +
     71              'called with a zero target value'
     72        );
     73 
     74        osc.start();
     75        osc.stop(1.0);
     76 
     77        const renderedBuffer = await context.startRendering();
     78 
     79        assert_equals(
     80          amp1.gain.value, 1.0,
     81          'amp1.gain.value should remain default 1 because setValueAtTime threw'
     82        );
     83 
     84        assert_equals(
     85          amp2.gain.value, 0.5,
     86          'amp2.gain.value should be set by setValueAtTime ' +
     87              'since exponentialRampToValueAtTime threw'
     88        );
     89      }, 'AudioParam: Chaining with invalid operations does ' +
     90          'not apply later effects');
     91 
     92      // Task: verify if the method chaining actually works. Create an arbitrary
     93      // envelope and compare the result with the expected one created by JS
     94      // code.
     95      promise_test(async () => {
     96        const context = new OfflineAudioContext(1, sampleRate * 4, sampleRate);
     97        const constantBuffer = createConstantBuffer(context, 1, 1.0);
     98 
     99        const source = context.createBufferSource();
    100        source.buffer = constantBuffer;
    101        source.loop = true;
    102 
    103        const envelope = context.createGain();
    104        source.connect(envelope);
    105        envelope.connect(context.destination);
    106 
    107        // Apply a series of scheduled events via method chaining.
    108        envelope.gain.setValueAtTime(0.0, 0.0)
    109          .linearRampToValueAtTime(1.0, 1.0)
    110          .exponentialRampToValueAtTime(0.5, 2.0)
    111          .setTargetAtTime(0.001, 2.0, 0.5);
    112 
    113        source.start();
    114 
    115        const rendered = await context.startRendering();
    116 
    117        // Create expected envelope using helper functions
    118        const expectedEnvelope = [
    119          ...createLinearRampArray(0.0, 1.0, 0.0, 1.0, sampleRate),
    120          ...createExponentialRampArray(1.0, 2.0, 1.0, 0.5, sampleRate),
    121          ...createExponentialApproachArray(2.0, 4.0, 0.5, 0.001,
    122              sampleRate, 0.5)
    123        ];
    124 
    125        // There are slight differences between JS implementation of
    126        // AudioParam envelope and the internal implementation. (i.e.
    127        // double/float and rounding up) The error threshold is adjusted
    128        // empirically through the local testing.
    129        assert_array_approx_equals(
    130          rendered.getChannelData(0),
    131          expectedEnvelope,
    132          4.0532e-6,
    133          'Rendered gain envelope should match expected ' +
    134              'values from scheduled events'
    135        );
    136      }, 'AudioParam: Chaining of envelope methods schedules ' +
    137          'values as expected');
    138    </script>
    139  </body>
    140 </html>