blob: 37062993f9dc8d90e2d418c842b30e20517bd21d [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>
Test Exceptions from setValueCurveAtTime
</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 id="layout-test-code">
let sampleRate = 48000;
// Some short duration because we don't need to run the test for very
// long.
let testDurationSec = 0.125;
let testDurationFrames = testDurationSec * sampleRate;
let audit = Audit.createTaskRunner();
audit.define('setValueCurve', (task, should) => {
let success = true;
let context =
new OfflineAudioContext(1, testDurationFrames, sampleRate);
let g = context.createGain();
let curve = new Float32Array(2);
// Start time and duration for setValueCurveAtTime
let curveStartTime = 0.1 * testDurationSec;
let duration = 0.1 * testDurationSec;
// Some time that is known to be during the setValueCurveTime interval.
let automationTime = curveStartTime + duration / 2;
should(
() => {
g.gain.setValueCurveAtTime(curve, curveStartTime, duration);
},
'setValueCurveAtTime(curve, ' + curveStartTime + ', ' + duration +
')')
.notThrow();
should(
function() {
g.gain.setValueAtTime(1, automationTime);
},
'setValueAtTime(1, ' + automationTime + ')')
.throw(DOMException, 'NotSupportedError');
should(
function() {
g.gain.linearRampToValueAtTime(1, automationTime);
},
'linearRampToValueAtTime(1, ' + automationTime + ')')
.throw(DOMException, 'NotSupportedError');
should(
function() {
g.gain.exponentialRampToValueAtTime(1, automationTime);
},
'exponentialRampToValueAtTime(1, ' + automationTime + ')')
.throw(DOMException, 'NotSupportedError');
should(
function() {
g.gain.setTargetAtTime(1, automationTime, 1);
},
'setTargetAtTime(1, ' + automationTime + ', 1)')
.throw(DOMException, 'NotSupportedError');
should(
function() {
g.gain.setValueAtTime(1, curveStartTime + 1.1 * duration);
},
'setValueAtTime(1, ' + (curveStartTime + 1.1 * duration) + ')')
.notThrow();
task.done();
});
audit.define('automations', (task, should) => {
let context =
new OfflineAudioContext(1, testDurationFrames, sampleRate);
let g = context.createGain();
let curve = new Float32Array(2);
// Start time and duration for setValueCurveAtTime
let startTime = 0;
let timeInterval = testDurationSec / 10;
let time;
startTime += timeInterval;
should(() => {
g.gain.linearRampToValueAtTime(1, startTime);
}, 'linearRampToValueAtTime(1, ' + startTime + ')').notThrow();
startTime += timeInterval;
should(() => {
g.gain.exponentialRampToValueAtTime(1, startTime);
}, 'exponentialRampToValueAtTime(1, ' + startTime + ')').notThrow();
startTime += timeInterval;
should(() => {
g.gain.setTargetAtTime(1, startTime, 0.1);
}, 'setTargetAtTime(1, ' + startTime + ', 0.1)').notThrow();
startTime += timeInterval;
should(() => {
g.gain.setValueCurveAtTime(curve, startTime, 0.1);
}, 'setValueCurveAtTime(curve, ' + startTime + ', 0.1)').notThrow();
// Now try to setValueCurve that overlaps each of the above automations
startTime = timeInterval / 2;
for (let k = 0; k < 4; ++k) {
time = startTime + timeInterval * k;
should(
() => {
g.gain.setValueCurveAtTime(curve, time, 0.01);
},
'setValueCurveAtTime(curve, ' + time + ', 0.01)')
.throw(DOMException, 'NotSupportedError');
}
// Elements of setValueCurve should be finite.
should(
() => {
g.gain.setValueCurveAtTime(
Float32Array.from([NaN, NaN]), time, 0.01);
},
'setValueCurveAtTime([NaN, NaN], ' + time + ', 0.01)')
.throw(TypeError);
should(
() => {
g.gain.setValueCurveAtTime(
Float32Array.from([1, Infinity]), time, 0.01);
},
'setValueCurveAtTime([1, Infinity], ' + time + ', 0.01)')
.throw(TypeError);
let d = context.createDelay();
// Check that we get warnings for out-of-range values and also throw for
// non-finite values.
should(
() => {
d.delayTime.setValueCurveAtTime(
Float32Array.from([1, 5]), time, 0.01);
},
'delayTime.setValueCurveAtTime([1, 5], ' + time + ', 0.01)')
.notThrow();
should(
() => {
d.delayTime.setValueCurveAtTime(
Float32Array.from([1, 5, Infinity]), time, 0.01);
},
'delayTime.setValueCurveAtTime([1, 5, Infinity], ' + time +
', 0.01)')
.throw(TypeError);
// One last test that prints out lots of digits for the time.
time = Math.PI / 100;
should(
() => {
g.gain.setValueCurveAtTime(curve, time, 0.01);
},
'setValueCurveAtTime(curve, ' + time + ', 0.01)')
.throw(DOMException, 'NotSupportedError');
task.done();
});
audit.define('catch-exception', (task, should) => {
// Verify that the curve isn't inserted into the time line even if we
// catch the exception.
let success = true;
let context =
new OfflineAudioContext(1, testDurationFrames, sampleRate);
let gain = context.createGain();
let source = context.createBufferSource();
let buffer = context.createBuffer(1, 1, context.sampleRate);
buffer.getChannelData(0)[0] = 1;
source.buffer = buffer;
source.loop = true;
source.connect(gain);
gain.connect(context.destination);
gain.gain.setValueAtTime(1, 0);
try {
// The value curve has an invalid element. This automation shouldn't
// be inserted into the timeline at all.
gain.gain.setValueCurveAtTime(
Float32Array.from([0, NaN]), 128 / context.sampleRate, .5);
} catch (e) {
};
source.start();
context.startRendering()
.then(function(resultBuffer) {
// Since the setValueCurve wasn't inserted, the output should be
// exactly 1 for the entire duration.
should(
resultBuffer.getChannelData(0),
'Handled setValueCurve exception so output')
.beConstantValueOf(1);
})
.then(() => task.done());
});
audit.define('start-end', (task, should) => {
let context =
new OfflineAudioContext(1, testDurationFrames, sampleRate);
let g = context.createGain();
let curve = new Float32Array(2);
// Verify that a setValueCurve can start at the end of an automation.
let time = 0;
let timeInterval = testDurationSec / 50;
should(() => {
g.gain.setValueAtTime(1, time);
}, 'setValueAtTime(1, ' + time + ')').notThrow();
time += timeInterval;
should(() => {
g.gain.linearRampToValueAtTime(0, time);
}, 'linearRampToValueAtTime(0, ' + time + ')').notThrow();
// setValueCurve starts at the end of the linear ramp. This should be
// fine.
should(
() => {
g.gain.setValueCurveAtTime(curve, time, timeInterval);
},
'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')')
.notThrow();
// exponentialRamp ending one interval past the setValueCurve should be
// fine.
time += 2 * timeInterval;
should(() => {
g.gain.exponentialRampToValueAtTime(1, time);
}, 'exponentialRampToValueAtTime(1, ' + time + ')').notThrow();
// setValueCurve starts at the end of the exponential ramp. This should
// be fine.
should(
() => {
g.gain.setValueCurveAtTime(curve, time, timeInterval);
},
'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')')
.notThrow();
// setValueCurve at the end of the setValueCurve should be fine.
time += timeInterval;
should(
() => {
g.gain.setValueCurveAtTime(curve, time, timeInterval);
},
'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')')
.notThrow();
// setValueAtTime at the end of setValueCurve should be fine.
time += timeInterval;
should(() => {
g.gain.setValueAtTime(0, time);
}, 'setValueAtTime(0, ' + time + ')').notThrow();
// setValueCurve at the end of setValueAtTime should be fine.
should(
() => {
g.gain.setValueCurveAtTime(curve, time, timeInterval);
},
'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')')
.notThrow();
// setTarget starting at the end of setValueCurve should be fine.
time += timeInterval;
should(() => {
g.gain.setTargetAtTime(1, time, 1);
}, 'setTargetAtTime(1, ' + time + ', 1)').notThrow();
task.done();
});
audit.define('curve overlap', (task, should) => {
let context =
new OfflineAudioContext(1, testDurationFrames, sampleRate);
let g = context.createGain();
let startTime = 5;
let startTimeLater = 10;
let startTimeEarlier = 2.5;
let curveDuration = 10;
let curveDurationShorter = 5;
let curve = [1, 2, 3];
// An initial curve event
should(
() => {
g.gain.setValueCurveAtTime(curve, startTime, curveDuration);
},
`g.gain.setValueCurveAtTime([${curve}], ${startTime}, ${curveDuration})`)
.notThrow();
// Check that an exception is thrown when trying to overlap two curves,
// in various ways
// Same start time and end time (curve exactly overlapping)
should(
() => {
g.gain.setValueCurveAtTime(curve, startTime, curveDuration);
},
`second g.gain.setValueCurveAtTime([${curve}], ${startTime}, ${curveDuration})`)
.throw(DOMException, 'NotSupportedError');
// Same start time, shorter end time
should(
() => {
g.gain.setValueCurveAtTime(curve, startTime, curveDurationShorter);
},
`g.gain.setValueCurveAtTime([${curve}], ${startTime}, ${curveDurationShorter})`)
.throw(DOMException, 'NotSupportedError');
// Earlier start time, end time after the start time an another curve
should(
() => {
g.gain.setValueCurveAtTime(curve, startTimeEarlier, curveDuration);
},
`g.gain.setValueCurveAtTime([${curve}], ${startTimeEarlier}, ${curveDuration})`)
.throw(DOMException, 'NotSupportedError');
// Start time after the start time of the other curve, but earlier than
// its end.
should(
() => {
g.gain.setValueCurveAtTime(curve, startTimeLater, curveDuration);
},
`g.gain.setValueCurveAtTime([${curve}], ${startTimeLater}, ${curveDuration})`)
.throw(DOMException, 'NotSupportedError');
// New event wholly contained inside existing event
should(
() => {
g.gain.setValueCurveAtTime(curve, startTime + 1, curveDuration - 1);
},
`g.gain.setValueCurveAtTime([${curve}], ${startTime+1}, ${curveDuration-1})`)
.throw(DOMException, 'NotSupportedError');
// Old event completely contained inside new event
should(
() => {
g.gain.setValueCurveAtTime(curve, startTime - 1, curveDuration + 1);
},
`g.gain.setValueCurveAtTime([${curve}], ${startTime-1}, ${curveDuration+1})`)
.throw(DOMException, 'NotSupportedError');
// Setting an event exactly at the end of the curve should work.
should(
() => {
g.gain.setValueAtTime(1.0, startTime + curveDuration);
},
`g.gain.setValueAtTime(1.0, ${startTime + curveDuration})`)
.notThrow();
task.done();
});
audit.define('curve lengths', (task, should) => {
let context =
new OfflineAudioContext(1, testDurationFrames, sampleRate);
let g = context.createGain();
let time = 0;
// Check for invalid curve lengths
should(
() => {
g.gain.setValueCurveAtTime(Float32Array.from([]), time, 0.01);
},
'setValueCurveAtTime([], ' + time + ', 0.01)')
.throw(DOMException, 'InvalidStateError');
should(
() => {
g.gain.setValueCurveAtTime(Float32Array.from([1]), time, 0.01);
},
'setValueCurveAtTime([1], ' + time + ', 0.01)')
.throw(DOMException, 'InvalidStateError');
should(() => {
g.gain.setValueCurveAtTime(Float32Array.from([1, 2]), time, 0.01);
}, 'setValueCurveAtTime([1,2], ' + time + ', 0.01)').notThrow();
task.done();
});
audit.run();
</script>
</body>
</html>