Skip to content

Commit 703601b

Browse files
committed
AudioBufferSourceNode.start with duration fails sometimes
https://bugs.webkit.org/show_bug.cgi?id=248658 rdar://103178030 Reviewed by Eric Carlson and Jer Noble. Per the specification [1], if you call AudioBufferNode.start() with a `when` value that is less than the AudioContext's currentTime, it should start immediately. Previously, we were missing this logic, causing the AudioBufferNode to not start if the `when` value was in the past. [1] https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-start-when-offset-duration-when * LayoutTests/webaudio/audiobuffersource-start-when-in-past-expected.txt: Added. * LayoutTests/webaudio/audiobuffersource-start-when-in-past.html: Added. * Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp: (WebCore::AudioBufferSourceNode::startPlaying): Canonical link: https://commits.webkit.org/259234@main
1 parent 419bb9a commit 703601b

File tree

3 files changed

+93
-1
lines changed

3 files changed

+93
-1
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Tests AudioBufferSourceNode starts immediately if the `when` value passed to start() is in the past.
2+
3+
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
4+
5+
6+
PASS First half of the rendered data is identical to the source buffer
7+
PASS Second half of the rendered data is identical to the source buffer as well
8+
PASS successfullyParsed is true
9+
10+
TEST COMPLETE
11+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="resources/audio-testing.js"></script>
5+
<script src="resources/audiobuffersource-testing-legacy.js"></script>
6+
<script src="../resources/js-test.js"></script>
7+
</head>
8+
<body>
9+
<script>
10+
description("Tests AudioBufferSourceNode starts immediately if the `when` value passed to start() is in the past.");
11+
jsTestIsAsync = true;
12+
13+
const numberOfFrames = 256;
14+
const sourceBufferSize = numberOfFrames / 2;
15+
const sampleRate = 44100.;
16+
17+
let context = new OfflineAudioContext(1, numberOfFrames, sampleRate);
18+
let source = null;
19+
20+
function createSourceNode()
21+
{
22+
if (source) {
23+
source.disconnect();
24+
source.stop();
25+
}
26+
source = context.createBufferSource();
27+
source.buffer = createTestBuffer(context, numberOfFrames / 2);
28+
source.loop = true;
29+
source.connect(context.destination);
30+
source.start(0, 0, sourceBufferSize / sampleRate);
31+
}
32+
33+
function convertToRegularJSArray(data, offset, count)
34+
{
35+
let array = new Array();
36+
let outputIndex = 0;
37+
for (let i = offset; i < offset + count; ++i)
38+
array[outputIndex++] = data[i];
39+
return array;
40+
}
41+
42+
context.suspend(sourceBufferSize / sampleRate).then(() => {
43+
createSourceNode();
44+
context.resume();
45+
});
46+
createSourceNode();
47+
context.startRendering().then((renderBuffer) => {
48+
let renderedData = renderBuffer.getChannelData(0);
49+
let expectedData = source.buffer.getChannelData(0);
50+
let success = true;
51+
for (var i = 0; i < expectedData.length; ++i) {
52+
if (expectedData[i] != renderedData[i]) {
53+
var s = "First half: expected: " + convertToRegularJSArray(expectedData, 0, sourceBufferSize) + " actual: " + convertToRegularJSArray(renderedData, 0, sourceBufferSize);
54+
testFailed(s);
55+
success = false;
56+
break;
57+
}
58+
}
59+
if (success)
60+
testPassed("First half of the rendered data is identical to the source buffer");
61+
62+
success = true;
63+
for (var i = 0; i < expectedData.length; ++i) {
64+
if (expectedData[i] != renderedData[sourceBufferSize + i]) {
65+
var s = "Second half: expected: " + convertToRegularJSArray(expectedData, 0, sourceBufferSize) + " actual: " + convertToRegularJSArray(renderedData, sourceBufferSize, sourceBufferSize);
66+
testFailed(s);
67+
success = false;
68+
break;
69+
}
70+
}
71+
if (success)
72+
testPassed("Second half of the rendered data is identical to the source buffer as well");
73+
74+
finishJSTest();
75+
});
76+
</script>
77+
</body>
78+
</html>

Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,10 @@ ExceptionOr<void> AudioBufferSourceNode::startPlaying(double when, double grainO
505505
m_grainOffset = grainOffset;
506506
m_grainDuration = grainDuration.value_or(0);
507507
m_wasGrainDurationGiven = !!grainDuration;
508-
m_startTime = when;
508+
509+
// If 0 is passed in for |when| or if the value is less than currentTime, then the sound will start playing immediately.
510+
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-start-when-offset-duration-when
511+
m_startTime = std::max(when, context().currentTime());
509512

510513
adjustGrainParameters();
511514

0 commit comments

Comments
 (0)