blob: b012b24eca3c282936a308aff0de19ff37e4e206 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<script src="resources/audio-testing.js"></script>
<script src="resources/compatibility.js"></script>
<script src="resources/audiobuffersource-testing.js"></script>
<script src="../resources/js-test.js"></script>
</head>
<body>
<script>
description("Tests AudioBufferSourceNode looping with a variety of loop points.");
// 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.
var 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] }
];
var sampleRate = 44100;
var buffer;
var bufferFrameLength = 8;
var testSpacingFrames = 32;
var testSpacingSeconds = testSpacingFrames / sampleRate;
var totalRenderLengthFrames = tests.length * testSpacingFrames;
function runLoopTest(context, testNumber, test) {
var 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;
var offset = test.offsetFrame ? test.offsetFrame / context.sampleRate : 0;
source.connect(context.destination);
// Render each test one after the other, spaced apart by testSpacingSeconds.
var startTime = testNumber * testSpacingSeconds;
// If durationFrames is given, run the test for the specified duration.
if (test.durationFrames) {
if (!test.renderFrames) {
testFailed("renderFrames is required for test " + testNumber + ": " + test.description);
} else {
if (test.durationFrames > testSpacingFrames || test.durationFrames < 0) {
testFailed("Test " + testNumber
+ ": durationFrames (" + test.durationFrames + ") outside the range [0, "
+ testSpacingFrames + "]");
}
source.start(startTime, offset, test.durationFrames / context.sampleRate);
}
} else if (test.renderFrames) {
var duration = test.renderFrames / context.sampleRate;
if (test.renderFrames > testSpacingFrames || test.renderFrames < 0) {
testFailed("Test " + testNumber
+ ": renderFrames (" + test.renderFrames + ") outside the range [0, "
+ testSpacingFrames + "]");
}
source.start(startTime, offset);
source.stop(startTime + duration);
} else {
testFailed("Test " + testNumber + " must specify renderFrames and possibly durationFrames");
}
}
function runTest() {
window.jsTestIsAsync = true;
// Create offline audio context.
var context = new OfflineAudioContext(1, totalRenderLengthFrames, sampleRate);
buffer = createTestBuffer(context, bufferFrameLength);
for (var i = 0; i < tests.length; ++i)
runLoopTest(context, i, tests[i]);
context.oncomplete = checkAllTests;
context.startRendering();
}
runTest();
successfullyParsed = true;
</script>
</body>
</html>