Diff
Modified: trunk/LayoutTests/ChangeLog (106579 => 106580)
--- trunk/LayoutTests/ChangeLog 2012-02-02 20:29:20 UTC (rev 106579)
+++ trunk/LayoutTests/ChangeLog 2012-02-02 20:46:43 UTC (rev 106580)
@@ -1,5 +1,29 @@
2012-02-02 Raymond Toy <[email protected]>
+ Constant values to set "distanceModel" are undefined
+ https://bugs.webkit.org/show_bug.cgi?id=74273
+
+ Reviewed by Kenneth Russell.
+
+ * webaudio/distance-exponential-expected.txt: Added.
+ * webaudio/distance-exponential.html: Added.
+ * webaudio/distance-inverse-expected.txt: Added.
+ * webaudio/distance-inverse.html: Added.
+ * webaudio/distance-linear-expected.txt: Added.
+ * webaudio/distance-linear.html: Added.
+ * webaudio/resources/distance-model-testing.js: Added.
+ (createImpulseBuffer):
+ (linearDistance):
+ (exponentialDistance):
+ (inverseDistance):
+ (createGraph):
+ (startSources):
+ (equalPowerGain):
+ (checkDistanceResult.return.renderedBuffer):
+ (checkDistanceResult):
+
+2012-02-02 Raymond Toy <[email protected]>
+
Illegal panner model values should throw an exception
https://bugs.webkit.org/show_bug.cgi?id=77235
Added: trunk/LayoutTests/webaudio/distance-exponential-expected.txt (0 => 106580)
--- trunk/LayoutTests/webaudio/distance-exponential-expected.txt (rev 0)
+++ trunk/LayoutTests/webaudio/distance-exponential-expected.txt 2012-02-02 20:46:43 UTC (rev 106580)
@@ -0,0 +1,12 @@
+Test exponential distance model of AudioPannerNode.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS Distance model value matched expected value.
+PASS Number of impulses found matches number of panner nodes.
+PASS Distance gains are correct.
+PASS Distance test passed for distance model 2
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/webaudio/distance-exponential.html (0 => 106580)
--- trunk/LayoutTests/webaudio/distance-exponential.html (rev 0)
+++ trunk/LayoutTests/webaudio/distance-exponential.html 2012-02-02 20:46:43 UTC (rev 106580)
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <link rel="stylesheet" href=""
+ <script src=""
+ <script src=""
+ <script src=""
+ </head>
+
+ <body>
+ <div id="description"></div>
+ <div id="console"></div>
+
+ <script>
+ description("Test exponential distance model of AudioPannerNode.");
+
+ function runTest() {
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ window.jsTestIsAsync = true;
+
+ // Create offline audio context.
+ context = new webkitAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
+
+ // Temp panner node so we can get the EXPONENTIAL_DISTANCE value.
+ var tempPanner = context.createPanner();
+ createTestAndRun(context, tempPanner.EXPONENTIAL_DISTANCE, 2);
+ }
+
+ runTest();
+ successfullyParsed = true;
+
+ </script>
+
+<script src=""
+ </body>
+</html>
Added: trunk/LayoutTests/webaudio/distance-inverse-expected.txt (0 => 106580)
--- trunk/LayoutTests/webaudio/distance-inverse-expected.txt (rev 0)
+++ trunk/LayoutTests/webaudio/distance-inverse-expected.txt 2012-02-02 20:46:43 UTC (rev 106580)
@@ -0,0 +1,12 @@
+Test inverse distance model of AudioPannerNode.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS Distance model value matched expected value.
+PASS Number of impulses found matches number of panner nodes.
+PASS Distance gains are correct.
+PASS Distance test passed for distance model 1
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/webaudio/distance-inverse.html (0 => 106580)
--- trunk/LayoutTests/webaudio/distance-inverse.html (rev 0)
+++ trunk/LayoutTests/webaudio/distance-inverse.html 2012-02-02 20:46:43 UTC (rev 106580)
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <link rel="stylesheet" href=""
+ <script src=""
+ <script src=""
+ <script src=""
+ </head>
+
+ <body>
+ <div id="description"></div>
+ <div id="console"></div>
+
+ <script>
+ description("Test inverse distance model of AudioPannerNode.");
+
+ function runTest() {
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ window.jsTestIsAsync = true;
+
+ // Create offline audio context.
+ context = new webkitAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
+
+ // Temp panner node so we can get the INVERSE_DISTANCE value.
+ var tempPanner = context.createPanner();
+ createTestAndRun(context, tempPanner.INVERSE_DISTANCE, 1);
+ }
+
+ runTest();
+ successfullyParsed = true;
+
+ </script>
+
+<script src=""
+ </body>
+</html>
Added: trunk/LayoutTests/webaudio/distance-linear-expected.txt (0 => 106580)
--- trunk/LayoutTests/webaudio/distance-linear-expected.txt (rev 0)
+++ trunk/LayoutTests/webaudio/distance-linear-expected.txt 2012-02-02 20:46:43 UTC (rev 106580)
@@ -0,0 +1,12 @@
+Test linear distance model of AudioPannerNode.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS Distance model value matched expected value.
+PASS Number of impulses found matches number of panner nodes.
+PASS Distance gains are correct.
+PASS Distance test passed for distance model 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/webaudio/distance-linear.html (0 => 106580)
--- trunk/LayoutTests/webaudio/distance-linear.html (rev 0)
+++ trunk/LayoutTests/webaudio/distance-linear.html 2012-02-02 20:46:43 UTC (rev 106580)
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <link rel="stylesheet" href=""
+ <script src=""
+ <script src=""
+ <script src=""
+ </head>
+
+ <body>
+ <div id="description"></div>
+ <div id="console"></div>
+
+ <script>
+ description("Test linear distance model of AudioPannerNode.");
+
+ function runTest() {
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ window.jsTestIsAsync = true;
+
+ // Create offline audio context.
+ context = new webkitAudioContext(2, sampleRate * renderLengthSeconds, sampleRate);
+
+ // Create temp panner to get LINEAR_DISTANCE value for testing.
+ var pannerModel = context.createPanner();
+ createTestAndRun(context, pannerModel.LINEAR_DISTANCE, 0);
+ }
+
+ runTest();
+ successfullyParsed = true;
+
+ </script>
+
+<script src=""
+ </body>
+</html>
Added: trunk/LayoutTests/webaudio/resources/distance-model-testing.js (0 => 106580)
--- trunk/LayoutTests/webaudio/resources/distance-model-testing.js (rev 0)
+++ trunk/LayoutTests/webaudio/resources/distance-model-testing.js 2012-02-02 20:46:43 UTC (rev 106580)
@@ -0,0 +1,212 @@
+var sampleRate = 44100.0;
+
+// How many panner nodes to create for the test.
+var nodesToCreate = 100;
+
+// Time step when each panner node starts.
+var timeStep = 0.001;
+
+// Make sure we render long enough to get all of our nodes.
+var renderLengthSeconds = timeStep * (nodesToCreate + 1);
+
+// Length of an impulse signal.
+var pulseLengthFrames = Math.round(timeStep * sampleRate);
+
+// Globals to make debugging a little easier.
+var context;
+var impulse;
+var bufferSource;
+var panner;
+var position;
+var time;
+
+// For the record, these distance formulas were taken from the OpenAL
+// spec
+// (http://connect.creativelabs.com/openal/Documentation/OpenAL%201.1%20Specification.pdf),
+// not the code. The Web Audio spec follows the OpenAL formulas.
+
+function linearDistance(panner, x, y, z) {
+ var distance = Math.sqrt(x * x + y * y + z * z);
+ distance = Math.min(distance, panner.maxDistance);
+ var rolloff = panner.rolloffFactor;
+ var gain = (1 - rolloff * (distance - panner.refDistance) / (panner.maxDistance - panner.refDistance));
+
+ return gain;
+}
+
+function inverseDistance(panner, x, y, z) {
+ var distance = Math.sqrt(x * x + y * y + z * z);
+ distance = Math.min(distance, panner.maxDistance);
+ var rolloff = panner.rolloffFactor;
+ var gain = panner.refDistance / (panner.refDistance + rolloff * (distance - panner.refDistance));
+
+ return gain;
+}
+
+function exponentialDistance(panner, x, y, z) {
+ var distance = Math.sqrt(x * x + y * y + z * z);
+ distance = Math.min(distance, panner.maxDistance);
+ var rolloff = panner.rolloffFactor;
+ var gain = Math.pow(distance / panner.refDistance, -rolloff);
+
+ return gain;
+}
+
+// This array must be arranged in the numeric order of the distance
+// model values.
+var distanceModelFunction = [linearDistance, inverseDistance, exponentialDistance];
+
+function createGraph(context, distanceModel, nodeCount) {
+ bufferSource = new Array(nodeCount);
+ panner = new Array(nodeCount);
+ position = new Array(nodeCount);
+ time = new Array(nodesToCreate);
+
+ impulse = createImpulseBuffer(context, pulseLengthFrames);
+
+ // Create all the sources and panners.
+ //
+ // We MUST use the EQUALPOWER panning model so that we can easily
+ // figure out the gain introduced by the panner.
+ //
+ // We want to stay in the middle of the panning range, which means
+ // we want to stay on the z-axis. If we don't, then the effect of
+ // panning model will be much more complicated. We're not testing
+ // the panner, but the distance model, so we want the panner effect
+ // to be simple.
+ //
+ // The panners are placed at a uniform intervals between the panner
+ // reference distance and the panner max distance. The source is
+ // also started at regular intervals.
+ for (var k = 0; k < nodeCount; ++k) {
+ bufferSource[k] = context.createBufferSource();
+ bufferSource[k].buffer = impulse;
+
+ panner[k] = context.createPanner();
+ panner[k].panningModel = panner.EQUALPOWER;
+ panner[k].distanceModel = distanceModel;
+
+ var distanceStep = (panner[k].maxDistance - panner[k].refDistance) / nodeCount;
+ position[k] = distanceStep * k + panner[k].refDistance;
+ panner[k].setPosition(0, 0, position[k]);
+
+ bufferSource[k].connect(panner[k]);
+ panner[k].connect(context.destination);
+
+ time[k] = k * timeStep;
+ bufferSource[k].noteOn(time[k]);
+ }
+}
+
+// distanceModel should be the distance model constant like
+// LINEAR_DISTANCE, INVERSE_DISTANCE, and EXPONENTIAL_DISTANCE. The
+// expectedModel is the expected actual numeric value of the constant.
+function createTestAndRun(context, distanceModel, expectedModel) {
+ // To test the distance models, we create a number of panners at
+ // uniformly spaced intervals on the z-axis. Each of these are
+ // started at equally spaced time intervals. After rendering the
+ // signals, we examine where each impulse is located and the
+ // attenuation of the impulse. The attenuation is compared
+ // against our expected attenuation.
+
+ createGraph(context, distanceModel, nodesToCreate);
+
+ context._oncomplete_ = checkDistanceResult(distanceModel, expectedModel);
+ context.startRendering();
+}
+
+// The gain caused by the EQUALPOWER panning model, if we stay on the
+// z axis, with the default orientations.
+function equalPowerGain() {
+ return Math.SQRT1_2;
+}
+
+function checkDistanceResult(model, expectedModel) {
+ return function(event) {
+ renderedBuffer = event.renderedBuffer;
+ renderedData = renderedBuffer.getChannelData(0);
+
+ // The max allowed error between the actual gain and the expected
+ // value. This is determined experimentally. Set to 0 to see what
+ // the actual errors are.
+ var maxAllowedError = 2.3e-6;
+
+ var success = true;
+
+ // Number of impulses we found in the rendered result.
+ var impulseCount = 0;
+
+ // Maximum relative error in the gain of the impulses.
+ var maxError = 0;
+
+ // Array of locations of the impulses that were not at the
+ // expected location. (Contains the actual and expected frame
+ // of the impulse.)
+ var impulsePositionErrors = new Array();
+
+ // Step through the rendered data to find all the non-zero points
+ // so we can find where our distance-attenuated impulses are.
+ // These are tested against the expected attenuations at that
+ // distance.
+ for (var k = 0; k < renderedData.length; ++k) {
+ if (renderedData[k] != 0) {
+ var distanceFunction = distanceModelFunction[panner[impulseCount].distanceModel];
+ var expected = distanceFunction(panner[impulseCount], 0, 0, position[impulseCount]);
+
+ // Adjust for the center-panning of the EQUALPOWER panning
+ // model that we're using.
+ expected *= equalPowerGain();
+
+ var error = Math.abs(renderedData[k] - expected) / Math.abs(expected);
+
+ maxError = Math.max(maxError, Math.abs(error));
+
+ // Keep track of any impulses that aren't where we expect them
+ // to be.
+ var expectedOffset = timeToSampleFrame(time[impulseCount], sampleRate);
+ if (k != expectedOffset) {
+ impulsePositionErrors.push({ actual : k, expected : expectedOffset});
+ }
+ ++impulseCount;
+ }
+ }
+
+ if (model == expectedModel) {
+ testPassed("Distance model value matched expected value.");
+ } else {
+ testFailed("Distance model value does not match expected value.");
+ success = false;
+ }
+
+ if (impulseCount == nodesToCreate) {
+ testPassed("Number of impulses found matches number of panner nodes.");
+ } else {
+ testFailed("Number of impulses is incorrect. Found " + impulseCount + " but expected " + nodesToCreate + ".");
+ success = false;
+ }
+
+ if (maxError <= maxAllowedError) {
+ testPassed("Distance gains are correct.");
+ } else {
+ testFailed("Distance gains are incorrect. Max rel error = " + maxError + " (maxAllowedError = " + maxAllowedError + ")");
+ success = false;
+ }
+
+ // Display any timing errors that we found.
+ if (impulsePositionErrors.length > 0) {
+ success = false;
+ testFailed(impulsePositionErrors.length + " timing errors found");
+ for (var k = 0; k < impulsePositionErrors.length; ++k) {
+ testFailed("Sample at frame " + impulsePositionErrors[k].actual + " but expected " + impulsePositionErrors[k].expected);
+ }
+ }
+
+ if (success) {
+ testPassed("Distance test passed for distance model " + model);
+ } else {
+ testFailed("Distance test failed for distance model " + model);
+ }
+
+ finishJSTest();
+ }
+}
Modified: trunk/Source/WebCore/ChangeLog (106579 => 106580)
--- trunk/Source/WebCore/ChangeLog 2012-02-02 20:29:20 UTC (rev 106579)
+++ trunk/Source/WebCore/ChangeLog 2012-02-02 20:46:43 UTC (rev 106580)
@@ -1,5 +1,21 @@
2012-02-02 Raymond Toy <[email protected]>
+ Constant values to set "distanceModel" are undefined
+ https://bugs.webkit.org/show_bug.cgi?id=74273
+
+ Reviewed by Kenneth Russell.
+
+ Tests: webaudio/distance-exponential.html
+ webaudio/distance-inverse.html
+ webaudio/distance-linear.html
+
+ * webaudio/AudioPannerNode.h: Define enum for the new constants
+ for the distance models.
+ * webaudio/AudioPannerNode.idl: Define matching constants for the
+ distance models.
+
+2012-02-02 Raymond Toy <[email protected]>
+
Illegal panner model values should throw an exception
https://bugs.webkit.org/show_bug.cgi?id=77235
Modified: trunk/Source/WebCore/webaudio/AudioPannerNode.h (106579 => 106580)
--- trunk/Source/WebCore/webaudio/AudioPannerNode.h 2012-02-02 20:29:20 UTC (rev 106579)
+++ trunk/Source/WebCore/webaudio/AudioPannerNode.h 2012-02-02 20:46:43 UTC (rev 106580)
@@ -53,6 +53,14 @@
SOUNDFIELD = 2,
};
+ // These must be defined as in the .idl file and must match those
+ // in the DistanceEffect class.
+ enum {
+ LINEAR_DISTANCE = 0,
+ INVERSE_DISTANCE = 1,
+ EXPONENTIAL_DISTANCE = 2,
+ };
+
static PassRefPtr<AudioPannerNode> create(AudioContext* context, float sampleRate)
{
return adoptRef(new AudioPannerNode(context, sampleRate));
Modified: trunk/Source/WebCore/webaudio/AudioPannerNode.idl (106579 => 106580)
--- trunk/Source/WebCore/webaudio/AudioPannerNode.idl 2012-02-02 20:29:20 UTC (rev 106579)
+++ trunk/Source/WebCore/webaudio/AudioPannerNode.idl 2012-02-02 20:46:43 UTC (rev 106580)
@@ -32,6 +32,11 @@
const unsigned short HRTF = 1;
const unsigned short SOUNDFIELD = 2;
+ // Distance model
+ const unsigned short LINEAR_DISTANCE = 0;
+ const unsigned short INVERSE_DISTANCE = 1;
+ const unsigned short EXPONENTIAL_DISTANCE = 2;
+
// Default model for stereo is HRTF
// FIXME: use unsigned short when glue generation supports it
attribute unsigned long panningModel