Skip to main content

CdReader

Struct CdReader 

Source
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 a physical thing and they really want to have exclusive access, because of that currently only sequential access is supported.

Implementations§

Source§

impl CdReader

Source

pub fn list_drives() -> Result<Vec<DriveInfo>, CdReaderError>

Enumerate candidate optical drives and probe whether they currently have an audio CD.

This method does not work on macOS due to the fact for reliable confirmation whether the drive has an Audio CD we need to mount it and later release; macOS can assign a different drive name afterwards, so reading that name is unreliable. Instead, use open_drive(drive) or open_default(), which acquires the exclusivity

Source

pub fn open_default() -> Result<Self, CdReaderError>

Open the first discovered drive that currently has an audio CD.

On macOS, we open each drive returned from diskutil list, and evaluate each disk. Once we are able to open it and read correct TOC, we return it back with already acquired exclusivity.

Source§

impl CdReader

Source

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 acess.

You don’t need to close the drive, it will be handled automatically when the CdReader is dropped. On macOS, that will cause the CD drive to be remounted, and the default application (like Apple Music) will be called.

§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

Examples found in repository?
examples/read_track.rs (line 24)
23fn read_cd(path: &str) -> Result<(), Box<dyn std::error::Error>> {
24    let reader = CdReader::open(path)?;
25    let toc = reader.read_toc()?;
26    println!("{toc:#?}");
27
28    let last_audio_track = toc
29        .tracks
30        .iter()
31        .rev()
32        .find(|track| track.is_audio)
33        .ok_or_else(|| std::io::Error::other("no audio tracks in TOC"))?;
34
35    println!("Reading track {}", last_audio_track.number);
36    let data = reader.read_track(&toc, last_audio_track.number)?;
37    let wav_track = CdReader::create_wav(data);
38    std::fs::write("myfile.wav", wav_track)?;
39
40    Ok(())
41}
Source

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 from read_track function
Examples found in repository?
examples/read_track.rs (line 37)
23fn read_cd(path: &str) -> Result<(), Box<dyn std::error::Error>> {
24    let reader = CdReader::open(path)?;
25    let toc = reader.read_toc()?;
26    println!("{toc:#?}");
27
28    let last_audio_track = toc
29        .tracks
30        .iter()
31        .rev()
32        .find(|track| track.is_audio)
33        .ok_or_else(|| std::io::Error::other("no audio tracks in TOC"))?;
34
35    println!("Reading track {}", last_audio_track.number);
36    let data = reader.read_track(&toc, last_audio_track.number)?;
37    let wav_track = CdReader::create_wav(data);
38    std::fs::write("myfile.wav", wav_track)?;
39
40    Ok(())
41}
Source

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?
examples/read_track.rs (line 25)
23fn read_cd(path: &str) -> Result<(), Box<dyn std::error::Error>> {
24    let reader = CdReader::open(path)?;
25    let toc = reader.read_toc()?;
26    println!("{toc:#?}");
27
28    let last_audio_track = toc
29        .tracks
30        .iter()
31        .rev()
32        .find(|track| track.is_audio)
33        .ok_or_else(|| std::io::Error::other("no audio tracks in TOC"))?;
34
35    println!("Reading track {}", last_audio_track.number);
36    let data = reader.read_track(&toc, last_audio_track.number)?;
37    let wav_track = CdReader::create_wav(data);
38    std::fs::write("myfile.wav", wav_track)?;
39
40    Ok(())
41}
Source

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?
examples/read_track.rs (line 36)
23fn read_cd(path: &str) -> Result<(), Box<dyn std::error::Error>> {
24    let reader = CdReader::open(path)?;
25    let toc = reader.read_toc()?;
26    println!("{toc:#?}");
27
28    let last_audio_track = toc
29        .tracks
30        .iter()
31        .rev()
32        .find(|track| track.is_audio)
33        .ok_or_else(|| std::io::Error::other("no audio tracks in TOC"))?;
34
35    println!("Reading track {}", last_audio_track.number);
36    let data = reader.read_track(&toc, last_audio_track.number)?;
37    let wav_track = CdReader::create_wav(data);
38    std::fs::write("myfile.wav", wav_track)?;
39
40    Ok(())
41}
Source

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.

Trait Implementations§

Source§

impl Drop for CdReader

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

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 = Infallible

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.