mirror of
https://github.com/servo/servo.git
synced 2025-10-04 02:29:12 +01:00
161 lines
5.9 KiB
HTML
161 lines
5.9 KiB
HTML
<!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>
|