From d92785c1cb8d505ca33aa83ed15d407d3d717996 Mon Sep 17 00:00:00 2001 From: Marco Farruggio Date: Mon, 21 Sep 2026 16:41:02 +0100 Subject: [PATCH 1/2] removed the null host --- src/host/mod.rs | 16 ---- src/host/null/mod.rs | 169 ------------------------------------------- src/platform/mod.rs | 12 +-- 3 files changed, 7 insertions(+), 190 deletions(-) delete mode 100644 src/host/null/mod.rs diff --git a/src/host/mod.rs b/src/host/mod.rs index 801d94cff..986cf1637 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -86,22 +86,6 @@ pub(crate) mod webaudio; #[cfg(feature = "custom")] pub(crate) mod custom; -#[cfg(not(any( - windows, - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_vendor = "apple", - target_os = "android", - all( - target_arch = "wasm32", - any(target_os = "emscripten", target_os = "unknown"), - feature = "wasm-bindgen" - ), -)))] -pub(crate) mod null; - #[cfg(any( target_vendor = "apple", target_os = "windows", diff --git a/src/host/null/mod.rs b/src/host/null/mod.rs deleted file mode 100644 index 2cce813f3..000000000 --- a/src/host/null/mod.rs +++ /dev/null @@ -1,169 +0,0 @@ -//! Null backend implementation. -//! -//! Fallback no-op backend for unsupported platforms. - -use std::fmt; -use std::time::Duration; - -use crate::{ - CallbackInfo, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, FrameCount, - SampleFormat, StreamConfig, StreamInstant, SupportedStreamConfig, SupportedStreamConfigRange, - traits::{DeviceTrait, HostTrait, StreamTrait}, -}; - -pub struct Devices; - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct Device; - -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()) - } -} - -pub struct Host; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Stream; - -#[derive(Clone)] -pub struct SupportedInputConfigs; -#[derive(Clone)] -pub struct SupportedOutputConfigs; - -impl Host { - #[allow(dead_code)] - pub fn new() -> Result { - Ok(Self) - } -} - -impl DeviceTrait for Device { - type SupportedInputConfigs = SupportedInputConfigs; - type SupportedOutputConfigs = SupportedOutputConfigs; - type Stream = Stream; - - fn description(&self) -> Result { - Ok(DeviceDescriptionBuilder::new("Null Device").build()) - } - - fn id(&self) -> Result { - Ok(DeviceId::new(crate::platform::HostId::Null, "")) - } - - fn supported_input_configs(&self) -> Result { - unimplemented!() - } - - fn supported_output_configs(&self) -> Result { - unimplemented!() - } - - fn default_input_config(&self) -> Result { - unimplemented!() - } - - fn default_output_config(&self) -> Result { - unimplemented!() - } - - fn build_input_stream_raw( - &self, - _config: StreamConfig, - _sample_format: SampleFormat, - _data_callback: D, - _error_callback: E, - _timeout: Option, - ) -> Result - where - D: FnMut(&Data, &CallbackInfo) + Send + 'static, - E: FnMut(Error) + Send + 'static, - { - unimplemented!() - } - - /// Create an output stream. - fn build_output_stream_raw( - &self, - _config: StreamConfig, - _sample_format: SampleFormat, - _data_callback: D, - _error_callback: E, - _timeout: Option, - ) -> Result - where - D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, - E: FnMut(Error) + Send + 'static, - { - unimplemented!() - } -} - -impl HostTrait for Host { - type Devices = Devices; - type Device = Device; - - fn is_available() -> bool { - false - } - - fn devices(&self) -> Result { - Ok(Devices) - } - - fn default_input_device(&self) -> Option { - None - } - - fn default_output_device(&self) -> Option { - None - } -} - -impl StreamTrait for Stream { - fn start(&self) -> Result<(), Error> { - unimplemented!() - } - - fn pause(&self) -> Result<(), Error> { - unimplemented!() - } - - fn stop(&self, _timeout: Option) -> Result<(), Error> { - unimplemented!() - } - - fn now(&self) -> StreamInstant { - unimplemented!() - } - - fn buffer_size(&self) -> Result { - unimplemented!() - } -} - -impl Iterator for Devices { - type Item = Device; - - fn next(&mut self) -> Option { - None - } -} - -impl Iterator for SupportedInputConfigs { - type Item = SupportedStreamConfigRange; - - fn next(&mut self) -> Option { - None - } -} - -impl Iterator for SupportedOutputConfigs { - type Item = SupportedStreamConfigRange; - - fn next(&mut self) -> Option { - None - } -} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index a9083d695..02e64db71 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -119,7 +119,6 @@ macro_rules! impl_platform_host { /// - `"coreaudio"` - CoreAudio /// - `"custom"` - Custom host (requires `custom` feature) /// - `"jack"` - JACK Audio Connection Kit - /// - `"null"` - Null host /// - `"wasapi"` - Windows Audio Session API /// - `"webaudio"` - Web Audio API /// @@ -1010,17 +1009,20 @@ mod platform_impl { ), )))] mod platform_impl { - use crate::host::null::Host as NullHost; - impl_platform_host!( - Null => NullHost, #[cfg(feature = "custom")] Custom => super::CustomHost, ); /// The default host for the current compilation target platform. + #[cfg(feature = "custom")] pub fn default_host() -> Host { - NullHost::new() + super::CustomHost::new() .expect("the default host should always be available") .into() } + + #[cfg(not(feature = "custom"))] + pub fn default_host() -> Host { + compile_error!("The current compilation target has no audio backends, or a custom host") + } } From f8ab847b080add0b04aa0cab055443c5d2578ae8 Mon Sep 17 00:00:00 2001 From: Marco Farruggio Date: Mon, 21 Sep 2026 16:41:17 +0100 Subject: [PATCH 2/2] update docs to show null host removal --- CHANGELOG.md | 1 + UPGRADING.md | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb428dd03..82da6a414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `StreamInstant` creation is now `const`. - `SampleFormat` methods are now `const`, and take `self`. - Renamed the `wasm-beep` and `audioworklet-beep` examples to `webaudio` and `audioworklet`. +- **Null**: Removed the null host. - **ALSA**: Update `alsa` dependency to 0.12. - **CoreAudio**: `DeviceDescription::interface_type()` now reports the device transport instead of only marking aggregate devices. - **CoreAudio**: A `None` timeout now waits indefinitely for sample rate and format changes, instead of giving up after 1 or 2 seconds. diff --git a/UPGRADING.md b/UPGRADING.md index 4834150f0..1d0a37c89 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -12,6 +12,7 @@ This guide covers breaking changes requiring code updates. See [CHANGELOG.md](CH - [ ] Replace `InputStreamTimestamp`/`OutputStreamTimestamp` with `StreamTimestamp`; `capture`/`playback` is now `device`. - [ ] Remove `ErrorKind::Xrun` match arms; read `CallbackInfo::xrun()` instead. - [ ] Update `SampleFormat` method calls to use `self` instead of `&self`; methods are now `const`. +- [ ] If you relied on the `Null` host exisiting on exotic targets, `cfg()` gate `default_host()`, or impl a `Custom` host. ## 1. `DeviceTrait` and `StreamTrait` require `Send + Sync` @@ -116,6 +117,28 @@ formats.retain(|f| f.is_int()); **Why:** `SampleFormat` is a simple enum, there was no reason why it shouldn't be const-friendly, and since every method was both `inline` and it implements `Copy`, there is no performance downside to it taking `self`, but simply more legible than dereferencing. [`SampleFormat`]: https://docs.rs/cpal/latest/cpal/enum.SampleFormat.html + +## 6. `Null` host removed. + +**What changed:** The `Null` host was removed. + +```rust +// Target: x86_64-unknown-illumos + +// Before (v0.18): +let host = cpal::default_host(); + +let device = host.default_output_device().unwrap(); +// Panic: No output devices + +// After (v0.19): no longer compiles +let host = cpal::default_host(); +// Compilation error: No audio hosts +``` + +**Impact:** Compilations on targets without cpal audio backends must either gate it, or implement a `Custom` host. + +**Why:** `Null` served no purpose except to allow compilations to succeed, when in reality, cpal already had specific platform restrictions, and as such it is no longer required. --- # Upgrading from v0.17 to v0.18