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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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

Expand Down
15 changes: 4 additions & 11 deletions src/host/aaudio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AudioDeviceInfo>);

impl Device {
pub fn new() -> Self {
Self::default()
}
}

/// Stream wraps AudioStream in Arc<Mutex<>> to provide Send + Sync semantics.
///
/// While the underlying ndk::audio::AudioStream is neither Send nor Sync in ndk 0.9.0
Expand Down Expand Up @@ -199,16 +192,16 @@ impl HostTrait for Host {
.collect::<Vec<_>>()
.into_iter())
} else {
Ok(vec![Device::new()].into_iter())
Ok(vec![Device(None)].into_iter())
}
}

fn default_input_device(&self) -> Option<Self::Device> {
Some(Device::default())
Some(Device(None))
}

fn default_output_device(&self) -> Option<Self::Device> {
Some(Device::default())
Some(Device(None))
}
}

Expand Down
30 changes: 13 additions & 17 deletions src/host/alsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -138,11 +149,11 @@ impl HostTrait for Host {
}

fn default_input_device(&self) -> Option<Self::Device> {
Some(Self::Device::default())
Some(self.default_device())
}

fn default_output_device(&self) -> Option<Self::Device> {
Some(Self::Device::default())
Some(self.default_device())
}
}

Expand Down Expand Up @@ -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 {
Expand Down
17 changes: 2 additions & 15 deletions src/host/audioworklet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,16 @@ 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)?;
f.write_str(desc.name())
}
}

#[derive(Default)]
pub struct Host;

pub struct Stream {
Expand Down Expand Up @@ -120,7 +113,7 @@ impl HostTrait for Host {

impl Devices {
fn new() -> Result<Self, Error> {
Ok(Self::default())
Ok(Devices(Host::is_available()))
}
}

Expand Down Expand Up @@ -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;

Expand Down
14 changes: 1 addition & 13 deletions src/host/coreaudio/ios/enumerate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Device>);

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<Device>);

Comment thread
roderickvd marked this conversation as resolved.
impl Iterator for Devices {
type Item = Device;
Expand Down
9 changes: 2 additions & 7 deletions src/host/coreaudio/ios/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -46,7 +46,6 @@ impl fmt::Display for Device {
}
}

#[derive(Default)]
pub struct Host;

impl Host {
Expand All @@ -64,7 +63,7 @@ impl HostTrait for Host {
}

fn devices(&self) -> Result<Self::Devices, Error> {
Ok(Devices::new())
Ok(Devices(vec![Device].into_iter()))
}
Comment thread
roderickvd marked this conversation as resolved.

fn default_input_device(&self) -> Option<Self::Device> {
Expand All @@ -77,10 +76,6 @@ impl HostTrait for Host {
}

impl Device {
pub fn new() -> Self {
Self
}

fn description(&self) -> Result<DeviceDescription, Error> {
// Query AVAudioSession to determine actual input/output availability
// SAFETY: AVAudioSession::sharedInstance() returns the global audio session singleton
Expand Down
2 changes: 1 addition & 1 deletion src/host/coreaudio/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
42 changes: 5 additions & 37 deletions src/host/null/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -25,58 +24,27 @@ 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<Self, Error> {
Ok(Self)
}
}

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<Self, Error> {
Ok(Self)
}
}

impl DeviceTrait for Device {
type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputConfigs = SupportedOutputConfigs;
Expand Down Expand Up @@ -147,7 +115,7 @@ impl HostTrait for Host {
}

fn devices(&self) -> Result<Self::Devices, Error> {
Devices::new()
Ok(Devices)
}

fn default_input_device(&self) -> Option<Self::Device> {
Expand Down
2 changes: 1 addition & 1 deletion src/host/wasapi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
16 changes: 2 additions & 14 deletions src/host/webaudio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type ClosureHandle = Arc<RwLock<Option<Closure<dyn FnMut()>>>>;
/// 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 {
Expand All @@ -47,7 +47,6 @@ impl fmt::Display for Device {
}
}

#[derive(Default)]
pub struct Host;

pub struct Stream {
Expand Down Expand Up @@ -108,15 +107,11 @@ impl HostTrait for Host {

impl Devices {
fn new() -> Result<Self, Error> {
Ok(Self::default())
Ok(Devices(is_webaudio_available()))
}
}

impl Device {
pub fn new() -> Self {
Self
}

fn description(&self) -> Result<DeviceDescription, Error> {
Ok(DeviceDescriptionBuilder::new("Default Device")
.direction(DeviceDirection::Output)
Expand Down Expand Up @@ -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;

Expand Down
Loading