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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `StreamTrait::play` is renamed to `start`.
- `InputCallbackInfo`/`OutputCallbackInfo` merged into `CallbackInfo`.
- `InputStreamTimestamp`/`OutputStreamTimestamp` merged into `StreamTimestamp`; `capture`/`playback` renamed `device`.
- `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`.
- **ALSA**: Update `alsa` dependency to 0.12.
- **CoreAudio**: `DeviceDescription::interface_type()` now reports the device transport instead of only marking aggregate devices.
Expand Down
34 changes: 29 additions & 5 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This guide covers breaking changes requiring code updates. See [CHANGELOG.md](CH
- [ ] Replace `InputCallbackInfo`/`OutputCallbackInfo` with `CallbackInfo`.
- [ ] 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`.

## 1. `DeviceTrait` and `StreamTrait` require `Send + Sync`

Expand Down Expand Up @@ -92,6 +93,29 @@ Ordering of `xrun()` relative to the glitch it reports varies by host; see [`Cal

[`CallbackInfo::xrun()`]: https://docs.rs/cpal/latest/cpal/struct.CallbackInfo.html#method.xrun

## 5. `SampleFormat` methods made `const`, and take `self`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, this should also have an entry in the numbered list at the top.


**What changed:** `SampleFormat` methods are now constant, and don't take a reference anymore.

```rust
// Before (v0.18):
let i16_is_int: bool = SampleFormat::I16.is_int();

let mut formats = vec![SampleFormat::I16, SampleFormat::F32];
formats.retain(SampleFormat::is_int);

// After (v0.19): constant, and not referenced
const I16_IS_INT: bool = SampleFormat::I16.is_int();

let mut formats = vec![SampleFormat::I16, SampleFormat::F32];
formats.retain(|f| f.is_int());
```

**Impact:** `SampleFormat` can now be used in a `const` environment.

**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
---

# Upgrading from v0.17 to v0.18
Expand Down Expand Up @@ -406,13 +430,13 @@ let device = host.device_by_id(&id);

```rust
// Before (v0.17)
for line in desc.extended() { // &[String]
println!("{}", line); // line: &String
for line in desc.extended() { // &[String]
println!("{line}"); // line: &String

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spacing of the comment seems unaligned with the one above it.

}

// After (v0.18)
for line in desc.extended() { // impl Iterator<Item = &str>
println!("{}", line); // line: &str — Display, write!, format! all unchanged
for line in desc.extended() { // impl Iterator<Item = &str>
println!("{line}"); // line: &str — Display, write!, format! all unchanged
}
```

Expand Down Expand Up @@ -540,7 +564,7 @@ let name = device.name()?;

// New: For user-facing display
let desc = device.description()?;
println!("Device: {}", desc); // or desc.name() for just the name
println!("Device: {desc}"); // or desc.name() for just the name

// New: For stable identification and persistence
let id = device.id()?;
Expand Down
2 changes: 1 addition & 1 deletion src/device_description.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ impl fmt::Display for DeviceDescription {
write!(f, "{}", self.name)?;

if let Some(mfr) = &self.manufacturer {
write!(f, " ({})", mfr)?;
write!(f, " ({mfr})")?;
}

if self.device_type != DeviceType::Unknown {
Expand Down
4 changes: 2 additions & 2 deletions src/host/alsa/enumerate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ fn format_device_description(phys_dev: &PhysicalDevice, prefix: &str) -> String
_ => "",
};

format!("{}\n{}", first_line, second_line)
format!("{first_line}\n{second_line}")
}

fn physical_devices() -> Vec<PhysicalDevice> {
Expand Down Expand Up @@ -135,7 +135,7 @@ fn physical_devices() -> Vec<PhysicalDevice> {
}
};

let device_name = device_name.unwrap_or_else(|| format!("Device {}", device_index));
let device_name = device_name.unwrap_or_else(|| format!("Device {device_index}"));
devices.push(PhysicalDevice {
card_index,
card_name: card_name.clone(),
Expand Down
4 changes: 2 additions & 2 deletions src/host/jack/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl Device {
connect_ports_automatically: bool,
start_server_automatically: bool,
) -> Result<Self, Error> {
let output_client_name = format!("{}_out", name);
let output_client_name = format!("{name}_out");
Device::new_device(
output_client_name,
connect_ports_automatically,
Expand All @@ -116,7 +116,7 @@ impl Device {
connect_ports_automatically: bool,
start_server_automatically: bool,
) -> Result<Self, Error> {
let input_client_name = format!("{}_in", name);
let input_client_name = format!("{name}_in");
Device::new_device(
input_client_name,
connect_ports_automatically,
Expand Down
10 changes: 7 additions & 3 deletions src/host/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,19 +254,23 @@ pub(crate) use error_emit::try_emit_error;
),
))]
#[inline]
pub(crate) fn frames_to_duration(
pub(crate) const fn frames_to_duration(
frames: crate::FrameCount,
rate: crate::SampleRate,
) -> std::time::Duration {
if rate == 0 {
return std::time::Duration::ZERO;
}

let frames = frames as u64;
let rate = rate as u64;
let secs = frames as u64 / rate;

let secs = frames / rate;
// rem_frames < rate <= u32::MAX, so rem_frames * 1_000_000_000 < u64::MAX
let rem_frames = frames as u64 % rate;
let rem_frames = frames % rate;
// Round to nearest so the duration isn't biased.
let nanos = (rem_frames * 1_000_000_000 + rate / 2) / rate;

std::time::Duration::new(secs, nanos as u32)
}

Expand Down
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
//! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap();
//! # let supported_config = device.default_output_config().unwrap();
//! let err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err);
//! let err_fn = |err| eprintln!("an error occurred on the output audio stream: {err}");
//! let sample_format = supported_config.sample_format();
//! let config = supported_config.into();
//! let stream = match sample_format {
Expand Down Expand Up @@ -284,7 +284,7 @@ pub type FrameCount = u32;
///
/// // Serialize to string (e.g., for storage in config file)
/// let id_string = device_id.to_string();
/// println!("Device ID: {}", id_string); // e.g., "wasapi:device_identifier"
/// println!("Device ID: {id_string}"); // e.g., "wasapi:device_identifier"
///
/// // Deserialize from string
/// match DeviceId::from_str(&id_string) {
Expand All @@ -294,7 +294,7 @@ pub type FrameCount = u32;
/// println!("Found device: {:?}", device.id());
/// }
/// }
/// Err(e) => eprintln!("Failed to parse device ID: {}", e),
/// Err(e) => eprintln!("Failed to parse device ID: {e}"),
/// }
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -400,7 +400,7 @@ impl std::str::FromStr for DeviceId {
/// // Check supported buffer size range
/// match config.buffer_size() {
/// SupportedBufferSize::Range { min, max } => {
/// println!("Buffer size range: {} - {}", min, max);
/// println!("Buffer size range: {min} - {max}");
/// // Request a small buffer for low latency
/// let mut stream_config = config.config();
/// stream_config.buffer_size = BufferSize::Fixed(256);
Expand Down
7 changes: 2 additions & 5 deletions src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ pub use crate::host::custom::{Device as CustomDevice, Host as CustomHost, Stream
/// For example the invocation `impl_platform_host(Wasapi wasapi "WASAPI", Asio asio "ASIO")`,
/// this macro should expand to:
///
// This sample code block is marked as text because it's not a valid test,
// it's just illustrative. (see rust issue #96573)
/// ```text
/// ```rust,ignore
/// pub enum HostId {
/// Wasapi,
/// Asio,
Expand All @@ -59,7 +57,6 @@ pub use crate::host::custom::{Device as CustomDevice, Host as CustomHost, Stream
///
/// And so on for Device, Devices, Host, Stream, SupportedInputConfigs,
/// SupportedOutputConfigs and all their necessary trait implementations.
///
macro_rules! impl_platform_host {
($($(#[cfg($feat: meta)])? $HostVariant:ident $($HostName:literal)? => $Host:ty),* $(,)?) => {
/// All hosts supported by CPAL on this platform.
Expand Down Expand Up @@ -146,7 +143,7 @@ macro_rules! impl_platform_host {
///
/// // Parse host string (may fail if host is not available on this platform)
/// if let Ok(host_id) = HostId::from_str(host_string) {
/// println!("Successfully parsed: {}", host_id);
/// println!("Successfully parsed: {host_id}");
/// }
/// }
/// ```
Expand Down
Loading
Loading