use cpal::traits::{DeviceTrait, HostTrait};
use super::OscilloscopeBackend;
use crate::backend::{BackendKind, Result};
use crate::error::Error;
#[derive(Debug, Clone)]
pub struct OscilloscopeDeviceInfo {
pub name: String,
pub sample_rate: u32,
}
pub struct OscilloscopeDiscovery {
devices: Vec<OscilloscopeDeviceInfo>,
}
impl OscilloscopeDiscovery {
pub fn new() -> Self {
Self {
devices: Vec::new(),
}
}
pub fn scan(&mut self) -> Vec<OscilloscopeDeviceInfo> {
self.devices.clear();
let host = cpal::default_host();
let devices = match host.output_devices() {
Ok(d) => d,
Err(e) => {
log::warn!("Failed to enumerate audio devices: {}", e);
return Vec::new();
}
};
for device in devices {
let name = match device.name() {
Ok(n) => n,
Err(_) => continue,
};
let config = match device.default_output_config() {
Ok(c) => c,
Err(_) => continue,
};
if config.channels() < 2 {
continue;
}
let sample_rate = config.sample_rate().0;
let info = OscilloscopeDeviceInfo { name, sample_rate };
self.devices.push(info);
}
self.devices.clone()
}
pub fn connect(&self, device_name: &str) -> Result<BackendKind> {
let info = self
.devices
.iter()
.find(|info| info.name == device_name)
.ok_or_else(|| {
Error::disconnected(format!("Audio device '{}' not found", device_name))
})?;
Ok(BackendKind::Fifo(Box::new(OscilloscopeBackend::new(
info.name.clone(),
info.sample_rate,
))))
}
pub fn connect_by_index(&self, index: usize) -> Result<BackendKind> {
let info = self.devices.get(index).ok_or_else(|| {
Error::disconnected(format!(
"Audio device index {} out of range (found {} devices)",
index,
self.devices.len()
))
})?;
Ok(BackendKind::Fifo(Box::new(OscilloscopeBackend::new(
info.name.clone(),
info.sample_rate,
))))
}
pub fn get_info(&self, index: usize) -> Option<&OscilloscopeDeviceInfo> {
self.devices.get(index)
}
pub fn len(&self) -> usize {
self.devices.len()
}
pub fn is_empty(&self) -> bool {
self.devices.is_empty()
}
}
impl Default for OscilloscopeDiscovery {
fn default() -> Self {
Self::new()
}
}