Skip to main content

TrackStream

Struct TrackStream 

Source
pub struct TrackStream<'a> { /* private fields */ }
Expand description

Track-scoped streaming reader for audio or data sectors.

You can pull sector-aligned chunks incrementally and seek to track-relative sector or time positions. Create a stream with CdReader::open_track_stream.

Implementations§

Source§

impl<'a> TrackStream<'a>

Source

pub fn with_sectors_per_chunk(self, sectors: u32) -> Self

Set the target chunk size in sectors (default 27).

The byte size of a chunk also depends on the SectorReadFormat selected in ReadOptions. A value of zero is normalized to one sector.

Source

pub fn next_chunk(&mut self) -> Result<Option<Vec<u8>>, CdReaderError>

Read the next chunk of sector data.

Returns Ok(None) when end-of-track is reached. The bytes per sector depend on the SectorReadFormat selected in ReadOptions.

§Errors

Returns CdReaderError::Io or CdReaderError::Scsi if the drive read fails. The stream position does not advance on error.

Examples found in repository?
examples/stream_last_track.rs (line 22)
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let output_dir = common::fresh_output_dir("stream_last_track")?;
8    let reader = CdReader::open_default()?;
9    let toc = reader.read_toc()?;
10
11    let last_audio = toc
12        .tracks
13        .iter()
14        .rev()
15        .find(|t| t.is_audio)
16        .ok_or("no audio tracks found")?;
17
18    println!("Streaming track {}...", last_audio.number);
19    let mut stream = reader.open_track_stream(&toc, last_audio.number)?;
20
21    let mut pcm = Vec::new();
22    while let Some(chunk) = stream.next_chunk()? {
23        pcm.extend_from_slice(&chunk);
24    }
25
26    let wav = create_wav(pcm);
27    let output_path = output_dir.join(format!("track{:02}.wav", last_audio.number));
28    std::fs::write(&output_path, wav)?;
29    println!("Saved {}", output_path.display());
30
31    Ok(())
32}
More examples
Hide additional examples
examples/save_data_track.rs (line 106)
92fn stream_track_to_file(
93    reader: &CdReader,
94    toc: &Toc,
95    track_no: u8,
96    format: SectorReadFormat,
97    path: &Path,
98) -> Result<u64, Box<dyn std::error::Error>> {
99    let options = ReadOptions::default().with_format(format);
100    let mut stream = reader.open_track_stream_with_options(toc, track_no, &options)?;
101
102    let total_sectors = stream.total_sectors();
103    let mut writer = BufWriter::new(File::create(path)?);
104    let mut written = 0u64;
105
106    while let Some(chunk) = stream.next_chunk()? {
107        writer.write_all(&chunk)?;
108        written += chunk.len() as u64;
109
110        let done = stream.current_sector();
111        let pct = done as f32 / total_sectors as f32 * 100.0;
112        eprint!("\r  {done}/{total_sectors} sectors ({pct:5.1}%)");
113    }
114    eprintln!("\r  {total_sectors}/{total_sectors} sectors (100.0%)");
115
116    writer.flush()?;
117    Ok(written)
118}
examples/stream_with_progress.rs (line 28)
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let output_dir = common::fresh_output_dir("stream_with_progress")?;
8    let reader = CdReader::open_default()?;
9    let toc = reader.read_toc()?;
10
11    let first_audio = toc
12        .tracks
13        .iter()
14        .find(|t| t.is_audio)
15        .ok_or("no audio tracks found")?;
16
17    let mut stream = reader.open_track_stream(&toc, first_audio.number)?;
18
19    let total_secs = stream.total_seconds();
20    println!(
21        "Track {} — {} sectors ({:.0}s)\n",
22        first_audio.number,
23        stream.total_sectors(),
24        total_secs,
25    );
26
27    let mut pcm = Vec::new();
28    while let Some(chunk) = stream.next_chunk()? {
29        pcm.extend_from_slice(&chunk);
30
31        let cur = stream.current_seconds();
32        let pct = cur / total_secs * 100.0;
33        eprint!("\r  [{:>5.1}s / {:.1}s] {:5.1}%", cur, total_secs, pct,);
34    }
35    eprintln!("\r  [{:.1}s / {:.1}s] 100.0%", total_secs, total_secs);
36
37    let wav = create_wav(pcm);
38    let output_path = output_dir.join(format!("track{:02}.wav", first_audio.number));
39    std::fs::write(&output_path, wav)?;
40    println!("\nSaved {}", output_path.display());
41
42    Ok(())
43}
Source

pub fn total_sectors(&self) -> u32

Total number of sectors in this track stream.

Examples found in repository?
examples/save_data_track.rs (line 102)
92fn stream_track_to_file(
93    reader: &CdReader,
94    toc: &Toc,
95    track_no: u8,
96    format: SectorReadFormat,
97    path: &Path,
98) -> Result<u64, Box<dyn std::error::Error>> {
99    let options = ReadOptions::default().with_format(format);
100    let mut stream = reader.open_track_stream_with_options(toc, track_no, &options)?;
101
102    let total_sectors = stream.total_sectors();
103    let mut writer = BufWriter::new(File::create(path)?);
104    let mut written = 0u64;
105
106    while let Some(chunk) = stream.next_chunk()? {
107        writer.write_all(&chunk)?;
108        written += chunk.len() as u64;
109
110        let done = stream.current_sector();
111        let pct = done as f32 / total_sectors as f32 * 100.0;
112        eprint!("\r  {done}/{total_sectors} sectors ({pct:5.1}%)");
113    }
114    eprintln!("\r  {total_sectors}/{total_sectors} sectors (100.0%)");
115
116    writer.flush()?;
117    Ok(written)
118}
More examples
Hide additional examples
examples/stream_with_progress.rs (line 23)
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let output_dir = common::fresh_output_dir("stream_with_progress")?;
8    let reader = CdReader::open_default()?;
9    let toc = reader.read_toc()?;
10
11    let first_audio = toc
12        .tracks
13        .iter()
14        .find(|t| t.is_audio)
15        .ok_or("no audio tracks found")?;
16
17    let mut stream = reader.open_track_stream(&toc, first_audio.number)?;
18
19    let total_secs = stream.total_seconds();
20    println!(
21        "Track {} — {} sectors ({:.0}s)\n",
22        first_audio.number,
23        stream.total_sectors(),
24        total_secs,
25    );
26
27    let mut pcm = Vec::new();
28    while let Some(chunk) = stream.next_chunk()? {
29        pcm.extend_from_slice(&chunk);
30
31        let cur = stream.current_seconds();
32        let pct = cur / total_secs * 100.0;
33        eprint!("\r  [{:>5.1}s / {:.1}s] {:5.1}%", cur, total_secs, pct,);
34    }
35    eprintln!("\r  [{:.1}s / {:.1}s] 100.0%", total_secs, total_secs);
36
37    let wav = create_wav(pcm);
38    let output_path = output_dir.join(format!("track{:02}.wav", first_audio.number));
39    std::fs::write(&output_path, wav)?;
40    println!("\nSaved {}", output_path.display());
41
42    Ok(())
43}
Source

pub fn current_sector(&self) -> u32

Current stream position as a track-relative sector index. Keep in mind that if you are playing the sound directly, this is likely not the track’s current position because you probably keep some of the data in your buffer.

Examples found in repository?
examples/save_data_track.rs (line 110)
92fn stream_track_to_file(
93    reader: &CdReader,
94    toc: &Toc,
95    track_no: u8,
96    format: SectorReadFormat,
97    path: &Path,
98) -> Result<u64, Box<dyn std::error::Error>> {
99    let options = ReadOptions::default().with_format(format);
100    let mut stream = reader.open_track_stream_with_options(toc, track_no, &options)?;
101
102    let total_sectors = stream.total_sectors();
103    let mut writer = BufWriter::new(File::create(path)?);
104    let mut written = 0u64;
105
106    while let Some(chunk) = stream.next_chunk()? {
107        writer.write_all(&chunk)?;
108        written += chunk.len() as u64;
109
110        let done = stream.current_sector();
111        let pct = done as f32 / total_sectors as f32 * 100.0;
112        eprint!("\r  {done}/{total_sectors} sectors ({pct:5.1}%)");
113    }
114    eprintln!("\r  {total_sectors}/{total_sectors} sectors (100.0%)");
115
116    writer.flush()?;
117    Ok(written)
118}
Source

pub fn seek_to_sector(&mut self, sector: u32) -> Result<(), CdReaderError>

Seek to a sector position relative to the start of the track.

Valid range is 0..=total_sectors().

§Errors

Returns CdReaderError::Io containing std::io::ErrorKind::InvalidInput if sector exceeds the track length.

Source

pub fn current_seconds(&self) -> f32

Current stream position in seconds. Functionally equivalent to “current_sector”, but converted to seconds.

CD addresses advance at 75 sectors = 1 second.

Examples found in repository?
examples/stream_with_progress.rs (line 31)
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let output_dir = common::fresh_output_dir("stream_with_progress")?;
8    let reader = CdReader::open_default()?;
9    let toc = reader.read_toc()?;
10
11    let first_audio = toc
12        .tracks
13        .iter()
14        .find(|t| t.is_audio)
15        .ok_or("no audio tracks found")?;
16
17    let mut stream = reader.open_track_stream(&toc, first_audio.number)?;
18
19    let total_secs = stream.total_seconds();
20    println!(
21        "Track {} — {} sectors ({:.0}s)\n",
22        first_audio.number,
23        stream.total_sectors(),
24        total_secs,
25    );
26
27    let mut pcm = Vec::new();
28    while let Some(chunk) = stream.next_chunk()? {
29        pcm.extend_from_slice(&chunk);
30
31        let cur = stream.current_seconds();
32        let pct = cur / total_secs * 100.0;
33        eprint!("\r  [{:>5.1}s / {:.1}s] {:5.1}%", cur, total_secs, pct,);
34    }
35    eprintln!("\r  [{:.1}s / {:.1}s] 100.0%", total_secs, total_secs);
36
37    let wav = create_wav(pcm);
38    let output_path = output_dir.join(format!("track{:02}.wav", first_audio.number));
39    std::fs::write(&output_path, wav)?;
40    println!("\nSaved {}", output_path.display());
41
42    Ok(())
43}
Source

pub fn total_seconds(&self) -> f32

Total stream duration in seconds. Functionally equivalent to “total_sectors”, but converted to seconds.

CD addresses advance at 75 sectors = 1 second.

Examples found in repository?
examples/stream_with_progress.rs (line 19)
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let output_dir = common::fresh_output_dir("stream_with_progress")?;
8    let reader = CdReader::open_default()?;
9    let toc = reader.read_toc()?;
10
11    let first_audio = toc
12        .tracks
13        .iter()
14        .find(|t| t.is_audio)
15        .ok_or("no audio tracks found")?;
16
17    let mut stream = reader.open_track_stream(&toc, first_audio.number)?;
18
19    let total_secs = stream.total_seconds();
20    println!(
21        "Track {} — {} sectors ({:.0}s)\n",
22        first_audio.number,
23        stream.total_sectors(),
24        total_secs,
25    );
26
27    let mut pcm = Vec::new();
28    while let Some(chunk) = stream.next_chunk()? {
29        pcm.extend_from_slice(&chunk);
30
31        let cur = stream.current_seconds();
32        let pct = cur / total_secs * 100.0;
33        eprint!("\r  [{:>5.1}s / {:.1}s] {:5.1}%", cur, total_secs, pct,);
34    }
35    eprintln!("\r  [{:.1}s / {:.1}s] 100.0%", total_secs, total_secs);
36
37    let wav = create_wav(pcm);
38    let output_path = output_dir.join(format!("track{:02}.wav", first_audio.number));
39    std::fs::write(&output_path, wav)?;
40    println!("\nSaved {}", output_path.display());
41
42    Ok(())
43}
Source

pub fn seek_to_seconds(&mut self, seconds: f32) -> Result<(), CdReaderError>

Seek to a time position relative to the start of the track in seconds.

Input is converted to sector offset and clamped to track bounds.

§Errors

Returns CdReaderError::Io containing std::io::ErrorKind::InvalidInput if seconds is negative or not finite.

Auto Trait Implementations§

§

impl<'a> Freeze for TrackStream<'a>

§

impl<'a> RefUnwindSafe for TrackStream<'a>

§

impl<'a> Send for TrackStream<'a>

§

impl<'a> Sync for TrackStream<'a>

§

impl<'a> Unpin for TrackStream<'a>

§

impl<'a> UnsafeUnpin for TrackStream<'a>

§

impl<'a> UnwindSafe for TrackStream<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.