content/media/webaudio/test/blink/panner-model-testing.js

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

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

mercurial