summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html')
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html161
1 files changed, 161 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html
new file mode 100644
index 0000000000..b5555b0137
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html
@@ -0,0 +1,161 @@
+<!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>