Source/WebCore/ChangeLog

 12012-01-09 Raymond Toy <rtoy@google.com>
 2
 3 Constant values to set "distanceModel" are undefined
 4 https://bugs.webkit.org/show_bug.cgi?id=74273
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Tests: webaudio/distance-exponential.html
 9 webaudio/distance-inverse.html
 10 webaudio/distance-linear.html
 11
 12 * webaudio/AudioPannerNode.h: Define enum for the new constants
 13 for the distance models.
 14 * webaudio/AudioPannerNode.idl: Define matching constants for the
 15 distance models.
 16
1172012-01-09 Rob Buis <rbuis@rim.com>
218
319 Upstream change to disable input[color] css rules for BlackBerry port

Source/WebCore/webaudio/AudioPannerNode.h

@@public:
5353 SOUNDFIELD = 2,
5454 };
5555
 56 // These must be defined as in the .idl file and must match those
 57 // in the DistanceEffect class.
 58 enum {
 59 LINEAR_DISTANCE = 0,
 60 INVERSE_DISTANCE = 1,
 61 EXPONENTIAL_DISTANCE = 2,
 62 };
 63
5664 static PassRefPtr<AudioPannerNode> create(AudioContext* context, float sampleRate)
5765 {
5866 return adoptRef(new AudioPannerNode(context, sampleRate));

Source/WebCore/webaudio/AudioPannerNode.idl

@@module audio {
3333 const unsigned short HRTF = 1;
3434 const unsigned short SOUNDFIELD = 2;
3535
 36 // Distance model
 37 const unsigned short LINEAR_DISTANCE = 0;
 38 const unsigned short INVERSE_DISTANCE = 1;
 39 const unsigned short EXPONENTIAL_DISTANCE = 2;
 40
3641 // Default model for stereo is HRTF
3742 attribute unsigned long panningModel; // FIXME: use unsigned short when glue generation supports it
3843

LayoutTests/ChangeLog

 12012-01-09 Raymond Toy <rtoy@google.com>
 2
 3 Constant values to set "distanceModel" are undefined
 4 https://bugs.webkit.org/show_bug.cgi?id=74273
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * webaudio/distance-exponential-expected.txt: Added.
 9 * webaudio/distance-exponential.html: Added.
 10 * webaudio/distance-inverse-expected.txt: Added.
 11 * webaudio/distance-inverse.html: Added.
 12 * webaudio/distance-linear-expected.txt: Added.
 13 * webaudio/distance-linear.html: Added.
 14 * webaudio/resources/distance-model-testing.js: Added.
 15 (createImpulseBuffer):
 16 (linearDistance):
 17 (exponentialDistance):
 18 (inverseDistance):
 19 (createGraph):
 20 (startSources):
 21 (equalPowerGain):
 22 (checkDistanceResult.return.renderedBuffer):
 23 (checkDistanceResult):
 24
1252012-01-09 Dmitry Titov <dimich@chromium.org>
226
327 [Chromium] Unreviewed update for test_expectations.txt.

LayoutTests/webaudio/distance-exponential-expected.txt

 1Test exponential distance model of AudioPannerNode.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5PASS Distance model value matched expected value.
 6PASS Number of nodes is correct.
 7PASS Distance gains are correct.
 8PASS Distance test passed for distance model 2
 9PASS successfullyParsed is true
 10
 11TEST COMPLETE
 12

LayoutTests/webaudio/distance-exponential.html

 1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 2<html>
 3 <head>
 4 <link rel="stylesheet" href="../fast/js/resources/js-test-style.css"/>
 5 <script src="resources/audio-testing.js"></script>
 6 <script src="../fast/js/resources/js-test-pre.js"></script>
 7 <script src="resources/distance-model-testing.js"></script>
 8 </head>
 9
 10 <body>
 11 <div id="description"></div>
 12 <div id="console"></div>
 13
 14 <script>
 15 description("Test exponential distance model of AudioPannerNode.");
 16
 17 function runTest() {
 18 if (window.layoutTestController) {
 19 layoutTestController.dumpAsText();
 20 layoutTestController.waitUntilDone();
 21 }
 22
 23 window.jsTestIsAsync = true;
 24
 25 // Create offline audio context.
 26 context = new webkitAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
 27
 28 // Threshold experimentally determined.
 29 var threshold = 1.3e-7;
 30
 31 // Temp panner node so we can get the EXPONENTIAL_DISTANCE value.
 32 var tempPanner = context.createPanner();
 33 createTestAndRun(context, tempPanner.EXPONENTIAL_DISTANCE, 2, threshold);
 34 }
 35
 36 runTest();
 37 successfullyParsed = true;
 38
 39 </script>
 40
 41<script src="../fast/js/resources/js-test-post.js"></script>
 42 </body>
 43</html>

LayoutTests/webaudio/distance-inverse-expected.txt

 1Test inverse distance model of AudioPannerNode.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5PASS Distance model value matched expected value.
 6PASS Number of nodes is correct.
 7PASS Distance gains are correct.
 8PASS Distance test passed for distance model 1
 9PASS successfullyParsed is true
 10
 11TEST COMPLETE
 12

LayoutTests/webaudio/distance-inverse.html

 1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 2<html>
 3 <head>
 4 <link rel="stylesheet" href="../fast/js/resources/js-test-style.css"/>
 5 <script src="resources/audio-testing.js"></script>
 6 <script src="../fast/js/resources/js-test-pre.js"></script>
 7 <script src="resources/distance-model-testing.js"></script>
 8 </head>
 9
 10 <body>
 11 <div id="description"></div>
 12 <div id="console"></div>
 13
 14 <script>
 15 description("Test inverse distance model of AudioPannerNode.");
 16
 17 function runTest() {
 18 if (window.layoutTestController) {
 19 layoutTestController.dumpAsText();
 20 layoutTestController.waitUntilDone();
 21 }
 22
 23 window.jsTestIsAsync = true;
 24
 25 // Create offline audio context.
 26 context = new webkitAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
 27
 28 // Threshold experimentally determined.
 29 var threshold = 1.3e-7;
 30
 31 // Temp panner node so we can get the INVERSE_DISTANCE value.
 32 var tempPanner = context.createPanner();
 33 createTestAndRun(context, tempPanner.INVERSE_DISTANCE, 1, threshold);
 34 }
 35
 36 runTest();
 37 successfullyParsed = true;
 38
 39 </script>
 40
 41<script src="../fast/js/resources/js-test-post.js"></script>
 42 </body>
 43</html>

LayoutTests/webaudio/distance-linear-expected.txt

 1Test linear distance model of AudioPannerNode.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5PASS Distance model value matched expected value.
 6PASS Number of nodes is correct.
 7PASS Distance gains are correct.
 8PASS Distance test passed for distance model 0
 9PASS successfullyParsed is true
 10
 11TEST COMPLETE
 12

LayoutTests/webaudio/distance-linear.html

 1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 2<html>
 3 <head>
 4 <link rel="stylesheet" href="../fast/js/resources/js-test-style.css"/>
 5 <script src="resources/audio-testing.js"></script>
 6 <script src="../fast/js/resources/js-test-pre.js"></script>
 7 <script src="resources/distance-model-testing.js"></script>
 8 </head>
 9
 10 <body>
 11 <div id="description"></div>
 12 <div id="console"></div>
 13
 14 <script>
 15 description("Test linear distance model of AudioPannerNode.");
 16
 17 function runTest() {
 18 if (window.layoutTestController) {
 19 layoutTestController.dumpAsText();
 20 layoutTestController.waitUntilDone();
 21 }
 22
 23 window.jsTestIsAsync = true;
 24
 25 // Threshold experimentally determined.
 26 var threshold = 2.3e-6;
 27 // Create offline audio context.
 28 context = new webkitAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
 29 var pannerModel = context.createPanner();
 30 createTestAndRun(context, pannerModel.LINEAR_DISTANCE, 0, threshold);
 31 }
 32
 33 runTest();
 34 successfullyParsed = true;
 35
 36 </script>
 37
 38<script src="../fast/js/resources/js-test-post.js"></script>
 39 </body>
 40</html>

LayoutTests/webaudio/resources/distance-model-testing.js

 1var sampleRate = 44100.0;
 2
 3var renderLengthSeconds = 8;
 4var pulseLengthSeconds = 1;
 5var pulseLengthFrames = pulseLengthSeconds * sampleRate;
 6
 7// How many panner nodes to create for the test
 8var nodesToCreate = 100;
 9
 10// Time step when each panner node starts.
 11var timeStep = 0.05;
 12
 13// Globals to make debugging a little easier.
 14var context;
 15var impulse;
 16var bufferSource;
 17var panner;
 18var position;
 19var time;
 20
 21function createImpulseBuffer(context, sampleFrameLength) {
 22 var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
 23 var n = audioBuffer.length;
 24 var data = audioBuffer.getChannelData(0);
 25
 26 for (var k = 0; k < n; ++k) {
 27 data[k] = 0;
 28 }
 29 data[0] = 1;
 30
 31 return audioBuffer;
 32}
 33
 34// For the record, these distance formulas were taken from the OpenAL
 35// spec, not the code.
 36function linearDistance(panner, x, y, z) {
 37 var distance = Math.sqrt(x * x + y * y + z * z);
 38 distance = Math.min(distance, panner.maxDistance);
 39 var rolloff = panner.rolloffFactor;
 40 var gain = (1 - rolloff * (distance - panner.refDistance) / (panner.maxDistance - panner.refDistance));
 41
 42 return gain;
 43}
 44
 45function exponentialDistance(panner, x, y, z) {
 46 var distance = Math.sqrt(x * x + y * y + z * z);
 47 distance = Math.min(distance, panner.maxDistance);
 48 var rolloff = panner.rolloffFactor;
 49 var gain = Math.pow(distance / panner.refDistance, -rolloff);
 50
 51 return gain;
 52}
 53
 54function inverseDistance(panner, x, y, z) {
 55 var distance = Math.sqrt(x * x + y * y + z * z);
 56 distance = Math.min(distance, panner.maxDistance);
 57 var rolloff = panner.rolloffFactor;
 58 var gain = panner.refDistance / (panner.refDistance + rolloff * (distance - panner.refDistance));
 59
 60 return gain;
 61}
 62
 63// This array must be arranged in the numeric order of the distance
 64// model values.
 65var distanceModelFunction = [linearDistance, inverseDistance, exponentialDistance];
 66
 67function createGraph(context, distanceModel, nodeCount) {
 68 // Create all the sources, which are impulses.
 69 bufferSource = new Array(nodeCount);
 70 for (var k = 0; k < nodeCount; ++k) {
 71 bufferSource[k] = context.createBufferSource();
 72 impulse = createImpulseBuffer(context, pulseLengthFrames);
 73 bufferSource[k].buffer = impulse;
 74 }
 75
 76 // Create all the panners.
 77 //
 78 // We MUST use the EQUALPOWER panning model so that we can easily
 79 // figure out the gain introduced by the panner.
 80 //
 81 // We want to stay in the middle of the panning range, which means
 82 // we want to stay on the z-axis. If we don't, then the effect of
 83 // panning model will be much more complicated. We're not testing
 84 // the panner, but the distance model, so we want the panner effect
 85 // to be simple.
 86 panner = new Array(nodeCount);
 87 position = new Array(nodeCount);
 88 for (var k = 0; k < nodeCount; ++k) {
 89 panner[k] = context.createPanner();
 90 panner[k].panningModel = panner.EQUALPOWER;
 91 panner[k].distanceModel = distanceModel;
 92 // The distance is uniform spacing between the max and ref
 93 // distance.
 94 var distanceStep = (panner[k].maxDistance - panner[k].refDistance) / nodeCount;
 95 position[k] = distanceStep * k + panner[k].refDistance;
 96 panner[k].setPosition(0, 0, position[k]);
 97 }
 98
 99 // Connect the sources and panners.
 100 for (var k = 0; k < nodeCount; ++k) {
 101 bufferSource[k].connect(panner[k]);
 102 panner[k].connect(context.destination);
 103 }
 104}
 105
 106function startSources() {
 107 // Turn on each source at uniform intervals.
 108 time = new Array(nodesToCreate);
 109 for (var k = 0; k < nodesToCreate; ++k) {
 110 time[k] = k * timeStep;
 111 bufferSource[k].noteOn(time[k]);
 112 }
 113}
 114
 115// distanceModel should be the distance model constant like
 116// LINEAR_DISTANCE, INVERSE_DISTANCE, and EXPONENTIAL_DISTANCE. The
 117// expectedModel is the expected actual numeric value of the constant.
 118function createTestAndRun(context, distanceModel, expectedModel, threshold) {
 119 createGraph(context, distanceModel, nodesToCreate);
 120 startSources();
 121
 122 context.oncomplete = checkDistanceResult(distanceModel, expectedModel, threshold, false);
 123 context.startRendering();
 124}
 125
 126// The gain caused by the EQUALPOWER panning model, if we stay on the
 127// z axis, with the default orientations.
 128function equalPowerGain() {
 129 return Math.SQRT1_2;
 130}
 131
 132function checkDistanceResult(model, expectedModel, threshold, debug) {
 133 return function(event) {
 134 renderedBuffer = event.renderedBuffer;
 135 renderedData = renderedBuffer.getChannelData(0);
 136
 137 var success = true;
 138
 139 if (model == expectedModel) {
 140 testPassed("Distance model value matched expected value.");
 141 } else {
 142 testPassed("Distance model value matched expected value.");
 143 success = false;
 144 }
 145
 146 // Number of impulses we found in the rendered result.
 147 var impulseCount = 0;
 148 // Maximum relative error in the gain of the impulses.
 149 var maxError = 0;
 150 // Number of impulses that were not at the expected location.
 151 // (For debugging.)
 152 var timeErrorCount = 0;
 153 // Array of locations of the impulses that were not at the
 154 // expected location. (For debugging.)
 155 var timeErrors = new Array();
 156
 157 // Step through the rendered data to find all the non-zero points
 158 // so we can find where our impulses are.
 159 for (var k = 0; k < renderedData.length; ++k) {
 160 if (renderedData[k] != 0) {
 161 var distanceFunction = distanceModelFunction[panner[impulseCount].distanceModel];
 162 var expected = distanceFunction(panner[impulseCount], 0, 0, position[impulseCount]);
 163 expected *= equalPowerGain();
 164
 165 var error = Math.abs(renderedData[k] - expected)/Math.abs(expected);
 166 if (debug) {
 167 console.log(k + ": " + renderedData[k] + " (expected " + expected + ", rel = ", error + ")");
 168 }
 169 maxError = Math.max(maxError, Math.abs(error));
 170 if (k != Math.round(sampleRate * time[impulseCount])) {
 171 timeErrors[timeErrorCount] = { actual : k, expected : Math.round(sampleRate * time[impulseCount])};
 172 ++timeErrorCount;
 173 }
 174 ++impulseCount;
 175 }
 176 }
 177
 178 if (impulseCount == nodesToCreate) {
 179 testPassed("Number of nodes is correct.");
 180 } else {
 181 testFailed("Number of nodes is incorrect. (Found " + impulseCount + " but expected " + nodesToCreate + ")");
 182 success = false;
 183 }
 184
 185 if (maxError <= threshold) {
 186 testPassed("Distance gains are correct.");
 187 } else {
 188 testFailed("Distance gains are incorrect. Max rel error = " + maxError + " (threshold = " + threshold + ")");
 189 success = false;
 190 }
 191
 192 // For debugging. It seems some impulses don't show up at exactly
 193 // the expected rendered location. Log these to the console if
 194 // debugging is true.
 195 //
 196 // FIXME: File a bug about this.
 197 if (debug && timeErrors.length > 0) {
 198 console.log(timeErrors.length + " timing errors found");
 199 for (var k = 0; k < timeErrors.length; ++k) {
 200 console.log("Sample " + timeErrors[k].actual + " but expected " + timeErrors[k].expected);
 201 }
 202 }
 203
 204 if (success) {
 205 testPassed("Distance test passed for distance model " + model);
 206 } else {
 207 testFailed("Distance test failed for distance model " + model);
 208 }
 209
 210 finishJSTest();
 211 }
 212}