use anyhow::{Context, Result}; use cpal::traits::{DeviceTrait, HostTrait}; use cpal::{ Device, FromSample, Host, Sample, SampleFormat, SizedSample, Stream, StreamConfig, SupportedStreamConfig, }; use ringbuf::traits::Producer; const PREFERRED_INPUTS: [&str; 3] = ["pipewire", "pulse", "default"]; pub fn find_input(host: &Host, name: Option<&str>) -> Result { match name { Some(name) => host .input_devices() .context("failed to enumerate input devices")? .find(|d| d.name().map(|n| n == name).unwrap_or(false)) .with_context(|| format!("input device {name:?} not found")), None => { let mut devices = host .input_devices() .context("failed to enumerate input devices")?; if let Some(device) = devices.find(|d| { d.name() .map(|n| PREFERRED_INPUTS.iter().any(|p| n == *p)) .unwrap_or(false) }) { return Ok(device); } host.default_input_device() .context("no default input device found") } } } /// Open the input stream. Captured samples are converted to `f32` (interleaved /// frames) and pushed into `tx`. pub fn start

(device: &Device, supported: &SupportedStreamConfig, tx: P) -> Result where P: Producer + Send + 'static, { let config: StreamConfig = supported.config(); match supported.sample_format() { SampleFormat::F32 => build::(device, &config, tx), SampleFormat::F64 => build::(device, &config, tx), SampleFormat::I8 => build::(device, &config, tx), SampleFormat::I16 => build::(device, &config, tx), SampleFormat::I32 => build::(device, &config, tx), SampleFormat::I64 => build::(device, &config, tx), SampleFormat::U8 => build::(device, &config, tx), SampleFormat::U16 => build::(device, &config, tx), SampleFormat::U32 => build::(device, &config, tx), SampleFormat::U64 => build::(device, &config, tx), format => anyhow::bail!("unsupported input sample format: {format:?}"), } } fn build(device: &Device, config: &StreamConfig, mut tx: P) -> Result where T: SizedSample, f32: FromSample, P: Producer + Send + 'static, { let channels = config.channels as usize; let mut frame = vec![0f32; channels]; let err_fn = |err| eprintln!("capture stream error: {err}"); device .build_input_stream( config, move |data: &[T], _: &cpal::InputCallbackInfo| { for chunk in data.chunks(channels) { for (slot, sample) in frame.iter_mut().zip(chunk) { *slot = f32::from_sample(*sample); } let _ = tx.push_slice(&frame); } }, err_fn, None, ) .map_err(|e| anyhow::anyhow!("failed to build input stream: {e}")) }