Repository navigation
fix(player): play the end of a track out of the ring before the next one starts - #933
Merged
Merged
Conversation
…one starts The decoder runs a ring (1 s at 48 kHz stereo) ahead of the output. A natural end was declared at the decoder's EOF and the advance's LoadAndPlay drained what was still queued, cutting the last second of every track; the gapless and crossfade triggers read the heard clock, so the gapless swap never ran. The triggers now read the decoder's position and both play loops wait for the ring to play out before declaring the end. Refs #932
A pause blocks the decoder in drain_commands, so the tail wait counted the paused time as a stall and cut the rest on resume; a gap between checks now restarts the stall clock. An A-B jump made while the end plays out cleared nothing and ended the track; it now resets the EOF state like a seek.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #932, its first point: "every track stops about 1 second before its real end and the next track starts immediately". The second point, single-click play, is being clarified with the reporter.
Cause
The decoder runs up to a ring ahead of the output.
RING_CAPACITYis 96 000 samples: 1 s at 48 kHz stereo, 0.27 s on an 8-channel mixer at 44.1 kHz. Two things followed from that, and together they cut the end of every track that had another one after it.LoadAndPlay, and itsunpause_and_drain_ring(right for a skip) threw that audio away.current_position_ms), a ring behind the decoder:This has been the behaviour since the drain on load arrived in April, so it is not a 1.9.5 regression. The last track of a queue was spared, because nothing follows it.
Fix
SharedPlayback::decoded_position_ms(queued): where the decoder is, counted from the raw counters as samples played plus samples queued, minus the lead-in. It is not computed as the heard position plus the queue, because the heard position stops at 0 while a gapless lead-in plays, and adding the queue to it would count the finished track's tail as the new track's. Both triggers now read it.TailDrain: at EOF,play_trackandplay_dop_trackwait until the output has played the ring out, then emitplayer:track-ended. While they wait:As a side effect, the sleep timer's end of track and
listened_msnow match the end that was heard.Docs:
docs/features/playback.mdexplains both rules and the measurement.Checks
End-to-end measurement: sandboxed debug builds of
mainand of this branch were driven over CDP. A queue of three FLAC files was played, then seeked to 4 s before the end of the first one (139.2 s long). The test machine's mixer is 8 channels, so its ring is 0.27 s.mainTests:
decoded_position_ms: one test for the ring ahead, including at double speed, and one for the gapless lead-in;TailDrain: an empty ring, a ring being played out, a pause, and a stall.The audio module's 179 tests pass on macOS, where the app crate's test binary runs.
cargo fmt --checkandcargo clippy --workspace --all-targetspass on Windows and on macOS (-D warnings).The local CodeRabbit review found the lead-in miscount described above, which is now fixed.
Known side effect
Now that the gapless swap actually runs, the analytics
CrossfadeStartedit sends (title, cover) arrives at the decoder's EOF, so about a ring before the next track is heard. The position stays at 0 through the lead-in, so lyrics and the seek bar are not ahead. Moving that UI switch is left for a follow-up.Summary by CodeRabbit