fix(player): resume playback-state tracking when gapless lands late
When a track ends before its gapless prefetch completes, 'track finished (engine empty)' sets thread_state.is_playing=false, and the late PlayNext still plays (the engine's append re-opens the writer gate) but nothing restores the player-level flag: the position monitor freezes, no next gapless arm, no QConnect reports, the queue cursor stays on the finished track, and playback dies silently at the next end. The PlayNext handler now captures engine.empty() before appending and restores is_playing when the append landed on an empty engine — a user-paused engine still holds its source (not empty), so manual pause is never resumed by this. km
This commit is contained in:
1 file changed
+23
-1
@@ -3630,7 +3630,21 @@ impl Player {
|
||||
&analyzer_enabled,
|
||||
);
|
||||
|
||||
// Append to existing Sink (gapless queue)
|
||||
// Append to existing Sink (gapless queue).
|
||||
// Late-gapless race: this append can arrive AFTER
|
||||
// the engine went empty at natural end ("track
|
||||
// finished" already set thread_state.is_playing =
|
||||
// false). The engine's own append re-opens its
|
||||
// writer gate and the track PLAYS, but the
|
||||
// player-level flag stays false — freezing the
|
||||
// position monitor, the next gapless arm, the
|
||||
// QConnect reports and the queue cursor on the
|
||||
// finished track while the next one plays (the
|
||||
// "invisible track" desync). Capture emptiness
|
||||
// BEFORE the append: a user-paused engine still
|
||||
// holds its current source (NOT empty), so a
|
||||
// paused session is never resumed by this.
|
||||
let engine_was_empty = engine.empty();
|
||||
if let Err(e) = engine.append(source) {
|
||||
log::error!(
|
||||
"Gapless: failed to append track {} to engine: {}",
|
||||
@@ -3640,6 +3654,14 @@ impl Player {
|
||||
thread_state.set_gapless_ready(false);
|
||||
return;
|
||||
}
|
||||
if engine_was_empty
|
||||
&& !thread_state.is_playing.load(Ordering::SeqCst)
|
||||
{
|
||||
log::info!(
|
||||
"Gapless: PlayNext landed after track finished — resuming playback-state tracking"
|
||||
);
|
||||
thread_state.is_playing.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
// Store pending gapless data for transition detection
|
||||
*gapless_pending = Some(GaplessPending {
|
||||
|
||||
Reference in new issue
Block a user