From 35c4516cced828da017ec584e20e12b6818199c0 Mon Sep 17 00:00:00 2001 From: strict-flower <126295255+strict-flower@users.noreply.github.com> Date: Tue, 11 Aug 2026 09:59:43 +0900 Subject: [PATCH 01/11] Add an option to requesting read speed into ReadOptions (working at macOS only) #59 --- build.rs | 2 ++ src/data_reader/mod.rs | 19 +++++++++++--- src/data_reader/read_speed.rs | 13 ++++++++++ src/lib.rs | 3 ++- src/platform/macos/device.rs | 2 +- src/platform/macos/ffi.rs | 1 + src/platform/macos/mod.rs | 10 +++++++- .../macos/native/request_read_speed.c | 12 +++++++++ src/platform/macos/speed.rs | 25 +++++++++++++++++++ 9 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 src/data_reader/read_speed.rs create mode 100644 src/platform/macos/native/request_read_speed.c create mode 100644 src/platform/macos/speed.rs diff --git a/build.rs b/build.rs index 43b900a..e95698c 100644 --- a/build.rs +++ b/build.rs @@ -13,6 +13,7 @@ fn main() { println!("cargo:rerun-if-changed={NATIVE_DIR}/toc_reader.c"); println!("cargo:rerun-if-changed={NATIVE_DIR}/track_information.c"); println!("cargo:rerun-if-changed={NATIVE_DIR}/read_cd.c"); + println!("cargo:rerun-if-changed={NATIVE_DIR}/request_read_speed.c"); println!("cargo:rustc-link-lib=framework=IOKit"); println!("cargo:rustc-link-lib=framework=CoreFoundation"); @@ -22,6 +23,7 @@ fn main() { .file(format!("{NATIVE_DIR}/toc_reader.c")) .file(format!("{NATIVE_DIR}/track_information.c")) .file(format!("{NATIVE_DIR}/read_cd.c")) + .file(format!("{NATIVE_DIR}/request_read_speed.c")) .include(NATIVE_DIR) // force C compilation .flag("-x") diff --git a/src/data_reader/mod.rs b/src/data_reader/mod.rs index 2a435f0..35f51c1 100644 --- a/src/data_reader/mod.rs +++ b/src/data_reader/mod.rs @@ -1,8 +1,10 @@ mod detect; mod raw_sector; +mod read_speed; mod sector_read_format; pub(crate) mod track_information; +pub use read_speed::ReadSpeed; pub use sector_read_format::SectorReadFormat; use crate::retry::RetryConfig; @@ -16,6 +18,7 @@ use crate::{CdReaderError, Track}; pub struct ReadOptions { format: SectorReadFormat, retry: RetryConfig, + read_speed: ReadSpeed, } impl ReadOptions { @@ -31,6 +34,11 @@ impl ReadOptions { self } + pub fn with_read_speed(mut self, read_speed: ReadSpeed) -> Self { + self.read_speed = read_speed; + self + } + pub(crate) fn format(&self) -> SectorReadFormat { self.format } @@ -38,6 +46,10 @@ impl ReadOptions { pub(crate) fn retry(&self) -> &RetryConfig { &self.retry } + + pub(crate) fn read_speed(&self) -> ReadSpeed { + self.read_speed + } } impl Default for ReadOptions { @@ -45,6 +57,7 @@ impl Default for ReadOptions { Self { format: SectorReadFormat::Audio, retry: RetryConfig::default(), + read_speed: ReadSpeed::Unchanged, } } } @@ -81,7 +94,7 @@ pub(crate) fn build_read_cd_cdb(lba: u32, sectors: u32, format: SectorReadFormat #[cfg(test)] mod tests { - use super::{ReadOptions, SectorReadFormat, build_read_cd_cdb, validate_track_format}; + use super::{build_read_cd_cdb, validate_track_format, ReadOptions, SectorReadFormat}; use crate::{CdReaderError, Track}; #[test] @@ -139,9 +152,7 @@ mod tests { fn builds_read_cd_cdb() { assert_eq!( build_read_cd_cdb(0x1234_5678, 0x0000_ABCD, SectorReadFormat::Mode1Raw), - [ - 0xBE, 0x08, 0x12, 0x34, 0x56, 0x78, 0x00, 0xAB, 0xCD, 0xF8, 0x00, 0x00, - ] + [0xBE, 0x08, 0x12, 0x34, 0x56, 0x78, 0x00, 0xAB, 0xCD, 0xF8, 0x00, 0x00,] ); } } diff --git a/src/data_reader/read_speed.rs b/src/data_reader/read_speed.rs new file mode 100644 index 0000000..c4fbe5f --- /dev/null +++ b/src/data_reader/read_speed.rs @@ -0,0 +1,13 @@ +/// Representation of read speed that requested by SET CD SPEED (0xBB) command +#[derive(Debug, Clone, Copy)] +pub enum ReadSpeed { + /// Don't change the speed + /// The speed depends to the OS, previous configuration, and others + Unchanged, + /// Request to use the optimal speed + /// By MMC-3 specification, It can be set to the optimal speed of the drive when it executes SET CD SPEED command with read speed (KB/s) = 0xFFFF. + /// On the Linux, the read speed will selected by the CDROM_SELECT_SPEED ioctl with speed = 0. It'll set the speed automatically and highest speed that supported by the drive. + Optimal, + /// Use the custom speed with specified multiplier. + CustomMultiplier(u16), +} diff --git a/src/lib.rs b/src/lib.rs index c142ca1..2c098c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,7 +145,7 @@ mod read_loop; mod retry; mod stream; mod utils; -pub use data_reader::{ReadOptions, SectorReadFormat}; +pub use data_reader::{ReadOptions, ReadSpeed, SectorReadFormat}; pub use discovery::DriveInfo; pub use errors::{CdReaderError, ScsiError, ScsiOp}; pub use retry::RetryConfig; @@ -276,6 +276,7 @@ impl CdReader { options: &ReadOptions, ) -> Result, CdReaderError> { let format = options.format(); + self.drive.request_read_speed(options.read_speed())?; read_loop::read_sectors_chunked( start_lba, sectors, diff --git a/src/platform/macos/device.rs b/src/platform/macos/device.rs index 63eca8a..40cb44e 100644 --- a/src/platform/macos/device.rs +++ b/src/platform/macos/device.rs @@ -4,7 +4,7 @@ use std::io; use std::os::fd::{AsRawFd, FromRawFd, RawFd}; use std::{ptr, slice}; -use super::ffi::{MacDriveInfo, cd_free, list_cd_drives, open_cd_raw_device}; +use super::ffi::{cd_free, list_cd_drives, open_cd_raw_device, MacDriveInfo}; pub(crate) struct Drive { // file closes the file descriptor on drop automatically diff --git a/src/platform/macos/ffi.rs b/src/platform/macos/ffi.rs index b4b31bd..1843a18 100644 --- a/src/platform/macos/ffi.rs +++ b/src/platform/macos/ffi.rs @@ -43,6 +43,7 @@ unsafe extern "C" { out_len: *mut u32, out_err: *mut MacScsiError, ) -> bool; + pub(super) fn request_cd_read_speed(fd: libc::c_int, multiplier: libc::c_ushort); pub(super) fn cd_free(pointer: *mut libc::c_void); pub(super) fn list_cd_drives(out_drives: *mut *mut MacDriveInfo, out_count: *mut u32) -> bool; pub(super) fn open_cd_raw_device(bsd_name: *const libc::c_char) -> libc::c_int; diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index a780fc1..7a64a0d 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -1,10 +1,11 @@ mod device; mod ffi; mod read_cd; +mod speed; mod toc; mod track_information; -pub(crate) use device::{Drive, list_drive_paths}; +pub(crate) use device::{list_drive_paths, Drive}; use crate::{CdReaderError, SectorReadFormat, Toc}; @@ -28,4 +29,11 @@ impl Drive { ) -> Result, CdReaderError> { read_cd::read_cd_chunk(self, lba, sectors, format) } + + pub(crate) fn request_read_speed( + &self, + read_speed: crate::data_reader::ReadSpeed, + ) -> Result<(), CdReaderError> { + speed::request_read_speed(self, read_speed) + } } diff --git a/src/platform/macos/native/request_read_speed.c b/src/platform/macos/native/request_read_speed.c new file mode 100644 index 0000000..19d0c40 --- /dev/null +++ b/src/platform/macos/native/request_read_speed.c @@ -0,0 +1,12 @@ +#include "shim_common.h" +#include + +Boolean request_cd_read_speed(int fd, uint16_t target_speed_kbs) { + int ret = ioctl(fd, DKIOCCDSETSPEED, target_speed_kbs); + if (ret < 0) { + fprintf(stderr, "[SPEED] DKIOCCDSETSPEED failed (errno=%d\n)", errno); + return false; + } + + return true; +} diff --git a/src/platform/macos/speed.rs b/src/platform/macos/speed.rs new file mode 100644 index 0000000..161d7f6 --- /dev/null +++ b/src/platform/macos/speed.rs @@ -0,0 +1,25 @@ +use super::device::Drive; +use crate::data_reader::ReadSpeed; +use crate::CdReaderError; + +pub(super) fn request_read_speed( + drive: &Drive, + target_read_speed: ReadSpeed, +) -> Result<(), CdReaderError> { + let multiplier = match target_read_speed { + ReadSpeed::Unchanged => return Ok(()), + ReadSpeed::Optimal => 0, + ReadSpeed::CustomMultiplier(x) => x as u32, + }; + let target_speed_kbs = if multiplier == 0 { + 0xffff + } else { + multiplier * 176400 / 1000 + }; + if target_speed_kbs > u16::MAX.into() { + // TODO: Implement error handle (CdReaderError?) + todo!(); + } + unsafe { super::ffi::request_cd_read_speed(drive.fd(), target_speed_kbs as u16) }; + Ok(()) +} From 32b14574ea131d5c09f2a638851eebf0deb4ca58 Mon Sep 17 00:00:00 2001 From: strict-flower <126295255+strict-flower@users.noreply.github.com> Date: Sun, 16 Aug 2026 14:59:16 +0900 Subject: [PATCH 02/11] Fix typo and change the type of multiplier to u8 --- src/data_reader/read_speed.rs | 16 +++++++++++++--- src/platform/macos/native/request_read_speed.c | 2 +- src/platform/macos/speed.rs | 6 ++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/data_reader/read_speed.rs b/src/data_reader/read_speed.rs index c4fbe5f..90051f6 100644 --- a/src/data_reader/read_speed.rs +++ b/src/data_reader/read_speed.rs @@ -4,10 +4,20 @@ pub enum ReadSpeed { /// Don't change the speed /// The speed depends to the OS, previous configuration, and others Unchanged, + /// Request to use the optimal speed - /// By MMC-3 specification, It can be set to the optimal speed of the drive when it executes SET CD SPEED command with read speed (KB/s) = 0xFFFF. - /// On the Linux, the read speed will selected by the CDROM_SELECT_SPEED ioctl with speed = 0. It'll set the speed automatically and highest speed that supported by the drive. + /// By MMC-3 specification, It can be set to the optimal speed of the drive + /// when it executes SET CD SPEED command with read speed (KB/s) = 0xFFFF. + /// On the Linux, the read speed will selected by the CDROM_SELECT_SPEED + /// ioctl with speed = 0. It'll set the speed automatically and highest + /// speed that supported by the drive. Optimal, + /// Use the custom speed with specified multiplier. - CustomMultiplier(u16), + /// + /// Although CD-DA reading speed should be requested by KB/s unit by the + /// specification, this constant will use multiplier for simple. + /// For example, `ReadSpeed::CustomMultiplier(10)` is a representation + /// of 10x speed. + CustomMultiplier(u8), } diff --git a/src/platform/macos/native/request_read_speed.c b/src/platform/macos/native/request_read_speed.c index 19d0c40..b759c3f 100644 --- a/src/platform/macos/native/request_read_speed.c +++ b/src/platform/macos/native/request_read_speed.c @@ -2,7 +2,7 @@ #include Boolean request_cd_read_speed(int fd, uint16_t target_speed_kbs) { - int ret = ioctl(fd, DKIOCCDSETSPEED, target_speed_kbs); + int ret = ioctl(fd, DKIOCCDSETSPEED, &target_speed_kbs); if (ret < 0) { fprintf(stderr, "[SPEED] DKIOCCDSETSPEED failed (errno=%d\n)", errno); return false; diff --git a/src/platform/macos/speed.rs b/src/platform/macos/speed.rs index 161d7f6..cfc7eec 100644 --- a/src/platform/macos/speed.rs +++ b/src/platform/macos/speed.rs @@ -14,12 +14,10 @@ pub(super) fn request_read_speed( let target_speed_kbs = if multiplier == 0 { 0xffff } else { + // `multiplier` must be less than 256, so below expression will not overflow + // For reference, It is 0xb066 when multiplier = 256. multiplier * 176400 / 1000 }; - if target_speed_kbs > u16::MAX.into() { - // TODO: Implement error handle (CdReaderError?) - todo!(); - } unsafe { super::ffi::request_cd_read_speed(drive.fd(), target_speed_kbs as u16) }; Ok(()) } From b6367f41b2df00434b3831529346b68375430d04 Mon Sep 17 00:00:00 2001 From: strict-flower <126295255+strict-flower@users.noreply.github.com> Date: Sun, 16 Aug 2026 15:36:30 +0900 Subject: [PATCH 03/11] Fix rustdoc --- src/data_reader/read_speed.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/data_reader/read_speed.rs b/src/data_reader/read_speed.rs index 90051f6..8ba5db0 100644 --- a/src/data_reader/read_speed.rs +++ b/src/data_reader/read_speed.rs @@ -1,23 +1,28 @@ -/// Representation of read speed that requested by SET CD SPEED (0xBB) command +/// Representation of read speed requested by the SET CD SPEED (0xBB) command. #[derive(Debug, Clone, Copy)] pub enum ReadSpeed { /// Don't change the speed - /// The speed depends to the OS, previous configuration, and others + /// + /// The speed depends to the OS, previous configuration, and other factors. Unchanged, - /// Request to use the optimal speed - /// By MMC-3 specification, It can be set to the optimal speed of the drive - /// when it executes SET CD SPEED command with read speed (KB/s) = 0xFFFF. - /// On the Linux, the read speed will selected by the CDROM_SELECT_SPEED - /// ioctl with speed = 0. It'll set the speed automatically and highest - /// speed that supported by the drive. + /// Request the optimal speed. + /// + /// According to the MMC-3 specification, the drive can select its optimal + /// speed when the set SET CD SPEED command is executed with the read + /// speed (KB/s) set to 0xFFFF. + /// + /// On the Linux, the read speed is selected by the CDROM_SELECT_SPEED + /// ioctl with speed = 0. It selects the speed automatically up to the + /// highest speed supported by the drive. Optimal, - /// Use the custom speed with specified multiplier. + /// Use a custom speed with the specified multiplier. + /// + /// Although the CD-DA read speed should be requested in KB/s according to + /// the specification, this variant uses a multiplier for simplicity. + /// Internally, it will be calculated as `176.4 KB/s * multiplier`. /// - /// Although CD-DA reading speed should be requested by KB/s unit by the - /// specification, this constant will use multiplier for simple. - /// For example, `ReadSpeed::CustomMultiplier(10)` is a representation - /// of 10x speed. + /// For example, `ReadSpeed::CustomMultiplier(10)` represents 10x speed. CustomMultiplier(u8), } From 29b3e7447a6dbded580638c83156236241fb6dd3 Mon Sep 17 00:00:00 2001 From: strict-flower <126295255+strict-flower@users.noreply.github.com> Date: Sun, 16 Aug 2026 19:12:00 +0900 Subject: [PATCH 04/11] Add a speed change request feature for Linux --- src/platform/linux/mod.rs | 8 ++++++++ src/platform/linux/speed.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/platform/linux/speed.rs diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 25da844..4664456 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -1,6 +1,7 @@ mod device; mod read_cd; mod sg_io; +mod speed; mod toc; mod track_information; @@ -28,4 +29,11 @@ impl Drive { ) -> Result, CdReaderError> { read_cd::read_cd_chunk(self, lba, sectors, format) } + + pub(crate) fn request_read_speed( + &self, + read_speed: crate::data_reader::ReadSpeed, + ) -> Result<(), CdReaderError> { + speed::request_read_speed(self, read_speed) + } } diff --git a/src/platform/linux/speed.rs b/src/platform/linux/speed.rs new file mode 100644 index 0000000..edeb40c --- /dev/null +++ b/src/platform/linux/speed.rs @@ -0,0 +1,29 @@ +use super::device::Drive; +use crate::CdReaderError; +use crate::data_reader::ReadSpeed; +use std::os::fd::RawFd; + +// From linux/include/uapi/linux/cdrom.h +const CDROM_SELECT_SPEED: libc::c_ulong = 0x5322; + +pub(super) fn request_read_speed( + drive: &Drive, + target_read_speed: ReadSpeed, +) -> Result<(), CdReaderError> { + let multiplier = match target_read_speed { + ReadSpeed::Unchanged => return Ok(()), + ReadSpeed::Optimal => 0, + ReadSpeed::CustomMultiplier(x) => x, + }; + + execute_request_read_speed(drive.fd(), multiplier) +} + +fn execute_request_read_speed(fd: RawFd, multiplier: u8) -> Result<(), CdReaderError> { + let result = unsafe { libc::ioctl(fd, CDROM_SELECT_SPEED, multiplier as libc::c_ulong) }; + if result < 0 { + Err(CdReaderError::Io(std::io::Error::last_os_error())) + } else { + Ok(()) + } +} From 255147bc62ac6af0f4cf34fcc553c21ac6738344 Mon Sep 17 00:00:00 2001 From: strict-flower <126295255+strict-flower@users.noreply.github.com> Date: Sun, 16 Aug 2026 21:27:08 +0900 Subject: [PATCH 05/11] Improve an error handling flow of request_cd_read_speed --- src/platform/macos/ffi.rs | 2 +- src/platform/macos/speed.rs | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/platform/macos/ffi.rs b/src/platform/macos/ffi.rs index 1843a18..5277725 100644 --- a/src/platform/macos/ffi.rs +++ b/src/platform/macos/ffi.rs @@ -43,7 +43,7 @@ unsafe extern "C" { out_len: *mut u32, out_err: *mut MacScsiError, ) -> bool; - pub(super) fn request_cd_read_speed(fd: libc::c_int, multiplier: libc::c_ushort); + pub(super) fn request_cd_read_speed(fd: libc::c_int, multiplier: libc::c_ushort) -> bool; pub(super) fn cd_free(pointer: *mut libc::c_void); pub(super) fn list_cd_drives(out_drives: *mut *mut MacDriveInfo, out_count: *mut u32) -> bool; pub(super) fn open_cd_raw_device(bsd_name: *const libc::c_char) -> libc::c_int; diff --git a/src/platform/macos/speed.rs b/src/platform/macos/speed.rs index cfc7eec..5fcfbcd 100644 --- a/src/platform/macos/speed.rs +++ b/src/platform/macos/speed.rs @@ -18,6 +18,12 @@ pub(super) fn request_read_speed( // For reference, It is 0xb066 when multiplier = 256. multiplier * 176400 / 1000 }; - unsafe { super::ffi::request_cd_read_speed(drive.fd(), target_speed_kbs as u16) }; + let response = + unsafe { super::ffi::request_cd_read_speed(drive.fd(), target_speed_kbs as u16) }; + + if !response { + return Err(CdReaderError::Io(std::io::Error::last_os_error())); + } + Ok(()) } From 7f3f5ce1f34b5d4006171fd81ed3d914cf4f67ea Mon Sep 17 00:00:00 2001 From: strict-flower <126295255+strict-flower@users.noreply.github.com> Date: Sun, 16 Aug 2026 22:36:15 +0900 Subject: [PATCH 06/11] Fix rustdoc --- src/data_reader/read_speed.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/data_reader/read_speed.rs b/src/data_reader/read_speed.rs index 8ba5db0..33e69ad 100644 --- a/src/data_reader/read_speed.rs +++ b/src/data_reader/read_speed.rs @@ -3,26 +3,33 @@ pub enum ReadSpeed { /// Don't change the speed /// - /// The speed depends to the OS, previous configuration, and other factors. + /// The speed depends on the OS, previous configuration, and other factors. Unchanged, - /// Request the optimal speed. + /// Request the drive-selected/optimal speed. /// /// According to the MMC-3 specification, the drive can select its optimal - /// speed when the set SET CD SPEED command is executed with the read + /// speed when the SET CD SPEED command is executed with the read /// speed (KB/s) set to 0xFFFF. /// - /// On the Linux, the read speed is selected by the CDROM_SELECT_SPEED - /// ioctl with speed = 0. It selects the speed automatically up to the - /// highest speed supported by the drive. + /// On macOS, this variant requests SET CD SPEED with 0xFFFF. + /// + /// On Linux, the read speed is selected by the CDROM_SELECT_SPEED + /// ioctl with speed = 0. It requests automatic speed selection. Optimal, /// Use a custom speed with the specified multiplier. /// /// Although the CD-DA read speed should be requested in KB/s according to - /// the specification, this variant uses a multiplier for simplicity. - /// Internally, it will be calculated as `176.4 KB/s * multiplier`. + /// the specification, this variant uses a multiplier for simplicity. + /// + /// For example, `ReadSpeed::CustomMultiplier(1)` represents the nominal + /// CD-DA 1x read rate (176.4 KB/s). The exact conversion is + /// platform-dependent. + /// + /// Another example: `ReadSpeed::CustomMultiplier(10)` represents 10x speed. /// - /// For example, `ReadSpeed::CustomMultiplier(10)` represents 10x speed. + /// `ReadSpeed::CustomMultiplier(0)` is equivalent to `ReadSpeed::Optimal`. + /// The value 0 is used as a sentinel. CustomMultiplier(u8), } From df6b9b2aaa01b519712cbc96721a987fdd37a0d3 Mon Sep 17 00:00:00 2001 From: strict-flower <126295255+strict-flower@users.noreply.github.com> Date: Mon, 24 Aug 2026 21:03:47 +0900 Subject: [PATCH 07/11] Add an example for ReadSpeed --- examples/read_speed.rs | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 examples/read_speed.rs diff --git a/examples/read_speed.rs b/examples/read_speed.rs new file mode 100644 index 0000000..a27f254 --- /dev/null +++ b/examples/read_speed.rs @@ -0,0 +1,50 @@ +/// Read the first audio track at 10x speed, and read the second audio track at `Optimal` +/// speed. +mod common; + +use cd_da_reader::{CdReader, ReadOptions, ReadSpeed, Track}; + +fn main() -> Result<(), Box> { + let output_dir = common::fresh_output_dir("read_first_track")?; + let reader = CdReader::open_default()?; + let toc = reader.read_toc()?; + + let audio_tracks: Vec<&Track> = toc.tracks.iter().filter(|t| t.is_audio).collect(); + + 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 + ); + let data = reader.read_track_with_options(&toc, second_track.number, &options_optimal)?; + + 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()); + } + + Ok(()) +} From 08d48c8ed7fdb3201eade9571c11263dd830f152 Mon Sep 17 00:00:00 2001 From: strict-flower <126295255+strict-flower@users.noreply.github.com> Date: Mon, 24 Aug 2026 21:04:17 +0900 Subject: [PATCH 08/11] Update rustdoc --- src/data_reader/mod.rs | 11 +++++++++-- src/data_reader/read_speed.rs | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/data_reader/mod.rs b/src/data_reader/mod.rs index 35f51c1..a9bcaba 100644 --- a/src/data_reader/mod.rs +++ b/src/data_reader/mod.rs @@ -34,6 +34,11 @@ impl ReadOptions { self } + /// Set the read speed to request from the drive. See [`ReadSpeed`] for + /// details. + /// + /// # Note + /// For simplicity, this crate doesn't restore the previous speed setting. pub fn with_read_speed(mut self, read_speed: ReadSpeed) -> Self { self.read_speed = read_speed; self @@ -94,7 +99,7 @@ pub(crate) fn build_read_cd_cdb(lba: u32, sectors: u32, format: SectorReadFormat #[cfg(test)] mod tests { - use super::{build_read_cd_cdb, validate_track_format, ReadOptions, SectorReadFormat}; + use super::{ReadOptions, SectorReadFormat, build_read_cd_cdb, validate_track_format}; use crate::{CdReaderError, Track}; #[test] @@ -152,7 +157,9 @@ mod tests { fn builds_read_cd_cdb() { assert_eq!( build_read_cd_cdb(0x1234_5678, 0x0000_ABCD, SectorReadFormat::Mode1Raw), - [0xBE, 0x08, 0x12, 0x34, 0x56, 0x78, 0x00, 0xAB, 0xCD, 0xF8, 0x00, 0x00,] + [ + 0xBE, 0x08, 0x12, 0x34, 0x56, 0x78, 0x00, 0xAB, 0xCD, 0xF8, 0x00, 0x00, + ] ); } } diff --git a/src/data_reader/read_speed.rs b/src/data_reader/read_speed.rs index 33e69ad..e0033c0 100644 --- a/src/data_reader/read_speed.rs +++ b/src/data_reader/read_speed.rs @@ -1,4 +1,13 @@ -/// Representation of read speed requested by the SET CD SPEED (0xBB) command. +/// Representation of read speed requested by the `SET CD SPEED` (0xBB) command. +/// +/// # Note +/// +/// According to the MMC-3 specification, the requested speed doesn't necessarily +/// match the actual read speed. The drive may select the specified read speed +/// or any higher rate. +/// +/// Therefore, this enum represents a requested speed, not a guaranteed actual +/// read speed. The actual behaviour is drive-dependent. #[derive(Debug, Clone, Copy)] pub enum ReadSpeed { /// Don't change the speed @@ -9,12 +18,12 @@ pub enum ReadSpeed { /// Request the drive-selected/optimal speed. /// /// According to the MMC-3 specification, the drive can select its optimal - /// speed when the SET CD SPEED command is executed with the read + /// speed when the `SET CD SPEED` command is executed with the read /// speed (KB/s) set to 0xFFFF. /// - /// On macOS, this variant requests SET CD SPEED with 0xFFFF. + /// On macOS, this variant requests `SET CD SPEED` with 0xFFFF. /// - /// On Linux, the read speed is selected by the CDROM_SELECT_SPEED + /// On Linux, the read speed is selected by the `CDROM_SELECT_SPEED` /// ioctl with speed = 0. It requests automatic speed selection. Optimal, From 003bc856d749b7bbf44c93a6e3ca0e2b62bc6909 Mon Sep 17 00:00:00 2001 From: strict-flower <126295255+strict-flower@users.noreply.github.com> Date: Mon, 24 Aug 2026 22:39:34 +0900 Subject: [PATCH 09/11] Fix for review --- examples/read_speed.rs | 2 +- src/platform/macos/native/request_read_speed.c | 2 +- src/platform/macos/speed.rs | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/read_speed.rs b/examples/read_speed.rs index a27f254..6837626 100644 --- a/examples/read_speed.rs +++ b/examples/read_speed.rs @@ -5,7 +5,7 @@ mod common; use cd_da_reader::{CdReader, ReadOptions, ReadSpeed, Track}; fn main() -> Result<(), Box> { - let output_dir = common::fresh_output_dir("read_first_track")?; + let output_dir = common::fresh_output_dir("read_speed")?; let reader = CdReader::open_default()?; let toc = reader.read_toc()?; diff --git a/src/platform/macos/native/request_read_speed.c b/src/platform/macos/native/request_read_speed.c index b759c3f..8ec2f63 100644 --- a/src/platform/macos/native/request_read_speed.c +++ b/src/platform/macos/native/request_read_speed.c @@ -4,7 +4,7 @@ Boolean request_cd_read_speed(int fd, uint16_t target_speed_kbs) { int ret = ioctl(fd, DKIOCCDSETSPEED, &target_speed_kbs); if (ret < 0) { - fprintf(stderr, "[SPEED] DKIOCCDSETSPEED failed (errno=%d\n)", errno); + fprintf(stderr, "[SPEED] DKIOCCDSETSPEED failed (errno=%d)\n", errno); return false; } diff --git a/src/platform/macos/speed.rs b/src/platform/macos/speed.rs index 5fcfbcd..771aa02 100644 --- a/src/platform/macos/speed.rs +++ b/src/platform/macos/speed.rs @@ -1,6 +1,6 @@ use super::device::Drive; -use crate::data_reader::ReadSpeed; use crate::CdReaderError; +use crate::data_reader::ReadSpeed; pub(super) fn request_read_speed( drive: &Drive, @@ -14,8 +14,6 @@ pub(super) fn request_read_speed( let target_speed_kbs = if multiplier == 0 { 0xffff } else { - // `multiplier` must be less than 256, so below expression will not overflow - // For reference, It is 0xb066 when multiplier = 256. multiplier * 176400 / 1000 }; let response = From 68b824b51eb6cc0473b6a2db6ad3607e2e10b7e9 Mon Sep 17 00:00:00 2001 From: strict-flower <126295255+strict-flower@users.noreply.github.com> Date: Mon, 24 Aug 2026 22:44:56 +0900 Subject: [PATCH 10/11] Add a stub for a Windows backend --- src/platform/windows/mod.rs | 8 ++++++++ src/platform/windows/speed.rs | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/platform/windows/speed.rs diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index 2efbe9f..4dd7ee0 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -1,5 +1,6 @@ mod device; mod read_cd; +mod speed; mod spti; mod toc; mod track_information; @@ -28,4 +29,11 @@ impl Drive { ) -> Result, CdReaderError> { read_cd::read_cd_chunk(self, lba, sectors, format) } + + pub(crate) fn request_read_speed( + &self, + read_speed: crate::data_reader::ReadSpeed, + ) -> Result<(), CdReaderError> { + speed::request_read_speed(self, read_speed) + } } diff --git a/src/platform/windows/speed.rs b/src/platform/windows/speed.rs new file mode 100644 index 0000000..fa438a2 --- /dev/null +++ b/src/platform/windows/speed.rs @@ -0,0 +1,18 @@ +use super::device::Drive; +use crate::CdReaderError; +use crate::data_reader::ReadSpeed; + +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(()) +} From 64afa231063931e305c283ff039446842f7aa471 Mon Sep 17 00:00:00 2001 From: strict-flower <126295255+strict-flower@users.noreply.github.com> Date: Mon, 24 Aug 2026 22:54:42 +0900 Subject: [PATCH 11/11] Apply cargo fmt --- src/platform/macos/device.rs | 2 +- src/platform/macos/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/macos/device.rs b/src/platform/macos/device.rs index 8db0217..f4cc7c1 100644 --- a/src/platform/macos/device.rs +++ b/src/platform/macos/device.rs @@ -4,7 +4,7 @@ use std::io; use std::os::fd::{AsRawFd, FromRawFd, RawFd}; use std::{ptr, slice}; -use super::ffi::{cd_free, list_cd_drives, open_cd_raw_device, MacDriveInfo}; +use super::ffi::{MacDriveInfo, cd_free, list_cd_drives, open_cd_raw_device}; pub(crate) struct Drive { // file closes the file descriptor on drop automatically diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index 7a64a0d..65ff721 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -5,7 +5,7 @@ mod speed; mod toc; mod track_information; -pub(crate) use device::{list_drive_paths, Drive}; +pub(crate) use device::{Drive, list_drive_paths}; use crate::{CdReaderError, SectorReadFormat, Toc};