Skip to main content

scope/input/
cpal.rs

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}