scope-tui 0.3.5

A simple oscilloscope/vectorscope/spectroscope for your terminal
Documentation
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use std::sync::mpsc;

use super::{stream_to_matrix, Matrix};

pub struct DefaultAudioDeviceWithCPAL {
	rx: mpsc::Receiver<Matrix<f64>>,
	#[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<Box<impl super::DataSource<f64>>, 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<f64> for DefaultAudioDeviceWithCPAL {
	fn recv(&mut self) -> Option<super::Matrix<f64>> {
		match self.rx.recv_timeout(self.timeout) {
			Ok(x) => Some(x),
			Err(e) => {
				println!("error receiving from source? {e}");
				None
			}
		}
	}
}