use cpal::Device;
use cpal::DeviceId;
use crate::input;
use crate::output::{self, device_name, OutputType};
use crate::plugins::Plugin;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Direction {
Input,
Output,
}
#[derive(Clone)]
pub struct AtomeDevice {
device: Device,
direction: Direction,
host: OutputType,
plugins: Vec<Plugin>,
routing: Option<Vec<String>>,
pub device_id: DeviceId,
}
impl AtomeDevice {
pub fn input(device: Device, host: OutputType) -> Self {
AtomeDevice::new(device, Direction::Input, host)
}
pub fn output(device: Device, host: OutputType) -> Self {
AtomeDevice::new(device, Direction::Output, host)
}
pub fn default_input(host: OutputType) -> Option<Self> {
input::default_device().map(|device| AtomeDevice::input(device, host))
}
pub fn default_output(host: OutputType) -> Option<Self> {
output::default_device().map(|device| AtomeDevice::output(device, host))
}
fn new(device: Device, direction: Direction, host: OutputType) -> Self {
use cpal::traits::DeviceTrait;
let device_id = match device.id() {
Ok(id) => id,
Err(e) => {
eprintln!("Failed to get device ID: {}", e);
eprintln!("Falling back to device name-based ID");
let name = device_name(&device);
let name_hash = {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
name.hash(&mut hasher);
format!("{:x}", hasher.finish())
};
#[cfg(target_vendor = "apple")]
let fallback_host = cpal::platform::HostId::CoreAudio;
#[cfg(target_os = "linux")]
let fallback_host = cpal::platform::HostId::Alsa;
#[cfg(target_os = "windows")]
let fallback_host = cpal::platform::HostId::Wasapi;
#[cfg(target_os = "android")]
let fallback_host = cpal::platform::HostId::AAudio;
DeviceId::new(fallback_host, name_hash)
}
};
AtomeDevice {
device,
direction,
host,
plugins: Vec::new(),
routing: None,
device_id,
}
}
pub fn with_plugin(mut self, plugin: Plugin) -> Self {
self.plugins.push(plugin);
self
}
pub fn with_plugins(mut self, plugins: impl IntoIterator<Item = Plugin>) -> Self {
self.plugins.extend(plugins);
self
}
pub fn route_to(mut self, outputs: impl IntoIterator<Item = impl Into<String>>) -> Self {
self.routing = Some(outputs.into_iter().map(Into::into).collect());
self
}
pub fn direction(&self) -> Direction {
self.direction
}
pub fn host(&self) -> OutputType {
self.host
}
pub fn device(&self) -> &Device {
&self.device
}
pub fn plugins(&self) -> &[Plugin] {
&self.plugins
}
pub fn plugins_mut(&mut self) -> &mut Vec<Plugin> {
&mut self.plugins
}
pub fn routing(&self) -> Option<&[String]> {
self.routing.as_deref()
}
pub fn name(&self) -> String {
device_name(&self.device)
}
}
impl std::fmt::Debug for AtomeDevice {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("AtomeDevice")
.field("name", &self.name())
.field("device_id", &self.device_id)
.field("direction", &self.direction)
.field("host", &self.host)
.field("plugins", &self.plugins.len())
.field("routing", &self.routing)
.finish()
}
}