tor-browser

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

panner-model-testing.js (6928B)


      1 var sampleRate = 48000.0;
      2 
      3 var numberOfChannels = 1;
      4 
      5 // Time step when each panner node starts.
      6 var timeStep = 0.001;
      7 
      8 // Length of the impulse signal.
      9 var pulseLengthFrames = Math.round(timeStep * sampleRate);
     10 
     11 // How many panner nodes to create for the test
     12 var nodesToCreate = 100;
     13 
     14 // Be sure we render long enough for all of our nodes.
     15 var renderLengthSeconds = timeStep * (nodesToCreate + 1);
     16 
     17 // These are global mostly for debugging.
     18 var context;
     19 var impulse;
     20 var bufferSource;
     21 var panner;
     22 var position;
     23 var time;
     24      
     25 var renderedBuffer;
     26 var renderedLeft;
     27 var renderedRight;
     28 
     29 function createGraph(context, nodeCount) {
     30    bufferSource = new Array(nodeCount);
     31    panner = new Array(nodeCount);
     32    position = new Array(nodeCount);
     33    time = new Array(nodeCount);
     34    // Angle between panner locations.  (nodeCount - 1 because we want
     35    // to include both 0 and 180 deg.
     36    var angleStep = Math.PI / (nodeCount - 1);
     37 
     38    if (numberOfChannels == 2) {
     39        impulse = createStereoImpulseBuffer(context, pulseLengthFrames);
     40    }
     41    else
     42        impulse = createImpulseBuffer(context, pulseLengthFrames);
     43 
     44    for (var k = 0; k < nodeCount; ++k) {
     45        bufferSource[k] = context.createBufferSource();
     46        bufferSource[k].buffer = impulse;
     47 
     48        panner[k] = context.createPanner();
     49        panner[k].panningModel = "equalpower";
     50        panner[k].distanceModel = "linear";
     51 
     52        var angle = angleStep * k;
     53        position[k] = {angle : angle, x : Math.cos(angle), z : Math.sin(angle)};
     54        panner[k].positionX.value = position[k].x;
     55        panner[k].positionZ.value = position[k].z;
     56 
     57        bufferSource[k].connect(panner[k]);
     58        panner[k].connect(context.destination);
     59 
     60        // Start the source
     61        time[k] = k * timeStep;
     62        bufferSource[k].start(time[k]);
     63    }
     64 }
     65 
     66 function createTestAndRun(context, nodeCount, numberOfSourceChannels) {
     67    numberOfChannels = numberOfSourceChannels;
     68 
     69    createGraph(context, nodeCount);
     70 
     71    context.oncomplete = checkResult;
     72    context.startRendering();
     73 }
     74 
     75 // Map our position angle to the azimuth angle (in degrees).
     76 //
     77 // An angle of 0 corresponds to an azimuth of 90 deg; pi, to -90 deg.
     78 function angleToAzimuth(angle) {
     79    return 90 - angle * 180 / Math.PI;
     80 }
     81 
     82 // The gain caused by the EQUALPOWER panning model
     83 function equalPowerGain(angle) {
     84    var azimuth = angleToAzimuth(angle);
     85 
     86    if (numberOfChannels == 1) {
     87        var panPosition = (azimuth + 90) / 180;
     88 
     89        var gainL = Math.cos(0.5 * Math.PI * panPosition);
     90        var gainR = Math.sin(0.5 * Math.PI * panPosition);
     91 
     92        return { left : gainL, right : gainR };
     93    } else {
     94        if (azimuth <= 0) {
     95            var panPosition = (azimuth + 90) / 90;
     96    
     97            var gainL = 1 + Math.cos(0.5 * Math.PI * panPosition);
     98            var gainR = Math.sin(0.5 * Math.PI * panPosition);
     99    
    100            return { left : gainL, right : gainR };
    101        } else {
    102            var panPosition = azimuth / 90;
    103    
    104            var gainL = Math.cos(0.5 * Math.PI * panPosition);
    105            var gainR = 1 + Math.sin(0.5 * Math.PI * panPosition);
    106    
    107            return { left : gainL, right : gainR };
    108        }
    109    }
    110 }
    111 
    112 function checkResult(event) {
    113    renderedBuffer = event.renderedBuffer;
    114    renderedLeft = renderedBuffer.getChannelData(0);
    115    renderedRight = renderedBuffer.getChannelData(1);
    116 
    117    // The max error we allow between the rendered impulse and the
    118    // expected value.  This value is experimentally determined.  Set
    119    // to 0 to make the test fail to see what the actual error is.
    120    var maxAllowedError = 1.3e-6;
    121  
    122    var success = true;
    123 
    124    // Number of impulses found in the rendered result.
    125    var impulseCount = 0;
    126 
    127    // Max (relative) error and the index of the maxima for the left
    128    // and right channels.
    129    var maxErrorL = 0;
    130    var maxErrorIndexL = 0;
    131    var maxErrorR = 0;
    132    var maxErrorIndexR = 0;
    133 
    134    // Number of impulses that don't match our expected locations.
    135    var timeCount = 0;
    136 
    137    // Locations of where the impulses aren't at the expected locations.
    138    var timeErrors = new Array();
    139 
    140    for (var k = 0; k < renderedLeft.length; ++k) {
    141        // We assume that the left and right channels start at the same instant.
    142        if (renderedLeft[k] != 0 || renderedRight[k] != 0) {
    143            // The expected gain for the left and right channels.
    144            var pannerGain = equalPowerGain(position[impulseCount].angle);
    145            var expectedL = pannerGain.left;
    146            var expectedR = pannerGain.right;
    147 
    148            // Absolute error in the gain.
    149            var errorL = Math.abs(renderedLeft[k] - expectedL);
    150            var errorR = Math.abs(renderedRight[k] - expectedR);
    151 
    152            if (Math.abs(errorL) > maxErrorL) {
    153                maxErrorL = Math.abs(errorL);
    154                maxErrorIndexL = impulseCount;
    155            }
    156            if (Math.abs(errorR) > maxErrorR) {
    157                maxErrorR = Math.abs(errorR);
    158                maxErrorIndexR = impulseCount;
    159            }
    160 
    161            // Keep track of the impulses that didn't show up where we
    162            // expected them to be.
    163            var expectedOffset = timeToSampleFrame(time[impulseCount], sampleRate);
    164            if (k != expectedOffset) {
    165                timeErrors[timeCount] = { actual : k, expected : expectedOffset};
    166                ++timeCount;
    167            }
    168            ++impulseCount;
    169        }
    170    }
    171 
    172    if (impulseCount == nodesToCreate) {
    173        testPassed("Number of impulses matches the number of panner nodes.");
    174    } else {
    175        testFailed("Number of impulses is incorrect.  (Found " + impulseCount + " but expected " + nodesToCreate + ")");
    176        success = false;
    177    }
    178 
    179    if (timeErrors.length > 0) {
    180        success = false;
    181        testFailed(timeErrors.length + " timing errors found in " + nodesToCreate + " panner nodes.");
    182        for (var k = 0; k < timeErrors.length; ++k) {
    183            testFailed("Impulse at sample " + timeErrors[k].actual + " but expected " + timeErrors[k].expected);
    184        }
    185    } else {
    186        testPassed("All impulses at expected offsets.");
    187    }
    188 
    189    if (maxErrorL <= maxAllowedError) {
    190        testPassed("Left channel gain values are correct.");
    191    } else {
    192        testFailed("Left channel gain values are incorrect.  Max error = " + maxErrorL + " at time " + time[maxErrorIndexL] + " (threshold = " + maxAllowedError + ")");
    193        success = false;
    194    }
    195 
    196    if (maxErrorR <= maxAllowedError) {
    197        testPassed("Right channel gain values are correct.");
    198    } else {
    199        testFailed("Right channel gain values are incorrect.  Max error = " + maxErrorR + " at time " + time[maxErrorIndexR] + " (threshold = " + maxAllowedError + ")");
    200        success = false;
    201    }
    202 
    203    if (success) {
    204        testPassed("EqualPower panner test passed");
    205    } else {
    206        testFailed("EqualPower panner test failed");
    207    }
    208 
    209    finishJSTest();
    210 }