1use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
2use std::sync::mpsc;
3
4use super::{stream_to_matrix, Matrix};
5
6pub struct DefaultAudioDeviceWithCPAL {
7 rx: mpsc::Receiver<Matrix<f64>>,
8 #[allow(unused)]
9 stream: cpal::Stream,
10 timeout: std::time::Duration,
11}
12
13#[derive(Debug, thiserror::Error)]
14pub enum AudioDeviceErrors {
15 #[error("{0}")]
16 Device(#[from] cpal::DevicesError),
17
18 #[error("device not found")]
19 NotFound,
20
21 #[error("{0}")]
22 BuildStream(#[from] cpal::BuildStreamError),
23
24 #[error("{0}")]
25 PlayStream(#[from] cpal::PlayStreamError),
26
27 #[error("{0}")]
28 SupportedStreamsError(#[from] cpal::SupportedStreamConfigsError),
29}
30
31impl DefaultAudioDeviceWithCPAL {
32 pub fn instantiate(
33 device: Option<&str>,
34 opts: &crate::cfg::SourceOptions,
35 timeout_secs: u64,
36 ) -> Result<Box<impl super::DataSource<f64>>, AudioDeviceErrors> {
37 let host = cpal::default_host();
38 let device = match device {
39 Some(name) => host
40 .input_devices()?
41 .find(|x| x.name().as_deref().unwrap_or("") == name)
42 .ok_or(AudioDeviceErrors::NotFound)?,
43 None => host
44 .default_input_device()
45 .ok_or(AudioDeviceErrors::NotFound)?,
46 };
47
48 let max_channels = device
49 .supported_input_configs()?
50 .map(|x| x.channels())
51 .max()
52 .unwrap_or(opts.channels as u16);
53
54 let actual_channels = std::cmp::min(opts.channels as u16, max_channels);
55
56 let cfg = cpal::StreamConfig {
57 channels: actual_channels,
58 buffer_size: cpal::BufferSize::Fixed(opts.buffer),
59 sample_rate: cpal::SampleRate(opts.sample_rate),
60 };
61 let (tx, rx) = mpsc::channel();
62 let timeout = std::time::Duration::from_secs(timeout_secs);
63 let stream = device.build_input_stream(
64 &cfg,
65 move |data: &[f32], _info| {
66 tx.send(stream_to_matrix(
67 data.iter().cloned(),
68 actual_channels as usize,
69 1.,
70 ))
71 .unwrap_or(())
72 },
73 |e| eprintln!("error in input stream: {e}"),
74 Some(timeout),
75 )?;
76 stream.play()?;
77
78 Ok(Box::new(DefaultAudioDeviceWithCPAL { stream, rx, timeout }))
79 }
80}
81
82impl super::DataSource<f64> for DefaultAudioDeviceWithCPAL {
83 fn recv(&mut self) -> Option<super::Matrix<f64>> {
84 match self.rx.recv_timeout(self.timeout) {
85 Ok(x) => Some(x),
86 Err(e) => {
87 println!("error receiving from source? {e}");
88 None
89 }
90 }
91 }
92}