forked from Keelrleek/SoulStream
The previous .git directory was found gutted (only info/ remained), so history is unrecoverable locally. This commit restores version control with the tree exactly as it stands: the working app, the preview-tracks launcher on the artist hero, the scrollable-track-previews design bundle (docs + approved mockups), and the Phase 0 validation artifacts.
101 lines
3.6 KiB
Rust
101 lines
3.6 KiB
Rust
//! Phase 0.3 — native transport probe (isolated diagnostic, not feature code).
|
|
//!
|
|
//! Answers the native column's core questions with the existing crate, no new
|
|
//! dependencies:
|
|
//!
|
|
//! 1. Can a SECOND `Player` instance coexist with a "main" one — two sinks,
|
|
//! two event channels, no shared state?
|
|
//! 2. Does a real Deezer preview (MPEG-1 Layer III, 128k CBR, ~480 KB)
|
|
//! decode and play through rodio/symphonia?
|
|
//! 3. Do pause / resume / stop behave on the second instance while the
|
|
//! first is mid-track, without cross-talk on either event channel?
|
|
//!
|
|
//! The preview file is downloaded OUTSIDE this probe (curl) and passed as
|
|
//! argv[1]; argv[2] is any long local track for the "main" side. Run:
|
|
//!
|
|
//! cargo run -p player --example preview_probe -- preview.mp3 main.mp3
|
|
|
|
use std::sync::mpsc::RecvTimeoutError;
|
|
use std::time::{Duration, Instant};
|
|
|
|
use player::{Player, PlayerEvent};
|
|
|
|
fn drain(
|
|
label: &str,
|
|
rx: &std::sync::mpsc::Receiver<PlayerEvent>,
|
|
for_ms: u64,
|
|
log: &mut Vec<String>,
|
|
) {
|
|
let end = Instant::now() + Duration::from_millis(for_ms);
|
|
loop {
|
|
let left = end.saturating_duration_since(Instant::now());
|
|
if left.is_zero() {
|
|
break;
|
|
}
|
|
match rx.recv_timeout(left) {
|
|
Ok(ev) => log.push(format!("{label}: {ev:?}")),
|
|
Err(RecvTimeoutError::Timeout) => break,
|
|
Err(RecvTimeoutError::Disconnected) => {
|
|
log.push(format!("{label}: channel closed"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let mut args = std::env::args().skip(1);
|
|
let preview = args.next().expect("argv[1]: downloaded preview mp3");
|
|
let main_track = args.next();
|
|
|
|
let mut log = Vec::new();
|
|
|
|
// "Main" player — only if a second file was provided; the coexistence
|
|
// half of the probe is skipped otherwise.
|
|
let main_pair = main_track.map(|p| {
|
|
let (main, main_rx) = Player::spawn();
|
|
main.play_local(&p, None);
|
|
(main, main_rx, p)
|
|
});
|
|
|
|
// Preview player: a wholly separate instance and event channel.
|
|
let (prev, prev_rx) = Player::spawn();
|
|
let t0 = Instant::now();
|
|
prev.play_local(&preview, Some(30.0));
|
|
|
|
drain("preview", &prev_rx, 2500, &mut log);
|
|
let started = log.iter().any(|l| l.contains("Playing"));
|
|
println!("PROBE preview-starts: {started} ({} ms to first events)", t0.elapsed().as_millis());
|
|
|
|
prev.pause();
|
|
drain("preview", &prev_rx, 600, &mut log);
|
|
let paused = log.iter().any(|l| l.contains("Paused"));
|
|
println!("PROBE preview-pauses: {paused}");
|
|
|
|
prev.resume();
|
|
drain("preview", &prev_rx, 900, &mut log);
|
|
|
|
if let Some((_main, main_rx, ref path)) = main_pair {
|
|
let mut main_log = Vec::new();
|
|
drain("main", &main_rx, 1200, &mut main_log);
|
|
let main_alive = main_log.iter().any(|l| l.contains("Playing") || l.contains("Position"));
|
|
println!("PROBE main-coexists: {main_alive} ({path})");
|
|
// cross-talk check: no preview-channel event may appear on main's
|
|
// channel or vice versa — separate receivers make this structural,
|
|
// so what we actually assert is that both channels stayed live.
|
|
println!("PROBE channels-independent: true (separate mpsc receivers by construction)");
|
|
} else {
|
|
println!("PROBE main-coexists: skipped (no second file)");
|
|
}
|
|
|
|
prev.stop();
|
|
drain("preview", &prev_rx, 600, &mut log);
|
|
let stopped = log.iter().any(|l| l.contains("Stopped"));
|
|
println!("PROBE preview-stops: {stopped}");
|
|
|
|
println!("--- event log ---");
|
|
for l in &log {
|
|
println!("{l}");
|
|
}
|
|
}
|