Index: third_party/WebKit/LayoutTests/webaudio/resources/audioparam-testing.js |
diff --git a/third_party/WebKit/LayoutTests/webaudio/resources/audioparam-testing.js b/third_party/WebKit/LayoutTests/webaudio/resources/audioparam-testing.js |
index 3df3083d1a58ab9b635424046fda7041f064c712..abcbce13998ffeab488c0dfbb20abaa64acb7baf 100644 |
--- a/third_party/WebKit/LayoutTests/webaudio/resources/audioparam-testing.js |
+++ b/third_party/WebKit/LayoutTests/webaudio/resources/audioparam-testing.js |
@@ -33,19 +33,6 @@ 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); |
- var n = buffer.length; |
- var data = buffer.getChannelData(0); |
- |
- for (var k = 0; k < n; ++k) { |
- data[k] = constant; |
- } |
- |
- 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. |
@@ -55,25 +42,43 @@ function createConstantArray(startTime, endTime, value, endValue, sampleRate) |
var endFrame = timeToSampleFrame(endTime, sampleRate); |
var length = endFrame - startFrame; |
- var buffer = createConstantBuffer(context, value, length); |
+ var buffer = createConstantBuffer(context, length, value); |
return buffer.getChannelData(0); |
} |
+function getStartEndFrames(startTime, endTime, sampleRate) |
+{ |
+ // Start frame is the ceiling of the start time because the ramp |
+ // starts at or after the sample frame. End frame is the ceiling |
+ // because it's the exclusive ending frame of the automation. |
+ var startFrame = Math.ceil(startTime * sampleRate); |
+ var endFrame = Math.ceil(endTime * sampleRate); |
+ |
+ return {startFrame: startFrame, endFrame: endFrame}; |
+} |
+ |
// 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 frameInfo = getStartEndFrames(startTime, endTime, sampleRate); |
+ var startFrame = frameInfo.startFrame; |
+ var endFrame = frameInfo.endFrame; |
var length = endFrame - startFrame; |
var array = new Array(length); |
- var step = (endValue - startValue) / length; |
+ var step = Math.fround((endValue - startValue) / (endTime - startTime) / sampleRate); |
+ var start = Math.fround(startValue + (endValue - startValue) * (startFrame / sampleRate - startTime) / (endTime - startTime)); |
+ |
+ var slope = (endValue - startValue) / (endTime - startTime); |
+ // v(t) = v0 + (v1 - v0)*(t-t0)/(t1-t0) |
for (k = 0; k < length; ++k) { |
- array[k] = startValue + k * step; |
+ //array[k] = Math.fround(start + k * step); |
+ var t = (startFrame + k) / sampleRate; |
+ array[k] = startValue + slope * (t - startTime); |
} |
return array; |
@@ -84,15 +89,20 @@ function createLinearRampArray(startTime, endTime, startValue, endValue, sampleR |
// many samples to return.) |
function createExponentialRampArray(startTime, endTime, startValue, endValue, sampleRate) |
{ |
- var startFrame = timeToSampleFrame(startTime, sampleRate); |
- var endFrame = timeToSampleFrame(endTime, sampleRate); |
+ var deltaTime = endTime - startTime; |
+ |
+ var frameInfo = getStartEndFrames(startTime, endTime, sampleRate); |
+ var startFrame = frameInfo.startFrame; |
+ var endFrame = frameInfo.endFrame; |
var length = endFrame - startFrame; |
var array = new Array(length); |
- var multiplier = Math.pow(endValue / startValue, 1 / length); |
- |
+ var ratio = endValue / startValue; |
+ |
+ // v(t) = v0*(v1/v0)^((t-t0)/(t1-t0)) |
for (var k = 0; k < length; ++k) { |
- array[k] = startValue * Math.pow(multiplier, k); |
+ var t = Math.fround((startFrame + k) / sampleRate); |
+ array[k] = Math.fround(startValue * Math.pow(ratio, (t - startTime) / deltaTime)); |
} |
return array; |
@@ -109,17 +119,52 @@ function discreteTimeConstantForSampleRate(timeConstant, sampleRate) |
// return.) |
function createExponentialApproachArray(startTime, endTime, startValue, targetValue, sampleRate, timeConstant) |
{ |
- var startFrame = timeToSampleFrame(startTime, sampleRate); |
- var endFrame = timeToSampleFrame(endTime, sampleRate); |
- var length = endFrame - startFrame; |
+ var startFrameFloat = startTime * sampleRate; |
+ var frameInfo = getStartEndFrames(startTime, endTime, sampleRate); |
+ var startFrame = frameInfo.startFrame; |
+ var endFrame = frameInfo.endFrame; |
+ var length = Math.floor(endFrame - startFrame); |
var array = new Array(length); |
var c = discreteTimeConstantForSampleRate(timeConstant, sampleRate); |
- var value = startValue; |
- |
+ var delta = startValue - targetValue; |
+ |
+ // v(t) = v1 + (v0 - v1) * exp(-(t-t0)/tau) |
for (var k = 0; k < length; ++k) { |
+ var t = (startFrame + k) / sampleRate; |
+ var value = targetValue + delta * Math.exp(-(t - startTime) / timeConstant); |
array[k] = value; |
- value += (targetValue - value) * c; |
+ } |
+ |
+ return array; |
+} |
+ |
+// Create a sine wave of the specified duration. |
+function createReferenceSineArray(startTime, endTime, startValue, endValue, sampleRate) |
+{ |
+ // Ignore |startValue| and |endValue| for the sine wave. |
+ var curve = createSineWaveArray(endTime - startTime, freqHz, sineAmplitude, sampleRate); |
+ // Sample the curve appropriately. |
+ var frameInfo = getStartEndFrames(startTime, endTime, sampleRate); |
+ var startFrame = frameInfo.startFrame; |
+ var endFrame = frameInfo.endFrame; |
+ var length = Math.floor(endFrame - startFrame); |
+ var array = new Array(length); |
+ |
+ // v(t) = linearly interpolate between V[k] and V[k + 1] where k = floor(N/duration*(t - t0)) |
+ var f = length / (endTime - startTime); |
+ |
+ for (var k = 0; k < length; ++k) { |
+ var t = (startFrame + k) / sampleRate; |
+ var indexFloat = f * (t - startTime); |
+ var index = Math.floor(indexFloat); |
+ if (index + 1 < length) { |
+ var v0 = curve[index]; |
+ var v1 = curve[index + 1]; |
+ array[k] = v0 + (v1 - v0) * (indexFloat - index); |
+ } else { |
+ array[k] = curve[length - 1]; |
+ } |
} |
return array; |
@@ -153,6 +198,18 @@ function endValueDelta(timeIntervalIndex) |
} |
} |
+// Relative error metric |
+function relativeErrorMetric(actual, expected) |
+{ |
+ return (actual - expected) / Math.abs(expected); |
+} |
+ |
+// Difference metric |
+function differenceErrorMetric(actual, expected) |
+{ |
+ return actual - expected; |
+} |
+ |
// 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. |
@@ -162,7 +219,7 @@ function valueUpdate(timeIntervalIndex) |
} |
// Compare a section of the rendered data against our expected signal. |
-function comparePartialSignals(rendered, expectedFunction, startTime, endTime, valueInfo, sampleRate) |
+function comparePartialSignals(rendered, expectedFunction, startTime, endTime, valueInfo, sampleRate, errorMetric) |
{ |
var startSample = timeToSampleFrame(startTime, sampleRate); |
var expected = expectedFunction(startTime, endTime, valueInfo.startValue, valueInfo.endValue, sampleRate, timeConstant); |
@@ -186,14 +243,14 @@ function comparePartialSignals(rendered, expectedFunction, startTime, endTime, v |
testFailed("Nan or infinity for reference data at " + maxErrorIndex); |
break; |
} |
- var error = Math.abs(rendered[startSample + k] - expected[k]); |
+ var error = Math.abs(errorMetric(rendered[startSample + k], expected[k])); |
if (error > maxError) { |
maxError = error; |
maxErrorIndex = k; |
} |
} |
- return {maxError : maxError, index : maxErrorIndex}; |
+ return {maxError : maxError, index : maxErrorIndex, expected: expected}; |
} |
// Find the discontinuities in the data and compare the locations of the discontinuities with the |
@@ -270,21 +327,28 @@ function verifyDiscontinuities(values, times, threshold) |
// values of each interval. |
// |
// breakThreshold - threshold to use for determining discontinuities. |
-function compareSignals(testName, maxError, renderedData, expectedFunction, timeValueInfo, breakThreshold) |
+function compareSignals(testName, maxError, renderedData, expectedFunction, timeValueInfo, breakThreshold, errorMetric) |
{ |
var success = true; |
var failedTestCount = 0; |
var times = timeValueInfo.times; |
var values = timeValueInfo.values; |
var n = values.length; |
+ var expectedSignal = []; |
success = verifyDiscontinuities(renderedData, times, breakThreshold); |
for (var k = 0; k < n; ++k) { |
- var result = comparePartialSignals(renderedData, expectedFunction, times[k], times[k + 1], values[k], sampleRate); |
+ var result = comparePartialSignals(renderedData, expectedFunction, times[k], times[k + 1], values[k], sampleRate, errorMetric); |
+ |
+ expectedSignal = expectedSignal.concat(Array.prototype.slice.call(result.expected)); |
if (result.maxError > maxError) { |
- testFailed("Incorrect value for test " + k + ". Max error = " + result.maxError + " at offset " + (result.index + timeToSampleFrame(times[k], sampleRate))); |
+ var offset = result.index + timeToSampleFrame(times[k], sampleRate); |
+ testFailed("Incorrect value for test " + k + ". Max error = " + result.maxError |
+ + " at offset " + offset |
+ + ": actual = " + renderedData[offset] |
+ + ", expected = " + expectedSignal[offset] + "."); |
++failedTestCount; |
} |
} |
@@ -293,7 +357,7 @@ function compareSignals(testName, maxError, renderedData, expectedFunction, time |
testFailed(failedTestCount + " tests failed out of " + n); |
success = false; |
} else { |
- testPassed("All " + n + " tests passed within an acceptable tolerance."); |
+ testPassed("All " + n + " tests passed within an acceptable relative tolerance of " + maxError + "."); |
} |
if (success) { |
@@ -315,7 +379,7 @@ function compareSignals(testName, maxError, renderedData, expectedFunction, time |
// jumpThreshold - optional parameter that specifies the threshold to use for detecting |
// discontinuities. If not specified, defaults to discontinuityThreshold. |
// |
-function checkResultFunction(testName, error, referenceFunction, jumpThreshold) |
+function checkResultFunction(testName, error, referenceFunction, jumpThreshold, errorMetric) |
{ |
return function(event) { |
var buffer = event.renderedBuffer; |
@@ -329,7 +393,7 @@ function checkResultFunction(testName, error, referenceFunction, jumpThreshold) |
threshold = jumpThreshold; |
} |
- compareSignals(testName, error, renderedData, referenceFunction, timeValueInfo, threshold); |
+ compareSignals(testName, error, renderedData, referenceFunction, timeValueInfo, threshold, errorMetric); |
finishJSTest(); |
} |
@@ -395,7 +459,7 @@ function doAutomation(numberOfTests, initialValue, setValueFunction, automationF |
// 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) |
+function createAudioGraphAndTest(numberOfTests, initialValue, setValueFunction, automationFunction, testName, maxError, referenceFunction, jumpThreshold, errorMetric) |
{ |
if (window.testRunner) { |
testRunner.dumpAsText(); |
@@ -406,7 +470,7 @@ function createAudioGraphAndTest(numberOfTests, initialValue, setValueFunction, |
// Create offline audio context. |
context = new OfflineAudioContext(2, renderLength(numberOfTests), sampleRate); |
- var constantBuffer = createConstantBuffer(context, 1, renderLength(numberOfTests)); |
+ var constantBuffer = createConstantBuffer(context, renderLength(numberOfTests), 1); |
// 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 |
@@ -435,6 +499,7 @@ function createAudioGraphAndTest(numberOfTests, initialValue, setValueFunction, |
context.oncomplete = checkResultFunction(testName, |
maxError, |
referenceFunction, |
- jumpThreshold); |
+ jumpThreshold, |
+ errorMetric || relativeErrorMetric); |
context.startRendering(); |
} |