diff --git a/CHANGELOG.md b/CHANGELOG.md index e0f149de..00c60cbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed sources to detect parameter updates after mid-span seeks. - Fixed `Stoppable` and `Skippable` not signaling exhaustion. - Fixed `SpatialAudio` left and write channel swapping +- Fixed playback on devices that cap the buffer size below the default we aim for, such as bluetooth outputs. ## Version [0.22.2] (2026-03-05) diff --git a/src/stream.rs b/src/stream.rs index fc0a4531..9887167e 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -220,7 +220,12 @@ impl DeviceSinkBuilder { // aim for 50ms of audio let sample_rate = device.config.sample_rate().get(); - let safe_buffer_size = nearest_multiple_of_two(sample_rate / (1000 / 50)); + let mut safe_buffer_size = nearest_multiple_of_two(sample_rate / (1000 / 50)); + // Opening fails on a fixed size outside the device's range, and + // bluetooth outputs cap it well below 50ms. + if let cpal::SupportedBufferSize::Range { min, max } = default_config.buffer_size() { + safe_buffer_size = safe_buffer_size.clamp(*min, *max); + } // This is suboptimal, the builder might still change the sample rate or // channel count which would throw the buffer size off. We have fixed