blob: a0d4e6fe0e2a0be90d5afb37a4488b27e1dca938 [file] [log] [blame]
<!doctype html>
<html>
<head>
<title>Test setTargetAtTime Approach to Limit</title>
<script src="../resources/js-test.js"></script>
<script src="resources/compatibility.js"></script>
<script src="resources/audio-testing.js"></script>
<script src="resources/audioparam-testing.js"></script>
</head>
<body>
<script>
description("Test setTargetAtTime Approach to Limit");
window.jsTestIsAsync = true;
var audit = Audit.createTaskRunner();
audit.defineTask("approach 1", function(done) {
var sampleRate = 48000;
// A really short time constant so that setTargetAtTime approaches the limiting value well
// before the end of the test.
var timeConstant = 0.001;
// Find the time where setTargetAtTime is close enough to the limit. Since we're
// approaching 1, use a value of eps smaller than kSetTargetThreshold (5e-7) in
// AudioParamTimeline.cpp. This is to account for round-off in the actual implementation
// (which uses a filter and not the formula.)
var limitThreshold = 1e-7;
runTest({
sampleRate: sampleRate,
v0: 0,
v1: 1,
timeConstant: timeConstant,
eps: limitThreshold,
// Experimentally determined
threshold: 2.4273e-7
}).then(done);
})
audit.defineTask("approach 0", function(done) {
// Use the equation for setTargetAtTime to figure out when we are close to 0:
//
// v(t) = exp(-t/tau)
//
// So find t such that exp(-t/tau) <= eps. Thus t >= - tau * log(eps).
//
// For eps, use 1e-20 (kSetTargetZeroThreshold in AudioParamTimeline.cpp).
var sampleRate = 48000;
// A really short time constant so that setTargetAtTime approaches the limiting value well
// before the end of the test.
var timeConstant = 0.001;
// Find the time where setTargetAtTime is close enough to the limit. Since we're
// approaching 0, use a value of eps smaller than kSetTargetZeroThreshold (1e-20) in
// AudioParamTimeline.cpp. This is to account for round-off in the actual implementation
// (which uses a filter and not the formula.)
var limitThreshold = 1e-21;
runTest({
sampleRate: sampleRate,
v0: 1,
v1: 0,
timeConstant: timeConstant,
eps: limitThreshold,
// Experimentally determined
threshold: 4.7470e-8
}).then(done);
});
function findLimitTime(v0, v1, timeConstant, eps) {
// Find the time at which the setTargetAtTime is close enough to the target value |v1| where
// we can consider the curve to have reached its limiting value.
//
// If v1 = 0, |eps| is the absolute error between the actual value and
// |v1|. Otherwise, |eps| is the relative error between the actual value and |v1|.
//
// The curve is
//
// v(t) = v1 - (v1 - v0) * exp(-t/timeConstant)
//
// If v1 = 0,
//
// v(t) = v0 * exp(-t/timeConstant)
//
// Solve this for when |v(t)| <= eps:
//
// t >= timeConstant * log(v0/eps)
//
// For v1 not zero, we want |v(t) - v1|/|v1| <= eps:
//
// t >= timeConstant * log(abs(v1-v0)/eps/v1)
if (v1)
return timeConstant * Math.log(Math.abs(v1-v0)/eps/v1);
else
return timeConstant * Math.log(v0/eps);
}
function runTest(options) {
var renderLength = 1;
var context = new OfflineAudioContext(1, renderLength * sampleRate, options.sampleRate);
// A constant source
var source = context.createBufferSource();
source.buffer = createConstantBuffer(context, 1, 1);
source.loop = true;
var gain = context.createGain();
gain.gain.setValueAtTime(options.v0, 0);
gain.gain.setTargetAtTime(options.v1, 0, options.timeConstant);
source.connect(gain);
gain.connect(context.destination);
source.start();
return context.startRendering().then(function (resultBuffer) {
var actual = resultBuffer.getChannelData(0);
var expected = createExponentialApproachArray(0, renderLength,
options.v0, options.v1,
options.sampleRate, options.timeConstant);
var message = "setTargetAtTime(" + options.v1 + ", 0, " + options.timeConstant + ")";
// Determine where the tail of the curve begins. (Where the curve has basically reached
// the limit value.)
var tailTime = findLimitTime(options.v0, options.v1, options.timeConstant, options.eps);
var tailFrame = Math.ceil(tailTime * options.sampleRate);
var success = true;
success = Should("Initial output of " + tailFrame + " samples for " + message,
actual.slice(0, tailFrame))
.beCloseToArray(expected.slice(0, tailFrame), options.threshold) && success;
success = Should("Tail output for " + message,
actual.slice(tailFrame))
.containValues([options.v1]) && success;
if (success)
testPassed(message + " had the expected values.\n");
else
testFailed(message + " did not have the expected values.\n");
});
}
audit.defineTask("finish", function (done) {
finishJSTest();
done();
});
audit.runTasks();
</script>
</body>
</html>