use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use std::sync::mpsc; use super::{stream_to_matrix, Matrix}; pub struct DefaultAudioDeviceWithCPAL { rx: mpsc::Receiver>, #[allow(unused)] stream: cpal::Stream, timeout: std::time::Duration, } #[derive(Debug, thiserror::Error)] pub enum AudioDeviceErrors { #[error("{0}")] Device(#[from] cpal::DevicesError), #[error("device not found")] NotFound, #[error("{0}")] BuildStream(#[from] cpal::BuildStreamError), #[error("{0}")] PlayStream(#[from] cpal::PlayStreamError), #[error("{0}")] SupportedStreamsError(#[from] cpal::SupportedStreamConfigsError), } impl DefaultAudioDeviceWithCPAL { pub fn instantiate( device: Option<&str>, opts: &crate::cfg::SourceOptions, timeout_secs: u64, ) -> Result>, AudioDeviceErrors> { let host = cpal::default_host(); let device = match device { Some(name) => host .input_devices()? .find(|x| x.name().as_deref().unwrap_or("") == name) .ok_or(AudioDeviceErrors::NotFound)?, None => host .default_input_device() .ok_or(AudioDeviceErrors::NotFound)?, }; let max_channels = device .supported_input_configs()? .map(|x| x.channels()) .max() .unwrap_or(opts.channels as u16); let actual_channels = std::cmp::min(opts.channels as u16, max_channels); let cfg = cpal::StreamConfig { channels: actual_channels, buffer_size: cpal::BufferSize::Fixed(opts.buffer), sample_rate: cpal::SampleRate(opts.sample_rate), }; let (tx, rx) = mpsc::channel(); let timeout = std::time::Duration::from_secs(timeout_secs); let stream = device.build_input_stream( &cfg, move |data: &[f32], _info| { tx.send(stream_to_matrix( data.iter().cloned(), actual_channels as usize, 1., )) .unwrap_or(()) }, |e| eprintln!("error in input stream: {e}"), Some(timeout), )?; stream.play()?; Ok(Box::new(DefaultAudioDeviceWithCPAL { stream, rx, timeout })) } } impl super::DataSource for DefaultAudioDeviceWithCPAL { fn recv(&mut self) -> Option> { match self.rx.recv_timeout(self.timeout) { Ok(x) => Some(x), Err(e) => { println!("error receiving from source? {e}"); None } } } }