tor-browser

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

convolution-testing.js (4990B)


      1 let sampleRate = 44100.0;
      2 
      3 let renderLengthSeconds = 8;
      4 let pulseLengthSeconds = 1;
      5 let pulseLengthFrames = pulseLengthSeconds * sampleRate;
      6 
      7 function createSquarePulseBuffer(context, sampleFrameLength) {
      8  let audioBuffer =
      9      context.createBuffer(1, sampleFrameLength, context.sampleRate);
     10 
     11  let n = audioBuffer.length;
     12  let data = audioBuffer.getChannelData(0);
     13 
     14  for (let i = 0; i < n; ++i)
     15    data[i] = 1;
     16 
     17  return audioBuffer;
     18 }
     19 
     20 // The triangle buffer holds the expected result of the convolution.
     21 // It linearly ramps up from 0 to its maximum value (at the center)
     22 // then linearly ramps down to 0.  The center value corresponds to the
     23 // point where the two square pulses overlap the most.
     24 function createTrianglePulseBuffer(context, sampleFrameLength) {
     25  let audioBuffer =
     26      context.createBuffer(1, sampleFrameLength, context.sampleRate);
     27 
     28  let n = audioBuffer.length;
     29  let halfLength = n / 2;
     30  let data = audioBuffer.getChannelData(0);
     31 
     32  for (let i = 0; i < halfLength; ++i)
     33    data[i] = i + 1;
     34 
     35  for (let i = halfLength; i < n; ++i)
     36    data[i] = n - i - 1;
     37 
     38  return audioBuffer;
     39 }
     40 
     41 function log10(x) {
     42  return Math.log(x) / Math.LN10;
     43 }
     44 
     45 function linearToDecibel(x) {
     46  return 20 * log10(x);
     47 }
     48 
     49 // Verify that the rendered result is very close to the reference
     50 // triangular pulse.
     51 function checkTriangularPulse(rendered, reference, should) {
     52  let match = true;
     53  let maxDelta = 0;
     54  let valueAtMaxDelta = 0;
     55  let maxDeltaIndex = 0;
     56 
     57  for (let i = 0; i < reference.length; ++i) {
     58    let diff = rendered[i] - reference[i];
     59    let x = Math.abs(diff);
     60    if (x > maxDelta) {
     61      maxDelta = x;
     62      valueAtMaxDelta = reference[i];
     63      maxDeltaIndex = i;
     64    }
     65  }
     66 
     67  // allowedDeviationFraction was determined experimentally.  It
     68  // is the threshold of the relative error at the maximum
     69  // difference between the true triangular pulse and the
     70  // rendered pulse.
     71  let allowedDeviationDecibels = -124.41;
     72  let maxDeviationDecibels = linearToDecibel(maxDelta / valueAtMaxDelta);
     73 
     74  should(
     75      maxDeviationDecibels,
     76      'Deviation (in dB) of triangular portion of convolution')
     77      .beLessThanOrEqualTo(allowedDeviationDecibels);
     78 
     79  return match;
     80 }
     81 
     82 // Verify that the rendered data is close to zero for the first part
     83 // of the tail.
     84 function checkTail1(data, reference, breakpoint, should) {
     85  let isZero = true;
     86  let tail1Max = 0;
     87 
     88  for (let i = reference.length; i < reference.length + breakpoint; ++i) {
     89    let mag = Math.abs(data[i]);
     90    if (mag > tail1Max) {
     91      tail1Max = mag;
     92    }
     93  }
     94 
     95  // Let's find the peak of the reference (even though we know a
     96  // priori what it is).
     97  let refMax = 0;
     98  for (let i = 0; i < reference.length; ++i) {
     99    refMax = Math.max(refMax, Math.abs(reference[i]));
    100  }
    101 
    102  // This threshold is experimentally determined by examining the
    103  // value of tail1MaxDecibels.
    104  let threshold1 = -129.7;
    105 
    106  let tail1MaxDecibels = linearToDecibel(tail1Max / refMax);
    107  should(tail1MaxDecibels, 'Deviation in first part of tail of convolutions')
    108      .beLessThanOrEqualTo(threshold1);
    109 
    110  return isZero;
    111 }
    112 
    113 // Verify that the second part of the tail of the convolution is
    114 // exactly zero.
    115 function checkTail2(data, reference, breakpoint, should) {
    116  let isZero = true;
    117  let tail2Max = 0;
    118  // For the second part of the tail, the maximum value should be
    119  // exactly zero.
    120  let threshold2 = 0;
    121  for (let i = reference.length + breakpoint; i < data.length; ++i) {
    122    if (Math.abs(data[i]) > 0) {
    123      isZero = false;
    124      break;
    125    }
    126  }
    127 
    128  should(isZero, 'Rendered signal after tail of convolution is silent')
    129      .beTrue();
    130 
    131  return isZero;
    132 }
    133 
    134 function checkConvolvedResult(renderedBuffer, trianglePulse, should) {
    135  let referenceData = trianglePulse.getChannelData(0);
    136  let renderedData = renderedBuffer.getChannelData(0);
    137 
    138  let success = true;
    139 
    140  // Verify the triangular pulse is actually triangular.
    141 
    142  success =
    143      success && checkTriangularPulse(renderedData, referenceData, should);
    144 
    145  // Make sure that portion after convolved portion is totally
    146  // silent.  But round-off prevents this from being completely
    147  // true.  At the end of the triangle, it should be close to
    148  // zero.  If we go farther out, it should be even closer and
    149  // eventually zero.
    150 
    151  // For the tail of the convolution (where the result would be
    152  // theoretically zero), we partition the tail into two
    153  // parts.  The first is the at the beginning of the tail,
    154  // where we tolerate a small but non-zero value.  The second part is
    155  // farther along the tail where the result should be zero.
    156 
    157  // breakpoint is the point dividing the first two tail parts
    158  // we're looking at.  Experimentally determined.
    159  let breakpoint = 12800;
    160 
    161  success =
    162      success && checkTail1(renderedData, referenceData, breakpoint, should);
    163 
    164  success =
    165      success && checkTail2(renderedData, referenceData, breakpoint, should);
    166 
    167  should(success, 'Test signal convolved').message('correctly', 'incorrectly');
    168 }