tor-browser

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

biquad-testing.js (5313B)


      1 // Globals, to make testing and debugging easier.
      2 var context;
      3 var filter;
      4 var signal;
      5 var renderedBuffer;
      6 var renderedData;
      7 
      8 var sampleRate = 44100.0;
      9 var pulseLengthFrames = .1 * sampleRate;
     10 
     11 // Maximum allowed error for the test to succeed.  Experimentally determined.
     12 var maxAllowedError = 5.9e-8;
     13 
     14 // This must be large enough so that the filtered result is
     15 // essentially zero.  See comments for createTestAndRun.
     16 var timeStep = .1;
     17 
     18 // Maximum number of filters we can process (mostly for setting the
     19 // render length correctly.)
     20 var maxFilters = 5;
     21 
     22 // How long to render.  Must be long enough for all of the filters we
     23 // want to test.
     24 var renderLengthSeconds = timeStep * (maxFilters + 1) ;
     25 
     26 var renderLengthSamples = Math.round(renderLengthSeconds * sampleRate);
     27 
     28 // Number of filters that will be processed.
     29 var nFilters;
     30 
     31 function createImpulseBuffer(context, length) {
     32    var impulse = context.createBuffer(1, length, context.sampleRate);
     33    var data = impulse.getChannelData(0);
     34    for (var k = 1; k < data.length; ++k) {
     35        data[k] = 0;
     36    }
     37    data[0] = 1;
     38 
     39    return impulse;
     40 }
     41 
     42 
     43 function createTestAndRun(context, filterType, filterParameters) {
     44    // To test the filters, we apply a signal (an impulse) to each of
     45    // the specified filters, with each signal starting at a different
     46    // time.  The output of the filters is summed together at the
     47    // output.  Thus for filter k, the signal input to the filter
     48    // starts at time k * timeStep.  For this to work well, timeStep
     49    // must be large enough for the output of each filter to have
     50    // decayed to zero with timeStep seconds.  That way the filter
     51    // outputs don't interfere with each other.
     52 
     53    nFilters = Math.min(filterParameters.length, maxFilters);
     54 
     55    signal = new Array(nFilters);
     56    filter = new Array(nFilters);
     57 
     58    impulse = createImpulseBuffer(context, pulseLengthFrames);
     59 
     60    // Create all of the signal sources and filters that we need.
     61    for (var k = 0; k < nFilters; ++k) {
     62        signal[k] = context.createBufferSource();
     63        signal[k].buffer = impulse;
     64 
     65        filter[k] = context.createBiquadFilter();
     66        filter[k].type = filterType;
     67        filter[k].frequency.value = context.sampleRate / 2 * filterParameters[k].cutoff;
     68        filter[k].detune.value = (filterParameters[k].detune === undefined) ? 0 : filterParameters[k].detune;
     69        filter[k].Q.value = filterParameters[k].q;
     70        filter[k].gain.value = filterParameters[k].gain;
     71 
     72        signal[k].connect(filter[k]);
     73        filter[k].connect(context.destination);
     74 
     75        signal[k].start(timeStep * k);
     76    }
     77 
     78    context.oncomplete = checkFilterResponse(filterType, filterParameters);
     79    context.startRendering();
     80 }
     81 
     82 function addSignal(dest, src, destOffset) {
     83    // Add src to dest at the given dest offset.
     84    for (var k = destOffset, j = 0; k < dest.length, j < src.length; ++k, ++j) {
     85        dest[k] += src[j];
     86    }
     87 }
     88 
     89 function generateReference(filterType, filterParameters) {
     90    var result = new Array(renderLengthSamples);
     91    var data = new Array(renderLengthSamples);
     92    // Initialize the result array and data.
     93    for (var k = 0; k < result.length; ++k) {
     94        result[k] = 0;
     95        data[k] = 0;
     96    }
     97    // Make data an impulse.
     98    data[0] = 1;
     99 
    100    for (var k = 0; k < nFilters; ++k) {
    101        // Filter an impulse
    102        var detune = (filterParameters[k].detune === undefined) ? 0 : filterParameters[k].detune;
    103        var frequency = filterParameters[k].cutoff * Math.pow(2, detune / 1200); // Apply detune, converting from Cents.
    104 
    105        var filterCoef = createFilter(filterType,
    106                                      frequency,
    107                                      filterParameters[k].q,
    108                                      filterParameters[k].gain);
    109        var y = filterData(filterCoef, data, renderLengthSamples);
    110 
    111        // Accumulate this filtered data into the final output at the desired offset.
    112        addSignal(result, y, timeToSampleFrame(timeStep * k, sampleRate));
    113    }
    114 
    115    return result;
    116 }
    117 
    118 function checkFilterResponse(filterType, filterParameters) {
    119    return function(event) {
    120        renderedBuffer = event.renderedBuffer;
    121        renderedData = renderedBuffer.getChannelData(0);
    122 
    123        reference = generateReference(filterType, filterParameters);
    124 
    125        var len = Math.min(renderedData.length, reference.length);
    126 
    127        var success = true;
    128 
    129        // Maximum error between rendered data and expected data
    130        var maxError = 0;
    131 
    132        // Sample offset where the maximum error occurred.
    133        var maxPosition = 0;
    134 
    135        // Number of infinities or NaNs that occurred in the rendered data.
    136        var invalidNumberCount = 0;
    137 
    138        ok(nFilters == filterParameters.length, "Test wanted " + filterParameters.length + " filters but only " + maxFilters + " allowed.");
    139 
    140        compareChannels(renderedData, reference, len, 0, 0, true);
    141 
    142        // Check for bad numbers in the rendered output too.
    143        // There shouldn't be any.
    144        for (var k = 0; k < len; ++k) {
    145            if (!isValidNumber(renderedData[k])) {
    146                ++invalidNumberCount;
    147            }
    148        }
    149 
    150        ok(invalidNumberCount == 0, "Rendered output has " + invalidNumberCount + " infinities or NaNs.");
    151        SimpleTest.finish();
    152    }
    153 }