pub struct CdReader {}Expand description
Helper struct to interact with the audio CD. While it doesn’t hold any internal data
directly, it implements Drop trait, so that the CD drive handle is properly closed.
Please note that you should not read multiple CDs at the same time, and preferably do not use it in multiple threads. CD drives are physical devices, so currently only sequential access is properly tested and supported.
Implementations§
Source§impl CdReader
impl CdReader
Sourcepub fn list_drives() -> Result<Vec<DriveInfo>, CdReaderError>
pub fn list_drives() -> Result<Vec<DriveInfo>, CdReaderError>
Enumerate candidate optical drives and probe whether they currently have an audio CD.
On Windows, we try to read type of every drive from A to Z. On Linux, we read /sys/class/block directory and check every entry starting with “sr”
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let drives = CdReader::list_drives()?;
6
7 if drives.is_empty() {
8 println!("No optical drives found.");
9 return Ok(());
10 }
11
12 println!("Found {} drive(s):\n", drives.len());
13 for drive in &drives {
14 let name = drive.display_name.as_deref().unwrap_or("(unknown)");
15 let status = if drive.has_audio_cd {
16 "audio CD inserted"
17 } else {
18 "no audio CD"
19 };
20 println!(
21 "Drive: {}, name: {}, status: [{}]",
22 drive.path, name, status
23 );
24 }
25
26 Ok(())
27}Sourcepub fn open_default() -> Result<Self, CdReaderError>
pub fn open_default() -> Result<Self, CdReaderError>
Open the first discovered drive that currently has an audio CD.
On Windows and Linux, we get the first device from the list and try to open it, returning an error if it fails.
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let first_audio = toc
9 .tracks
10 .iter()
11 .find(|t| t.is_audio)
12 .ok_or("no audio tracks found")?;
13
14 println!("Reading track {}...", first_audio.number);
15 let data = reader.read_track(&toc, first_audio.number)?;
16
17 let wav = CdReader::create_wav(data);
18 let filename = format!("track{:02}.wav", first_audio.number);
19 std::fs::write(&filename, wav)?;
20 println!("Saved {}", filename);
21
22 Ok(())
23}More examples
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let last_audio = toc
9 .tracks
10 .iter()
11 .rev()
12 .find(|t| t.is_audio)
13 .ok_or("no audio tracks found")?;
14
15 println!("Streaming track {}...", last_audio.number);
16 let mut stream =
17 reader.open_track_stream(&toc, last_audio.number, TrackStreamConfig::default())?;
18
19 let mut pcm = Vec::new();
20 while let Some(chunk) = stream.next_chunk()? {
21 pcm.extend_from_slice(&chunk);
22 }
23
24 let wav = CdReader::create_wav(pcm);
25 let filename = format!("track{:02}.wav", last_audio.number);
26 std::fs::write(&filename, wav)?;
27 println!("Saved {}", filename);
28
29 Ok(())
30}4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 println!("Table of Contents\n");
9
10 println!(
11 "Tracks {}-{} ({} total), lead-out at LBA {}\n",
12 toc.first_track,
13 toc.last_track,
14 toc.tracks.len(),
15 toc.leadout_lba,
16 );
17
18 for track in &toc.tracks {
19 let kind = if track.is_audio { "audio" } else { "data " };
20 let (m, s, f) = track.start_msf;
21 let sectors = next_track_lba(&toc, track.number) - track.start_lba;
22 let duration_secs = sectors as f64 / 75.0;
23 let mins = (duration_secs / 60.0) as u32;
24 let secs = (duration_secs % 60.0) as u32;
25
26 println!(
27 " #{:>2} {} LBA {:>6} MSF {:02}:{:02}.{:02} duration: {:02}:{:02}",
28 track.number, kind, track.start_lba, m, s, f, mins, secs,
29 );
30 }
31
32 Ok(())
33}8fn main() -> Result<(), Box<dyn std::error::Error>> {
9 let reader = CdReader::open_default()?;
10 let toc = reader.read_toc()?;
11
12 let first_audio = toc
13 .tracks
14 .iter()
15 .find(|t| t.is_audio)
16 .ok_or("no audio tracks found")?;
17
18 // More attempts, longer backoff, and sector reduction down to 1
19 // for maximum resilience on scratched media.
20 let retry = RetryConfig {
21 max_attempts: 8,
22 initial_backoff_ms: 50,
23 max_backoff_ms: 1000,
24 reduce_chunk_on_retry: true,
25 min_sectors_per_read: 1,
26 };
27
28 println!(
29 "Reading track {} with aggressive retry...",
30 first_audio.number
31 );
32 let data = reader.read_track_with_retry(&toc, first_audio.number, &retry)?;
33
34 let wav = CdReader::create_wav(data);
35 let filename = format!("track{:02}.wav", first_audio.number);
36 std::fs::write(&filename, wav)?;
37 println!("Saved {}", filename);
38
39 Ok(())
40}4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let audio_tracks: Vec<_> = toc.tracks.iter().filter(|t| t.is_audio).collect();
9 println!("Found {} audio track(s)\n", audio_tracks.len());
10
11 let mut failed = Vec::new();
12
13 for track in &audio_tracks {
14 print!("Reading track {:>2}... ", track.number);
15 match reader.read_track(&toc, track.number) {
16 Ok(data) => {
17 let wav = CdReader::create_wav(data);
18 let filename = format!("track{:02}.wav", track.number);
19 std::fs::write(&filename, wav)?;
20 println!("saved {}", filename);
21 }
22 Err(e) => {
23 println!("FAILED: {}", e);
24 failed.push(track.number);
25 }
26 }
27 }
28
29 if !failed.is_empty() {
30 eprintln!("\nFailed to read tracks: {:?}", failed);
31 }
32
33 Ok(())
34}4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let first_audio = toc
9 .tracks
10 .iter()
11 .find(|t| t.is_audio)
12 .ok_or("no audio tracks found")?;
13
14 let mut stream =
15 reader.open_track_stream(&toc, first_audio.number, TrackStreamConfig::default())?;
16
17 let total_secs = stream.total_seconds();
18 println!(
19 "Track {} — {} sectors ({:.0}s)\n",
20 first_audio.number,
21 stream.total_sectors(),
22 total_secs,
23 );
24
25 let mut pcm = Vec::new();
26 while let Some(chunk) = stream.next_chunk()? {
27 pcm.extend_from_slice(&chunk);
28
29 let cur = stream.current_seconds();
30 let pct = cur / total_secs * 100.0;
31 eprint!("\r [{:>5.1}s / {:.1}s] {:5.1}%", cur, total_secs, pct,);
32 }
33 eprintln!("\r [{:.1}s / {:.1}s] 100.0%", total_secs, total_secs);
34
35 let wav = CdReader::create_wav(pcm);
36 let filename = format!("track{:02}.wav", first_audio.number);
37 std::fs::write(&filename, wav)?;
38 println!("\nSaved {}", filename);
39
40 Ok(())
41}Source§impl CdReader
impl CdReader
Sourcepub fn open_track_stream<'a>(
&'a self,
toc: &Toc,
track_no: u8,
cfg: TrackStreamConfig,
) -> Result<TrackStream<'a>, CdReaderError>
pub fn open_track_stream<'a>( &'a self, toc: &Toc, track_no: u8, cfg: TrackStreamConfig, ) -> Result<TrackStream<'a>, CdReaderError>
Open a streaming reader for a specific track in the provided TOC. It is important to create track streams through this method so the drive session is managed through a single CDReader instance.
Use TrackStream::next_chunk to pull sector-aligned PCM chunks.
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let last_audio = toc
9 .tracks
10 .iter()
11 .rev()
12 .find(|t| t.is_audio)
13 .ok_or("no audio tracks found")?;
14
15 println!("Streaming track {}...", last_audio.number);
16 let mut stream =
17 reader.open_track_stream(&toc, last_audio.number, TrackStreamConfig::default())?;
18
19 let mut pcm = Vec::new();
20 while let Some(chunk) = stream.next_chunk()? {
21 pcm.extend_from_slice(&chunk);
22 }
23
24 let wav = CdReader::create_wav(pcm);
25 let filename = format!("track{:02}.wav", last_audio.number);
26 std::fs::write(&filename, wav)?;
27 println!("Saved {}", filename);
28
29 Ok(())
30}More examples
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let first_audio = toc
9 .tracks
10 .iter()
11 .find(|t| t.is_audio)
12 .ok_or("no audio tracks found")?;
13
14 let mut stream =
15 reader.open_track_stream(&toc, first_audio.number, TrackStreamConfig::default())?;
16
17 let total_secs = stream.total_seconds();
18 println!(
19 "Track {} — {} sectors ({:.0}s)\n",
20 first_audio.number,
21 stream.total_sectors(),
22 total_secs,
23 );
24
25 let mut pcm = Vec::new();
26 while let Some(chunk) = stream.next_chunk()? {
27 pcm.extend_from_slice(&chunk);
28
29 let cur = stream.current_seconds();
30 let pct = cur / total_secs * 100.0;
31 eprint!("\r [{:>5.1}s / {:.1}s] {:5.1}%", cur, total_secs, pct,);
32 }
33 eprintln!("\r [{:.1}s / {:.1}s] 100.0%", total_secs, total_secs);
34
35 let wav = CdReader::create_wav(pcm);
36 let filename = format!("track{:02}.wav", first_audio.number);
37 std::fs::write(&filename, wav)?;
38 println!("\nSaved {}", filename);
39
40 Ok(())
41}Source§impl CdReader
impl CdReader
Sourcepub fn open(path: &str) -> Result<Self>
pub fn open(path: &str) -> Result<Self>
Opens a CD drive at the specified path in order to read data.
It is crucial to call this function and not to create the Reader by yourself, as each OS needs its own way of handling the drive access.
You don’t need to close the drive; it will be handled automatically
when the CdReader is dropped.
§Arguments
path- The device path (e.g., “/dev/sr0” on Linux, “disk6” on macOS, and r“\.\E:“ on Windows)
§Errors
Returns an error if the drive cannot be opened
Sourcepub fn create_wav(data: Vec<u8>) -> Vec<u8> ⓘ
pub fn create_wav(data: Vec<u8>) -> Vec<u8> ⓘ
While this is a low-level library and does not include any codecs to compress the audio, it includes a helper function to convert raw PCM data into a wav file, which is done by prepending a 44 RIFF bytes header
§Arguments
data- vector of bytes received fromread_trackfunction
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let first_audio = toc
9 .tracks
10 .iter()
11 .find(|t| t.is_audio)
12 .ok_or("no audio tracks found")?;
13
14 println!("Reading track {}...", first_audio.number);
15 let data = reader.read_track(&toc, first_audio.number)?;
16
17 let wav = CdReader::create_wav(data);
18 let filename = format!("track{:02}.wav", first_audio.number);
19 std::fs::write(&filename, wav)?;
20 println!("Saved {}", filename);
21
22 Ok(())
23}More examples
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let last_audio = toc
9 .tracks
10 .iter()
11 .rev()
12 .find(|t| t.is_audio)
13 .ok_or("no audio tracks found")?;
14
15 println!("Streaming track {}...", last_audio.number);
16 let mut stream =
17 reader.open_track_stream(&toc, last_audio.number, TrackStreamConfig::default())?;
18
19 let mut pcm = Vec::new();
20 while let Some(chunk) = stream.next_chunk()? {
21 pcm.extend_from_slice(&chunk);
22 }
23
24 let wav = CdReader::create_wav(pcm);
25 let filename = format!("track{:02}.wav", last_audio.number);
26 std::fs::write(&filename, wav)?;
27 println!("Saved {}", filename);
28
29 Ok(())
30}8fn main() -> Result<(), Box<dyn std::error::Error>> {
9 let reader = CdReader::open_default()?;
10 let toc = reader.read_toc()?;
11
12 let first_audio = toc
13 .tracks
14 .iter()
15 .find(|t| t.is_audio)
16 .ok_or("no audio tracks found")?;
17
18 // More attempts, longer backoff, and sector reduction down to 1
19 // for maximum resilience on scratched media.
20 let retry = RetryConfig {
21 max_attempts: 8,
22 initial_backoff_ms: 50,
23 max_backoff_ms: 1000,
24 reduce_chunk_on_retry: true,
25 min_sectors_per_read: 1,
26 };
27
28 println!(
29 "Reading track {} with aggressive retry...",
30 first_audio.number
31 );
32 let data = reader.read_track_with_retry(&toc, first_audio.number, &retry)?;
33
34 let wav = CdReader::create_wav(data);
35 let filename = format!("track{:02}.wav", first_audio.number);
36 std::fs::write(&filename, wav)?;
37 println!("Saved {}", filename);
38
39 Ok(())
40}4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let audio_tracks: Vec<_> = toc.tracks.iter().filter(|t| t.is_audio).collect();
9 println!("Found {} audio track(s)\n", audio_tracks.len());
10
11 let mut failed = Vec::new();
12
13 for track in &audio_tracks {
14 print!("Reading track {:>2}... ", track.number);
15 match reader.read_track(&toc, track.number) {
16 Ok(data) => {
17 let wav = CdReader::create_wav(data);
18 let filename = format!("track{:02}.wav", track.number);
19 std::fs::write(&filename, wav)?;
20 println!("saved {}", filename);
21 }
22 Err(e) => {
23 println!("FAILED: {}", e);
24 failed.push(track.number);
25 }
26 }
27 }
28
29 if !failed.is_empty() {
30 eprintln!("\nFailed to read tracks: {:?}", failed);
31 }
32
33 Ok(())
34}4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let first_audio = toc
9 .tracks
10 .iter()
11 .find(|t| t.is_audio)
12 .ok_or("no audio tracks found")?;
13
14 let mut stream =
15 reader.open_track_stream(&toc, first_audio.number, TrackStreamConfig::default())?;
16
17 let total_secs = stream.total_seconds();
18 println!(
19 "Track {} — {} sectors ({:.0}s)\n",
20 first_audio.number,
21 stream.total_sectors(),
22 total_secs,
23 );
24
25 let mut pcm = Vec::new();
26 while let Some(chunk) = stream.next_chunk()? {
27 pcm.extend_from_slice(&chunk);
28
29 let cur = stream.current_seconds();
30 let pct = cur / total_secs * 100.0;
31 eprint!("\r [{:>5.1}s / {:.1}s] {:5.1}%", cur, total_secs, pct,);
32 }
33 eprintln!("\r [{:.1}s / {:.1}s] 100.0%", total_secs, total_secs);
34
35 let wav = CdReader::create_wav(pcm);
36 let filename = format!("track{:02}.wav", first_audio.number);
37 std::fs::write(&filename, wav)?;
38 println!("\nSaved {}", filename);
39
40 Ok(())
41}Sourcepub fn read_toc(&self) -> Result<Toc, CdReaderError>
pub fn read_toc(&self) -> Result<Toc, CdReaderError>
Read Table of Contents for the opened drive. You’ll likely only need to access
tracks from the returned value in order to iterate and read each track’s raw data.
Please note that each track in the vector has number property, which you should use
when calling read_track, as it doesn’t start with 0. It is important to do so,
because in the future it might include 0 for the hidden track.
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let first_audio = toc
9 .tracks
10 .iter()
11 .find(|t| t.is_audio)
12 .ok_or("no audio tracks found")?;
13
14 println!("Reading track {}...", first_audio.number);
15 let data = reader.read_track(&toc, first_audio.number)?;
16
17 let wav = CdReader::create_wav(data);
18 let filename = format!("track{:02}.wav", first_audio.number);
19 std::fs::write(&filename, wav)?;
20 println!("Saved {}", filename);
21
22 Ok(())
23}More examples
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let last_audio = toc
9 .tracks
10 .iter()
11 .rev()
12 .find(|t| t.is_audio)
13 .ok_or("no audio tracks found")?;
14
15 println!("Streaming track {}...", last_audio.number);
16 let mut stream =
17 reader.open_track_stream(&toc, last_audio.number, TrackStreamConfig::default())?;
18
19 let mut pcm = Vec::new();
20 while let Some(chunk) = stream.next_chunk()? {
21 pcm.extend_from_slice(&chunk);
22 }
23
24 let wav = CdReader::create_wav(pcm);
25 let filename = format!("track{:02}.wav", last_audio.number);
26 std::fs::write(&filename, wav)?;
27 println!("Saved {}", filename);
28
29 Ok(())
30}4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 println!("Table of Contents\n");
9
10 println!(
11 "Tracks {}-{} ({} total), lead-out at LBA {}\n",
12 toc.first_track,
13 toc.last_track,
14 toc.tracks.len(),
15 toc.leadout_lba,
16 );
17
18 for track in &toc.tracks {
19 let kind = if track.is_audio { "audio" } else { "data " };
20 let (m, s, f) = track.start_msf;
21 let sectors = next_track_lba(&toc, track.number) - track.start_lba;
22 let duration_secs = sectors as f64 / 75.0;
23 let mins = (duration_secs / 60.0) as u32;
24 let secs = (duration_secs % 60.0) as u32;
25
26 println!(
27 " #{:>2} {} LBA {:>6} MSF {:02}:{:02}.{:02} duration: {:02}:{:02}",
28 track.number, kind, track.start_lba, m, s, f, mins, secs,
29 );
30 }
31
32 Ok(())
33}8fn main() -> Result<(), Box<dyn std::error::Error>> {
9 let reader = CdReader::open_default()?;
10 let toc = reader.read_toc()?;
11
12 let first_audio = toc
13 .tracks
14 .iter()
15 .find(|t| t.is_audio)
16 .ok_or("no audio tracks found")?;
17
18 // More attempts, longer backoff, and sector reduction down to 1
19 // for maximum resilience on scratched media.
20 let retry = RetryConfig {
21 max_attempts: 8,
22 initial_backoff_ms: 50,
23 max_backoff_ms: 1000,
24 reduce_chunk_on_retry: true,
25 min_sectors_per_read: 1,
26 };
27
28 println!(
29 "Reading track {} with aggressive retry...",
30 first_audio.number
31 );
32 let data = reader.read_track_with_retry(&toc, first_audio.number, &retry)?;
33
34 let wav = CdReader::create_wav(data);
35 let filename = format!("track{:02}.wav", first_audio.number);
36 std::fs::write(&filename, wav)?;
37 println!("Saved {}", filename);
38
39 Ok(())
40}4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let audio_tracks: Vec<_> = toc.tracks.iter().filter(|t| t.is_audio).collect();
9 println!("Found {} audio track(s)\n", audio_tracks.len());
10
11 let mut failed = Vec::new();
12
13 for track in &audio_tracks {
14 print!("Reading track {:>2}... ", track.number);
15 match reader.read_track(&toc, track.number) {
16 Ok(data) => {
17 let wav = CdReader::create_wav(data);
18 let filename = format!("track{:02}.wav", track.number);
19 std::fs::write(&filename, wav)?;
20 println!("saved {}", filename);
21 }
22 Err(e) => {
23 println!("FAILED: {}", e);
24 failed.push(track.number);
25 }
26 }
27 }
28
29 if !failed.is_empty() {
30 eprintln!("\nFailed to read tracks: {:?}", failed);
31 }
32
33 Ok(())
34}4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let first_audio = toc
9 .tracks
10 .iter()
11 .find(|t| t.is_audio)
12 .ok_or("no audio tracks found")?;
13
14 let mut stream =
15 reader.open_track_stream(&toc, first_audio.number, TrackStreamConfig::default())?;
16
17 let total_secs = stream.total_seconds();
18 println!(
19 "Track {} — {} sectors ({:.0}s)\n",
20 first_audio.number,
21 stream.total_sectors(),
22 total_secs,
23 );
24
25 let mut pcm = Vec::new();
26 while let Some(chunk) = stream.next_chunk()? {
27 pcm.extend_from_slice(&chunk);
28
29 let cur = stream.current_seconds();
30 let pct = cur / total_secs * 100.0;
31 eprint!("\r [{:>5.1}s / {:.1}s] {:5.1}%", cur, total_secs, pct,);
32 }
33 eprintln!("\r [{:.1}s / {:.1}s] 100.0%", total_secs, total_secs);
34
35 let wav = CdReader::create_wav(pcm);
36 let filename = format!("track{:02}.wav", first_audio.number);
37 std::fs::write(&filename, wav)?;
38 println!("\nSaved {}", filename);
39
40 Ok(())
41}Sourcepub fn read_track(
&self,
toc: &Toc,
track_no: u8,
) -> Result<Vec<u8>, CdReaderError>
pub fn read_track( &self, toc: &Toc, track_no: u8, ) -> Result<Vec<u8>, CdReaderError>
Read raw data for the specified track number from the TOC.
It returns raw PCM data, but if you want to save it directly and make it playable,
wrap the result with create_wav function, that will prepend a RIFF header and
make it a proper music file.
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let first_audio = toc
9 .tracks
10 .iter()
11 .find(|t| t.is_audio)
12 .ok_or("no audio tracks found")?;
13
14 println!("Reading track {}...", first_audio.number);
15 let data = reader.read_track(&toc, first_audio.number)?;
16
17 let wav = CdReader::create_wav(data);
18 let filename = format!("track{:02}.wav", first_audio.number);
19 std::fs::write(&filename, wav)?;
20 println!("Saved {}", filename);
21
22 Ok(())
23}More examples
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let reader = CdReader::open_default()?;
6 let toc = reader.read_toc()?;
7
8 let audio_tracks: Vec<_> = toc.tracks.iter().filter(|t| t.is_audio).collect();
9 println!("Found {} audio track(s)\n", audio_tracks.len());
10
11 let mut failed = Vec::new();
12
13 for track in &audio_tracks {
14 print!("Reading track {:>2}... ", track.number);
15 match reader.read_track(&toc, track.number) {
16 Ok(data) => {
17 let wav = CdReader::create_wav(data);
18 let filename = format!("track{:02}.wav", track.number);
19 std::fs::write(&filename, wav)?;
20 println!("saved {}", filename);
21 }
22 Err(e) => {
23 println!("FAILED: {}", e);
24 failed.push(track.number);
25 }
26 }
27 }
28
29 if !failed.is_empty() {
30 eprintln!("\nFailed to read tracks: {:?}", failed);
31 }
32
33 Ok(())
34}Sourcepub fn read_track_with_retry(
&self,
toc: &Toc,
track_no: u8,
cfg: &RetryConfig,
) -> Result<Vec<u8>, CdReaderError>
pub fn read_track_with_retry( &self, toc: &Toc, track_no: u8, cfg: &RetryConfig, ) -> Result<Vec<u8>, CdReaderError>
Read raw data for the specified track number from the TOC using explicit retry config.
Examples found in repository?
8fn main() -> Result<(), Box<dyn std::error::Error>> {
9 let reader = CdReader::open_default()?;
10 let toc = reader.read_toc()?;
11
12 let first_audio = toc
13 .tracks
14 .iter()
15 .find(|t| t.is_audio)
16 .ok_or("no audio tracks found")?;
17
18 // More attempts, longer backoff, and sector reduction down to 1
19 // for maximum resilience on scratched media.
20 let retry = RetryConfig {
21 max_attempts: 8,
22 initial_backoff_ms: 50,
23 max_backoff_ms: 1000,
24 reduce_chunk_on_retry: true,
25 min_sectors_per_read: 1,
26 };
27
28 println!(
29 "Reading track {} with aggressive retry...",
30 first_audio.number
31 );
32 let data = reader.read_track_with_retry(&toc, first_audio.number, &retry)?;
33
34 let wav = CdReader::create_wav(data);
35 let filename = format!("track{:02}.wav", first_audio.number);
36 std::fs::write(&filename, wav)?;
37 println!("Saved {}", filename);
38
39 Ok(())
40}