Diff
Modified: trunk/LayoutTests/ChangeLog (109517 => 109518)
--- trunk/LayoutTests/ChangeLog 2012-03-02 07:33:24 UTC (rev 109517)
+++ trunk/LayoutTests/ChangeLog 2012-03-02 08:01:01 UTC (rev 109518)
@@ -1,3 +1,40 @@
+2012-03-02 Raymond Toy <[email protected]>
+
+ AudioParam needs tests for the parameter automation routines.
+ https://bugs.webkit.org/show_bug.cgi?id=77666
+
+ Reviewed by Chris Rogers.
+
+ * webaudio/audioparam-exponentialRampToValueAtTime.html:
+ * webaudio/audioparam-linearRampToValueAtTime-expected.txt: Added.
+ * webaudio/audioparam-linearRampToValueAtTime.html: Added.
+ * webaudio/audioparam-setTargetValueAtTime-expected.txt: Added.
+ * webaudio/audioparam-setTargetValueAtTime.html: Added.
+ * webaudio/audioparam-setValueAtTime-expected.txt: Added.
+ * webaudio/audioparam-setValueAtTime.html: Added.
+ * webaudio/audioparam-setValueCurveAtTime-expected.txt: Added.
+ * webaudio/audioparam-setValueCurveAtTime.html: Added.
+ * webaudio/resources/audioparam-testing.js:
+ (renderLength):
+ (createConstantArray):
+ (createLinearRampArray):
+ (discreteTimeConstantForSampleRate):
+ (createExponentialApproachArray):
+ (createSineWaveArray):
+ (endValueDelta):
+ (valueUpdate):
+ (comparePartialSignals):
+ (verifyDiscontinuities):
+ (compareSignals):
+ (checkResultFunction.return.var):
+ (checkResultFunction):
+ (doAutomation):
+ (createAudioGraphAndTest):
+ * webaudio/resources/audio-testing.js: Moved isValidNumber from
+ biquad-testing.js to here.
+ * webaudio/resources/biquad-testing.js: Moved isValidNumber from
+ here to audio-testing.js.
+
2012-03-01 Dirk Pranke <[email protected]>
Moar tightening of expectations (mac this time).
Modified: trunk/LayoutTests/webaudio/audioparam-exponentialRampToValueAtTime.html (109517 => 109518)
--- trunk/LayoutTests/webaudio/audioparam-exponentialRampToValueAtTime.html 2012-03-02 07:33:24 UTC (rev 109517)
+++ trunk/LayoutTests/webaudio/audioparam-exponentialRampToValueAtTime.html 2012-03-02 08:01:01 UTC (rev 109518)
@@ -20,45 +20,15 @@
// discontinuity is for testing timing. Also, we alternate between an increasing and
// decreasing ramp for each interval.
+ // Number of tests to run.
+ var numberOfTests = 100;
+
// Max allowed difference between the rendered data and the expected result.
var maxAllowedError = 6.75e-4;
- // Information about the starting and ending times and starting and ending values for each
- // time interval.
- var timeValueInfo;
-
// The AudioGainNode starts with this value instead of the default value.
var initialValue = 100;
- // The difference between starting values between each time interval.
- var startingValueDelta = initialValue / numberOfTests;
-
- // Each exponential ramp will increase or decrease by this much from the start to the end of
- // the time interval. We choose half of startingValueDelta so that the ending value of the
- // ramp will be distinct from the starting value of the ramp for next time interval. This
- // allows us to detect where the ramp begins and ends.
- var startEndValueChange = startingValueDelta / 2;
-
- // Threshold to use for detecting discontinuities that should appear at each time interval.
- var discontinuityThreshold = startEndValueChange / 2;
-
- var gainNode;
-
- var constantBuffer;
- var bufferSource;
-
- // Return the difference between the starting value and the ending value for time interval
- // |timeIntervalIndex|. We alternate between an end value that is above or below the starting
- // value.
- function endValueDelta(timeIntervalIndex)
- {
- if (timeIntervalIndex & 1) {
- return -startEndValueChange;
- } else {
- return startEndValueChange;
- }
- }
-
// Set the gain node value to the specified value at the specified time.
function setValue(value, time)
{
@@ -73,65 +43,16 @@
gainNode.gain.exponentialRampToValueAtTime(value, endTime)
}
- // Return the difference between the starting value at |timeIntervalIndex| and the starting
- // value at the next time interval. Since we started at a large initial value, we decrease
- // the value at each time interval.
- function valueUpdate(timeIntervalIndex)
- {
- return -startingValueDelta;
- }
-
- function checkResult(event)
- {
- var buffer = event.renderedBuffer;
- renderedData = buffer.getChannelData(0);
-
- compareSignals("exponentialRampToValueAtTime()", maxAllowedError, renderedData, createExponentialRampArray, timeValueInfo, discontinuityThreshold);
-
- finishJSTest();
- }
-
function runTest()
{
- if (window.layoutTestController) {
- layoutTestController.dumpAsText();
- layoutTestController.waitUntilDone();
- }
+ createAudioGraphAndTest(numberOfTests,
+ initialValue,
+ setValue,
+ generateRamp,
+ "exponentialRampToValueAtTime()",
+ maxAllowedError,
+ createExponentialRampArray);
- window.jsTestIsAsync = true;
-
- // Create offline audio context.
- context = new webkitAudioContext(2, renderLength, sampleRate);
- constantBuffer = createConstantBuffer(context, 1, renderLength);
-
- // We use an AudioGainNode here simply as a convenient way to test the AudioParam
- // automation, since it's easy to pass a constant value through the node, automate the
- // .gain attribute and observe the resulting values.
-
- gainNode = context.createGainNode();
-
- bufferSource = context.createBufferSource();
- bufferSource.buffer = constantBuffer;
- bufferSource.connect(gainNode);
- gainNode.connect(context.destination);
-
- // At each time interval, we adjust the value by startingValueDelta. However, the
- // exponential ramp end value is the value +/- startEndValueChange. (We alternately
- // specify the end value to be greater or smaller than the starting value, to test the
- // ramp going up and down.)
- //
- // By doing it this way, we get a discontinuity at each time interval which we can use to
- // test if the ramp started and ended at the correct time.
- timeValueInfo = doAutomation(initialValue,
- endValueDelta,
- setValue,
- generateRamp,
- valueUpdate);
-
- bufferSource.noteOn(0);
-
- context._oncomplete_ = checkResult;
- context.startRendering();
}
runTest();
Added: trunk/LayoutTests/webaudio/audioparam-linearRampToValueAtTime-expected.txt (0 => 109518)
--- trunk/LayoutTests/webaudio/audioparam-linearRampToValueAtTime-expected.txt (rev 0)
+++ trunk/LayoutTests/webaudio/audioparam-linearRampToValueAtTime-expected.txt 2012-03-02 08:01:01 UTC (rev 109518)
@@ -0,0 +1,11 @@
+Test AudioParam linearRampToValueAtTime() functionality.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS All 100 tests started and ended at the correct time.
+PASS All 100 tests passed within an acceptable tolerance.
+PASS AudioParam linearRampToValueAtTime() test passed.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/webaudio/audioparam-linearRampToValueAtTime.html (0 => 109518)
--- trunk/LayoutTests/webaudio/audioparam-linearRampToValueAtTime.html (rev 0)
+++ trunk/LayoutTests/webaudio/audioparam-linearRampToValueAtTime.html 2012-03-02 08:01:01 UTC (rev 109518)
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<script src=""
+<script src=""
+<script src=""
+</head>
+
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<script>
+description("Test AudioParam linearRampToValueAtTime() functionality.");
+
+// Play a long DC signal out through an AudioGainNode, and call setValueAtTime() and
+// linearRampToValueAtTime() at regular intervals to set the starting and ending values for a
+// linear ramp. Each time interval has a ramp with a different starting and ending value so
+// that there is a discontinuity at each time interval boundary. The discontinuity is for
+// testing timing. Also, we alternate between an increasing and decreasing ramp for each
+// interval.
+
+// Number of tests to run.
+var numberOfTests = 100;
+
+// Max allowed difference between the rendered data and the expected result.
+var maxAllowedError = 6.5e-7;
+
+// Set the gain node value to the specified value at the specified time.
+function setValue(value, time)
+{
+ gainNode.gain.setValueAtTime(value, time);
+}
+
+// Generate a linear ramp ending at time |endTime| with an ending value of |value|.
+function generateRamp(value, startTime, endTime)
+{
+ // |startTime| is ignored because the linear ramp uses the value from the setValueAtTime() call above.
+ gainNode.gain.linearRampToValueAtTime(value, endTime)
+}
+
+function runTest()
+{
+ createAudioGraphAndTest(numberOfTests,
+ 1,
+ setValue,
+ generateRamp,
+ "linearRampToValueAtTime()",
+ maxAllowedError,
+ createLinearRampArray);
+}
+
+runTest();
+successfullyParsed = true;
+
+</script>
+
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/webaudio/audioparam-setTargetValueAtTime-expected.txt (0 => 109518)
--- trunk/LayoutTests/webaudio/audioparam-setTargetValueAtTime-expected.txt (rev 0)
+++ trunk/LayoutTests/webaudio/audioparam-setTargetValueAtTime-expected.txt 2012-03-02 08:01:01 UTC (rev 109518)
@@ -0,0 +1,11 @@
+Test AudioParam setTargetValueAtTime() functionality.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS All 100 tests started and ended at the correct time.
+PASS All 100 tests passed within an acceptable tolerance.
+PASS AudioParam setTargetValueAtTime() test passed.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/webaudio/audioparam-setTargetValueAtTime.html (0 => 109518)
--- trunk/LayoutTests/webaudio/audioparam-setTargetValueAtTime.html (rev 0)
+++ trunk/LayoutTests/webaudio/audioparam-setTargetValueAtTime.html 2012-03-02 08:01:01 UTC (rev 109518)
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<script src=""
+<script src=""
+<script src=""
+</head>
+
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<script>
+description("Test AudioParam setTargetValueAtTime() functionality.");
+
+// Play a long DC signal out through an AudioGainNode, and call setValueAtTime() and
+// setTargetValueAtTime at regular intervals to set the starting value and the target
+// value. Each time interval has a ramp with a different starting and target value so that
+// there is a discontinuity at each time interval boundary. The discontinuity is for testing
+// timing. Also, we alternate between an increasing and decreasing ramp for each interval.
+
+// Number of tests to run.
+var numberOfTests = 100;
+
+// Max allowed difference between the rendered data and the expected result.
+var maxAllowedError = 2.79e-5;
+
+// The AudioGainNode starts with this value instead of the default value.
+var initialValue = 100;
+
+// Set the gain node value to the specified value at the specified time.
+function setValue(value, time)
+{
+ gainNode.gain.setValueAtTime(value, time);
+}
+
+// Generate an exponential approach starting at |startTime| with a target value of |value|.
+function automation(value, startTime, endTime)
+{
+ // endTime is not used for setTargetValueAtTime.
+ gainNode.gain.setTargetValueAtTime(value, startTime, timeConstant)
+}
+
+function runTest()
+{
+ createAudioGraphAndTest(numberOfTests,
+ initialValue,
+ setValue,
+ automation,
+ "setTargetValueAtTime()",
+ maxAllowedError,
+ createExponentialApproachArray);
+}
+
+runTest();
+successfullyParsed = true;
+
+</script>
+
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/webaudio/audioparam-setValueAtTime-expected.txt (0 => 109518)
--- trunk/LayoutTests/webaudio/audioparam-setValueAtTime-expected.txt (rev 0)
+++ trunk/LayoutTests/webaudio/audioparam-setValueAtTime-expected.txt 2012-03-02 08:01:01 UTC (rev 109518)
@@ -0,0 +1,11 @@
+Test AudioParam setValueAtTime() functionality.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS All 100 tests started and ended at the correct time.
+PASS All 100 tests passed within an acceptable tolerance.
+PASS AudioParam setValueAtTime() test passed.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/webaudio/audioparam-setValueAtTime.html (0 => 109518)
--- trunk/LayoutTests/webaudio/audioparam-setValueAtTime.html (rev 0)
+++ trunk/LayoutTests/webaudio/audioparam-setValueAtTime.html 2012-03-02 08:01:01 UTC (rev 109518)
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<script src=""
+<script src=""
+<script src=""
+</head>
+
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<script>
+description("Test AudioParam setValueAtTime() functionality.");
+
+// Play a long DC signal out through an AudioGainNode, and call setValueAtTime() at regular
+// intervals to set the value for the duration of the interval. Each time interval has
+// different value so that there is a discontinuity at each time interval boundary. The
+// discontinuity is for testing timing.
+
+// Number of tests to run.
+var numberOfTests = 100;
+
+// Max allowed difference between the rendered data and the expected result.
+var maxAllowedError = 6e-8;
+
+// Set the gain node value to the specified value at the specified time.
+function setValue(value, time)
+{
+ gainNode.gain.setValueAtTime(value, time);
+}
+
+// For testing setValueAtTime(), we don't need to do anything for automation. because the value at
+// the beginning of the interval is set by setValue and it remains constant for the duration, which
+// is what we want.
+function automation(value, startTime, endTime)
+{
+ // Do nothing.
+}
+
+function runTest()
+{
+ createAudioGraphAndTest(numberOfTests,
+ 1,
+ setValue,
+ automation,
+ "setValueAtTime()",
+ maxAllowedError,
+ createConstantArray);
+}
+
+runTest();
+successfullyParsed = true;
+
+</script>
+
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/webaudio/audioparam-setValueCurveAtTime-expected.txt (0 => 109518)
--- trunk/LayoutTests/webaudio/audioparam-setValueCurveAtTime-expected.txt (rev 0)
+++ trunk/LayoutTests/webaudio/audioparam-setValueCurveAtTime-expected.txt 2012-03-02 08:01:01 UTC (rev 109518)
@@ -0,0 +1,11 @@
+Test AudioParam setValueCurveAtTime() functionality.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS All 20 tests started and ended at the correct time.
+PASS All 20 tests passed within an acceptable tolerance.
+PASS AudioParam setValueCurveAtTime() test passed.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/webaudio/audioparam-setValueCurveAtTime.html (0 => 109518)
--- trunk/LayoutTests/webaudio/audioparam-setValueCurveAtTime.html (rev 0)
+++ trunk/LayoutTests/webaudio/audioparam-setValueCurveAtTime.html 2012-03-02 08:01:01 UTC (rev 109518)
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<script src=""
+<script src=""
+<script src=""
+</head>
+
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<script>
+description("Test AudioParam setValueCurveAtTime() functionality.");
+
+// Play a long DC signal out through an AudioGainNode and for each time interval call
+// setValueCurveAtTime() to set the values for the duration of the interval. Each curve is a sine
+// wave, and we assume that the time interval is not an exact multiple of the period. This causes a
+// discontinuity between time intervals which is used to test timing.
+
+// Number of tests to run.
+var numberOfTests = 20;
+
+// Max allowed difference between the rendered data and the expected result. (The error is zero
+// because the rendered curve should really be exactly the same as the reference.)
+var maxAllowedError = 0;
+
+// The amplitude of the sine wave.
+var sineAmplitude = 1;
+
+// Frequency of the sine wave.
+var freqHz = 440;
+
+// Curve to use for setValueCurveAtTime().
+var curve;
+
+// Sets the curve data for the entire time interval.
+function automation(value, startTime, endTime)
+{
+ gainNode.gain.setValueCurveAtTime(curve, startTime, endTime - startTime);
+}
+
+// Create a sine wave of the specified duration.
+function createReferenceSineArray(startTime, endTime, startValue, endValue, sampleRate)
+{
+ // Ignore |startValue| and |endValue| for the sine wave.
+ return createSineWaveArray(endTime - startTime, freqHz, sineAmplitude, sampleRate);
+}
+
+function runTest()
+{
+ // The curve of values to use.
+ curve = createSineWaveArray(timeInterval, freqHz, sineAmplitude, sampleRate);
+
+ createAudioGraphAndTest(numberOfTests,
+ sineAmplitude,
+ function(k) {
+ // Don't need to set the value.
+ },
+ automation,
+ "setValueCurveAtTime()",
+ maxAllowedError,
+ createReferenceSineArray,
+ 2 * Math.PI * sineAmplitude * freqHz / sampleRate);
+}
+
+runTest();
+successfullyParsed = true;
+
+</script>
+
+<script src=""
+</body>
+</html>
Modified: trunk/LayoutTests/webaudio/resources/audio-testing.js (109517 => 109518)
--- trunk/LayoutTests/webaudio/resources/audio-testing.js 2012-03-02 07:33:24 UTC (rev 109517)
+++ trunk/LayoutTests/webaudio/resources/audio-testing.js 2012-03-02 08:01:01 UTC (rev 109518)
@@ -128,3 +128,8 @@
return endFrame - startFrame;
}
+
+// True if the number is not an infinity or NaN
+function isValidNumber(x) {
+ return !isNaN(x) && (x != Infinity) && (x != -Infinity);
+}
Modified: trunk/LayoutTests/webaudio/resources/audioparam-testing.js (109517 => 109518)
--- trunk/LayoutTests/webaudio/resources/audioparam-testing.js 2012-03-02 07:33:24 UTC (rev 109517)
+++ trunk/LayoutTests/webaudio/resources/audioparam-testing.js 2012-03-02 08:01:01 UTC (rev 109518)
@@ -1,18 +1,38 @@
var sampleRate = 44100;
-// Number of tests to run.
-var numberOfTests = 100;
+// Information about the starting/ending times and starting/ending values for each time interval.
+var timeValueInfo;
-// Time interval between value changes. It is best if 1 / numberOfTests is not close to
-// timeInterval.
+// The difference between starting values between each time interval.
+var startingValueDelta;
+
+// For any automation function that has an end or target value, the end value is based the starting
+// value of the time interval. The starting value will be increased or decreased by
+// |startEndValueChange|. We choose half of |startingValueDelta| so that the ending value will be
+// distinct from the starting value for next time interval. This allows us to detect where the ramp
+// begins and ends.
+var startEndValueChange;
+
+// Default threshold to use for detecting discontinuities that should appear at each time interval.
+var discontinuityThreshold;
+
+// Time interval between value changes. It is best if 1 / numberOfTests is not close to timeInterval.
var timeInterval = .03;
-// Make sure we render long enough to capture all of our test data.
-var renderTime = (numberOfTests + 1) * timeInterval;
-var renderLength = timeToSampleFrame(renderTime, sampleRate);
+// Some suitable time constant so that we can see a significant change over a timeInterval. This is
+// only needed by setTargetValueAtTime() which needs a time constant.
+var timeConstant = timeInterval / 3;
+var gainNode;
+
var context;
+// Make sure we render long enough to capture all of our test data.
+function renderLength(numberOfTests)
+{
+ return timeToSampleFrame((numberOfTests + 1) * timeInterval, sampleRate);
+}
+
// Create a buffer containing the same constant value.
function createConstantBuffer(context, constant, length) {
var buffer = context.createBuffer(1, length, context.sampleRate);
@@ -26,6 +46,39 @@
return buffer;
}
+// Create a constant reference signal with the given |value|. Basically the same as
+// |createConstantBuffer|, but with the parameters to match the other create functions. The
+// |endValue| is ignored.
+function createConstantArray(startTime, endTime, value, endValue, sampleRate)
+{
+ var startFrame = timeToSampleFrame(startTime, sampleRate);
+ var endFrame = timeToSampleFrame(endTime, sampleRate);
+ var length = endFrame - startFrame;
+
+ var buffer = createConstantBuffer(context, value, length);
+
+ return buffer.getChannelData(0);
+}
+
+// Create a linear ramp starting at |startValue| and ending at |endValue|. The ramp starts at time
+// |startTime| and ends at |endTime|. (The start and end times are only used to compute how many
+// samples to return.)
+function createLinearRampArray(startTime, endTime, startValue, endValue, sampleRate)
+{
+ var startFrame = timeToSampleFrame(startTime, sampleRate);
+ var endFrame = timeToSampleFrame(endTime, sampleRate);
+ var length = endFrame - startFrame;
+ var array = new Array(length);
+
+ var step = (endValue - startValue) / length;
+
+ for (k = 0; k < length; ++k) {
+ array[k] = startValue + k * step;
+ }
+
+ return array;
+}
+
// Create an exponential ramp starting at |startValue| and ending at |endValue|. The ramp starts at
// time |startTime| and ends at |endTime|. (The start and end times are only used to compute how
// many samples to return.)
@@ -34,28 +87,105 @@
var startFrame = timeToSampleFrame(startTime, sampleRate);
var endFrame = timeToSampleFrame(endTime, sampleRate);
var length = endFrame - startFrame;
- var buffer = new Array(length);
+ var array = new Array(length);
var multiplier = Math.pow(endValue / startValue, 1 / length);
for (var k = 0; k < length; ++k) {
- buffer[k] = startValue * Math.pow(multiplier, k);
+ array[k] = startValue * Math.pow(multiplier, k);
}
- return buffer;
+ return array;
}
+function discreteTimeConstantForSampleRate(timeConstant, sampleRate)
+{
+ return 1 - Math.exp(-1 / (sampleRate * timeConstant));
+}
+
+// Create a signal that starts at |startValue| and exponentially approaches the target value of
+// |targetValue|, using a time constant of |timeConstant|. The ramp starts at time |startTime| and
+// ends at |endTime|. (The start and end times are only used to compute how many samples to
+// return.)
+function createExponentialApproachArray(startTime, endTime, startValue, targetValue, sampleRate, timeConstant)
+{
+ var startFrame = timeToSampleFrame(startTime, sampleRate);
+ var endFrame = timeToSampleFrame(endTime, sampleRate);
+ var length = endFrame - startFrame;
+ var array = new Array(length);
+ var c = discreteTimeConstantForSampleRate(timeConstant, sampleRate);
+
+ var value = startValue;
+
+ for (var k = 0; k < length; ++k) {
+ array[k] = value;
+ value += (targetValue - value) * c;
+ }
+
+ return array;
+}
+
+// Create a sine wave of the given frequency and amplitude. The sine wave is offset by half the
+// amplitude so that result is always positive.
+function createSineWaveArray(durationSeconds, freqHz, amplitude, sampleRate)
+{
+ var length = timeToSampleFrame(durationSeconds, sampleRate);
+ var signal = new Float32Array(length);
+ var omega = 2 * Math.PI * freqHz / sampleRate;
+ var halfAmplitude = amplitude / 2;
+
+ for (var k = 0; k < length; ++k) {
+ signal[k] = halfAmplitude + halfAmplitude * Math.sin(omega * k);
+ }
+
+ return signal;
+}
+
+// Return the difference between the starting value and the ending value for time interval
+// |timeIntervalIndex|. We alternate between an end value that is above or below the starting
+// value.
+function endValueDelta(timeIntervalIndex)
+{
+ if (timeIntervalIndex & 1) {
+ return -startEndValueChange;
+ } else {
+ return startEndValueChange;
+ }
+}
+
+// Return the difference between the starting value at |timeIntervalIndex| and the starting value at
+// the next time interval. Since we started at a large initial value, we decrease the value at each
+// time interval.
+function valueUpdate(timeIntervalIndex)
+{
+ return -startingValueDelta;
+}
+
// Compare a section of the rendered data against our expected signal.
function comparePartialSignals(rendered, expectedFunction, startTime, endTime, valueInfo, sampleRate)
{
var startSample = timeToSampleFrame(startTime, sampleRate);
- var expected = expectedFunction(startTime, endTime, valueInfo.startValue, valueInfo.endValue, sampleRate);
+ var expected = expectedFunction(startTime, endTime, valueInfo.startValue, valueInfo.endValue, sampleRate, timeConstant);
var n = expected.length;
var maxError = -1;
var maxErrorIndex = -1;
for (var k = 0; k < n; ++k) {
+ // Make sure we don't pass these tests because a NaN has been generated in either the
+ // rendered data or the reference data.
+ if (!isValidNumber(rendered[startSample + k])) {
+ maxError = Infinity;
+ maxErrorIndex = startSample + k;
+ testFailed("NaN or infinity for rendered data at " + maxErrorIndex);
+ break;
+ }
+ if (!isValidNumber(expected[k])) {
+ maxError = Infinity;
+ maxErrorIndex = startSample + k;
+ testFailed("Nan or infinity for reference data at " + maxErrorIndex);
+ break;
+ }
var error = Math.abs(rendered[startSample + k] - expected[k]);
if (error > maxError) {
maxError = error;
@@ -83,9 +213,26 @@
}
}
+ var testCount;
+
+ // If there are numberOfTests intervals, there are only numberOfTests - 1 internal interval
+ // boundaries. Hence the maximum number of discontinuties we expect to find is numberOfTests -
+ // 1. If we find more than that, we have no reference to compare against. We also assume that
+ // the actual discontinuities are close to the expected ones.
+ //
+ // This is just a sanity check when something goes really wrong. For example, if the threshold
+ // is too low, every sample frame looks like a discontinuity.
+ if (breaks.length >= numberOfTests) {
+ testCount = numberOfTests - 1;
+ testFailed("Found more discontinuities (" + breaks.length + ") than expected. Only comparing first " + testCount + "discontinuities.");
+ success = false;
+ } else {
+ testCount = breaks.length;
+ }
+
// Compare the location of each discontinuity with the end time of each interval. (There is no
// discontinuity at the start of the signal.)
- for (var k = 0; k < breaks.length; ++k) {
+ for (var k = 0; k < testCount; ++k) {
var expectedSampleFrame = timeToSampleFrame(times[k + 1], sampleRate);
if (breaks[k] != expectedSampleFrame) {
success = false;
@@ -96,8 +243,14 @@
if (badLocations) {
testFailed(badLocations + " discontinuities at incorrect locations");
+ success = false;
} else {
- testPassed("All " + (breaks.length + 1) + " tests started and ended at the correct time.");
+ if (breaks.length == numberOfTests - 1) {
+ testPassed("All " + numberOfTests + " tests started and ended at the correct time.");
+ } else {
+ testFailed("Found " + breaks.length + " discontinuities but expected " + (numberOfTests - 1));
+ success = false;
+ }
}
return success;
@@ -131,7 +284,7 @@
var result = comparePartialSignals(renderedData, expectedFunction, times[k], times[k + 1], values[k], sampleRate);
if (result.maxError > maxError) {
- testFailed("Incorrect gain for test " + k + ". Max error = " + result.maxError + " at offset " + (result.index + timeToSampleFrame(times[k], sampleRate)));
+ testFailed("Incorrect value for test " + k + ". Max error = " + result.maxError + " at offset " + (result.index + timeToSampleFrame(times[k], sampleRate)));
++failedTestCount;
}
}
@@ -150,23 +303,52 @@
}
}
+// Create a function to test the rendered data with the reference data.
+//
+// testName - string describing the test
+//
+// error - max allowed error between rendered data and the reference data.
+//
+// referenceFunction - function that generates the reference data to be compared with the rendered
+// data.
+//
+// jumpThreshold - optional parameter that specifies the threshold to use for detecting
+// discontinuities. If not specified, defaults to discontinuityThreshold.
+//
+function checkResultFunction(testName, error, referenceFunction, jumpThreshold)
+{
+ return function(event) {
+ var buffer = event.renderedBuffer;
+ renderedData = buffer.getChannelData(0);
+
+ var threshold;
+
+ if (!jumpThreshold) {
+ threshold = discontinuityThreshold;
+ } else {
+ threshold = jumpThreshold;
+ }
+
+ compareSignals(testName, error, renderedData, referenceFunction, timeValueInfo, threshold);
+
+ finishJSTest();
+ }
+}
+
// Run all the automation tests.
//
+// numberOfTests - number of tests (time intervals) to run.
+//
// initialValue - The initial value of the first time interval.
//
-// endValueDeltaFunction - How much the end value differs from the value at the start of the
-// interval.
-//
// setValueFunction - function that sets the specified value at the start of a time interval.
//
// automationFunction - function that sets the end value for the time interval. It specifies how
// the value approaches the end value.
//
-// valueUpdateFunction - The starting value at time interval k is adjusted by valueUpdateFunction(k) to give the new starting value at time interval k + 1.
-//
// An object is returned containing an array of start times for each time interval, and an array
// giving the start and end values for the interval.
-function doAutomation(initialValue, endValueDeltaFunction, setValueFunction, automationFunction, valueUpdateFunction)
+function doAutomation(numberOfTests, initialValue, setValueFunction, automationFunction)
{
var timeInfo = [0];
var valueInfo = [];
@@ -175,20 +357,84 @@
for (var k = 0; k < numberOfTests; ++k) {
var startTime = k * timeInterval;
var endTime = (k + 1) * timeInterval;
- var endValue = value + endValueDeltaFunction(k);
+ var endValue = value + endValueDelta(k);
// Set the value at the start of the time interval.
setValueFunction(value, startTime);
- // Specify the target value, and how we should approach the target value.
+ // Specify the end or target value, and how we should approach it.
automationFunction(endValue, startTime, endTime);
// Keep track of the start times, and the start and end values for each time interval.
timeInfo.push(endTime);
valueInfo.push({startValue: value, endValue : endValue});
- value += valueUpdateFunction(k);
+ value += valueUpdate(k);
}
return {times : timeInfo, values : valueInfo};
}
+
+// Create the audio graph for the test and then run the test.
+//
+// numberOfTests - number of time intervals (tests) to run.
+//
+// initialValue - the initial value of the gain at time 0.
+//
+// setValueFunction - function to set the value at the beginning of each time interval.
+//
+// automationFunction - the AudioParamTimeline automation function
+//
+// testName - string indicating the test that is being run.
+//
+// maxError - maximum allowed error between the rendered data and the reference data
+//
+// referenceFunction - function that generates the reference data to be compared against the
+// rendered data.
+//
+// jumpThreshold - optional parameter that specifies the threshold to use for detecting
+// discontinuities. If not specified, defaults to discontinuityThreshold.
+//
+function createAudioGraphAndTest(numberOfTests, initialValue, setValueFunction, automationFunction, testName, maxError, referenceFunction, jumpThreshold)
+{
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ window.jsTestIsAsync = true;
+
+ // Create offline audio context.
+ context = new webkitAudioContext(2, renderLength(numberOfTests), sampleRate);
+ var constantBuffer = createConstantBuffer(context, 1, renderLength(numberOfTests));
+
+ // We use an AudioGainNode here simply as a convenient way to test the AudioParam
+ // automation, since it's easy to pass a constant value through the node, automate the
+ // .gain attribute and observe the resulting values.
+
+ gainNode = context.createGainNode();
+
+ var bufferSource = context.createBufferSource();
+ bufferSource.buffer = constantBuffer;
+ bufferSource.connect(gainNode);
+ gainNode.connect(context.destination);
+
+ // Set up default values for the parameters that control how the automation test values progress
+ // for each time interval.
+ startingValueDelta = initialValue / numberOfTests;
+ startEndValueChange = startingValueDelta / 2;
+ discontinuityThreshold = startEndValueChange / 2;
+
+ // Run the automation tests.
+ timeValueInfo = doAutomation(numberOfTests,
+ initialValue,
+ setValueFunction,
+ automationFunction);
+ bufferSource.noteOn(0);
+
+ context._oncomplete_ = checkResultFunction(testName,
+ maxError,
+ referenceFunction,
+ jumpThreshold);
+ context.startRendering();
+}
Modified: trunk/LayoutTests/webaudio/resources/biquad-testing.js (109517 => 109518)
--- trunk/LayoutTests/webaudio/resources/biquad-testing.js 2012-03-02 07:33:24 UTC (rev 109517)
+++ trunk/LayoutTests/webaudio/resources/biquad-testing.js 2012-03-02 08:01:01 UTC (rev 109518)
@@ -480,11 +480,6 @@
return result;
}
-// True if the number is not an infinity or NaN
-function isValidNumber(x) {
- return !isNaN(x) && (x != Infinity) && (x != -Infinity);
-}
-
function checkFilterResponse(filterType, filterParameters) {
return function(event) {
renderedBuffer = event.renderedBuffer;
Modified: trunk/Source/WebCore/ChangeLog (109517 => 109518)
--- trunk/Source/WebCore/ChangeLog 2012-03-02 07:33:24 UTC (rev 109517)
+++ trunk/Source/WebCore/ChangeLog 2012-03-02 08:01:01 UTC (rev 109518)
@@ -1,3 +1,19 @@
+2012-03-02 Raymond Toy <[email protected]>
+
+ AudioParam needs tests for the parameter automation routines.
+ https://bugs.webkit.org/show_bug.cgi?id=77666
+
+ Reviewed by Chris Rogers.
+
+ Tests: webaudio/audioparam-linearRampToValueAtTime.html
+ webaudio/audioparam-setTargetValueAtTime.html
+ webaudio/audioparam-setValueAtTime.html
+ webaudio/audioparam-setValueCurveAtTime.html
+
+ * webaudio/AudioParamTimeline.cpp:
+ (WebCore::AudioParamTimeline::valuesForTimeRangeImpl): Round the
+ curveIndex to fix timing issue in setValueCurveAtTime.
+
2012-03-01 Pablo Flouret <[email protected]>
Fix code generators to correctly guard header declarations that have a [Conditional] attribute.
Modified: trunk/Source/WebCore/webaudio/AudioParamTimeline.cpp (109517 => 109518)
--- trunk/Source/WebCore/webaudio/AudioParamTimeline.cpp 2012-03-02 07:33:24 UTC (rev 109517)
+++ trunk/Source/WebCore/webaudio/AudioParamTimeline.cpp 2012-03-02 08:01:01 UTC (rev 109518)
@@ -329,7 +329,10 @@
// Render the stretched curve data using nearest neighbor sampling.
// Oversampled curve data can be provided if smoothness is desired.
for (; writeIndex < fillToFrame; ++writeIndex) {
- unsigned curveIndex = static_cast<unsigned>(curveVirtualIndex);
+ // Ideally we'd use round() from MathExtras, but we're in a tight loop here
+ // and we're trading off precision for extra speed.
+ unsigned curveIndex = static_cast<unsigned>(0.5 + curveVirtualIndex);
+
curveVirtualIndex += curvePointsPerFrame;
// Bounds check.