From f3ffcf6d89f00bfd83924ba1c0fa090e9a84c3ab Mon Sep 17 00:00:00 2001 From: Sam Sartor Date: Thu, 4 Dec 2025 21:07:49 -0700 Subject: [PATCH 1/2] log supported configs --- gui/src/imp/desktop.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gui/src/imp/desktop.rs b/gui/src/imp/desktop.rs index 1b70a8d..a00d755 100644 --- a/gui/src/imp/desktop.rs +++ b/gui/src/imp/desktop.rs @@ -68,6 +68,7 @@ impl AudioSystem { &mut self, mut each: impl FnMut(Vec) + Send + 'static, ) -> Result<(), Error> { + info!("creating recording on {:?} with {:#?}", self.input.name()?, self.input.supported_input_configs()?.collect::>()); let mut encoder = opus::Encoder::new(SAMPLE_RATE, opus::Channels::Mono, opus::Application::Voip)?; let mut current_processor = AudioProcessor::new_plain(); @@ -116,6 +117,7 @@ impl AudioSystem { } pub fn create_player(&mut self) -> Result { + info!("creating player on {:?} with {:#?}", self.output.name()?, self.output.supported_output_configs()?.collect::>()); let buffer = Arc::new(Mutex::new(dasp_ring_buffer::Bounded::from_raw_parts( 0, 0, -- 2.52.0 From fa6f336305df82990919399aba4338181f380c92 Mon Sep 17 00:00:00 2001 From: Sam Sartor Date: Thu, 4 Dec 2025 22:08:15 -0700 Subject: [PATCH 2/2] choose profile from supported --- gui/Cargo.toml | 2 +- gui/src/effects.rs | 6 ++--- gui/src/imp/desktop.rs | 54 ++++++++++++++++++++++++++++-------------- gui/src/imp/web.rs | 2 +- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/gui/Cargo.toml b/gui/Cargo.toml index a1d118f..71e99ac 100644 --- a/gui/Cargo.toml +++ b/gui/Cargo.toml @@ -130,4 +130,4 @@ desktop = [ "dasp_ring_buffer", "rfd/xdg-portal", "rfd/tokio", -] +] \ No newline at end of file diff --git a/gui/src/effects.rs b/gui/src/effects.rs index 85b6729..b1aa12e 100644 --- a/gui/src/effects.rs +++ b/gui/src/effects.rs @@ -97,13 +97,13 @@ impl AudioProcessor { } impl AudioProcessor { - pub fn process(&mut self, audio: &[f32], output: &mut Vec) { + pub fn process(&mut self, audio: &[f32], channels: usize, output: &mut Vec) { let mut include_raw = true; if self.denoise { with_denoising_model(&self.spawn, |df| { include_raw = false; - self.buffer.extend_from_slice(audio); + self.buffer.extend(audio.iter().step_by(channels).copied()); output.reserve(audio.len()); let hop = df.hop_size; @@ -130,7 +130,7 @@ impl AudioProcessor { } if include_raw { - output.extend_from_slice(audio); + output.extend(audio.iter().step_by(channels).copied()); } } } diff --git a/gui/src/imp/desktop.rs b/gui/src/imp/desktop.rs index a00d755..8e21adc 100644 --- a/gui/src/imp/desktop.rs +++ b/gui/src/imp/desktop.rs @@ -1,7 +1,7 @@ use crate::app::Command; use crate::effects::{AudioProcessor, AudioProcessorSender}; use color_eyre::eyre::{eyre, Context, Error}; -use cpal::traits::{DeviceTrait, HostTrait}; +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait as _}; use dioxus::hooks::UnboundedReceiver; use futures::io::{AsyncRead, AsyncWrite}; use mumble_protocol::control::ClientControlCodec; @@ -64,11 +64,34 @@ impl AudioSystem { self.processors.store(Some(processor)) } + fn choose_config(&self, configs: impl Iterator) -> Result { + let mut supported_configs: Vec<_> = configs + .filter_map(|cfg| cfg.try_with_sample_rate(cpal::SampleRate(SAMPLE_RATE))) + .filter(|cfg| cfg.sample_format() == cpal::SampleFormat::I16) + .map(|cfg| { + cpal::StreamConfig { + buffer_size: cpal::BufferSize::Fixed(match *cfg.buffer_size() { + cpal::SupportedBufferSize::Range { min, max } => 480.clamp(min, max), + cpal::SupportedBufferSize::Unknown => 480, + }), + ..cfg.config() + } + }) + .collect(); + supported_configs.sort_by(|a, b| { + let cpal::BufferSize::Fixed(a_buf) = a.buffer_size else { unreachable!() }; + let cpal::BufferSize::Fixed(b_buf) = b.buffer_size else { unreachable!() }; + Ord::cmp(&a.channels, &b.channels).then(Ord::cmp(&a_buf, &b_buf)) + }); + supported_configs.get(0).cloned().ok_or(eyre!("no supported stream configs")) + } + pub fn start_recording( &mut self, mut each: impl FnMut(Vec) + Send + 'static, ) -> Result<(), Error> { - info!("creating recording on {:?} with {:#?}", self.input.name()?, self.input.supported_input_configs()?.collect::>()); + let config = self.choose_config(self.input.supported_input_configs()?)?; + info!("creating recording on {:?} with {:#?}", self.input.name()?, config); let mut encoder = opus::Encoder::new(SAMPLE_RATE, opus::Channels::Mono, opus::Application::Voip)?; let mut current_processor = AudioProcessor::new_plain(); @@ -79,7 +102,7 @@ impl AudioSystem { if let Some(new_processor) = processors.take() { current_processor = new_processor; } - current_processor.process(frame, &mut output_buffer); + current_processor.process(frame, config.channels as usize, &mut output_buffer); if output_buffer.len() < PACKET_SAMPLES as usize { return; } @@ -96,16 +119,13 @@ impl AudioSystem { }; match self.input.build_input_stream( - &cpal::StreamConfig { - channels: 1, - sample_rate: cpal::SampleRate(SAMPLE_RATE), - buffer_size: cpal::BufferSize::Fixed(PACKET_SAMPLES), - }, + &config, data_callback, error_callback, None, ) { Ok(stream) => { + stream.play()?; self.recording_stream = Some(stream); Ok(()) } @@ -117,7 +137,8 @@ impl AudioSystem { } pub fn create_player(&mut self) -> Result { - info!("creating player on {:?} with {:#?}", self.output.name()?, self.output.supported_output_configs()?.collect::>()); + let config = self.choose_config(self.input.supported_input_configs()?)?; + info!("creating player on {:?} with {:#?}", self.output.name().ok(), &config); let buffer = Arc::new(Mutex::new(dasp_ring_buffer::Bounded::from_raw_parts( 0, 0, @@ -130,20 +151,16 @@ impl AudioSystem { let stream = { let buffer = buffer.clone(); self.output.build_output_stream( - &cpal::StreamConfig { - channels: 1, - sample_rate: cpal::SampleRate(SAMPLE_RATE), - buffer_size: cpal::BufferSize::Fixed(480), // 10ms playback delay - }, - move |frame, info| { + &config, + move |frame, _info| { let mut buffer = buffer.lock().unwrap(); - for x in frame.iter_mut() { + for x in frame.chunks_mut(config.channels as usize) { match buffer.pop() { Some(y) => { - *x = y; + x.fill(y); } None => { - *x = 0; + x.fill(0); } } } @@ -152,6 +169,7 @@ impl AudioSystem { None, )? }; + stream.play()?; Ok(AudioPlayer { decoder, stream, diff --git a/gui/src/imp/web.rs b/gui/src/imp/web.rs index 5dc2d0e..9272158 100644 --- a/gui/src/imp/web.rs +++ b/gui/src/imp/web.rs @@ -209,7 +209,7 @@ fn process_audio(frame: &JsValue, processor: &mut AudioProcessor) { }; let input = samples.to_vec(); let mut output = Vec::with_capacity(input.len()); - processor.process(&input, &mut output); + processor.process(&input, 1, &mut output); samples.copy_from(&output); } -- 2.52.0