Skip to main content

read_track

Function read_track 

Source
pub fn read_track<R: AudioSectorReader>(
    src: &R,
    toc: &Toc,
    track_no: u8,
) -> Result<Vec<u8>, CdReaderError>
Expand description

Reads one complete audio track from an AudioSectorReader into memory.

track_no is the disc track number stored in Track::number, not an index into Toc::tracks. The source and the Toc must use the same LBA address space.

This convenience function resolves the track’s sector range using TrackBounds::SessionGap. That policy is appropriate for physical discs and images that preserve the original CD geometry, including the CD-Extra inter-session gap. For a contiguous, gap-stripped source, use read_track_with_bounds with TrackBounds::Gapless.

The returned vector contains headerless CD-DA PCM in the format required by AudioSectorReader: signed 16-bit little-endian stereo at 44.1 kHz, with 2,352 bytes per sector. It can be passed directly to create_wav.

This is a blocking operation that buffers the entire track, which may require hundreds of megabytes. Use open_track_stream or open_track_stream_with_bounds to process the track incrementally.

Only audio tracks are meaningful for this API. Callers should select a track whose Track::is_audio field is true.

§Errors

Returns CdReaderError::Io if track_no is absent from the Toc or its calculated sector bounds are invalid.

Returns CdReaderError::Backend if the source cannot read the requested sectors. The source’s original error is preserved as the boxed source.

Examples found in repository?
examples/file_backend.rs (line 72)
37fn main() -> Result<(), Box<dyn std::error::Error>> {
38    let output_dir = common::fresh_output_dir("file_backend")?;
39
40    // A real backing derives this TOC from the image's own track metadata.
41    // Here we fabricate a 2-track disc: 2 seconds + 3 seconds of audio.
42    let track1_sectors = 75 * 2;
43    let track2_sectors = 75 * 3;
44    let total_sectors = track1_sectors + track2_sectors;
45
46    let toc = Toc {
47        first_track: 1,
48        last_track: 2,
49        tracks: vec![
50            Track {
51                number: 1,
52                start_lba: 0,
53                start_msf: lba_to_msf(0),
54                is_audio: true,
55            },
56            Track {
57                number: 2,
58                start_lba: track1_sectors,
59                start_msf: lba_to_msf(track1_sectors),
60                is_audio: true,
61            },
62        ],
63        leadout_lba: total_sectors,
64    };
65
66    // Silence, just for the demo — a real backing decodes actual audio here.
67    let disc = InMemoryDisc {
68        pcm: vec![0u8; total_sectors as usize * 2352],
69    };
70
71    for track in &toc.tracks {
72        let pcm = read_track(&disc, &toc, track.number)?;
73        println!(
74            "track {}: {} bytes ({} sectors)",
75            track.number,
76            pcm.len(),
77            pcm.len() / 2352
78        );
79
80        let wav = create_wav(pcm);
81        let output_path = output_dir.join(format!("track{:02}.wav", track.number));
82        std::fs::write(&output_path, wav)?;
83        println!("  wrote {}", output_path.display());
84    }
85
86    // The same backing can be streamed instead of buffered: pull sector-aligned
87    // chunks so a player never holds a whole track in memory at once. (A backing
88    // whose tracks are addressed contiguously — a gap-stripped extract — would
89    // open with `TrackBounds::Gapless`, or supply its own bounds via
90    // `open_track_stream_at`; this demo TOC has no trailing data track, so plain
91    // `open_track_stream` is equivalent.)
92    let mut stream = open_track_stream(&disc, &toc, 1)?;
93    let (mut chunks, mut bytes) = (0u32, 0usize);
94    while let Some(chunk) = stream.next_chunk()? {
95        chunks += 1;
96        bytes += chunk.len();
97    }
98    println!(
99        "streamed track 1: {bytes} bytes in {chunks} chunks ({:.1}s of audio)",
100        stream.total_seconds()
101    );
102
103    Ok(())
104}