tor-browser

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

waveshaper.html (4358B)


      1 <!DOCTYPE html>
      2 <html>
      3  <head>
      4    <title>
      5      waveshaper.html
      6    </title>
      7    <script src="/resources/testharness.js"></script>
      8    <script src="/resources/testharnessreport.js"></script>
      9  </head>
     10  <body>
     11    <script>
     12 
     13      const sampleRate = 44100;
     14      const lengthInSeconds = 4;
     15      const numberOfRenderFrames = sampleRate * lengthInSeconds;
     16      const numberOfCurveFrames = 65536;
     17 
     18      let context;
     19      let inputBuffer;
     20      let waveShapingCurve;
     21 
     22      function generateInputBuffer() {
     23        // Create mono input buffer.
     24        const buffer = new AudioBuffer({
     25          length: numberOfRenderFrames,
     26          numberOfChannels: 1,
     27          sampleRate: context.sampleRate
     28        });
     29        const data = buffer.getChannelData(0);
     30 
     31        // Generate an input vector with values from -1 -> +1 over a duration of
     32        // lengthInSeconds. This exercises the full nominal input range and will
     33        // touch every point of the shaping curve.
     34        for (let i = 0; i < numberOfRenderFrames; ++i) {
     35          let x = i / numberOfRenderFrames;
     36          x = 2 * x - 1;
     37          data[i] = x;
     38        }
     39 
     40        return buffer;
     41      }
     42 
     43      // Generates a symmetric curve: Math.atan(5 * x) / (0.5 * Math.PI)
     44      // (with x == 0 corresponding to the center of the array)
     45      // This curve is arbitrary, but would be useful in the real-world.
     46      // To some extent, the actual curve we choose is not important in this
     47      // test, since the input vector walks through all possible curve values.
     48      function generateWaveShapingCurve() {
     49        const curve = new Float32Array(numberOfCurveFrames);
     50        const n = numberOfCurveFrames;
     51        const n2 = n / 2;
     52 
     53        for (let i = 0; i < n; ++i) {
     54          const x = (i - n2) / n2;
     55          curve[i] = Math.atan(5 * x) / (0.5 * Math.PI);
     56        }
     57        return curve;
     58      }
     59      // Following spec algorithm from
     60      // https://webaudio.github.io/web-audio-api/#WaveShaperNode-attributes
     61      function checkShapedCurve(buffer) {
     62        const inputData = inputBuffer.getChannelData(0);
     63        const outputData = buffer.getChannelData(0);
     64        const c = waveShapingCurve;
     65        const N = numberOfCurveFrames;
     66        const tolerance = 1e-6;
     67 
     68        let firstMismatchIndex = -1;
     69        let actual = 0;
     70        let expected = 0;
     71 
     72        // Go through every sample and make sure it has been shaped exactly
     73        // according to the shaping curve we gave it.
     74        for (let i = 0; i < buffer.length; ++i) {
     75          const x = inputData[i];
     76 
     77          const v = ((N - 1) * 0.5) * (x + 1);
     78          const k = Math.floor(v);
     79          const f = v - k;
     80 
     81          let y;
     82          if (v < 0) {
     83            y = c[0];
     84          } else if (v >= N - 1) {
     85            y = c[N - 1];
     86          } else {
     87            y = (1 - f) * c[k] + f * c[k + 1];
     88          }
     89 
     90          if (Math.abs(outputData[i] - y) > tolerance) {
     91            firstMismatchIndex = i;
     92            actual = outputData[i];
     93            expected = y;
     94            break;
     95          }
     96        }
     97 
     98        assert_equals(
     99            firstMismatchIndex,
    100            -1,
    101            firstMismatchIndex === -1
    102                ? 'WaveShaperNode output should match the spec.'
    103                : `Mismatch at sample ${firstMismatchIndex}: ` +
    104                    `actual = ${actual}, expected = ${expected}, ` +
    105                    `tolerance = ${tolerance}`);
    106      }
    107 
    108      promise_test(async t => {
    109        // Create offline audio context.
    110        context = new OfflineAudioContext(1, numberOfRenderFrames, sampleRate);
    111 
    112        // source -> waveshaper -> destination
    113        const source = new AudioBufferSourceNode(context);
    114        const waveshaper = new WaveShaperNode(context);
    115        source.connect(waveshaper);
    116        waveshaper.connect(context.destination);
    117 
    118        // Create an input test vector.
    119        inputBuffer = generateInputBuffer();
    120        source.buffer = inputBuffer;
    121 
    122        // We'll apply non-linear distortion according to this shaping curve.
    123        waveShapingCurve = generateWaveShapingCurve();
    124        waveshaper.curve = waveShapingCurve;
    125 
    126        source.start();
    127 
    128        const renderedBuffer = await context.startRendering();
    129        checkShapedCurve(renderedBuffer);
    130      }, 'WaveShaperNode applies non-linear distortion correctly');
    131    </script>
    132  </body>
    133 </html>