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