mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
41e75663f1
Moves all audio playback code (Symphonia decode, rodio output, HTTP
streaming, gapless, previews, and the seven stream/ source-type
submodules from the prior split) out of the top crate into a new
psysonic-audio crate.
crates/psysonic-audio/ new lib crate, depends on
psysonic-core + psysonic-analysis
src/{engine,helpers,decode,…}.rs flattened layout (no more
extra audio/ namespace level)
src/stream/ seven submodules from M0
src/lib.rs re-exports macros from
psysonic-core and the public
API surface
The audio↔analysis edges identified in the dep survey are now real
crate deps (audio depends on analysis directly: AnalysisCache reads,
recommended_gain_for_target, submit_analysis_cpu_seed). Only the
analysis→audio back-edge goes through the PlaybackQueryHandle port
registered in M2.
Cross-crate ref migrations applied via batch sed:
crate::audio::* → crate::* (intra-crate)
crate::analysis_cache::* → psysonic_analysis::analysis_cache::*
crate::submit_analysis_cpu_seed → psysonic_analysis::analysis_runtime::*
crate::subsonic_wire_user_agent → psysonic_core::user_agent::*
Top crate keeps `crate::audio::*` paths working via
`pub use psysonic_audio as audio;` — lib_commands/cli callers untouched.
`stop_audio_engine` (mac process-exit cleanup) moved into the audio
crate as `pub fn stop_audio_engine` since it reaches AudioEngine
internals; tray.rs now re-exports the moved fn.
Two small visibility promotions in engine.rs:
pub(crate) fn analysis_track_id_is_current_playback → pub
pub(crate) fn ranged_loudness_backfill_should_defer → pub
Behaviour preserving. Cargo check + clippy --workspace clean.
46 lines
1.5 KiB
Rust
46 lines
1.5 KiB
Rust
//! Reopen CPAL/rodio output after system sleep/resume when the old stream can be silent
|
|
//! while the reported default device name is unchanged (Windows WASAPI, Linux PipeWire/ALSA, etc.).
|
|
|
|
use std::sync::Mutex;
|
|
use std::time::{Duration, Instant};
|
|
|
|
use tauri::AppHandle;
|
|
use tauri::Manager;
|
|
|
|
use super::device_watcher::{reopen_output_stream, ReopenNotify};
|
|
use super::engine::AudioEngine;
|
|
|
|
static RESUME_REOPEN_DEBOUNCE: Mutex<Option<Instant>> = Mutex::new(None);
|
|
const DEBOUNCE: Duration = Duration::from_millis(900);
|
|
|
|
/// Returns false if this resume should be ignored (coalesce bursts from the OS).
|
|
pub(crate) fn debounce_allow_resume_reopen() -> bool {
|
|
let mut g = RESUME_REOPEN_DEBOUNCE.lock().unwrap();
|
|
let now = Instant::now();
|
|
if let Some(t) = *g {
|
|
if now.duration_since(t) < DEBOUNCE {
|
|
return false;
|
|
}
|
|
}
|
|
*g = Some(now);
|
|
true
|
|
}
|
|
|
|
/// Delay so the audio stack re-enumerates before we open a new stream.
|
|
pub(crate) async fn reopen_audio_after_system_resume(app: &AppHandle) {
|
|
tokio::time::sleep(Duration::from_millis(400)).await;
|
|
|
|
let device_name = match app.try_state::<AudioEngine>() {
|
|
Some(e) => e.selected_device.lock().unwrap().clone(),
|
|
None => return,
|
|
};
|
|
|
|
if reopen_output_stream(app, device_name, ReopenNotify::DeviceChanged).await {
|
|
crate::app_eprintln!("[psysonic] audio output reopened after system resume");
|
|
} else {
|
|
crate::app_eprintln!(
|
|
"[psysonic] audio: stream reopen failed or timed out after system resume"
|
|
);
|
|
}
|
|
}
|