From 8fc4f0a467a28b5cf6800f2d8f5fe7ebf89252c3 Mon Sep 17 00:00:00 2001 From: Seva Zaikov Date: Mon, 24 Aug 2026 12:03:29 -0700 Subject: [PATCH 1/2] add speed option to Windows --- Cargo.toml | 1 + src/platform/windows/speed.rs | 47 ++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ea04f27..5be9547 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/src/platform/windows/speed.rs b/src/platform/windows/speed.rs index fa438a2..d38f8f7 100644 --- a/src/platform/windows/speed.rs +++ b/src/platform/windows/speed.rs @@ -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::() 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(()) + } } From 22d1ed0d8d96cad17db09e14ed584cc1a3999567 Mon Sep 17 00:00:00 2001 From: Seva Zaikov Date: Mon, 24 Aug 2026 12:12:40 -0700 Subject: [PATCH 2/2] update read speed test --- examples/read_speed.rs | 79 +++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/examples/read_speed.rs b/examples/read_speed.rs index 6837626..be41d17 100644 --- a/examples/read_speed.rs +++ b/examples/read_speed.rs @@ -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> { - 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(())