fix: gapless toggle now actually controls audio output lifecycle

When gapless is off, the AudioOutput is dropped after each track ends
naturally, producing a real gap on the next play. When on, the output
stays alive so tracks transition seamlessly. Also re-adds URL prefetch
gating behind the same toggle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
joren
2026-03-24 11:39:24 +01:00
parent c035ce2dee
commit 1e4c234b5c
9 changed files with 31 additions and 3 deletions

View File

@@ -74,6 +74,7 @@ int qobuz_backend_take_track_finished(QobuzBackendOpaque *backend);
// ReplayGain / Gapless
void qobuz_backend_set_replaygain(QobuzBackendOpaque *backend, bool enabled);
void qobuz_backend_set_gapless(QobuzBackendOpaque *backend, bool enabled);
void qobuz_backend_prefetch_track(QobuzBackendOpaque *backend, int64_t track_id, int32_t format_id);
// Playlist management

View File

@@ -478,6 +478,11 @@ pub unsafe extern "C" fn qobuz_backend_set_replaygain(ptr: *mut Backend, enabled
(*ptr).0.replaygain_enabled.store(enabled, std::sync::atomic::Ordering::Relaxed);
}
#[no_mangle]
pub unsafe extern "C" fn qobuz_backend_set_gapless(ptr: *mut Backend, enabled: bool) {
(*ptr).0.player.status.gapless.store(enabled, std::sync::atomic::Ordering::Relaxed);
}
#[no_mangle]
pub unsafe extern "C" fn qobuz_backend_prefetch_track(
ptr: *mut Backend,

View File

@@ -51,6 +51,8 @@ pub struct PlayerStatus {
pub seek_target_secs: Arc<AtomicU64>,
/// Linear gain factor to apply (1.0 = unity). Updated each time a new track starts.
pub replaygain_gain: Arc<std::sync::Mutex<f32>>,
/// When false the audio output is torn down after each track, producing a gap.
pub gapless: Arc<AtomicBool>,
}
impl PlayerStatus {
@@ -65,6 +67,7 @@ impl PlayerStatus {
seek_requested: Arc::new(AtomicBool::new(false)),
seek_target_secs: Arc::new(AtomicU64::new(0)),
replaygain_gain: Arc::new(std::sync::Mutex::new(1.0)),
gapless: Arc::new(AtomicBool::new(false)),
}
}
@@ -197,7 +200,10 @@ fn player_loop(rx: std::sync::mpsc::Receiver<PlayerCommand>, status: PlayerStatu
pending_info = Some(next_info);
}
Ok(None) => {
// Track finished naturally
// Track finished naturally — tear down audio output if gapless is off
if !status.gapless.load(Ordering::Relaxed) {
audio_output = None;
}
*status.state.lock().unwrap() = PlayerState::Idle;
status.track_finished.store(true, Ordering::SeqCst);
}