use std::{ env, path::{Path, PathBuf}, sync::{Arc, Mutex}, time::Duration, }; use anyhow::{Context, Result}; use cpal::traits::{DeviceTrait, StreamTrait}; use rodio::{buffer::SamplesBuffer, conversions::SampleTypeConverter}; use util::ResultExt; pub struct CaptureInput { pub name: String, config: cpal::SupportedStreamConfig, samples: Arc>>, _stream: cpal::Stream, } impl CaptureInput { pub fn start() -> anyhow::Result { let (device, config) = crate::default_device(true)?; let name = device.name().unwrap_or("".to_string()); log::info!("Using microphone: {}", name); let samples = Arc::new(Mutex::new(Vec::new())); let stream = start_capture(device, config.clone(), samples.clone())?; Ok(Self { name, _stream: stream, config, samples, }) } pub fn finish(self) -> Result { let name = self.name; let mut path = env::current_dir().context("Could not get current dir")?; path.push(&format!("test_recording_{name}.wav")); log::info!("Test recording written to: {}", path.display()); write_out(self.samples, self.config, &path)?; Ok(path) } } fn start_capture( device: cpal::Device, config: cpal::SupportedStreamConfig, samples: Arc>>, ) -> Result { let stream = device .build_input_stream_raw( &config.config(), config.sample_format(), move |data, _: &_| { let data = crate::get_sample_data(config.sample_format(), data).log_err(); let Some(data) = data else { return; }; samples .try_lock() .expect("Only locked after stream ends") .extend_from_slice(&data); }, |err| log::error!("error capturing audio track: {:?}", err), Some(Duration::from_millis(100)), ) .context("failed to build input stream")?; stream.play()?; Ok(stream) } fn write_out( samples: Arc>>, config: cpal::SupportedStreamConfig, path: &Path, ) -> Result<()> { let samples = std::mem::take( &mut *samples .try_lock() .expect("Stream has ended, callback cant hold the lock"), ); let samples: Vec = SampleTypeConverter::<_, f32>::new(samples.into_iter()).collect(); let mut samples = SamplesBuffer::new(config.channels(), config.sample_rate().0, samples); match rodio::output_to_wav(&mut samples, path) { Ok(_) => Ok(()), Err(e) => Err(anyhow::anyhow!("Failed to write wav file: {}", e)), } }