194 lines
6.7 KiB
HTML
194 lines
6.7 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>
|
|
Test setTargetAtTime Approach to Limit
|
|
</title>
|
|
<script src="../../imported/w3c/web-platform-tests/resources/testharness.js"></script>
|
|
<script src="../../resources/testharnessreport.js"></script>
|
|
<script src="../resources/audit-util.js"></script>
|
|
<script src="../resources/audit.js"></script>
|
|
<script src="../resources/audioparam-testing.js"></script>
|
|
<script src="../resources/audio-param.js"></script>
|
|
</head>
|
|
<body>
|
|
<script id="layout-test-code">
|
|
let audit = Audit.createTaskRunner();
|
|
|
|
audit.define('approach 1', (task, should) => {
|
|
let sampleRate = 48000;
|
|
|
|
// A really short time constant so that setTargetAtTime approaches the
|
|
// limiting value well before the end of the test.
|
|
let 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 (1.5e-6) in AudioParamTimeline.cpp. This is to
|
|
// account for round-off in the actual implementation (which uses a
|
|
// filter and not the formula.)
|
|
let limitThreshold = 1e-6;
|
|
|
|
runTest(should, {
|
|
sampleRate: sampleRate,
|
|
v0: 0,
|
|
v1: 1,
|
|
timeConstant: timeConstant,
|
|
eps: limitThreshold,
|
|
// Experimentally determined
|
|
threshold: 2.4e-5,
|
|
tailThreshold: {relativeThreshold: 9.8234e-7}
|
|
}).then(() => task.done());
|
|
})
|
|
|
|
audit.define('approach 0', (task, should) => {
|
|
// 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 exp(-10).
|
|
|
|
let sampleRate = 48000;
|
|
|
|
// A really short time constant so that setTargetAtTime approaches the
|
|
// limiting value well before the end of the test.
|
|
let 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.)
|
|
let limitThreshold = Math.exp(-10);
|
|
|
|
runTest(should, {
|
|
sampleRate: sampleRate,
|
|
v0: 1,
|
|
v1: 0,
|
|
timeConstant: timeConstant,
|
|
eps: limitThreshold,
|
|
// Experimentally determined
|
|
threshold: 4.7470e-8,
|
|
tailThreshold: {absoluteThreshold: 2.3310e-5}
|
|
}).then(() => task.done());
|
|
});
|
|
|
|
audit.define('crbug.com/835294', (task, should) => {
|
|
let sampleRate = 8000;
|
|
let duration = 8;
|
|
let context = new OfflineAudioContext({
|
|
length: duration * sampleRate,
|
|
sampleRate: sampleRate});
|
|
|
|
let src = new ConstantSourceNode(context);
|
|
let gain = new GainNode(context);
|
|
|
|
src.connect(gain).connect(context.destination);
|
|
|
|
let targetValue = 0.8;
|
|
let targetStartTime = 0;
|
|
let timeConstant = 2;
|
|
gain.gain.setValueAtTime(0, 0);
|
|
gain.gain.setTargetAtTime(targetValue, targetStartTime, timeConstant);
|
|
src.start();
|
|
|
|
context.startRendering()
|
|
.then(renderedBuffer => {
|
|
// The output should be a simple exponential approach to 0.8
|
|
// starting at 0 at time 0.1.
|
|
let actual = renderedBuffer.getChannelData(0);
|
|
let expected = createExponentialApproachArray(targetStartTime,
|
|
duration, 0, targetValue, context.sampleRate, timeConstant);
|
|
|
|
should(actual, 'Output')
|
|
.beCloseToArray(expected, {absoluteThreshold: 9.8172e-7});
|
|
})
|
|
.then(() => task.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(should, options) {
|
|
let renderLength = 1;
|
|
|
|
let context = new OfflineAudioContext(
|
|
1, renderLength * sampleRate, options.sampleRate);
|
|
|
|
// A constant source
|
|
let source = context.createBufferSource();
|
|
source.buffer = createConstantBuffer(context, 1, 1);
|
|
source.loop = true;
|
|
|
|
let 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) {
|
|
let actual = resultBuffer.getChannelData(0);
|
|
let expected = createExponentialApproachArray(
|
|
0, renderLength, options.v0, options.v1, options.sampleRate,
|
|
options.timeConstant);
|
|
|
|
let message = 'setTargetAtTime(' + options.v1 + ', 0, ' +
|
|
options.timeConstant + ')';
|
|
|
|
// Determine where the tail of the curve begins. (Where the curve has
|
|
// basically reached the limit value.)
|
|
let tailTime = findLimitTime(
|
|
options.v0, options.v1, options.timeConstant, options.eps);
|
|
let tailFrame = Math.ceil(tailTime * options.sampleRate);
|
|
|
|
should(
|
|
actual.slice(0, tailFrame),
|
|
'Initial output of ' + tailFrame + ' samples for ' + message)
|
|
.beCloseToArray(
|
|
expected.slice(0, tailFrame),
|
|
{absoluteThreshold: options.threshold});
|
|
|
|
should(actual.slice(tailFrame), 'Tail output for ' + message)
|
|
.beCloseToArray(expected.slice(tailFrame), options.tailThreshold);
|
|
});
|
|
}
|
|
|
|
audit.run();
|
|
</script>
|
|
</body>
|
|
</html>
|