tor-browser

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

sub-sample-buffer-stitching.html (4849B)


      1 <!doctype html>
      2 <html>
      3  <head>
      4    <title>
      5      Test Sub-Sample Accurate Stitching of ABSNs
      6    </title>
      7    <script src="/resources/testharness.js"></script>
      8    <script src="/resources/testharnessreport.js"></script>
      9    <script src="/webaudio/resources/audit-util.js"></script>
     10  </head>
     11  <body>
     12    <script>
     13 
     14      promise_test(async t => {
     15        // Sub‑sample stitching with identical context/buffer rates.
     16        const sampleRate = 44100;
     17        const bufferRate = 44100;
     18        const bufferLength = 30;
     19 
     20        // Experimentally determined thresholds. DO NOT relax these values
     21        // too far from these values to make the tests pass.
     22        const errorThreshold = 9.0957e-5;
     23        const snrThreshold = 85.580;
     24 
     25        assert_equals(sampleRate, 44100, 'Test 1: context.sampleRate');
     26 
     27        const resultBuffer =
     28            await testBufferStitching(sampleRate, bufferRate, bufferLength);
     29 
     30        const actual = resultBuffer.getChannelData(0);
     31        const expected = resultBuffer.getChannelData(1);
     32 
     33        assert_array_equal_within_eps(
     34            actual,
     35            expected,
     36            errorThreshold,
     37            `Stitched sine‑wave buffers at sample rate ${bufferRate}`);
     38 
     39        const SNR = 10 * Math.log10(computeSNR(actual, expected));
     40 
     41        assert_greater_than_equal(
     42            SNR,
     43            snrThreshold,
     44            `SNR (${SNR.toFixed(3)} dB) should be ≥ ${snrThreshold} dB`);
     45      }, 'buffer-stitching-1');
     46 
     47      promise_test(async t => {
     48        // Sub‑sample stitching with differing context/buffer rates.
     49        const sampleRate = 44100;
     50        const bufferRate = 43800;
     51        const bufferLength = 30;
     52 
     53        // Experimentally determined thresholds. DO NOT relax these values
     54        // too far from these values to make the tests pass.
     55        const errorThreshold = 3.8986e-3;
     56        const snrThreshold = 65.737;
     57 
     58        assert_equals(sampleRate, 44100, 'Test 2: context.sampleRate');
     59 
     60        const resultBuffer = await testBufferStitching(
     61            sampleRate,
     62            bufferRate,
     63            bufferLength);
     64 
     65        const actual = resultBuffer.getChannelData(0);
     66        const expected = resultBuffer.getChannelData(1);
     67 
     68        assert_array_equal_within_eps(
     69            actual,
     70            expected,
     71            errorThreshold,
     72            `Stitched sine‑wave buffers at sample rate ${bufferRate}`);
     73 
     74        const SNR = 10 * Math.log10(computeSNR(actual, expected));
     75 
     76        assert_greater_than_equal(
     77            SNR,
     78            snrThreshold,
     79            `SNR (${SNR.toFixed(3)} dB) should be ≥ ${snrThreshold} dB`);
     80      }, 'buffer-stitching-2');
     81 
     82      // Create graph to test stitching of consecutive ABSNs.  The context rate
     83      // is |sampleRate|, and the buffers have a fixed length of |bufferLength|
     84      // and rate of |bufferRate|.  The |bufferRate| should not be too different
     85      // from |sampleRate| because of interpolation of the buffer to the context
     86      // rate.
     87      function testBufferStitching(sampleRate, bufferRate, bufferLength) {
     88        // The OfflineAudioContext used for rendering.
     89        // Channel 0 captures the stitched output.
     90        // Channel 1 records the expected reference signal.
     91        const context = new OfflineAudioContext({
     92          numberOfChannels: 2,
     93          length: sampleRate,
     94          sampleRate: sampleRate,
     95        });
     96 
     97        const merger = new ChannelMergerNode(
     98            context, {numberOfInputs: context.destination.channelCount});
     99        merger.connect(context.destination);
    100 
    101        // Reference signal (channel 1): pure 440 Hz sine wave
    102        const ref = new OscillatorNode(context, {frequency: 440, type: 'sine'});
    103        ref.connect(merger, 0, 1);
    104        ref.start();
    105 
    106        // The test signal is a bunch of short AudioBufferSources containing
    107        // bits of a sine wave.
    108        const waveSignal = new Float32Array(context.length);
    109        const omega = 2 * Math.PI / bufferRate * ref.frequency.value;
    110        for (let k = 0; k < context.length; ++k) {
    111          waveSignal[k] = Math.sin(omega * k);
    112        }
    113 
    114        // Slice the sine wave into many little buffers to be assigned to ABSNs
    115        // that are started at the appropriate times to produce a final sine
    116        // wave.
    117        for (let k = 0; k < context.length; k += bufferLength) {
    118          const buffer =
    119              new AudioBuffer({length: bufferLength, sampleRate: bufferRate});
    120 
    121          // Copy the slice into the AudioBuffer’s first (and only) channel.
    122          buffer.copyToChannel(waveSignal.slice(k, k + bufferLength), 0);
    123 
    124          const src = new AudioBufferSourceNode(context, {buffer: buffer});
    125          src.connect(merger, 0, 0);
    126          src.start(k / bufferRate);
    127        }
    128 
    129        return context.startRendering();
    130      }
    131    </script>
    132  </body>
    133 </html>