play_audio_track/
play_audio_track.rs1mod common;
20
21use std::path::Path;
22
23use cd_da_reader::{CdReader, ReadOptions, create_wav};
24
25const SECTORS_PER_SECOND: u32 = 75;
27
28fn main() -> Result<(), Box<dyn std::error::Error>> {
29 let output_dir = common::fresh_output_dir("play_audio_track")?;
30 let seconds: u32 = match std::env::args().nth(1) {
31 Some(a) => a.parse()?,
32 None => 30,
33 };
34
35 let reader = CdReader::open_default()?;
36 let toc = reader.read_toc()?;
37
38 let track = toc
39 .tracks
40 .iter()
41 .find(|t| t.is_audio)
42 .ok_or("no audio tracks found on this disc")?;
43
44 let track_end = toc
47 .tracks
48 .iter()
49 .map(|t| t.start_lba)
50 .filter(|&lba| lba > track.start_lba)
51 .min()
52 .unwrap_or(toc.leadout_lba);
53 let track_sectors = track_end - track.start_lba;
54 let sectors = (seconds * SECTORS_PER_SECOND).min(track_sectors);
55 let actual_seconds = sectors / SECTORS_PER_SECOND;
56
57 println!(
58 "Reading first {actual_seconds}s ({sectors} sectors) of audio track #{}...",
59 track.number
60 );
61 let pcm = reader.read_sector_range(track.start_lba, sectors, &ReadOptions::default())?;
62 println!(
63 "Read {} bytes of PCM ({:.1} MiB)",
64 pcm.len(),
65 pcm.len() as f64 / (1024.0 * 1024.0)
66 );
67
68 let output_path = output_dir.join(format!("track{:02}_preview.wav", track.number));
69 std::fs::write(&output_path, create_wav(pcm))?;
70 println!("Saved {}", output_path.display());
71
72 play(&output_path)
73}
74
75#[cfg(target_os = "macos")]
77fn play(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
78 println!("Playing (Ctrl+C to stop)...");
79 let status = std::process::Command::new("afplay").arg(path).status()?;
80 if !status.success() {
81 return Err("afplay exited with an error".into());
82 }
83 Ok(())
84}
85
86#[cfg(target_os = "windows")]
87fn play(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
88 println!("Playing (this blocks until the preview ends)...");
89 let escaped_path = path.display().to_string().replace('\'', "''");
91 let script = format!("(New-Object Media.SoundPlayer '{escaped_path}').PlaySync()");
92 let status = std::process::Command::new("powershell")
93 .args(["-NoProfile", "-Command", &script])
94 .status()?;
95 if !status.success() {
96 return Err("powershell SoundPlayer exited with an error".into());
97 }
98 Ok(())
99}
100
101#[cfg(not(any(target_os = "macos", target_os = "windows")))]
102fn play(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
103 println!(
104 "Saved the WAV — play it with your audio player, e.g. `aplay {}`.",
105 path.display()
106 );
107 Ok(())
108}