diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cd617ab1..b7ed6a779 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,22 +36,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **AAudio**: Streams now request `PERFORMANCE_MODE_LOW_LATENCY` when the `realtime` feature is enabled; stream error callback receives `ErrorKind::RealtimeDenied` if not granted. - **AAudio**: `Device` now implements `PartialEq`, `Eq`, `Hash`, and `Debug`. -- **AAudio**: `Host` and `Device` now implement `Default`; added `Device::new()`. - **ALSA**: `device_by_id()` now accepts PCM shorthand names such as `hw:0,0` and `plughw:foo`. - **ASIO**: `Device` now implements `Debug`. -- **AudioWorklet**: `Device` and `Host` now implement `Default`; added `Device::new()`. -- **CoreAudio**: `Host` now implements `Default`. - **CoreAudio**: tvOS target support (Tier 3, requires nightly). - **CoreAudio**: `Device` now implements `PartialEq`, `Eq`, and `Hash`. -- **CoreAudio (iOS)**: `Device` now implements `Default`; added `Device::new()`. - **JACK**: `Device` now implements `PartialEq`, `Eq`, and `Hash`. -- **Null**: `Device`, `Host`, `Stream`, `SupportedInputConfigs`, and `SupportedOutputConfigs` now - implement `Default`, added `new()` on each. - **PipeWire**: New host for Linux and some BSDs using the PipeWire API. - **PulseAudio**: New host for Linux and some BSDs using the PulseAudio API. - **WASAPI**: `Device` now implements `PartialEq`, `Eq`, `Hash`, and `Debug`. -- **WASAPI**: `Host` now implements `Default`. -- **WebAudio**: `Device` and `Host` now implement `Default`; added `Device::new()`. ### Changed @@ -152,6 +144,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Replaced `StreamInstant::add()` and `sub()` by `checked_add()`/`+` and `checked_sub()`/`-`. - Removed deprecated `DeviceTrait::name()`. +- **ALSA**: `Default` implementation for `Device`. - **ALSA**: `AlsaHost` is no longer re-exported from `cpal::platform`. - **Emscripten**: Removed broken host; use the WebAudio host instead. - **WASAPI**: Removed `U24` incorrectly listed as a supported sample format. @@ -189,7 +182,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **ASIO**: Fix `driver.sample_rate()` failures at stream creation being silently ignored. - **ASIO**: Fix callbacks firing before `build_*_stream` returns the `Stream` handle. - **ASIO**: Fix overrun not being reported when the driver reports `kAsioOverload`. -- **AudioWorklet**: Fix `Devices::default()` to enumerate only if `Host::is_available()`. +- **AudioWorklet**: Fix `default_output_device()` to return `None` when AudioWorklet is unavailable. - **CoreAudio**: Fix default output streams silently stopping when the system default output device is unplugged; they now reroute automatically or report `ErrorKind::DeviceNotAvailable`. - **CoreAudio**: Fix undefined behaviour and silent failure in loopback device creation. @@ -216,6 +209,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 devices. - **WebAudio**: Fix duplicated callbacks on repeated `play()` calls. - **WebAudio**: Report errors through the callback instead of panicking. +- **WebAudio**: Fix `default_output_device()` to return `None` when WebAudio is unavailable. + ## [0.17.3] - 2026-02-18 diff --git a/src/host/aaudio/mod.rs b/src/host/aaudio/mod.rs index 39a402b66..7bae66576 100644 --- a/src/host/aaudio/mod.rs +++ b/src/host/aaudio/mod.rs @@ -121,18 +121,11 @@ const SAMPLE_RATES: [i32; 15] = [ /// The same default for blocking operations as Oboe uses const DEFAULT_TIMEOUT_NANOS: i64 = 2_000_000_000; -#[derive(Default)] pub struct Host; -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct Device(Option); -impl Device { - pub fn new() -> Self { - Self::default() - } -} - /// Stream wraps AudioStream in Arc> to provide Send + Sync semantics. /// /// While the underlying ndk::audio::AudioStream is neither Send nor Sync in ndk 0.9.0 @@ -199,16 +192,16 @@ impl HostTrait for Host { .collect::>() .into_iter()) } else { - Ok(vec![Device::new()].into_iter()) + Ok(vec![Device(None)].into_iter()) } } fn default_input_device(&self) -> Option { - Some(Device::default()) + Some(Device(None)) } fn default_output_device(&self) -> Option { - Some(Device::default()) + Some(Device(None)) } } diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index 6e2cbca12..a05eb2d8d 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -115,6 +115,17 @@ impl Host { inner: Arc::new(inner), }) } + + // "default" is a virtual ALSA device that redirects to the configured default. We cannot + // determine its actual capabilities without opening it, so we return Unknown direction. + fn default_device(&self) -> Device { + Device { + pcm_id: DEFAULT_DEVICE.to_owned(), + desc: Some("Default Audio Device".to_owned()), + direction: DeviceDirection::Unknown, + _context: self.inner.clone(), + } + } } impl HostTrait for Host { @@ -138,11 +149,11 @@ impl HostTrait for Host { } fn default_input_device(&self) -> Option { - Some(Self::Device::default()) + Some(self.default_device()) } fn default_output_device(&self) -> Option { - Some(Self::Device::default()) + Some(self.default_device()) } } @@ -651,21 +662,6 @@ impl std::hash::Hash for Device { } } -impl Default for Device { - fn default() -> Self { - // "default" is a virtual ALSA device that redirects to the configured default. We cannot - // determine its actual capabilities without opening it, so we return Unknown direction. - Self { - pcm_id: DEFAULT_DEVICE.to_owned(), - desc: Some("Default Audio Device".to_owned()), - direction: DeviceDirection::Unknown, - _context: Arc::new( - AlsaContext::new().expect("Failed to initialize ALSA configuration"), - ), - } - } -} - /// Strategy for pre-filling an output buffer with the equilibrium value. #[derive(Debug)] enum EquilibriumFill { diff --git a/src/host/audioworklet/mod.rs b/src/host/audioworklet/mod.rs index d578cd9ca..ee8637a59 100644 --- a/src/host/audioworklet/mod.rs +++ b/src/host/audioworklet/mod.rs @@ -30,15 +30,9 @@ use crate::dependent_module; /// Content is false if the iterator is empty. pub struct Devices(bool); -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Device; -impl Device { - pub fn new() -> Self { - Self - } -} - impl fmt::Display for Device { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let desc = self.description().map_err(|_| fmt::Error)?; @@ -46,7 +40,6 @@ impl fmt::Display for Device { } } -#[derive(Default)] pub struct Host; pub struct Stream { @@ -120,7 +113,7 @@ impl HostTrait for Host { impl Devices { fn new() -> Result { - Ok(Self::default()) + Ok(Devices(Host::is_available())) } } @@ -417,12 +410,6 @@ impl Drop for Stream { } } -impl Default for Devices { - fn default() -> Self { - Self(Host::is_available()) - } -} - impl Iterator for Devices { type Item = Device; diff --git a/src/host/coreaudio/ios/enumerate.rs b/src/host/coreaudio/ios/enumerate.rs index f5a74935c..32afcaa5a 100644 --- a/src/host/coreaudio/ios/enumerate.rs +++ b/src/host/coreaudio/ios/enumerate.rs @@ -4,19 +4,7 @@ use super::Device; pub use crate::iter::{SupportedInputConfigs, SupportedOutputConfigs}; // TODO: Support enumerating earpiece vs headset vs speaker etc? -pub struct Devices(VecIntoIter); - -impl Devices { - pub fn new() -> Self { - Self::default() - } -} - -impl Default for Devices { - fn default() -> Self { - Self(vec![Device].into_iter()) - } -} +pub struct Devices(pub(super) VecIntoIter); impl Iterator for Devices { type Item = Device; diff --git a/src/host/coreaudio/ios/mod.rs b/src/host/coreaudio/ios/mod.rs index a4283baaa..a08ed5fe0 100644 --- a/src/host/coreaudio/ios/mod.rs +++ b/src/host/coreaudio/ios/mod.rs @@ -36,7 +36,7 @@ use session_event_manager::SessionEventManager; // These days the default of iOS is now F32 and no longer I16 const SUPPORTED_SAMPLE_FORMAT: SampleFormat = SampleFormat::F32; -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Device; impl fmt::Display for Device { @@ -46,7 +46,6 @@ impl fmt::Display for Device { } } -#[derive(Default)] pub struct Host; impl Host { @@ -64,7 +63,7 @@ impl HostTrait for Host { } fn devices(&self) -> Result { - Ok(Devices::new()) + Ok(Devices(vec![Device].into_iter())) } fn default_input_device(&self) -> Option { @@ -77,10 +76,6 @@ impl HostTrait for Host { } impl Device { - pub fn new() -> Self { - Self - } - fn description(&self) -> Result { // Query AVAudioSession to determine actual input/output availability // SAFETY: AVAudioSession::sharedInstance() returns the global audio session singleton diff --git a/src/host/coreaudio/macos/mod.rs b/src/host/coreaudio/macos/mod.rs index d740bb4d8..82b0b0ee1 100644 --- a/src/host/coreaudio/macos/mod.rs +++ b/src/host/coreaudio/macos/mod.rs @@ -25,7 +25,7 @@ mod property_listener; pub use device::Device; /// Coreaudio host, the default host on macOS. -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Host; impl Host { diff --git a/src/host/null/mod.rs b/src/host/null/mod.rs index 463c68558..e675339ec 100644 --- a/src/host/null/mod.rs +++ b/src/host/null/mod.rs @@ -12,10 +12,9 @@ use crate::{ SupportedStreamConfig, SupportedStreamConfigRange, }; -#[derive(Default)] pub struct Devices; -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Device; impl fmt::Display for Device { @@ -25,27 +24,20 @@ impl fmt::Display for Device { } } -#[derive(Default)] pub struct Host; -#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Stream; // Compile-time assertion that Stream is Send and Sync crate::assert_stream_send!(Stream); crate::assert_stream_sync!(Stream); -#[derive(Clone, Default)] +#[derive(Clone)] pub struct SupportedInputConfigs; -#[derive(Clone, Default)] +#[derive(Clone)] pub struct SupportedOutputConfigs; -impl Device { - pub fn new() -> Self { - Self - } -} - impl Host { #[allow(dead_code)] pub fn new() -> Result { @@ -53,30 +45,6 @@ impl Host { } } -impl Stream { - pub fn new() -> Self { - Self - } -} - -impl SupportedInputConfigs { - pub fn new() -> Self { - Self - } -} - -impl SupportedOutputConfigs { - pub fn new() -> Self { - Self - } -} - -impl Devices { - pub fn new() -> Result { - Ok(Self) - } -} - impl DeviceTrait for Device { type SupportedInputConfigs = SupportedInputConfigs; type SupportedOutputConfigs = SupportedOutputConfigs; @@ -147,7 +115,7 @@ impl HostTrait for Host { } fn devices(&self) -> Result { - Devices::new() + Ok(Devices) } fn default_input_device(&self) -> Option { diff --git a/src/host/wasapi/mod.rs b/src/host/wasapi/mod.rs index 3562d4014..18e5e4f86 100644 --- a/src/host/wasapi/mod.rs +++ b/src/host/wasapi/mod.rs @@ -23,7 +23,7 @@ mod stream; /// Note: If you use a WASAPI output device as an input device it will /// transparently enable loopback mode (see /// https://docs.microsoft.com/en-us/windows/win32/coreaudio/loopback-recording). -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Host; impl Host { diff --git a/src/host/webaudio/mod.rs b/src/host/webaudio/mod.rs index d4866a5ac..3c4ef9723 100644 --- a/src/host/webaudio/mod.rs +++ b/src/host/webaudio/mod.rs @@ -37,7 +37,7 @@ type ClosureHandle = Arc>>>; /// Content is false if the iterator is empty. pub struct Devices(bool); -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Device; impl fmt::Display for Device { @@ -47,7 +47,6 @@ impl fmt::Display for Device { } } -#[derive(Default)] pub struct Host; pub struct Stream { @@ -108,15 +107,11 @@ impl HostTrait for Host { impl Devices { fn new() -> Result { - Ok(Self::default()) + Ok(Devices(is_webaudio_available())) } } impl Device { - pub fn new() -> Self { - Self - } - fn description(&self) -> Result { Ok(DeviceDescriptionBuilder::new("Default Device") .direction(DeviceDirection::Output) @@ -626,13 +621,6 @@ impl Drop for Stream { } } -impl Default for Devices { - fn default() -> Devices { - // We produce an empty iterator if the WebAudio API isn't available. - Devices(is_webaudio_available()) - } -} - impl Iterator for Devices { type Item = Device;