The prose describing the offset argument of start() states the negative-playbackRate clamp with the comparison inverted relative to the normative playback algorithm. Read literally it makes the offset argument almost useless whenever loop is true and the rate is negative. It appears to be a transcription error from #2031.
Current text
From the offset argument of start():
If offset is greater than loopEnd, playbackRate is positive or zero, and loop is true, playback will begin at loopEnd. If offset is greater than loopStart, playbackRate is negative, and loop is true, playback will begin at loopStart.
What the normative algorithm says
From the playback algorithm:
if (!started) {
if (loop && computedPlaybackRate >= 0 && offset >= actualLoopEnd) {
offset = actualLoopEnd;
}
if (computedPlaybackRate < 0 && loop && offset < actualLoopStart) {
offset = actualLoopStart;
}
bufferTime = offset;
started = true;
}
| Rate | Algorithm clamps when | Prose says clamp when |
|---|---|---|
| positive or zero | `offset >= actualLoopEnd` | `offset` greater than `loopEnd` ✓ |
| negative | `offset < actualLoopStart` | `offset` greater than `loopStart` ✗ |
The positive-rate sentence agrees with the algorithm. The negative-rate sentence inverts it.
Why this looks like a typo rather than an intentional rule
Issue #2031, which requested this sentence, asked for the opposite comparison:
The similar instruction "For negative playbackRate, if offset is less than loopStart, playback will begin at loopStart (and immediately loop to loopEnd)" applies in the other case.
Implementations and tests
webaudio/the-audio-api/the-audiobuffersourcenode-interface/audiobuffersource-playbackrate-negative.html in WPT covers both directions, and encodes the algorithm's reading:
- "AudioBufferSourceNode clamps offset to loopStart for playbackRate < 0" — 8-frame buffer,
loopStart frame 4, loopEnd frame 6, offset frame 2. Since the offset is below loopStart, it is clamped, expecting [5, 6, 5, 6].
- "AudioBufferSourceNode loops backwards with negative rate (offset in loop)" — the configuration in the table above, where the offset is above
loopStart. It is not clamped, expecting [3, 2, 3, 2, …].
Under the prose reading the second test would expect [2, 3, 2, 3, …].
Implementation status:
- Chrome — clamps only when the playhead is below the loop start (
if (is_looping_ && virtual_read_index < virtual_start_frame) in AudioBufferSourceHandler), matching the algorithm.
- WebKit — currently begins playback at
loopStart in this configuration, matching the prose reading, and fails the second subtest above.
- Firefox — It seems it does not implement negative
playbackRate at all?; ComputeFinalOutSampleRate returns the buffer sample rate when the computed rate is not positive, so a negative rate plays forward at 1×. Not informative for this question.
The prose describing the
offsetargument ofstart()states the negative-playbackRateclamp with the comparison inverted relative to the normative playback algorithm. Read literally it makes theoffsetargument almost useless wheneverloopistrueand the rate is negative. It appears to be a transcription error from #2031.Current text
From the
offsetargument ofstart():What the normative algorithm says
From the playback algorithm:
The positive-rate sentence agrees with the algorithm. The negative-rate sentence inverts it.
Why this looks like a typo rather than an intentional rule
Issue #2031, which requested this sentence, asked for the opposite comparison:
Implementations and tests
webaudio/the-audio-api/the-audiobuffersourcenode-interface/audiobuffersource-playbackrate-negative.htmlin WPT covers both directions, and encodes the algorithm's reading:loopStartframe 4,loopEndframe 6, offset frame 2. Since the offset is belowloopStart, it is clamped, expecting[5, 6, 5, 6].loopStart. It is not clamped, expecting[3, 2, 3, 2, …].Under the prose reading the second test would expect
[2, 3, 2, 3, …].Implementation status:
if (is_looping_ && virtual_read_index < virtual_start_frame)inAudioBufferSourceHandler), matching the algorithm.loopStartin this configuration, matching the prose reading, and fails the second subtest above.playbackRateat all?;ComputeFinalOutSampleRatereturns the buffer sample rate when the computed rate is not positive, so a negative rate plays forward at 1×. Not informative for this question.