blob: 476df826ecdc878a0f92190f3b52557b66ae6b5c [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>
audiobuffersource-loop-comprehensive.html
</title>
<script src="../../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/audiobuffersource-testing.js"></script>
</head>
<body>
<script id="layout-test-code">
let audit = Audit.createTaskRunner();
// The following test cases assume an AudioBuffer of length 8 whose PCM
// data is a linear ramp, 0, 1, 2, 3,... |description| is optional and
// will be computed from the other parameters. |offsetFrame| is optional
// and defaults to 0.
let tests = [
{
description:
'loop whole buffer by default with loopStart == loopEnd == 0',
loopStartFrame: 0,
loopEndFrame: 0,
renderFrames: 16,
playbackRate: 1,
expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7]
},
{
description: 'loop whole buffer explicitly',
loopStartFrame: 0,
loopEndFrame: 8,
renderFrames: 16,
playbackRate: 1,
expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7]
},
{
description: 'loop from middle to end of buffer',
loopStartFrame: 4,
loopEndFrame: 8,
renderFrames: 16,
playbackRate: 1,
expected: [0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7]
},
{
description: 'loop from start to middle of buffer',
loopStartFrame: 0,
loopEndFrame: 4,
renderFrames: 16,
playbackRate: 1,
expected: [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]
},
{
loopStartFrame: 4,
loopEndFrame: 6,
renderFrames: 16,
playbackRate: 1,
expected: [0, 1, 2, 3, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5]
},
{
loopStartFrame: 3,
loopEndFrame: 7,
renderFrames: 16,
playbackRate: 1,
expected: [0, 1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 3, 4, 5, 6, 3]
},
{
loopStartFrame: 4,
loopEndFrame: 6,
renderFrames: 16,
playbackRate: 0.5,
expected:
[0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 4, 4.5, 5, 5.5]
},
{
loopStartFrame: 4,
loopEndFrame: 6,
renderFrames: 16,
playbackRate: 1.5,
expected:
[0, 1.5, 3, 4.5, 4, 5.5, 5, 4.5, 4, 5.5, 5, 4.5, 4, 5.5, 5, 4.5]
},
// Offset past loop end, so playback starts at loop start
{
loopStartFrame: 2,
loopEndFrame: 5,
renderFrames: 16,
playbackRate: 1,
offsetFrame: 6,
expected: [2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2]
},
// Offset before loop start, so start at offset and continue
{
loopStartFrame: 3,
loopEndFrame: 6,
renderFrames: 16,
playbackRate: 1,
offsetFrame: 1,
expected: [1, 2, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4]
},
// Offset between loop start and loop end, so start at offset and
// continue
{
loopStartFrame: 3,
loopEndFrame: 6,
renderFrames: 16,
playbackRate: 1,
offsetFrame: 4,
expected: [4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4]
},
{
description: 'illegal playbackRate of 47 greater than loop length',
loopStartFrame: 4,
loopEndFrame: 6,
renderFrames: 16,
playbackRate: 47,
expected: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
},
// Try illegal loop-points - they should be ignored and we'll loop the
// whole buffer.
{
description: 'illegal loop: loopStartFrame > loopEndFrame',
loopStartFrame: 7,
loopEndFrame: 3,
renderFrames: 16,
playbackRate: 1,
expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7]
},
{
description: 'illegal loop: loopStartFrame == loopEndFrame',
loopStartFrame: 3,
loopEndFrame: 3,
renderFrames: 16,
playbackRate: 1,
expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7]
},
{
description: 'illegal loop: loopStartFrame < 0',
loopStartFrame: -8,
loopEndFrame: 3,
renderFrames: 16,
playbackRate: 1,
expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7]
},
{
description: 'illegal loop: loopEndFrame > bufferLength',
loopStartFrame: 0,
loopEndFrame: 30000,
renderFrames: 16,
playbackRate: 1,
expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7]
},
// Start a loop with a duration longer than the buffer. The output
// should be the data from frame 1 to 6, and then looping from 3 to 5
// until 20 frames have been played.
{
description: 'loop from 3 -> 6 with offset 1 for 20 frames',
loopStartFrame: 3,
loopEndFrame: 6,
playbackRate: 1,
offsetFrame: 1,
renderFrames: 30,
durationFrames: 20,
expected: [
1, 2, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3,
4, 5, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
},
// Start a loop with a duration less than the length of the looping
// frames. The output should be the data from frame 1 to 3, and then
// stopping because duration = 3
{
description: 'loop from 3 -> 8 with offset 1 for 3 frames',
loopStartFrame: 3,
loopEndFrame: 8,
playbackRate: 1,
offsetFrame: 1,
durationFrames: 3,
renderFrames: 30,
expected: [
1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
},
// Start a loop with a duration less than the length of the looping
// frames. The output should be the data from frame 1 to 3, and then
// stopping because duration = 3
{
description: 'loop from 3 -> 8 with offset 7 for 3 frames',
loopStartFrame: 3,
loopEndFrame: 8,
playbackRate: 1,
offsetFrame: 7,
durationFrames: 3,
renderFrames: 30,
expected: [
7, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
}
];
let sampleRate = 32768;
let buffer;
let bufferFrameLength = 8;
let testSpacingFrames = 32;
let testSpacingSeconds = testSpacingFrames / sampleRate;
let totalRenderLengthFrames = tests.length * testSpacingFrames;
function runLoopTest(context, testNumber, test, should) {
let source = context.createBufferSource();
source.buffer = buffer;
source.playbackRate.value = test.playbackRate;
source.loop = true;
source.loopStart = test.loopStartFrame / context.sampleRate;
source.loopEnd = test.loopEndFrame / context.sampleRate;
let offset =
test.offsetFrame ? test.offsetFrame / context.sampleRate : 0;
source.connect(context.destination);
// Render each test one after the other, spaced apart by
// testSpacingSeconds.
let startTime = testNumber * testSpacingSeconds;
// If durationFrames is given, run the test for the specified duration.
if (test.durationFrames) {
if (!test.renderFrames) {
throw(
'renderFrames is required for test ' + testNumber + ': ' +
test.description);
} else {
if (test.durationFrames > testSpacingFrames ||
test.durationFrames < 0) {
throw(
'Test ' + testNumber + ': durationFrames (' +
test.durationFrames + ') outside the range [0, ' +
testSpacingFrames + ']');
}
source.start(
startTime, offset, test.durationFrames / context.sampleRate);
}
} else if (test.renderFrames) {
let duration = test.renderFrames / context.sampleRate;
if (test.renderFrames > testSpacingFrames || test.renderFrames < 0) {
throw(
'Test ' + testNumber + ': renderFrames (' + test.renderFrames +
') outside the range [0, ' + testSpacingFrames + ']');
}
source.start(startTime, offset);
source.stop(startTime + duration);
} else {
throw(
'Test ' + testNumber +
' must specify renderFrames and possibly durationFrames');
}
}
audit.define('AudioBufferSource looping test', function(task, should) {
// Create offline audio context.
let context =
new OfflineAudioContext(1, totalRenderLengthFrames, sampleRate);
buffer = createTestBuffer(context, bufferFrameLength);
should(function() {
for (let i = 0; i < tests.length; ++i)
runLoopTest(context, i, tests[i], should);
}, 'Generate ' + tests.length + ' test cases').notThrow();
context.startRendering().then(function(audioBuffer) {
checkAllTests(audioBuffer, should);
task.done();
});
});
audit.run();
</script>
</body>
</html>