fix(audio): reset clock.force-rate on stop for all forced sessions (#263 leak)
QBZ forces the global PipeWire clock for any non-locked PipeWire stream, but the stop/pause-suspend reset was gated on pw_force_bitperfect — which is only set in DAC-passthrough mode. So a plain PipeWire user (no passthrough) forced the graph clock on play and never reset it, leaving the whole graph force-clocked after stopping (a stale clock.force-rate that resamples every other app). Track whether QBZ actually wrote the force (CLOCK_FORCE_APPLIED) and make reset_pipewire_clock self-gating: it only resets a force WE applied, never clobbering another app's forced rate. The player's two reset call sites (Stop, pause-suspend) now call it unconditionally. Verified on hardware (Cambridge USB DAC, PipeWire + passthrough off): a 192kHz track forces clock.force-rate=192000; on pause-suspend the clock returns to 0 (previously stayed stuck at 192000). cc
This commit is contained in:
2 files changed
+31
-19
No files matched your search
@@ -2591,17 +2591,13 @@ impl Player {
|
||||
*pause_suspend_deadline = Some(
|
||||
Instant::now() + Duration::from_millis(PAUSE_SUSPEND_DELAY_MS),
|
||||
);
|
||||
// Reset PipeWire clock if bit-perfect was active
|
||||
// Reset the PipeWire clock if WE forced it. The call is
|
||||
// self-gating (reset_pipewire_clock no-ops unless QBZ set
|
||||
// the force), so it is now unconditional: the previous
|
||||
// pw_force_bitperfect gate missed plain (no-passthrough)
|
||||
// PipeWire users and leaked a forced rate after stop (#263).
|
||||
#[cfg(target_os = "linux")]
|
||||
if thread_settings
|
||||
.lock()
|
||||
.ok()
|
||||
.map(|s| s.pw_force_bitperfect)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
qbz_audio::pipewire_backend::PipeWireBackend::reset_pipewire_clock(
|
||||
);
|
||||
}
|
||||
qbz_audio::pipewire_backend::PipeWireBackend::reset_pipewire_clock();
|
||||
log::info!("Audio thread: stopped");
|
||||
}
|
||||
AudioCommand::SetVolume(volume) => {
|
||||
@@ -3228,16 +3224,12 @@ impl Player {
|
||||
}
|
||||
drop(stream_opt.take());
|
||||
pause_suspend_deadline = None;
|
||||
// Reset PipeWire clock if bit-perfect was active
|
||||
// Reset the PipeWire clock if WE forced it (self-gating;
|
||||
// see reset_pipewire_clock). Unconditional now — the
|
||||
// previous pw_force_bitperfect gate leaked a forced rate
|
||||
// for plain (no-passthrough) PipeWire users (#263).
|
||||
#[cfg(target_os = "linux")]
|
||||
if thread_settings
|
||||
.lock()
|
||||
.ok()
|
||||
.map(|s| s.pw_force_bitperfect)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
qbz_audio::pipewire_backend::PipeWireBackend::reset_pipewire_clock();
|
||||
}
|
||||
qbz_audio::pipewire_backend::PipeWireBackend::reset_pipewire_clock();
|
||||
log::info!("Audio thread: suspended stream after pause");
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user