| 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 530c98d45a85aa3c149b85f321dc987b81766cdc..bc90ddbef828510213d95cf40683e23237e56633 100644
|
| --- a/third_party/WebKit/LayoutTests/webaudio/resources/audioparam-testing.js
|
| +++ b/third_party/WebKit/LayoutTests/webaudio/resources/audioparam-testing.js
|
| @@ -1,515 +1,554 @@
|
| -var sampleRate = 44100;
|
| -
|
| -// Information about the starting/ending times and starting/ending values for
|
| -// each time interval.
|
| -var timeValueInfo;
|
| -
|
| -// 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;
|
| -
|
| -// Some suitable time constant so that we can see a significant change over a
|
| -// timeInterval. This is only needed by setTargetAtTime() 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 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, 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 frameInfo = getStartEndFrames(startTime, endTime, sampleRate);
|
| - var startFrame = frameInfo.startFrame;
|
| - var endFrame = frameInfo.endFrame;
|
| - var length = endFrame - startFrame;
|
| - var array = new Array(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] = Math.fround(start + k * step);
|
| - var t = (startFrame + k) / sampleRate;
|
| - array[k] = startValue + slope * (t - startTime);
|
| +(function(global) {
|
| +
|
| + // Information about the starting/ending times and starting/ending values for
|
| + // each time interval.
|
| + let timeValueInfo;
|
| +
|
| + // The difference between starting values between each time interval.
|
| + let 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.
|
| + let startEndValueChange;
|
| +
|
| + // Default threshold to use for detecting discontinuities that should appear
|
| + // at each time interval.
|
| + let discontinuityThreshold;
|
| +
|
| + // Time interval between value changes. It is best if 1 / numberOfTests is
|
| + // not close to timeInterval.
|
| + let timeIntervalInternal = .03;
|
| +
|
| + let context;
|
| +
|
| + // Make sure we render long enough to capture all of our test data.
|
| + function renderLength(numberOfTests) {
|
| + return timeToSampleFrame((numberOfTests + 1) * timeInterval, sampleRate);
|
| }
|
|
|
| - 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.)
|
| -function createExponentialRampArray(
|
| - startTime, endTime, startValue, endValue, 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 ratio = endValue / startValue;
|
| -
|
| - // v(t) = v0*(v1/v0)^((t-t0)/(t1-t0))
|
| - for (var k = 0; k < length; ++k) {
|
| - var t = Math.fround((startFrame + k) / sampleRate);
|
| - array[k] =
|
| - Math.fround(startValue * Math.pow(ratio, (t - startTime) / deltaTime));
|
| + // 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) {
|
| + let startFrame = timeToSampleFrame(startTime, sampleRate);
|
| + let endFrame = timeToSampleFrame(endTime, sampleRate);
|
| + let length = endFrame - startFrame;
|
| +
|
| + let buffer = createConstantBuffer(context, length, value);
|
| +
|
| + return buffer.getChannelData(0);
|
| }
|
|
|
| - 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 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 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;
|
| + 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.
|
| + let startFrame = Math.ceil(startTime * sampleRate);
|
| + let endFrame = Math.ceil(endTime * sampleRate);
|
| +
|
| + return {startFrame: startFrame, endFrame: endFrame};
|
| }
|
|
|
| - 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-1)/duration*(t - t0))
|
| - var f = (length - 1) / (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];
|
| + // 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) {
|
| + let frameInfo = getStartEndFrames(startTime, endTime, sampleRate);
|
| + let startFrame = frameInfo.startFrame;
|
| + let endFrame = frameInfo.endFrame;
|
| + let length = endFrame - startFrame;
|
| + let array = new Array(length);
|
| +
|
| + let step = Math.fround(
|
| + (endValue - startValue) / (endTime - startTime) / sampleRate);
|
| + let start = Math.fround(
|
| + startValue +
|
| + (endValue - startValue) * (startFrame / sampleRate - startTime) /
|
| + (endTime - startTime));
|
| +
|
| + let slope = (endValue - startValue) / (endTime - startTime);
|
| +
|
| + // v(t) = v0 + (v1 - v0)*(t-t0)/(t1-t0)
|
| + for (k = 0; k < length; ++k) {
|
| + // array[k] = Math.fround(start + k * step);
|
| + let t = (startFrame + k) / sampleRate;
|
| + array[k] = startValue + slope * (t - startTime);
|
| }
|
| - }
|
|
|
| - return array;
|
| -}
|
| + 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;
|
| + // 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.)
|
| + function createExponentialRampArray(
|
| + startTime, endTime, startValue, endValue, sampleRate) {
|
| + let deltaTime = endTime - startTime;
|
| +
|
| + let frameInfo = getStartEndFrames(startTime, endTime, sampleRate);
|
| + let startFrame = frameInfo.startFrame;
|
| + let endFrame = frameInfo.endFrame;
|
| + let length = endFrame - startFrame;
|
| + let array = new Array(length);
|
| +
|
| + let ratio = endValue / startValue;
|
| +
|
| + // v(t) = v0*(v1/v0)^((t-t0)/(t1-t0))
|
| + for (let k = 0; k < length; ++k) {
|
| + let t = Math.fround((startFrame + k) / sampleRate);
|
| + array[k] = Math.fround(
|
| + startValue * Math.pow(ratio, (t - startTime) / deltaTime));
|
| + }
|
|
|
| - for (var k = 0; k < length; ++k) {
|
| - signal[k] = halfAmplitude + halfAmplitude * Math.sin(omega * k);
|
| + return array;
|
| }
|
|
|
| - 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;
|
| + function discreteTimeConstantForSampleRate(timeConstant, sampleRate) {
|
| + return 1 - Math.exp(-1 / (sampleRate * timeConstant));
|
| }
|
| -}
|
| -
|
| -// 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.
|
| -function valueUpdate(timeIntervalIndex) {
|
| - return -startingValueDelta;
|
| -}
|
| -
|
| -// Compare a section of the rendered data against our expected signal.
|
| -function comparePartialSignals(
|
| - should, rendered, expectedFunction, startTime, endTime, valueInfo,
|
| - sampleRate, errorMetric) {
|
| - var startSample = timeToSampleFrame(startTime, 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;
|
| - should(
|
| - isValidNumber(rendered[startSample + k]),
|
| - 'NaN or infinity for rendered data at ' + maxErrorIndex)
|
| - .beTrue();
|
| - break;
|
| +
|
| + // 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) {
|
| + let startFrameFloat = startTime * sampleRate;
|
| + let frameInfo = getStartEndFrames(startTime, endTime, sampleRate);
|
| + let startFrame = frameInfo.startFrame;
|
| + let endFrame = frameInfo.endFrame;
|
| + let length = Math.floor(endFrame - startFrame);
|
| + let array = new Array(length);
|
| + let c = discreteTimeConstantForSampleRate(timeConstant, sampleRate);
|
| +
|
| + let delta = startValue - targetValue;
|
| +
|
| + // v(t) = v1 + (v0 - v1) * exp(-(t-t0)/tau)
|
| + for (let k = 0; k < length; ++k) {
|
| + let t = (startFrame + k) / sampleRate;
|
| + let value =
|
| + targetValue + delta * Math.exp(-(t - startTime) / timeConstant);
|
| + array[k] = value;
|
| }
|
| - if (!isValidNumber(expected[k])) {
|
| - maxError = Infinity;
|
| - maxErrorIndex = startSample + k;
|
| - should(
|
| - isValidNumber(expected[k]),
|
| - 'NaN or infinity for rendered data at ' + maxErrorIndex)
|
| - .beTrue();
|
| - break;
|
| +
|
| + 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.
|
| + let curve = createSineWaveArray(
|
| + endTime - startTime, freqHz, sineAmplitude, sampleRate);
|
| + // Sample the curve appropriately.
|
| + let frameInfo = getStartEndFrames(startTime, endTime, sampleRate);
|
| + let startFrame = frameInfo.startFrame;
|
| + let endFrame = frameInfo.endFrame;
|
| + let length = Math.floor(endFrame - startFrame);
|
| + let array = new Array(length);
|
| +
|
| + // v(t) = linearly interpolate between V[k] and V[k + 1] where k =
|
| + // floor((N-1)/duration*(t - t0))
|
| + let f = (length - 1) / (endTime - startTime);
|
| +
|
| + for (let k = 0; k < length; ++k) {
|
| + let t = (startFrame + k) / sampleRate;
|
| + let indexFloat = f * (t - startTime);
|
| + let index = Math.floor(indexFloat);
|
| + if (index + 1 < length) {
|
| + let v0 = curve[index];
|
| + let v1 = curve[index + 1];
|
| + array[k] = v0 + (v1 - v0) * (indexFloat - index);
|
| + } else {
|
| + array[k] = curve[length - 1];
|
| + }
|
| }
|
| - var error = Math.abs(errorMetric(rendered[startSample + k], expected[k]));
|
| - if (error > maxError) {
|
| - maxError = error;
|
| - maxErrorIndex = k;
|
| +
|
| + 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) {
|
| + let length = timeToSampleFrame(durationSeconds, sampleRate);
|
| + let signal = new Float32Array(length);
|
| + let omega = 2 * Math.PI * freqHz / sampleRate;
|
| + let halfAmplitude = amplitude / 2;
|
| +
|
| + for (let k = 0; k < length; ++k) {
|
| + signal[k] = halfAmplitude + halfAmplitude * Math.sin(omega * k);
|
| }
|
| +
|
| + return signal;
|
| }
|
|
|
| - return {maxError: maxError, index: maxErrorIndex, expected: expected};
|
| -}
|
| -
|
| -// Find the discontinuities in the data and compare the locations of the
|
| -// discontinuities with the times that define the time intervals. There is a
|
| -// discontinuity if the difference between successive samples exceeds the
|
| -// threshold.
|
| -function verifyDiscontinuities(should, values, times, threshold) {
|
| - var n = values.length;
|
| - var success = true;
|
| - var badLocations = 0;
|
| - var breaks = [];
|
| -
|
| - // Find discontinuities.
|
| - for (var k = 1; k < n; ++k) {
|
| - if (Math.abs(values[k] - values[k - 1]) > threshold) {
|
| - breaks.push(k);
|
| + // 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;
|
| }
|
| }
|
|
|
| - var testCount;
|
| + // Relative error metric
|
| + function relativeErrorMetric(actual, expected) {
|
| + return (actual - expected) / Math.abs(expected);
|
| + }
|
|
|
| - // 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;
|
| - should(breaks.length, 'Number of discontinuities')
|
| - .beLessThan(numberOfTests);
|
| - success = false;
|
| - } else {
|
| - testCount = breaks.length;
|
| + // Difference metric
|
| + function differenceErrorMetric(actual, expected) {
|
| + return actual - expected;
|
| }
|
|
|
| - // 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 < testCount; ++k) {
|
| - var expectedSampleFrame = timeToSampleFrame(times[k + 1], sampleRate);
|
| - if (breaks[k] != expectedSampleFrame) {
|
| - success = false;
|
| - ++badLocations;
|
| - should(breaks[k], 'Discontinuity at index')
|
| - .beEqualTo(expectedSampleFrame);
|
| + // 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(
|
| + should, rendered, expectedFunction, startTime, endTime, valueInfo,
|
| + sampleRate, errorMetric) {
|
| + let startSample = timeToSampleFrame(startTime, sampleRate);
|
| + let expected = expectedFunction(
|
| + startTime, endTime, valueInfo.startValue, valueInfo.endValue,
|
| + sampleRate, timeConstant);
|
| +
|
| + let n = expected.length;
|
| + let maxError = -1;
|
| + let maxErrorIndex = -1;
|
| +
|
| + for (let 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;
|
| + should(
|
| + isValidNumber(rendered[startSample + k]),
|
| + 'NaN or infinity for rendered data at ' + maxErrorIndex)
|
| + .beTrue();
|
| + break;
|
| + }
|
| + if (!isValidNumber(expected[k])) {
|
| + maxError = Infinity;
|
| + maxErrorIndex = startSample + k;
|
| + should(
|
| + isValidNumber(expected[k]),
|
| + 'NaN or infinity for rendered data at ' + maxErrorIndex)
|
| + .beTrue();
|
| + break;
|
| + }
|
| + let error = Math.abs(errorMetric(rendered[startSample + k], expected[k]));
|
| + if (error > maxError) {
|
| + maxError = error;
|
| + maxErrorIndex = k;
|
| + }
|
| }
|
| +
|
| + return {maxError: maxError, index: maxErrorIndex, expected: expected};
|
| }
|
|
|
| - if (badLocations) {
|
| - should(badLocations, 'Number of discontinuites at incorrect locations')
|
| - .beEqualTo(0);
|
| - success = false;
|
| - } else {
|
| - should(
|
| - breaks.length + 1,
|
| - 'Number of tests started and ended at the correct time')
|
| - .beEqualTo(numberOfTests);
|
| + // Find the discontinuities in the data and compare the locations of the
|
| + // discontinuities with the times that define the time intervals. There is a
|
| + // discontinuity if the difference between successive samples exceeds the
|
| + // threshold.
|
| + function verifyDiscontinuities(should, values, times, threshold) {
|
| + let n = values.length;
|
| + let success = true;
|
| + let badLocations = 0;
|
| + let breaks = [];
|
| +
|
| + // Find discontinuities.
|
| + for (let k = 1; k < n; ++k) {
|
| + if (Math.abs(values[k] - values[k - 1]) > threshold) {
|
| + breaks.push(k);
|
| + }
|
| + }
|
| +
|
| + let 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;
|
| + should(breaks.length, 'Number of discontinuities')
|
| + .beLessThan(numberOfTests);
|
| + 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 (let k = 0; k < testCount; ++k) {
|
| + let expectedSampleFrame = timeToSampleFrame(times[k + 1], sampleRate);
|
| + if (breaks[k] != expectedSampleFrame) {
|
| + success = false;
|
| + ++badLocations;
|
| + should(breaks[k], 'Discontinuity at index')
|
| + .beEqualTo(expectedSampleFrame);
|
| + }
|
| + }
|
| +
|
| + if (badLocations) {
|
| + should(badLocations, 'Number of discontinuites at incorrect locations')
|
| + .beEqualTo(0);
|
| + success = false;
|
| + } else {
|
| + should(
|
| + breaks.length + 1,
|
| + 'Number of tests started and ended at the correct time')
|
| + .beEqualTo(numberOfTests);
|
| + }
|
| +
|
| + return success;
|
| }
|
|
|
| - return success;
|
| -}
|
| -
|
| -// Compare the rendered data with the expected data.
|
| -//
|
| -// testName - string describing the test
|
| -//
|
| -// maxError - maximum allowed difference between the rendered data and the
|
| -// expected data
|
| -//
|
| -// rendererdData - array containing the rendered (actual) data
|
| -//
|
| -// expectedFunction - function to compute the expected data
|
| -//
|
| -// timeValueInfo - array containing information about the start and end times
|
| -// and the start and end values of each interval.
|
| -//
|
| -// breakThreshold - threshold to use for determining discontinuities.
|
| -function compareSignals(
|
| - should, 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(should, renderedData, times, breakThreshold);
|
| -
|
| - for (var k = 0; k < n; ++k) {
|
| - var result = comparePartialSignals(
|
| - should, renderedData, expectedFunction, times[k], times[k + 1],
|
| - values[k], sampleRate, errorMetric);
|
| -
|
| - expectedSignal =
|
| - expectedSignal.concat(Array.prototype.slice.call(result.expected));
|
| + // Compare the rendered data with the expected data.
|
| + //
|
| + // testName - string describing the test
|
| + //
|
| + // maxError - maximum allowed difference between the rendered data and the
|
| + // expected data
|
| + //
|
| + // rendererdData - array containing the rendered (actual) data
|
| + //
|
| + // expectedFunction - function to compute the expected data
|
| + //
|
| + // timeValueInfo - array containing information about the start and end times
|
| + // and the start and end values of each interval.
|
| + //
|
| + // breakThreshold - threshold to use for determining discontinuities.
|
| + function compareSignals(
|
| + should, testName, maxError, renderedData, expectedFunction, timeValueInfo,
|
| + breakThreshold, errorMetric) {
|
| + let success = true;
|
| + let failedTestCount = 0;
|
| + let times = timeValueInfo.times;
|
| + let values = timeValueInfo.values;
|
| + let n = values.length;
|
| + let expectedSignal = [];
|
| +
|
| + success =
|
| + verifyDiscontinuities(should, renderedData, times, breakThreshold);
|
| +
|
| + for (let k = 0; k < n; ++k) {
|
| + let result = comparePartialSignals(
|
| + should, renderedData, expectedFunction, times[k], times[k + 1],
|
| + values[k], sampleRate, errorMetric);
|
| +
|
| + expectedSignal =
|
| + expectedSignal.concat(Array.prototype.slice.call(result.expected));
|
| +
|
| + should(
|
| + result.maxError,
|
| + 'Max error for test ' + k + ' at offset ' +
|
| + (result.index + timeToSampleFrame(times[k], sampleRate)))
|
| + .beLessThanOrEqualTo(maxError);
|
| + }
|
|
|
| should(
|
| - result.maxError,
|
| - 'Max error for test ' + k + ' at offset ' +
|
| - (result.index + timeToSampleFrame(times[k], sampleRate)))
|
| - .beLessThanOrEqualTo(maxError);
|
| + failedTestCount,
|
| + 'Number of failed tests with an acceptable relative tolerance of ' +
|
| + maxError)
|
| + .beEqualTo(0);
|
| }
|
|
|
| - should(
|
| - failedTestCount,
|
| - 'Number of failed tests with an acceptable relative tolerance of ' +
|
| - maxError)
|
| - .beEqualTo(0);
|
| -}
|
| -
|
| -// 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(
|
| - task, should, testName, error, referenceFunction, jumpThreshold,
|
| - errorMetric) {
|
| - return function(event) {
|
| - var buffer = event.renderedBuffer;
|
| - renderedData = buffer.getChannelData(0);
|
| -
|
| - var threshold;
|
| -
|
| - if (!jumpThreshold) {
|
| - threshold = discontinuityThreshold;
|
| - } else {
|
| - threshold = jumpThreshold;
|
| + // 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(
|
| + task, should, testName, error, referenceFunction, jumpThreshold,
|
| + errorMetric) {
|
| + return function(event) {
|
| + let buffer = event.renderedBuffer;
|
| + renderedData = buffer.getChannelData(0);
|
| +
|
| + let threshold;
|
| +
|
| + if (!jumpThreshold) {
|
| + threshold = discontinuityThreshold;
|
| + } else {
|
| + threshold = jumpThreshold;
|
| + }
|
| +
|
| + compareSignals(
|
| + should, testName, error, renderedData, referenceFunction,
|
| + timeValueInfo, threshold, errorMetric);
|
| + task.done();
|
| }
|
| + }
|
| +
|
| + // Run all the automation tests.
|
| + //
|
| + // numberOfTests - number of tests (time intervals) to run.
|
| + //
|
| + // initialValue - The initial value of the first time 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.
|
| + //
|
| + // 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(
|
| + numberOfTests, initialValue, setValueFunction, automationFunction) {
|
| + let timeInfo = [0];
|
| + let valueInfo = [];
|
| + let value = initialValue;
|
| +
|
| + for (let k = 0; k < numberOfTests; ++k) {
|
| + let startTime = k * timeInterval;
|
| + let endTime = (k + 1) * timeInterval;
|
| + let endValue = value + endValueDelta(k);
|
| +
|
| + // Set the value at the start of the time interval.
|
| + setValueFunction(value, startTime);
|
| +
|
| + // 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 += valueUpdate(k);
|
| + }
|
| +
|
| + return {times: timeInfo, values: valueInfo};
|
| + }
|
|
|
| - compareSignals(
|
| - should, testName, error, renderedData, referenceFunction, timeValueInfo,
|
| - threshold, errorMetric);
|
| - task.done();
|
| + // 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(
|
| + task, should, numberOfTests, initialValue, setValueFunction,
|
| + automationFunction, testName, maxError, referenceFunction, jumpThreshold,
|
| + errorMetric) {
|
| + // Create offline audio context.
|
| + context =
|
| + new OfflineAudioContext(2, renderLength(numberOfTests), sampleRate);
|
| + let 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 .gain attribute and observe the resulting values.
|
| +
|
| + gainNode = context.createGain();
|
| +
|
| + let 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.start(0);
|
| +
|
| + context.oncomplete = checkResultFunction(
|
| + task, should, testName, maxError, referenceFunction, jumpThreshold,
|
| + errorMetric || relativeErrorMetric);
|
| + context.startRendering();
|
| }
|
| -}
|
| -
|
| -// Run all the automation tests.
|
| -//
|
| -// numberOfTests - number of tests (time intervals) to run.
|
| -//
|
| -// initialValue - The initial value of the first time 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.
|
| -//
|
| -// 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(
|
| - numberOfTests, initialValue, setValueFunction, automationFunction) {
|
| - var timeInfo = [0];
|
| - var valueInfo = [];
|
| - var value = initialValue;
|
| -
|
| - for (var k = 0; k < numberOfTests; ++k) {
|
| - var startTime = k * timeInterval;
|
| - var endTime = (k + 1) * timeInterval;
|
| - var endValue = value + endValueDelta(k);
|
| -
|
| - // Set the value at the start of the time interval.
|
| - setValueFunction(value, startTime);
|
| -
|
| - // 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 += valueUpdate(k);
|
| +
|
| + // Export local references to global scope. All the new objects in this file
|
| + // must be exported through this if it is to be used in the actual test HTML
|
| + // page.
|
| + let exports = {
|
| + 'sampleRate': 44100,
|
| + 'gainNode': null,
|
| + 'timeInterval': timeIntervalInternal,
|
| +
|
| + // Some suitable time constant so that we can see a significant change over
|
| + // a timeInterval. This is only needed by setTargetAtTime() which needs a
|
| + // time constant.
|
| + 'timeConstant': timeIntervalInternal / 3,
|
| +
|
| + 'renderLength': renderLength,
|
| + 'createConstantArray': createConstantArray,
|
| + 'getStartEndFrames': getStartEndFrames,
|
| + 'createLinearRampArray': createLinearRampArray,
|
| + 'createExponentialRampArray': createExponentialRampArray,
|
| + 'discreteTimeConstantForSampleRate': discreteTimeConstantForSampleRate,
|
| + 'createExponentialApproachArray': createExponentialApproachArray,
|
| + 'createReferenceSineArray': createReferenceSineArray,
|
| + 'createSineWaveArray': createSineWaveArray,
|
| + 'endValueDelta': endValueDelta,
|
| + 'relativeErrorMetric': relativeErrorMetric,
|
| + 'differenceErrorMetric': differenceErrorMetric,
|
| + 'valueUpdate': valueUpdate,
|
| + 'comparePartialSignals': comparePartialSignals,
|
| + 'verifyDiscontinuities': verifyDiscontinuities,
|
| + 'compareSignals': compareSignals,
|
| + 'checkResultFunction': checkResultFunction,
|
| + 'doAutomation': doAutomation,
|
| + 'createAudioGraphAndTest': createAudioGraphAndTest
|
| + };
|
| +
|
| + for (let reference in exports) {
|
| + global[reference] = exports[reference];
|
| }
|
|
|
| - 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(
|
| - task, should, numberOfTests, initialValue, setValueFunction,
|
| - automationFunction, testName, maxError, referenceFunction, jumpThreshold,
|
| - errorMetric) {
|
| - // Create offline audio context.
|
| - context = new OfflineAudioContext(2, renderLength(numberOfTests), sampleRate);
|
| - 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 .gain attribute and observe the resulting values.
|
| -
|
| - gainNode = context.createGain();
|
| -
|
| - 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.start(0);
|
| -
|
| - context.oncomplete = checkResultFunction(
|
| - task, should, testName, maxError, referenceFunction, jumpThreshold,
|
| - errorMetric || relativeErrorMetric);
|
| - context.startRendering();
|
| -}
|
| +})(window);
|
|
|