Skip to content
Open
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,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`

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.

Nitpick: trailing period.

- `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
30 changes: 27 additions & 3 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,30 @@ 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 derefrencing.

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.

Typo: "dereferencing".

In the rest of the file we don't use newlines.


[`SampleFormat`]: https://docs.rs/cpal/latest/cpal/enum.SampleFormat.html
---

# Upgrading from v0.17 to v0.18
Expand Down Expand Up @@ -407,12 +431,12 @@ let device = host.device_by_id(&id);
```rust
// Before (v0.17)
for line in desc.extended() { // &[String]
println!("{}", line); // line: &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
println!("{line}"); // line: &str — Display, write!, format! all unchanged

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.

}
```

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 @@ -87,7 +87,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 @@ -101,7 +101,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
4 changes: 2 additions & 2 deletions src/host/jack/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl Stream {
let mut port_names: Vec<String> = vec![];
for i in 0..channels {
let port = client
.register_port(&format!("in_{}", i), jack::AudioIn::default())
.register_port(&format!("in_{i}"), jack::AudioIn::default())
.context(format!("Failed to register input port {i}"))?;
if let Ok(port_name) = port.name() {
port_names.push(port_name);
Expand Down Expand Up @@ -115,7 +115,7 @@ impl Stream {
let mut port_names: Vec<String> = vec![];
for i in 0..channels {
let port = client
.register_port(&format!("out_{}", i), jack::AudioOut::default())
.register_port(&format!("out_{i}"), jack::AudioOut::default())
.context(format!("Failed to register output port {i}"))?;
if let Ok(port_name) = port.name() {
port_names.push(port_name);
Expand Down
10 changes: 7 additions & 3 deletions src/host/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,19 +248,23 @@ pub(crate) use error_emit::try_emit_error;
feature = "audioworklet",
))]
#[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
2 changes: 1 addition & 1 deletion src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,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