diff --git a/audio-device/Cargo.toml b/audio-device/Cargo.toml index 671ae40..913d63c 100644 --- a/audio-device/Cargo.toml +++ b/audio-device/Cargo.toml @@ -35,23 +35,20 @@ unix = ["libc"] wasapi = [ "windows", "events-driver", - "windows?/Win32_System_Threading", - "windows?/Win32_Foundation", - "windows?/Win32_System_Com_StructuredStorage", - "windows?/Win32_Security", - "windows?/Win32_System_WindowsProgramming", - "windows?/Win32_System_Com", - "windows?/Win32_Media_Audio", - "windows?/Win32_Media_KernelStreaming", - "windows?/Win32_Media_Multimedia", + "windows/Win32_System_Threading", + "windows/Win32_Foundation", + "windows/Win32_System_Com_StructuredStorage", + "windows/Win32_Security", + "windows/Win32_System_Com", + "windows/Win32_Media_Audio", + "windows/Win32_Media_KernelStreaming", + "windows/Win32_Media_Multimedia", ] sync = [] [dependencies] tracing = "0.1.36" audio-core = { version = "0.2.1", path = "../audio-core" } -thiserror = { version = "2.0.17", default-features = false } -rand = "0.9.2" ste = { version = "0.1.0-alpha.11", path = "../ste" } pulse-sys = { package = "audio-device-pulse-sys", version = "0.1.0-alpha.1", path = "../audio-device-pulse-sys", optional = true } @@ -62,6 +59,7 @@ alsa-sys = { package = "audio-device-alsa-sys", version = "0.1.0-alpha.1", path libc = { version = "0.2.125", optional = true } [dev-dependencies] +rand = "0.9.2" audio = { version = "0.2.1", path = "../audio" } audio-generator = { version = "0.1.0-alpha.2", path = "../audio-generator" } anyhow = "1.0.57" diff --git a/audio-device/examples/alsa-list.rs b/audio-device/examples/alsa-list.rs index 4e4ffa9..46702a7 100644 --- a/audio-device/examples/alsa-list.rs +++ b/audio-device/examples/alsa-list.rs @@ -1,6 +1,7 @@ -use audio_device::alsa; use std::ffi::CString; +use audio_device::alsa; + fn main() -> anyhow::Result<()> { let thread = ste::spawn(); diff --git a/audio-device/examples/pulse.rs b/audio-device/examples/pulse.rs index 1dedd91..150b7de 100644 --- a/audio-device/examples/pulse.rs +++ b/audio-device/examples/pulse.rs @@ -9,7 +9,7 @@ fn generate_audio() -> anyhow::Result<()> { context.set_callback(|c| { println!("state changed: {}", c.state()?); - Err(pulse::Error::User("hello".into())) + Err(pulse::Error::user("hello")) })?; context.connect()?; diff --git a/audio-device/src/alsa/access_mask.rs b/audio-device/src/alsa/access_mask.rs index d61fb00..b402460 100644 --- a/audio-device/src/alsa/access_mask.rs +++ b/audio-device/src/alsa/access_mask.rs @@ -4,6 +4,7 @@ use core::ptr::NonNull; use alsa_sys as alsa; +use crate::alsa::error::errno; use crate::alsa::{Access, Result}; /// Access mask used in combination with hardware parameters. diff --git a/audio-device/src/alsa/async_writer.rs b/audio-device/src/alsa/async_writer.rs index 649f65a..be847f8 100644 --- a/audio-device/src/alsa/async_writer.rs +++ b/audio-device/src/alsa/async_writer.rs @@ -2,9 +2,9 @@ use core::marker; use core::ffi::c_void; use crate::alsa::{Error, Pcm, Result}; +use crate::alsa::error::ErrorKind; use crate::libc as c; -use crate::unix::{Errno, PollFlags}; -use crate::unix::AsyncPoll; +use crate::unix::{PollFlags, AsyncPoll}; /// An interleaved type-checked async PCM writer. /// @@ -40,10 +40,10 @@ impl<'a, T> AsyncWriter<'a, T> { B: audio_core::Buf + audio_core::ReadBuf + audio_core::ExactSizeBuf + audio_core::InterleavedBuf, { if buf.channels() != self.channels { - return Err(Error::ChannelsMismatch { + return Err(Error::from(ErrorKind::ChannelsMismatch { actual: buf.channels(), expected: self.channels, - }); + })); } while buf.has_remaining() { @@ -58,7 +58,7 @@ impl<'a, T> AsyncWriter<'a, T> { let written = match result { Ok(written) => written as usize, - Err(Error::Sys(Errno::EWOULDBLOCK)) => { + Err(e) if e.would_block() => { loop { let guard = self.poll_handle.returned_events().await; self.pollfd.revents = guard.events(); diff --git a/audio-device/src/alsa/c_string.rs b/audio-device/src/alsa/c_string.rs new file mode 100644 index 0000000..9230104 --- /dev/null +++ b/audio-device/src/alsa/c_string.rs @@ -0,0 +1,41 @@ +use core::ffi::{CStr, c_char}; +use core::ops; + +/// A string allocated through libc. +#[repr(transparent)] +pub struct CString { + ptr: *mut c_char, +} + +// Safety: string is allocated with the libc allocator and can be freely shared +// across threads. +unsafe impl Send for CString {} +unsafe impl Sync for CString {} + +impl CString { + /// Construct a new string that was allocated through libc. + /// + /// This differs from [std::ffi::CString] in that it requires the underlying + /// string to have been allocated using libc allocators, and will free the + /// underlying string using those as well. + pub unsafe fn from_raw(ptr: *mut c_char) -> Self { + Self { ptr } + } +} + +impl Drop for CString { + fn drop(&mut self) { + unsafe { + libc::free(self.ptr.cast()); + } + } +} + +impl ops::Deref for CString { + type Target = CStr; + + #[inline] + fn deref(&self) -> &Self::Target { + unsafe { CStr::from_ptr(self.ptr) } + } +} diff --git a/audio-device/src/alsa/card.rs b/audio-device/src/alsa/card.rs index 1077dbc..19c5ba8 100644 --- a/audio-device/src/alsa/card.rs +++ b/audio-device/src/alsa/card.rs @@ -3,6 +3,7 @@ use core::mem; use alsa_sys as alsa; +use crate::alsa::error::errno; use crate::alsa::{CString, Result}; /// Construct an iterator over sounds cards. diff --git a/audio-device/src/alsa/configurator.rs b/audio-device/src/alsa/configurator.rs index 21520d0..d84dd36 100644 --- a/audio-device/src/alsa/configurator.rs +++ b/audio-device/src/alsa/configurator.rs @@ -2,6 +2,7 @@ use core::ffi::{c_uint, c_ulong}; use core::marker; use core::time::Duration; +use crate::alsa::error::ErrorKind; use crate::alsa::{Access, Direction, Error, Format, Pcm, Result, Sample}; /// Default access to configure. @@ -118,10 +119,10 @@ where /// ``` pub fn format(self, format: Format) -> Result { if !T::test(format) { - return Err(Error::FormatMismatch { + return Err(Error::from(ErrorKind::FormatMismatch { ty: T::describe(), format, - }); + })); } Ok(Self { format, ..self }) diff --git a/audio-device/src/alsa/control.rs b/audio-device/src/alsa/control.rs index 93e1b90..2b59920 100644 --- a/audio-device/src/alsa/control.rs +++ b/audio-device/src/alsa/control.rs @@ -4,6 +4,7 @@ use core::ptr::NonNull; use alsa_sys as alsa; +use crate::alsa::error::errno; use crate::alsa::{ControlElementInterface, Result}; /// A control associated with a device. diff --git a/audio-device/src/alsa/enums.rs b/audio-device/src/alsa/enums.rs index f86b4ec..0295408 100644 --- a/audio-device/src/alsa/enums.rs +++ b/audio-device/src/alsa/enums.rs @@ -4,6 +4,7 @@ use core::fmt; use alsa_sys as alsa; use crate::alsa::Result; +use crate::alsa::error::errno; macro_rules! decl_enum { ( diff --git a/audio-device/src/alsa/error.rs b/audio-device/src/alsa/error.rs new file mode 100644 index 0000000..eaebece --- /dev/null +++ b/audio-device/src/alsa/error.rs @@ -0,0 +1,146 @@ +use core::error::Error as CoreError; +use core::ffi::{c_int, c_uint}; +use core::fmt; + +use crate::alsa::Format; +use crate::unix::Errno; + +/// ALSA-specific result alias. +pub type Result = ::core::result::Result; + +macro_rules! __errno { + ($expr:expr) => {{ + let result = $expr; + + if result < 0 { + Err($crate::unix::Errno::new(-result as i32)) + } else { + Ok(result) + } + }}; +} + +pub(crate) use __errno as errno; + +/// ALSA-specific errors. +pub struct Error { + kind: ErrorKind, +} + +impl Error { + #[cfg(feature = "alsa")] + pub(crate) fn would_block(&self) -> bool { + matches!(self.kind, ErrorKind::Errno(errno) if errno == Errno::EWOULDBLOCK) + } +} + +impl From for Error { + #[inline] + fn from(kind: ErrorKind) -> Self { + Self { kind } + } +} + +impl From for Error { + #[inline] + fn from(errno: Errno) -> Self { + Self { + kind: ErrorKind::Errno(errno), + } + } +} + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.fmt(f) + } +} + +impl fmt::Debug for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.fmt(f) + } +} + +impl CoreError for Error { + #[inline] + fn source(&self) -> Option<&(dyn CoreError + 'static)> { + match self.kind { + #[cfg(feature = "unix")] + ErrorKind::Errno(ref error) => Some(error), + _ => None, + } + } +} + +#[derive(Debug)] +pub(crate) enum ErrorKind { + /// System error. + Errno(Errno), + /// Error raised when there's a format mismatch between an underlying stream + /// and the type attempting to be used with it. + FormatMismatch { + /// A description of the type expected. + ty: &'static str, + /// The format that mismatched. + format: Format, + }, + /// Error raised when there's a channel count mismatch between an underlying + /// stream and the type attempting to be used with it. + ChannelsMismatch { + /// The actual number of channels. + actual: usize, + /// The expected number of channels. + expected: usize, + }, + /// Underlying function call returned an illegal format identifier. + BadFormat(c_int), + /// Underlying function call returned an illegal access identifier. + BadAccess(c_uint), + /// Underlying function call returned an illegal timestamp identifier. + BadTimestamp(c_uint), + /// Underlying function call returned an illegal timestamp type identifier. + BadTimestampType(c_uint), + /// Underlying PCM was not set up for polling. + MissingPollFds, +} + +impl fmt::Display for ErrorKind { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::Errno(errno) => { + write!(f, "system error: {errno}") + } + Self::FormatMismatch { ty, format } => { + write!( + f, + "type `{ty}` is not appropriate to use with format `{format}`" + ) + } + Self::ChannelsMismatch { actual, expected } => { + write!( + f, + "mismatch in number of channels in buffer; actual = {actual}, expected = {expected}" + ) + } + Self::BadFormat(format) => { + write!(f, "bad format identifier ({format})") + } + Self::BadAccess(access) => { + write!(f, "bad access identifier ({access})") + } + Self::BadTimestamp(timestamp) => { + write!(f, "bad timestamp mode identifier ({timestamp})") + } + Self::BadTimestampType(ty) => { + write!(f, "bad timestamp type identifier ({ty})") + } + Self::MissingPollFds => { + write!(f, "pcm device is not pollable") + } + } + } +} diff --git a/audio-device/src/alsa/format_mask.rs b/audio-device/src/alsa/format_mask.rs index a401f64..10a27bd 100644 --- a/audio-device/src/alsa/format_mask.rs +++ b/audio-device/src/alsa/format_mask.rs @@ -4,6 +4,7 @@ use core::ptr::NonNull; use alsa_sys as alsa; +use crate::alsa::error::errno; use crate::alsa::{Format, Result}; /// Format mask used in combination with hardware parameters. diff --git a/audio-device/src/alsa/hardware_parameters.rs b/audio-device/src/alsa/hardware_parameters.rs index 3b293c1..2c66071 100644 --- a/audio-device/src/alsa/hardware_parameters.rs +++ b/audio-device/src/alsa/hardware_parameters.rs @@ -5,7 +5,8 @@ use core::ptr::NonNull; use alsa_sys as alsa; -use crate::alsa::{Access, AccessMask, Direction, Error, Format, FormatMask, Result}; +use crate::alsa::error::{ErrorKind, errno}; +use crate::alsa::{Access, AccessMask, Direction, Format, FormatMask, Result}; /// Collection of current hardware parameters being configured for a /// [Pcm][super::Pcm] handle. @@ -277,7 +278,7 @@ impl HardwareParameters { ))?; let format = format.assume_init(); - let format = Format::from_value(format).ok_or_else(|| Error::BadFormat(format))?; + let format = Format::from_value(format).ok_or_else(|| ErrorKind::BadFormat(format))?; Ok(format) } } @@ -328,7 +329,7 @@ impl HardwareParameters { ))?; let access = access.assume_init(); - let access = Access::from_value(access).ok_or_else(|| Error::BadAccess(access))?; + let access = Access::from_value(access).ok_or_else(|| ErrorKind::BadAccess(access))?; Ok(access) } } @@ -1771,7 +1772,7 @@ impl<'a> HardwareParametersMut<'a> { ))?; let format = format.assume_init(); - let format = Format::from_value(format).ok_or_else(|| Error::BadFormat(format))?; + let format = Format::from_value(format).ok_or_else(|| ErrorKind::BadFormat(format))?; Ok(format) } } @@ -1802,7 +1803,7 @@ impl<'a> HardwareParametersMut<'a> { ))?; let format = format.assume_init(); - let format = Format::from_value(format).ok_or_else(|| Error::BadFormat(format))?; + let format = Format::from_value(format).ok_or_else(|| ErrorKind::BadFormat(format))?; Ok(format) } } @@ -1943,7 +1944,7 @@ impl<'a> HardwareParametersMut<'a> { ))?; let access = access.assume_init(); - let access = Access::from_value(access).ok_or_else(|| Error::BadAccess(access))?; + let access = Access::from_value(access).ok_or_else(|| ErrorKind::BadAccess(access))?; Ok(access) } } @@ -1973,7 +1974,7 @@ impl<'a> HardwareParametersMut<'a> { ))?; let access = access.assume_init(); - let access = Access::from_value(access).ok_or_else(|| Error::BadAccess(access))?; + let access = Access::from_value(access).ok_or_else(|| ErrorKind::BadAccess(access))?; Ok(access) } } diff --git a/audio-device/src/alsa/mod.rs b/audio-device/src/alsa/mod.rs index 8032462..c07bff9 100644 --- a/audio-device/src/alsa/mod.rs +++ b/audio-device/src/alsa/mod.rs @@ -1,116 +1,11 @@ //! An idiomatic Rust ALSA interface. // Documentation: https://www.alsa-project.org/alsa-doc/alsa-lib/ -use core::ffi::{CStr, c_char, c_int, c_uint}; -use core::ops; +mod error; +pub use self::error::{Error, Result}; -use std::io; - -use crate::libc as c; -use crate::unix::Errno; - -use thiserror::Error; - -macro_rules! errno { - ($expr:expr) => {{ - let result = $expr; - - if result < 0 { - Err(crate::unix::Errno::new(-result as i32)) - } else { - Ok(result) - } - }}; -} - -/// A string allocated through libc. -#[repr(transparent)] -pub struct CString { - ptr: *mut c_char, -} - -impl CString { - /// Construct a new string that was allocated through libc. - /// - /// This differs from [std::ffi::CString] in that it requires the underlying - /// string to have been allocated using libc allocators, and will free the - /// underlying string using those as well. - pub unsafe fn from_raw(ptr: *mut c_char) -> Self { - Self { ptr } - } -} - -impl Drop for CString { - fn drop(&mut self) { - unsafe { - c::free(self.ptr as *mut _); - } - } -} - -impl ops::Deref for CString { - type Target = CStr; - - fn deref(&self) -> &Self::Target { - unsafe { CStr::from_ptr(self.ptr) } - } -} - -// Safety: string is allocated with the libc allocator and can be freely shared -// across threads. -unsafe impl Send for CString {} -unsafe impl Sync for CString {} - -/// Errors that can be raised by the ALSA layer. -#[derive(Debug, Error)] -pub enum Error { - /// System error. - #[error("system error: {0}")] - Sys(#[from] Errno), - /// I/O error. - #[error("i/o error: {0}")] - Io( - #[source] - #[from] - io::Error, - ), - /// Error raised when there's a format mismatch between an underlying stream - /// and the type attempting to be used with it. - #[error("type `{ty}` is not appropriate to use with format `{format}`")] - FormatMismatch { - /// A description of the type expected. - ty: &'static str, - /// The format that mismatched. - format: Format, - }, - /// Error raised when there's a channel count mismatch between an underlying - /// stream and the type attempting to be used with it. - #[error("mismatch in number of channels in buffer; actual = {actual}, expected = {expected}")] - ChannelsMismatch { - /// The actual number of channels. - actual: usize, - /// The expected number of channels. - expected: usize, - }, - /// Underlying function call returned an illegal format identifier. - #[error("bad format identifier ({0})")] - BadFormat(c_int), - /// Underlying function call returned an illegal access identifier. - #[error("bad access identifier ({0})")] - BadAccess(c_uint), - /// Underlying function call returned an illegal timestamp identifier. - #[error("bad timestamp mode identifier ({0})")] - BadTimestamp(c_uint), - /// Underlying function call returned an illegal timestamp type identifier. - #[error("bad timestamp type identifier ({0})")] - BadTimestampType(c_uint), - /// Underlying PCM was not set up for polling. - #[error("pcm device is not pollable")] - MissingPollFds, -} - -/// Helper result wrapper. -pub type Result = ::core::result::Result; +mod c_string; +pub use self::c_string::CString; mod card; pub use self::card::{Card, cards}; diff --git a/audio-device/src/alsa/pcm.rs b/audio-device/src/alsa/pcm.rs index eea40af..0310e84 100644 --- a/audio-device/src/alsa/pcm.rs +++ b/audio-device/src/alsa/pcm.rs @@ -8,6 +8,7 @@ use alsa_sys as alsa; #[cfg(feature = "poll-driver")] use crate::alsa::AsyncWriter; +use crate::alsa::error::{ErrorKind, errno}; use crate::alsa::{ ChannelArea, Configurator, Error, HardwareParameters, HardwareParametersMut, Result, Sample, SoftwareParameters, SoftwareParametersMut, State, Stream, Writer, @@ -490,10 +491,10 @@ impl Pcm { let format = hw.format()?; if !T::test(format) { - return Err(Error::FormatMismatch { + return Err(Error::from(ErrorKind::FormatMismatch { ty: T::describe(), format, - }); + })); } unsafe { Ok(Writer::new(self, channels)) } @@ -539,17 +540,17 @@ impl Pcm { let format = hw.format()?; if !T::test(format) { - return Err(Error::FormatMismatch { + return Err(Error::from(ErrorKind::FormatMismatch { ty: T::describe(), format, - }); + })); } let mut fds = Vec::new(); self.poll_descriptors_vec(&mut fds)?; if fds.len() != 1 { - return Err(Error::MissingPollFds); + return Err(Error::from(ErrorKind::MissingPollFds)); } let fd = fds[0]; diff --git a/audio-device/src/alsa/software_parameters.rs b/audio-device/src/alsa/software_parameters.rs index abd837b..03d11b3 100644 --- a/audio-device/src/alsa/software_parameters.rs +++ b/audio-device/src/alsa/software_parameters.rs @@ -5,7 +5,8 @@ use core::ptr::NonNull; use alsa_sys as alsa; -use crate::alsa::{Error, Result, Timestamp, TimestampType}; +use crate::alsa::error::{ErrorKind, errno}; +use crate::alsa::{Result, Timestamp, TimestampType}; /// Collection of software parameters being configured for a [Pcm][super::Pcm] /// handle. @@ -109,7 +110,7 @@ impl SoftwareParameters { ); let timestamp_mode = timestamp_mode.assume_init(); let timestamp_mode = Timestamp::from_value(timestamp_mode) - .ok_or_else(|| Error::BadTimestamp(timestamp_mode))?; + .ok_or_else(|| ErrorKind::BadTimestamp(timestamp_mode))?; Ok(timestamp_mode) } } @@ -138,7 +139,7 @@ impl SoftwareParameters { ); let timestamp_type = timestamp_type.assume_init(); let timestamp_type = TimestampType::from_value(timestamp_type) - .ok_or_else(|| Error::BadTimestampType(timestamp_type))?; + .ok_or_else(|| ErrorKind::BadTimestampType(timestamp_type))?; Ok(timestamp_type) } } diff --git a/audio-device/src/alsa/writer.rs b/audio-device/src/alsa/writer.rs index 136a419..4edef92 100644 --- a/audio-device/src/alsa/writer.rs +++ b/audio-device/src/alsa/writer.rs @@ -1,6 +1,7 @@ use core::ffi::c_void; use core::marker; +use crate::alsa::error::ErrorKind; use crate::alsa::{Error, Pcm, Result}; /// A interleaved type-checked PCM writer. @@ -36,10 +37,10 @@ impl<'a, T> Writer<'a, T> { + audio_core::InterleavedBuf, { if buf.channels() != self.channels { - return Err(Error::ChannelsMismatch { + return Err(Error::from(ErrorKind::ChannelsMismatch { actual: buf.channels(), expected: self.channels, - }); + })); } let frames = buf.frames() as usize; diff --git a/audio-device/src/error.rs b/audio-device/src/error.rs index 0d63ec9..8652a5e 100644 --- a/audio-device/src/error.rs +++ b/audio-device/src/error.rs @@ -1,21 +1,84 @@ -use thiserror::Error; +use core::error::Error as CoreError; +use core::fmt; -/// Audio runtime errors. -#[derive(Debug, Error)] -pub enum Error { +#[cfg(feature = "windows")] +use windows::core::Error as WindowsError; + +#[cfg(feature = "unix")] +use crate::unix::Errno; + +/// Audio device-specific result alias. +pub type Result = ::core::result::Result; + +/// Audio device-specific errors. +pub struct Error { + kind: ErrorKind, +} + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.fmt(f) + } +} + +impl fmt::Debug for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.fmt(f) + } +} + +impl CoreError for Error { + #[inline] + fn source(&self) -> Option<&(dyn CoreError + 'static)> { + match self.kind { + #[cfg(feature = "unix")] + ErrorKind::Unix(ref error) => Some(error), + #[cfg(feature = "windows")] + ErrorKind::Windows(ref error) => Some(error), + } + } +} + +#[derive(Debug)] +enum ErrorKind { #[cfg(feature = "unix")] - #[error("system error: {0}")] /// A unix system error. - Unix(#[from] crate::unix::Errno), + Unix(Errno), #[cfg(feature = "windows")] - #[error("system error: {0}")] /// A windows system error. - Windows( - #[from] - #[source] - windows::core::Error, - ), + Windows(WindowsError), } -/// The re-exported error type. -pub type Result = ::core::result::Result; +impl fmt::Display for ErrorKind { + #[inline] + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + #[cfg(feature = "unix")] + Self::Unix(ref error) => write!(_f, "unix system error: {error}"), + #[cfg(feature = "windows")] + Self::Windows(ref error) => write!(_f, "windows system error: {error}"), + } + } +} + +#[cfg(feature = "unix")] +impl From for Error { + #[inline] + fn from(errno: Errno) -> Self { + Self { + kind: ErrorKind::Unix(errno), + } + } +} + +#[cfg(feature = "windows")] +impl From for Error { + #[inline] + fn from(error: WindowsError) -> Self { + Self { + kind: ErrorKind::Windows(error), + } + } +} diff --git a/audio-device/src/pulse/context.rs b/audio-device/src/pulse/context.rs index d52d5f2..f5cb479 100644 --- a/audio-device/src/pulse/context.rs +++ b/audio-device/src/pulse/context.rs @@ -6,7 +6,8 @@ use alloc::vec::Vec; use pulse_sys as pulse; -use crate::pulse::{ContextState, Error, Result, error}; +use crate::pulse::error::{ErrorKind, error, ffi_error}; +use crate::pulse::{ContextState, Result, error}; /// Structure holding onto a registered callback. pub struct Callback { @@ -105,7 +106,7 @@ impl Context { pub fn state(&self) -> Result { unsafe { let state = pulse::pa_context_get_state(self.handle.as_ptr()); - ContextState::from_value(state).ok_or_else(|| Error::BadContextState(state)) + Ok(ContextState::from_value(state).ok_or_else(|| ErrorKind::BadContextState(state))?) } } } diff --git a/audio-device/src/pulse/error.rs b/audio-device/src/pulse/error.rs index d5ac309..3308d1e 100644 --- a/audio-device/src/pulse/error.rs +++ b/audio-device/src/pulse/error.rs @@ -1,5 +1,7 @@ use core::cell::Cell; +use core::error::Error as CoreError; use core::ffi::c_uint; +use core::fmt; use core::ptr; use alloc::boxed::Box; @@ -8,23 +10,20 @@ use std::thread_local; use crate::unix::Errno; -use thiserror::Error; - -macro_rules! error { +macro_rules! __error { ($s:expr, $expr:expr) => {{ let result = $expr; if result < 0 { let errno = { pulse::pa_context_errno($s.handle.as_ptr()) }; - - Err(crate::pulse::Error::Sys(crate::unix::Errno::new(errno))) + Err(crate::pulse::Error::from(crate::unix::Errno::new(errno))) } else { - ffi_error!(result) + $crate::pulse::error::ffi_error!(result) } }}; } -macro_rules! ffi_error { +macro_rules! __ffi_error { ($expr:expr) => {{ let result = $expr; @@ -36,6 +35,9 @@ macro_rules! ffi_error { }}; } +pub(crate) use __error as error; +pub(crate) use __ffi_error as ffi_error; + thread_local! { /// The last error encountered on this thread. /// @@ -78,19 +80,112 @@ where } } -/// Errors that can be raised by the PulseAudio layer. -#[derive(Debug, Error)] -pub enum Error { +/// PulseAudio-specific result alias. +pub type Result = ::core::result::Result; + +/// PulseAudio-specific errors. +pub struct Error { + kind: ErrorKind, +} + +impl Error { + /// Create a new user-defined error. + pub fn user(error: E) -> Self + where + E: fmt::Display + fmt::Debug + Send + Sync + 'static, + { + Self { + kind: ErrorKind::User(Box::new(DisplayError(error))), + } + } +} + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.fmt(f) + } +} + +impl fmt::Debug for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.fmt(f) + } +} + +impl CoreError for Error { + #[inline] + fn source(&self) -> Option<&(dyn CoreError + 'static)> { + match self.kind { + ErrorKind::Errno(ref error) => Some(error), + ErrorKind::BadContextState(..) => None, + ErrorKind::User(ref error) => Some(&**error), + } + } +} + +impl From for Error { + #[inline] + fn from(errno: Errno) -> Self { + Self { + kind: ErrorKind::Errno(errno), + } + } +} + +impl From for Error { + #[inline] + fn from(kind: ErrorKind) -> Self { + Self { kind } + } +} + +#[derive(Debug)] +pub(crate) enum ErrorKind { /// System error. - #[error("system error: {0}")] - Sys(#[from] Errno), + Errno(Errno), /// Tried to decode bad context state. - #[error("bad context state identifier `{0}`")] BadContextState(c_uint), /// A custom user error. - #[error("error: {0}")] - User(#[source] Box), + User(Box), } -/// Helper result wrapper. -pub type Result = ::core::result::Result; +impl fmt::Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::Errno(errno) => { + write!(f, "system error: {errno}") + } + Self::BadContextState(state) => { + write!(f, "bad context state identifier `{state}`") + } + Self::User(ref error) => error.fmt(f), + } + } +} + +#[repr(transparent)] +struct DisplayError(E); + +impl fmt::Display for DisplayError +where + E: fmt::Display, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Debug for DisplayError +where + E: fmt::Debug, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl CoreError for DisplayError where E: fmt::Display + fmt::Debug {} diff --git a/audio-device/src/pulse/mod.rs b/audio-device/src/pulse/mod.rs index 951030c..c21e01d 100644 --- a/audio-device/src/pulse/mod.rs +++ b/audio-device/src/pulse/mod.rs @@ -2,7 +2,6 @@ // Documentation: https://freedesktop.org/software/pulseaudio/doxygen/ // Ref: https://gist.github.com/toroidal-code/8798775 -#[macro_use] mod error; pub use self::error::{Error, Result}; diff --git a/audio-device/src/runtime/events.rs b/audio-device/src/runtime/events.rs index 484a196..db0c4bf 100644 --- a/audio-device/src/runtime/events.rs +++ b/audio-device/src/runtime/events.rs @@ -1,12 +1,14 @@ use core::mem; +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; use alloc::vec::Vec; -use std::io; - +use windows::core::Result as WindowsResult; use windows::Win32::Foundation as f; use windows::Win32::System::Threading as th; -use windows::Win32::System::WindowsProgramming as wp; +use windows::core::Error as WindowsError; use crate::loom::sync::atomic::{AtomicBool, Ordering}; use crate::loom::sync::{Arc, Mutex}; @@ -22,6 +24,9 @@ struct Waker { handle: f::HANDLE, } +unsafe impl Send for Waker {} +unsafe impl Sync for Waker {} + struct Shared { running: AtomicBool, holders: Mutex, @@ -123,9 +128,9 @@ impl Driver { f::WAIT_ABANDONED_0 => panic!("wait abandoned"), f::WAIT_TIMEOUT => panic!("timed out"), f::WAIT_FAILED => { - panic!("wait failed: {}", io::Error::last_os_error()) + panic!("wait failed: {}", WindowsError::from_thread()) } - f::WIN32_ERROR(other) => { + f::WAIT_EVENT(other) => { let base = f::WAIT_OBJECT_0.0; if other < base { @@ -152,27 +157,20 @@ impl Driver { } let mut holders = self.shared.holders.lock(); - let mut added = mem::replace(&mut holders.added, Vec::new()); - for waker in added.drain(..) { + for waker in holders.added.drain(..) { self.events.push(waker.handle); guard.wakers.push(waker); } - holders.added = added; - - let mut removed = mem::replace(&mut holders.removed, Vec::new()); + for event in holders.removed.drain(..) { + let removed = unsafe { event.raw_event() }; - for event in removed.drain(..) { - let removed = unsafe { event.raw_event().0 }; - - if let Some(index) = guard.wakers.iter().position(|w| w.handle.0 == removed) { + if let Some(index) = guard.wakers.iter().position(|w| w.handle == removed) { guard.wakers.swap_remove(index); self.events.swap_remove(index + 1); } } - - holders.removed = removed; } mem::forget(guard); @@ -225,7 +223,7 @@ impl AsyncEvent { /// Panics unless an audio runtime is available. /// /// See [Runtime][crate::runtime::Runtime]. - pub fn new(initial_state: bool) -> windows::core::Result { + pub fn new(initial_state: bool) -> WindowsResult { crate::runtime::with_events(|events| { let event = Event::new(false, initial_state)?; let handle = unsafe { event.raw_event() }; @@ -249,10 +247,6 @@ impl AsyncEvent { /// Wait for the specified event handle to become set. pub async fn wait(&self) { - use std::future::Future; - use std::pin::Pin; - use std::task::{Context, Poll}; - return WaitFor { shared: &*self.shared, waker: &*self.waker, diff --git a/audio-device/src/unix/mod.rs b/audio-device/src/unix/mod.rs index 92522d5..0aca70f 100644 --- a/audio-device/src/unix/mod.rs +++ b/audio-device/src/unix/mod.rs @@ -1,6 +1,6 @@ //! Unix-specific types and definitions. -use core::error; +use core::error::Error as CoreError; use core::fmt; /// A unix error number. @@ -9,6 +9,7 @@ use core::fmt; pub struct Errno(i32); impl Errno { + #[cfg(feature = "alsa")] pub(crate) const EWOULDBLOCK: Self = Self(libc::EWOULDBLOCK); pub(crate) fn new(value: i32) -> Self { @@ -17,19 +18,51 @@ impl Errno { } impl fmt::Display for Errno { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Self::EWOULDBLOCK => { - write!(f, "EWOULDBLOCK") - } + match self.0 { + libc::EPERM => write!(f, "EPERM"), + libc::ENOENT => write!(f, "ENOENT"), + libc::ESRCH => write!(f, "ESRCH"), + libc::EINTR => write!(f, "EINTR"), + libc::EIO => write!(f, "EIO"), + libc::ENXIO => write!(f, "ENXIO"), + libc::E2BIG => write!(f, "E2BIG"), + libc::ENOEXEC => write!(f, "ENOEXEC"), + libc::EBADF => write!(f, "EBADF"), + libc::ECHILD => write!(f, "ECHILD"), + libc::EAGAIN => write!(f, "EAGAIN"), + libc::ENOMEM => write!(f, "ENOMEM"), + libc::EACCES => write!(f, "EACCES"), + libc::EFAULT => write!(f, "EFAULT"), + libc::ENOTBLK => write!(f, "ENOTBLK"), + libc::EBUSY => write!(f, "EBUSY"), + libc::EEXIST => write!(f, "EEXIST"), + libc::EXDEV => write!(f, "EXDEV"), + libc::ENODEV => write!(f, "ENODEV"), + libc::ENOTDIR => write!(f, "ENOTDIR"), + libc::EISDIR => write!(f, "EISDIR"), + libc::EINVAL => write!(f, "EINVAL"), + libc::ENFILE => write!(f, "ENFILE"), + libc::EMFILE => write!(f, "EMFILE"), + libc::ENOTTY => write!(f, "ENOTTY"), + libc::ETXTBSY => write!(f, "ETXTBSY"), + libc::EFBIG => write!(f, "EFBIG"), + libc::ENOSPC => write!(f, "ENOSPC"), + libc::ESPIPE => write!(f, "ESPIPE"), + libc::EROFS => write!(f, "EROFS"), + libc::EMLINK => write!(f, "EMLINK"), + libc::EPIPE => write!(f, "EPIPE"), + libc::EDOM => write!(f, "EDOM"), + libc::ERANGE => write!(f, "ERANGE"), errno => { - write!(f, "({})", errno) + write!(f, "UNKNOWN({})", errno) } } } } -impl error::Error for Errno {} +impl CoreError for Errno {} cfg_poll_driver! { /// Poll flags. diff --git a/audio-device/src/wasapi/buffer_mut.rs b/audio-device/src/wasapi/buffer_mut.rs index a3d40f7..76787e7 100644 --- a/audio-device/src/wasapi/buffer_mut.rs +++ b/audio-device/src/wasapi/buffer_mut.rs @@ -1,8 +1,10 @@ use core::marker; +use core::mem::take; use core::ops; use core::slice; use crate::wasapi::Error; +use crate::wasapi::error::ErrorKind; use windows::Win32::Media::Audio as audio; @@ -22,9 +24,11 @@ impl<'a, T> BufferMut<'a, T> { pub fn release(mut self) -> Result<(), Error> { self.tag.ensure_on_thread(); - if std::mem::take(&mut self.in_use) { + if take(&mut self.in_use) { unsafe { - self.render_client.ReleaseBuffer(self.frames, 0)?; + self.render_client + .ReleaseBuffer(self.frames, 0) + .map_err(ErrorKind::ReleaseBuffer)?; } } @@ -36,12 +40,9 @@ impl<'a, T> Drop for BufferMut<'a, T> { fn drop(&mut self) { self.tag.ensure_on_thread(); - if std::mem::take(&mut self.in_use) { + if take(&mut self.in_use) { unsafe { - self.render_client - .ReleaseBuffer(self.frames, 0) - .ok() - .unwrap(); + _ = self.render_client.ReleaseBuffer(self.frames, 0); } } } diff --git a/audio-device/src/wasapi/client.rs b/audio-device/src/wasapi/client.rs index 6779fcd..69b4661 100644 --- a/audio-device/src/wasapi/client.rs +++ b/audio-device/src/wasapi/client.rs @@ -6,8 +6,10 @@ use windows::Win32::Media::Audio as audio; use windows::Win32::Media::KernelStreaming as ks; use windows::Win32::Media::Multimedia as mm; use windows::Win32::System::Com as com; +use windows::core::Result as WindowsResult; use crate::loom::sync::Arc; +use crate::wasapi::error::ErrorKind; use crate::wasapi::{ClientConfig, Error, InitializedClient, Sample, SampleFormat}; use crate::windows::{AsyncEvent, Event, RawEvent}; @@ -25,7 +27,10 @@ impl Client { tracing::trace!(?tag, "get default client config"); unsafe { - let mix_format = self.audio_client.GetMixFormat()?; + let mix_format = self + .audio_client + .GetMixFormat() + .map_err(ErrorKind::GetMixFormat)?; let bits_per_sample = (*mix_format).wBitsPerSample; @@ -43,7 +48,7 @@ impl Client { { SampleFormat::F32 } else { - return Err(Error::UnsupportedMixFormat); + return Err(Error::from(ErrorKind::UnsupportedMixFormat)); } } audio::WAVE_FORMAT_PCM => { @@ -52,11 +57,11 @@ impl Client { if bits_per_sample == 16 { SampleFormat::I16 } else { - return Err(Error::UnsupportedMixFormat); + return Err(Error::from(ErrorKind::UnsupportedMixFormat)); } } _ => { - return Err(Error::UnsupportedMixFormat); + return Err(Error::from(ErrorKind::UnsupportedMixFormat)); } }; @@ -110,14 +115,13 @@ impl Client { } /// Try to initialize the client with the given configuration. - fn initialize_inner( + fn initialize_inner( &self, mut config: ClientConfig, - event: F, + make_event: impl FnOnce() -> WindowsResult, ) -> Result, Error> where T: Sample, - F: FnOnce() -> windows::core::Result, E: RawEvent, { unsafe { @@ -136,10 +140,10 @@ impl Client { com::CoTaskMemFree(Some(closest_match.cast())); } - result.ok()?; + result.ok().map_err(ErrorKind::IsFormatSupported)?; } else { if !T::is_compatible_with(&*closest_match) { - return Err(Error::UnsupportedMixFormat); + return Err(Error::from(ErrorKind::UnsupportedMixFormat)); } mix_format = *(closest_match as *mut audio::WAVEFORMATEXTENSIBLE); @@ -150,22 +154,29 @@ impl Client { tracing::trace!("initializing audio client"); - self.audio_client.Initialize( - audio::AUDCLNT_SHAREMODE_SHARED, - audio::AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - 0, - 0, - &mix_format.Format, - None, - )?; + self.audio_client + .Initialize( + audio::AUDCLNT_SHAREMODE_SHARED, + audio::AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + 0, + 0, + &mix_format.Format, + None, + ) + .map_err(ErrorKind::Initialize)?; - let event = Arc::new(event()?); + let event = Arc::new(make_event().map_err(ErrorKind::MakeEvent)?); tracing::trace!("set event handle"); - self.audio_client.SetEventHandle(event.raw_event())?; + self.audio_client + .SetEventHandle(event.raw_event()) + .map_err(ErrorKind::SetEventHandle)?; - let buffer_size = self.audio_client.GetBufferSize()?; + let buffer_size = self + .audio_client + .GetBufferSize() + .map_err(ErrorKind::GetBufferSize)?; tracing::trace!(?buffer_size, "initialized client"); @@ -183,7 +194,7 @@ impl Client { /// Start playback on device. pub fn start(&self) -> Result<(), Error> { unsafe { - self.audio_client.Start()?; + self.audio_client.Start().map_err(ErrorKind::Start)?; } Ok(()) @@ -192,7 +203,7 @@ impl Client { /// Stop playback on device. pub fn stop(&self) -> Result<(), Error> { unsafe { - self.audio_client.Stop()?; + self.audio_client.Stop().map_err(ErrorKind::Stop)?; } Ok(()) diff --git a/audio-device/src/wasapi/error.rs b/audio-device/src/wasapi/error.rs new file mode 100644 index 0000000..6d94936 --- /dev/null +++ b/audio-device/src/wasapi/error.rs @@ -0,0 +1,131 @@ +use core::error::Error as CoreError; +use core::fmt; + +use windows::core::Error as WindowsError; + +//// WASAPI-specific result alias. +pub type Result = ::core::result::Result; + +/// WASAPI-specific errors. +pub struct Error { + kind: ErrorKind, +} + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.fmt(f) + } +} + +impl fmt::Debug for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.fmt(f) + } +} + +impl CoreError for Error { + #[inline] + fn source(&self) -> Option<&(dyn CoreError + 'static)> { + match self.kind { + ErrorKind::CreateInstance(ref error) => Some(error), + ErrorKind::WaitError(ref error) => Some(error), + ErrorKind::IsFormatSupported(ref error) => Some(error), + ErrorKind::GetBufferSize(ref error) => Some(error), + ErrorKind::GetBuffer(ref error) => Some(error), + ErrorKind::ReleaseBuffer(ref error) => Some(error), + ErrorKind::GetCurrentPadding(ref error) => Some(error), + ErrorKind::Start(ref error) => Some(error), + ErrorKind::Stop(ref error) => Some(error), + ErrorKind::Initialize(ref error) => Some(error), + ErrorKind::MakeEvent(ref error) => Some(error), + ErrorKind::GetService(ref error) => Some(error), + ErrorKind::GetMixFormat(ref error) => Some(error), + ErrorKind::SetEventHandle(ref error) => Some(error), + ErrorKind::UnsupportedMixFormat => None, + } + } +} + +impl From for Error { + #[inline] + fn from(kind: ErrorKind) -> Self { + Self { kind } + } +} + +#[derive(Debug)] +pub(crate) enum ErrorKind { + CreateInstance(WindowsError), + WaitError(WindowsError), + IsFormatSupported(WindowsError), + GetBufferSize(WindowsError), + GetBuffer(WindowsError), + ReleaseBuffer(WindowsError), + GetCurrentPadding(WindowsError), + Start(WindowsError), + Stop(WindowsError), + Initialize(WindowsError), + MakeEvent(WindowsError), + GetService(WindowsError), + GetMixFormat(WindowsError), + SetEventHandle(WindowsError), + UnsupportedMixFormat, +} + +impl fmt::Display for ErrorKind { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::CreateInstance(error) => { + write!(f, "Error creating COM instance: {error}") + } + Self::WaitError(error) => { + write!(f, "Error waiting for event: {error}") + } + Self::IsFormatSupported(error) => { + write!(f, "Error checking format support: {error}") + } + Self::GetBufferSize(error) => { + write!(f, "Error getting buffer size: {error}") + } + Self::GetBuffer(error) => { + write!(f, "Error getting buffer from render client: {error}") + } + Self::ReleaseBuffer(error) => { + write!(f, "Error releasing buffer to render client: {error}") + } + Self::GetCurrentPadding(error) => { + write!( + f, + "Error getting current padding from render client: {error}" + ) + } + Self::Start(error) => { + write!(f, "Error starting audio client: {error}") + } + Self::Stop(error) => { + write!(f, "Error stopping audio client: {error}") + } + Self::Initialize(error) => { + write!(f, "Error initializing audio client: {error}") + } + Self::MakeEvent(error) => { + write!(f, "Error creating event: {error}") + } + Self::GetService(error) => { + write!(f, "Error getting audio service: {error}") + } + Self::GetMixFormat(error) => { + write!(f, "Error getting device mix format: {error}") + } + Self::SetEventHandle(error) => { + write!(f, "Error setting event handle: {error}") + } + Self::UnsupportedMixFormat => { + write!(f, "Device doesn't support a compatible mix format") + } + } + } +} diff --git a/audio-device/src/wasapi/initialized_client.rs b/audio-device/src/wasapi/initialized_client.rs index eec0b52..5fea9bf 100644 --- a/audio-device/src/wasapi/initialized_client.rs +++ b/audio-device/src/wasapi/initialized_client.rs @@ -3,6 +3,7 @@ use core::marker; use windows::Win32::Media::Audio as audio; use crate::loom::sync::Arc; +use crate::wasapi::error::ErrorKind; use crate::wasapi::{ClientConfig, Error, RenderClient, Sample}; /// A client that has been initialized with the given type `T`. @@ -34,7 +35,11 @@ where self.tag.ensure_on_thread(); - let render_client: audio::IAudioRenderClient = unsafe { self.audio_client.GetService()? }; + let render_client: audio::IAudioRenderClient = unsafe { + self.audio_client + .GetService() + .map_err(ErrorKind::GetService)? + }; Ok(RenderClient { tag: self.tag, diff --git a/audio-device/src/wasapi/mod.rs b/audio-device/src/wasapi/mod.rs index c86ac39..cbbd06a 100644 --- a/audio-device/src/wasapi/mod.rs +++ b/audio-device/src/wasapi/mod.rs @@ -1,11 +1,12 @@ //! An idiomatic Rust WASAPI interface. -use std::ptr; - -use thiserror::Error; use windows::Win32::Media::Audio as audio; use windows::Win32::System::Com as com; +mod error; +use self::error::ErrorKind; +pub use self::error::{Error, Result}; + mod initialized_client; pub use self::initialized_client::InitializedClient; @@ -21,26 +22,13 @@ pub use self::buffer_mut::BufferMut; mod sample; pub use self::sample::Sample; -/// WASAPI-specific errors. -#[derive(Debug, Error)] -pub enum Error { - /// A system error. - #[error("system error: {0}")] - Sys( - #[from] - #[source] - windows::core::Error, - ), - /// Trying to use a mix format which is not supported by the device. - #[error("Device doesn't support a compatible mix format")] - UnsupportedMixFormat, -} - /// The audio prelude to use for wasapi. pub fn audio_prelude() { unsafe { - if let Err(e) = com::CoInitializeEx(ptr::null_mut(), com::COINIT_MULTITHREADED) { - panic!("failed to initialize multithreaded apartment: {}", e); + let result = com::CoInitializeEx(None, com::COINIT_MULTITHREADED); + + if result.is_err() { + panic!("failed to initialize multithreaded apartment: {result}"); } } } @@ -73,18 +61,15 @@ pub struct ClientConfig { pub fn default_output_client() -> Result, Error> { let tag = ste::Tag::current_thread(); - let enumerator: audio::IMMDeviceEnumerator = - unsafe { com::CoCreateInstance(&audio::MMDeviceEnumerator, None, com::CLSCTX_ALL)? }; - unsafe { - let device = enumerator.GetDefaultAudioEndpoint(audio::eRender, audio::eConsole); + let enumerator: audio::IMMDeviceEnumerator = + com::CoCreateInstance(&audio::MMDeviceEnumerator, None, com::CLSCTX_ALL) + .map_err(ErrorKind::CreateInstance)?; - let device = match device { - Ok(device) => device, - Err(..) => return Ok(None), + let Ok(device) = enumerator.GetDefaultAudioEndpoint(audio::eRender, audio::eConsole) else { + return Ok(None); }; - tracing::trace!("got default audio endpoint"); let audio_client: audio::IAudioClient = device.Activate(com::CLSCTX_ALL, None)?; tracing::trace!("got audio client"); Ok(Some(Client { tag, audio_client })) diff --git a/audio-device/src/wasapi/render_client.rs b/audio-device/src/wasapi/render_client.rs index ecaea11..62e2bd5 100644 --- a/audio-device/src/wasapi/render_client.rs +++ b/audio-device/src/wasapi/render_client.rs @@ -3,9 +3,9 @@ use core::marker; use windows::Win32::Foundation as f; use windows::Win32::Media::Audio as audio; use windows::Win32::System::Threading as th; -use windows::Win32::System::WindowsProgramming as wp; use crate::loom::sync::Arc; +use crate::wasapi::error::ErrorKind; use crate::wasapi::{BufferMut, Error}; use crate::windows::{Event, RawEvent}; @@ -23,7 +23,11 @@ pub struct RenderClient { impl RenderClient { fn get_current_padding(&self) -> Result { unsafe { - let padding = self.audio_client.GetCurrentPadding()?; + let padding = self + .audio_client + .GetCurrentPadding() + .map_err(ErrorKind::GetCurrentPadding)?; + Ok(padding) } } @@ -31,9 +35,12 @@ impl RenderClient { /// Get the buffer associated with the render client. fn get_buffer(&self, frames: u32) -> Result<*mut T, Error> { unsafe { - let data = self.render_client.GetBuffer(frames)?; + let data = self + .render_client + .GetBuffer(frames) + .map_err(ErrorKind::GetBuffer)?; - Ok(data as *mut T) + Ok(data.cast()) } } } @@ -50,7 +57,9 @@ impl RenderClient { match th::WaitForSingleObject(self.event.raw_event(), th::INFINITE) { f::WAIT_OBJECT_0 => (), _ => { - return Err(Error::from(windows::core::Error::from_win32())); + return Err(Error::from(ErrorKind::WaitError( + windows::core::Error::from_thread(), + ))); } } diff --git a/audio-device/src/windows/event.rs b/audio-device/src/windows/event.rs index 65cbd96..d1cd3af 100644 --- a/audio-device/src/windows/event.rs +++ b/audio-device/src/windows/event.rs @@ -1,6 +1,7 @@ use windows::Win32::Foundation as f; use windows::Win32::System::Threading as th; use windows::core::PCSTR; +use windows::core::Result as WindowsResult; use crate::windows::RawEvent; @@ -11,9 +12,8 @@ pub struct Event { } impl Event { - pub(crate) fn new(manual_reset: bool, initial_state: bool) -> windows::core::Result { + pub(crate) fn new(manual_reset: bool, initial_state: bool) -> WindowsResult { let handle = unsafe { th::CreateEventA(None, manual_reset, initial_state, PCSTR::null())? }; - Ok(Self { handle }) } diff --git a/audio/src/buf/dynamic/iter.rs b/audio/src/buf/dynamic/iter.rs index c9aec6e..a69e7be 100644 --- a/audio/src/buf/dynamic/iter.rs +++ b/audio/src/buf/dynamic/iter.rs @@ -1,6 +1,7 @@ +use core::slice; + use crate::buf::dynamic::RawSlice; use crate::channel::{LinearChannel, LinearChannelMut}; -use std::slice; // Helper to forward slice-optimized iterator functions. macro_rules! forward { diff --git a/generate/Cargo.toml b/generate/Cargo.toml index 2f0f5d8..c17f346 100644 --- a/generate/Cargo.toml +++ b/generate/Cargo.toml @@ -6,8 +6,5 @@ publish = false [dependencies] anyhow = "1.0.57" -windows_macros = "0.31.0" -windows_gen = "0.31.0" -windows_reader = "0.31.0" bindgen = "0.72.1" pkg-config = "0.3.25" diff --git a/ste/src/worker.rs b/ste/src/worker.rs index 8674770..d595510 100644 --- a/ste/src/worker.rs +++ b/ste/src/worker.rs @@ -93,9 +93,8 @@ impl Shared { let mut node = Node::new(entry); let first = { - let _guard = match self.lock_queue() { - Some(guard) => guard, - None => panic!("background thread ended"), + let Some(_guard) = self.lock_queue() else { + panic!("background thread ended"); }; self.queue @@ -113,7 +112,7 @@ impl Shared { // thread safely. // // We also know fully that the parker is balanced - i.e. there are - // no sporadic wakes that can happen because we contrl the state of + // no sporadic wakes that can happen because we control the state of // the submitted task exactly above. parker.as_ref().park(); }