blob: b5555b0137af4c1c8f6c5578de4bc9c5eedfe405 [file] [log] [blame]
<!doctype html>
<html>
<head>
<title>Test AudioParam events very close in time</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
<script src="/webaudio/resources/audit.js"></script>
</head>
<body>
<script>
const audit = Audit.createTaskRunner();
// Largest sample rate that is required to be supported and is a power of
// two, to eliminate round-off as much as possible.
const sampleRate = 65536;
// Only need one render quantum for testing.
const testFrames = 128;
// Largest representable single-float number
const floatMax = Math.fround(3.4028234663852886e38);
// epspos is the smallest x such that 1 + x != 1
const epspos = 1.1102230246251568e-16;
// epsneg is the smallest x such that 1 - x != 1
const epsneg = 5.551115123125784e-17;
audit.define(
{label: 'no-nan', description: 'NaN does not occur'},
(task, should) => {
const context = new OfflineAudioContext({
numberOfChannels: 1,
sampleRate: sampleRate,
length: testFrames
});
const src0 = new ConstantSourceNode(context, {offset: 0});
// This should always succeed. We just want to print out a message
// that |src0| is a constant source node for the following
// processing.
should(src0, 'src0 = new ConstantSourceNode(context, {offset: 0})')
.beEqualTo(src0);
src0.connect(context.destination);
// Values for the first event (setValue). |time1| MUST be 0.
const time1 = 0;
const value1 = 10;
// Values for the second event (linearRamp). |value2| must be huge,
// and |time2| must be small enough that 1/|time2| overflows a
// single float. This value is the least positive single float.
const value2 = floatMax;
const time2 = 1.401298464324817e-45;
// These should always succeed; the messages are just informational
// to show the events that we scheduled.
should(
src0.offset.setValueAtTime(value1, time1),
`src0.offset.setValueAtTime(${value1}, ${time1})`)
.beEqualTo(src0.offset);
should(
src0.offset.linearRampToValueAtTime(value2, time2),
`src0.offset.linearRampToValueAtTime(${value2}, ${time2})`)
.beEqualTo(src0.offset);
src0.start();
context.startRendering()
.then(buffer => {
const output = buffer.getChannelData(0);
// Since time1 = 0, the output at frame 0 MUST be value1.
should(output[0], 'output[0]').beEqualTo(value1);
// Since time2 < 1, output from frame 1 and later must be a
// constant.
should(output.slice(1), 'output[1]')
.beConstantValueOf(value2);
})
.then(() => task.done());
});
audit.define(
{label: 'interpolation', description: 'Interpolation of linear ramp'},
(task, should) => {
const context = new OfflineAudioContext({
numberOfChannels: 1,
sampleRate: sampleRate,
length: testFrames
});
const src1 = new ConstantSourceNode(context, {offset: 0});
// This should always succeed. We just want to print out a message
// that |src1| is a constant source node for the following
// processing.
should(src1, 'src1 = new ConstantSourceNode(context, {offset: 0})')
.beEqualTo(src1);
src1.connect(context.destination);
const frame = 1;
// These time values are arranged so that time1 < frame/sampleRate <
// time2. This means we need to interpolate to get a value at given
// frame.
//
// The values are not so important, but |value2| should be huge.
const time1 = frame * (1 - epsneg) / context.sampleRate;
const value1 = 1e15;
const time2 = frame * (1 + epspos) / context.sampleRate;
const value2 = floatMax;
should(
src1.offset.setValueAtTime(value1, time1),
`src1.offset.setValueAtTime(${value1}, ${time1})`)
.beEqualTo(src1.offset);
should(
src1.offset.linearRampToValueAtTime(value2, time2),
`src1.offset.linearRampToValueAtTime(${value2}, ${time2})`)
.beEqualTo(src1.offset);
src1.start();
context.startRendering()
.then(buffer => {
const output = buffer.getChannelData(0);
// Sanity check
should(time2 - time1, 'Event time difference')
.notBeEqualTo(0);
// Because 0 < time1 < 1, output must be 0 at time 0.
should(output[0], 'output[0]').beEqualTo(0);
// Because time1 < 1/sampleRate < time2, we need to
// interpolate the value between these times to determine the
// output at frame 1.
const t = frame / context.sampleRate;
const v = value1 +
(value2 - value1) * (t - time1) / (time2 - time1);
should(output[1], 'output[1]').beCloseTo(v, {threshold: 0});
// Because 1 < time2 < 2, the output at frame 2 and higher is
// constant.
should(output.slice(2), 'output[2:]')
.beConstantValueOf(value2);
})
.then(() => task.done());
});
audit.run();
</script>
</body>
</html>