Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ windows-sys = { version = "0.60.2", features = [
"Win32_Security",
"Win32_System_IO",
"Win32_Storage_IscsiDisc",
"Win32_Devices_Cdrom",
]}

[target.'cfg(target_os = "macos")'.build-dependencies]
Expand Down
79 changes: 43 additions & 36 deletions examples/read_speed.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,56 @@
/// Read the first audio track at 10x speed, and read the second audio track at `Optimal`
/// speed.
mod common;
/// Reads the first audio track repeatedly at different requested speeds and reports the
/// elapsed time for each read.
use std::time::Instant;

use cd_da_reader::{CdReader, ReadOptions, ReadSpeed, Track};
use cd_da_reader::{CdReader, ReadOptions, ReadSpeed};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let output_dir = common::fresh_output_dir("read_speed")?;
let reader = CdReader::open_default()?;
let toc = reader.read_toc()?;

let audio_tracks: Vec<&Track> = toc.tracks.iter().filter(|t| t.is_audio).collect();
let first_audio = toc
.tracks
.iter()
.find(|track| track.is_audio)
.ok_or("no audio tracks found")?;

// Keep "unchanged" immediately after 1x so it tests whether the previous speed
// request remains in effect when no new speed command is sent.
let speed_tests = [
("1x", ReadSpeed::CustomMultiplier(1)),
("unchanged (after 1x)", ReadSpeed::Unchanged),
("10x", ReadSpeed::CustomMultiplier(10)),
("30x", ReadSpeed::CustomMultiplier(30)),
("optimal", ReadSpeed::Optimal),
];

println!(
"Reading audio track {} {} times\n",
first_audio.number,
speed_tests.len()
);

let mut timings = Vec::with_capacity(speed_tests.len());

for (label, read_speed) in speed_tests {
let options = ReadOptions::default().with_read_speed(read_speed);

println!("Reading at {label}...");
let started = Instant::now();
let data = reader.read_track_with_options(&toc, first_audio.number, &options)?;
let elapsed = started.elapsed();

if audio_tracks.len() < 2 {
panic!("This example requires at least two audio tracks");
}

let first_track = audio_tracks[0];
let second_track = audio_tracks[1];

// Read the first track with 10x speed
{
let options_10x = ReadOptions::default().with_read_speed(ReadSpeed::CustomMultiplier(10));

println!("Reading track {} with 10x speed...", first_track.number);
let data = reader.read_track_with_options(&toc, first_track.number, &options_10x)?;

let wav = CdReader::create_wav(data);
let output_path = output_dir.join(format!("track{:02}.wav", first_track.number));
std::fs::write(&output_path, wav)?;
println!("Saved {}", output_path.display());
}

// Read the second track with "optimal" speed
{
let options_optimal = ReadOptions::default().with_read_speed(ReadSpeed::Optimal);
println!(
"Reading track {} with optimal speed...",
second_track.number
"Read {} bytes in {:.3} seconds\n",
data.len(),
elapsed.as_secs_f64()
);
let data = reader.read_track_with_options(&toc, second_track.number, &options_optimal)?;
timings.push((label, elapsed));
}

let wav = CdReader::create_wav(data);
let output_path = output_dir.join(format!("track{:02}.wav", second_track.number));
std::fs::write(&output_path, wav)?;
println!("Saved {}", output_path.display());
println!("Timing summary:");
for (label, elapsed) in timings {
println!(" {label:<22} {:>10.3} seconds", elapsed.as_secs_f64());
}

Ok(())
Expand Down
47 changes: 43 additions & 4 deletions src/platform/windows/speed.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,57 @@
use std::ptr;

use super::device::Drive;
use crate::CdReaderError;
use crate::data_reader::ReadSpeed;

use windows_sys::Win32::Devices::Cdrom::{
CDROM_SET_SPEED, CdromDefaultRotation, CdromSetSpeed, IOCTL_CDROM_SET_SPEED,
};
use windows_sys::Win32::System::IO::DeviceIoControl;

pub(super) fn request_read_speed(
drive: &Drive,
target_read_speed: ReadSpeed,
) -> Result<(), CdReaderError> {
// stub
/*
let multiplier = match target_read_speed {
ReadSpeed::Unchanged => return Ok(()),
ReadSpeed::Optimal => 0,
ReadSpeed::CustomMultiplier(x) => x,
};
*/
Ok(())

// Windows expects KB/s rather than an X multiplier.
// 0xffff is the MMC drive-selected/maximum-speed sentinel.
let target_speed_kbs = if multiplier == 0 {
u16::MAX
} else {
(u32::from(multiplier) * 176_400 / 1000) as u16
};

let request = CDROM_SET_SPEED {
RequestType: CdromSetSpeed,
ReadSpeed: target_speed_kbs,
WriteSpeed: u16::MAX,
RotationControl: CdromDefaultRotation,
};

let mut bytes_returned = 0;

let result = unsafe {
DeviceIoControl(
drive.handle(),
IOCTL_CDROM_SET_SPEED,
&request as *const _ as *const _,
size_of::<CDROM_SET_SPEED>() as u32,
ptr::null_mut(),
0,
&mut bytes_returned,
ptr::null_mut(),
)
};

if result == 0 {
Err(CdReaderError::Io(std::io::Error::last_os_error()))
} else {
Ok(())
}
}
Loading