//! 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, for_ms: u64, log: &mut Vec, ) { 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}"); } }