fix: use with_buffer_size() after with_supported_config() to prevent underruns
rodio 0.22's with_supported_config() resets buffer_size to Default via ..Default::default(), so our BufferSize settings in StreamConfig were never reaching CPAL. MixerDeviceSink has zero internal buffering, making CPAL's buffer the ONLY buffer between the mixer and audio hardware. Chain .with_buffer_size(Fixed(sample_rate/10)) AFTER with_supported_config() to actually pass ~100ms buffer to CPAL, matching old vendored cpal behavior.
This commit is contained in:
3 files changed
+55
-22
No files matched your search
@@ -246,17 +246,12 @@ impl AudioBackend for PipeWireBackend {
|
||||
);
|
||||
|
||||
// Create StreamConfig with desired sample rate
|
||||
// Note: buffer_size here is unused — with_supported_config() resets it.
|
||||
// The actual buffer size is set via with_buffer_size() below.
|
||||
let stream_config = StreamConfig {
|
||||
channels: config.channels,
|
||||
sample_rate: config.sample_rate,
|
||||
buffer_size: if config.exclusive_mode {
|
||||
BufferSize::Fixed(512) // Lower latency for exclusive mode
|
||||
} else {
|
||||
// PipeWire manages its own buffer scheduling via quantum;
|
||||
// forcing a fixed buffer size here conflicts and causes underruns
|
||||
// at high sample rates (192kHz)
|
||||
BufferSize::Default
|
||||
},
|
||||
buffer_size: BufferSize::Default,
|
||||
};
|
||||
|
||||
// Check if device supports this configuration
|
||||
@@ -296,10 +291,24 @@ impl AudioBackend for PipeWireBackend {
|
||||
SampleFormat::F32,
|
||||
);
|
||||
|
||||
// Compute buffer size — must be applied AFTER with_supported_config()
|
||||
// because that method resets buffer_size to Default via ..Default::default().
|
||||
// MixerDeviceSink has zero internal buffering, so CPAL's buffer is the
|
||||
// ONLY buffer between the mixer and audio hardware.
|
||||
let cpal_buffer_size = if config.exclusive_mode {
|
||||
BufferSize::Fixed(512) // Low latency for exclusive mode
|
||||
} else {
|
||||
// ~100ms buffer, matching old vendored cpal period size.
|
||||
// Prevents underruns at high sample rates (192kHz = 19200 frames).
|
||||
BufferSize::Fixed(config.sample_rate / 10)
|
||||
};
|
||||
log::info!("[PipeWire Backend] Buffer size: {:?}", cpal_buffer_size);
|
||||
|
||||
// Create MixerDeviceSink with custom config
|
||||
let mixer_sink = DeviceSinkBuilder::from_device(device)
|
||||
.map_err(|e| format!("Failed to create device sink builder: {}", e))?
|
||||
.with_supported_config(&supported_config)
|
||||
.with_buffer_size(cpal_buffer_size)
|
||||
.open_stream()
|
||||
.map_err(|e| format!("Failed to create output stream at {}Hz: {}", config.sample_rate, e))?;
|
||||
|
||||
|
||||
@@ -352,14 +352,12 @@ fn create_output_stream_with_config(
|
||||
);
|
||||
|
||||
// Create StreamConfig with desired sample rate
|
||||
// Note: buffer_size here is unused — with_supported_config() resets it.
|
||||
// The actual buffer size is set via with_buffer_size() below.
|
||||
let config = StreamConfig {
|
||||
channels,
|
||||
sample_rate,
|
||||
buffer_size: if exclusive_mode {
|
||||
BufferSize::Fixed(512) // Lower latency for exclusive mode
|
||||
} else {
|
||||
BufferSize::Default
|
||||
},
|
||||
buffer_size: BufferSize::Default,
|
||||
};
|
||||
|
||||
// Check if device supports this configuration
|
||||
@@ -399,10 +397,25 @@ fn create_output_stream_with_config(
|
||||
SampleFormat::F32,
|
||||
);
|
||||
|
||||
// Compute buffer size — must be applied AFTER with_supported_config()
|
||||
// because that method resets buffer_size to Default via ..Default::default().
|
||||
// MixerDeviceSink has zero internal buffering, so CPAL's buffer is the
|
||||
// ONLY buffer between the mixer and audio hardware.
|
||||
let cpal_buffer_size = if exclusive_mode {
|
||||
BufferSize::Fixed(512) // Low latency for exclusive mode
|
||||
} else {
|
||||
// ~100ms buffer, matching old vendored cpal period size.
|
||||
// Prevents underruns at high sample rates (192kHz = 19200 frames).
|
||||
BufferSize::Fixed(sample_rate / 10)
|
||||
};
|
||||
log::info!("Buffer size: {:?}", cpal_buffer_size);
|
||||
|
||||
// Create MixerDeviceSink with custom config
|
||||
match DeviceSinkBuilder::from_device(device) {
|
||||
Ok(builder) => {
|
||||
match builder.with_supported_config(&supported_config).open_stream() {
|
||||
match builder.with_supported_config(&supported_config)
|
||||
.with_buffer_size(cpal_buffer_size)
|
||||
.open_stream() {
|
||||
Ok(mixer_sink) => {
|
||||
log::info!("MixerDeviceSink created successfully at {}Hz", sample_rate);
|
||||
Ok(mixer_sink)
|
||||
|
||||
@@ -379,16 +379,12 @@ fn create_output_stream_with_config(
|
||||
);
|
||||
|
||||
// Create StreamConfig with desired sample rate
|
||||
// Note: buffer_size here is unused — with_supported_config() resets it.
|
||||
// The actual buffer size is set via with_buffer_size() below.
|
||||
let config = StreamConfig {
|
||||
channels,
|
||||
sample_rate,
|
||||
buffer_size: if exclusive_mode {
|
||||
BufferSize::Fixed(512) // Lower latency for exclusive mode
|
||||
} else {
|
||||
// PipeWire manages its own buffer scheduling;
|
||||
// forcing a fixed size causes underruns at high sample rates
|
||||
BufferSize::Default
|
||||
},
|
||||
buffer_size: BufferSize::Default,
|
||||
};
|
||||
|
||||
// Check if device supports this configuration
|
||||
@@ -428,10 +424,25 @@ fn create_output_stream_with_config(
|
||||
SampleFormat::F32,
|
||||
);
|
||||
|
||||
// Compute buffer size — must be applied AFTER with_supported_config()
|
||||
// because that method resets buffer_size to Default via ..Default::default().
|
||||
// MixerDeviceSink has zero internal buffering, so CPAL's buffer is the
|
||||
// ONLY buffer between the mixer and audio hardware.
|
||||
let cpal_buffer_size = if exclusive_mode {
|
||||
BufferSize::Fixed(512) // Low latency for exclusive mode
|
||||
} else {
|
||||
// ~100ms buffer, matching old vendored cpal period size.
|
||||
// Prevents underruns at high sample rates (192kHz = 19200 frames).
|
||||
BufferSize::Fixed(sample_rate / 10)
|
||||
};
|
||||
log::info!("Buffer size: {:?}", cpal_buffer_size);
|
||||
|
||||
// Create MixerDeviceSink with custom config
|
||||
match DeviceSinkBuilder::from_device(device) {
|
||||
Ok(builder) => {
|
||||
match builder.with_supported_config(&supported_config).open_stream() {
|
||||
match builder.with_supported_config(&supported_config)
|
||||
.with_buffer_size(cpal_buffer_size)
|
||||
.open_stream() {
|
||||
Ok(mixer_sink) => {
|
||||
log::info!("MixerDeviceSink created successfully at {}Hz", sample_rate);
|
||||
Ok(mixer_sink)
|
||||
|
||||
Reference in new issue
Block a user