tor-browser

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

realtime-conv.html (5974B)


      1 <!DOCTYPE html>
      2 <html>
      3  <head>
      4    <title>
      5      Test Convolver on Real-time Context
      6    </title>
      7    <script src="/resources/testharness.js"></script>
      8    <script src="/resources/testharnessreport.js"></script>
      9    <script src="/webaudio/resources/convolution-testing.js"></script>
     10  </head>
     11  <body>
     12    <script>
     13      // Choose a length that is large enough to cause multiple threads to be
     14      // used in the convolver. For browsers that don't support this, this value
     15      // doesn't matter.
     16      const pulseLength = 16384;
     17 
     18      // The computed SNR should be at least this large.  This value depends on
     19      // the platform and browser.  Don't set this value to be too much lower
     20      // than this. It probably indicates a fairly inaccurate convolver or
     21      // ConstantSourceNode automations that should be fixed instead.
     22      //
     23      // Any major change of operating system or CPU architecture might affect
     24      // this value significantly. See: https://crbug.com/1339291
     25      const minRequiredSNR = 68.40;
     26 
     27      // To test the real‑time convolver, we convolve two square pulses
     28      // together to produce a triangular pulse. To verify the result
     29      // is correct we compare it against a ConstantSourceNode
     30      // configured to generate the expected ramp.
     31      promise_test(t => new Promise(resolve => {
     32        // Use a power of two for the sample‑rate to eliminate
     33        // round‑off in computing times from frames.
     34        const context = new AudioContext({sampleRate: 16384});
     35 
     36        // Square pulse for the ConvolverNode impulse response.
     37        const squarePulse = new AudioBuffer(
     38            {length: pulseLength, sampleRate: context.sampleRate});
     39        squarePulse.getChannelData(0).fill(1);
     40 
     41        const convolver = new ConvolverNode(
     42            context, {buffer: squarePulse, disableNormalization: true});
     43 
     44        // Square pulse for testing.
     45        const srcSquare = new ConstantSourceNode(context, {offset: 0});
     46        srcSquare.connect(convolver);
     47 
     48        // Reference ramp. Automations on this ConstantSourceNode will
     49        // generate the desired ramp.
     50        const srcRamp = new ConstantSourceNode(context, {offset: 0});
     51 
     52        // Use these GainNodes to compute the difference between the convolver
     53        // output and the expected ramp to create the error signal.
     54        const inverter = new GainNode(context, {gain: -1});
     55        const sum = new GainNode(context, {gain: 1});
     56        convolver.connect(sum);
     57        srcRamp.connect(inverter).connect(sum);
     58 
     59        // Square the error signal using this GainNode.
     60        const squarer = new GainNode(context, {gain: 0});
     61        sum.connect(squarer);
     62        sum.connect(squarer.gain);
     63 
     64        // Merge the error signal and the square source so we can integrate
     65        // the error signal to find an SNR.
     66        const merger = new ChannelMergerNode(context, {numberOfInputs: 2});
     67        squarer.connect(merger, 0, 0);
     68        srcSquare.connect(merger, 0, 1);
     69 
     70        // For simplicity, use a ScriptProcessor to integrate the error
     71        // signal. The square pulse signal is used as a gate over which the
     72        // integration is done.  When the pulse ends, the SNR is computed and
     73        // the test ends.
     74 
     75        // |doSum| is used to determine when to integrate and when it becomes
     76        // false, it signals the end of integration.
     77        let doSum = false;
     78 
     79        // |signalSum| is the energy in the square pulse.  |errorSum| is the
     80        // energy in the error signal.
     81        let signalSum = 0;
     82        let errorSum = 0;
     83 
     84        const spn = context.createScriptProcessor(0, 2, 1);
     85        spn.onaudioprocess = event => {
     86          // Sum the values on the first channel when the second channel is
     87          // not zero.  When the second channel goes from non‑zero to 0,
     88          // dump the value out and terminate the test.
     89          const c0 = event.inputBuffer.getChannelData(0);
     90          const c1 = event.inputBuffer.getChannelData(1);
     91 
     92          for (let k = 0; k < c1.length; ++k) {
     93            if (c1[k] === 0) {
     94              if (doSum) {
     95                doSum = false;
     96                // Square wave is now silent and we were integrating, so we
     97                // can stop now and verify the SNR.
     98                const snr = 10 * Math.log10(signalSum / errorSum);
     99                t.step(() =>
    100                  assert_greater_than_equal(snr, minRequiredSNR, 'SNR')
    101                );
    102                spn.onaudioprocess = null;
    103                context.close().then(resolve);
    104              }
    105            } else {
    106              // Signal is non‑zero so sum up the values.
    107              doSum = true;
    108              errorSum += c0[k];
    109              signalSum += c1[k] * c1[k];
    110            }
    111          }
    112        };
    113 
    114        merger.connect(spn).connect(context.destination);
    115 
    116        // Schedule everything to start a bit in the future from now, and end
    117        // |pulseLength| frames later.
    118        const now = context.currentTime;
    119 
    120        // |startFrame| is the number of frames to schedule ahead for testing.
    121        const startFrame = 512;
    122        const startTime = startFrame / context.sampleRate;
    123        const pulseDuration = pulseLength / context.sampleRate;
    124 
    125        // Create a square pulse in the ConstantSourceNode.
    126        srcSquare.offset.setValueAtTime(1, now + startTime);
    127        srcSquare.offset.setValueAtTime(0, now + startTime + pulseDuration);
    128 
    129        // Create the reference ramp.
    130        srcRamp.offset.setValueAtTime(1, now + startTime);
    131        srcRamp.offset.linearRampToValueAtTime(
    132            pulseLength,
    133            now + startTime + pulseDuration - 1 / context.sampleRate);
    134        srcRamp.offset.linearRampToValueAtTime(
    135            0,
    136            now + startTime + 2 * pulseDuration - 1 / context.sampleRate);
    137 
    138        // Start the ramps!
    139        srcRamp.start();
    140        srcSquare.start();
    141      }), 'Test convolver with real‑time context');
    142    </script>
    143  </body>
    144 </html>