use cpal::{Error, ErrorKind};
pub mod device;
pub mod import;
pub mod input;
pub mod output;
pub mod plugins;
pub use device::{AtomeDevice, Direction};
pub use input::InputClass;
pub use output::{OutputClass, SampleRate, SampleType};
pub use plugins::Plugin;
pub struct EngineInput<S: SampleType> {
device: AtomeDevice,
input: InputClass<S>,
routes: Vec<usize>,
}
impl<S: SampleType> EngineInput<S> {
pub fn device(&self) -> &AtomeDevice {
&self.device
}
pub fn input(&self) -> &InputClass<S> {
&self.input
}
pub fn input_mut(&mut self) -> &mut InputClass<S> {
&mut self.input
}
pub fn routes(&self) -> &[usize] {
&self.routes
}
}
pub struct EngineOutput<S: SampleType> {
device: AtomeDevice,
output: OutputClass<S>,
}
impl<S: SampleType> EngineOutput<S> {
pub fn device(&self) -> &AtomeDevice {
&self.device
}
pub fn output(&self) -> &OutputClass<S> {
&self.output
}
pub fn output_mut(&mut self) -> &mut OutputClass<S> {
&mut self.output
}
}
pub struct AudioEngine<S: SampleType> {
inputs: Vec<EngineInput<S>>,
outputs: Vec<EngineOutput<S>>,
sample_rate: SampleRate,
buffer_size: Option<i32>,
plugins: Vec<Plugin>,
}
impl<S: SampleType> AudioEngine<S> {
pub fn new(
inputs: Vec<AtomeDevice>,
outputs: Vec<AtomeDevice>,
sample_rate: SampleRate,
output_channels: Vec<u16>,
buffer_size: Option<i32>,
plugins: Vec<Plugin>,
) -> Result<Self, Error> {
if output_channels.len() != outputs.len() {
return Err(Error::with_message(
ErrorKind::InvalidInput,
format!(
"{} output devices but {} channel counts",
outputs.len(),
output_channels.len()
),
));
}
for device in &outputs {
if device.direction() != Direction::Output {
return Err(Error::with_message(
ErrorKind::InvalidInput,
format!("{} is an input device, listed as an output", device.name()),
));
}
}
for device in &inputs {
if device.direction() != Direction::Input {
return Err(Error::with_message(
ErrorKind::InvalidInput,
format!("{} is an output device, listed as an input", device.name()),
));
}
}
let built_outputs: Vec<EngineOutput<S>> = outputs
.into_iter()
.zip(output_channels)
.map(|(device, channels)| {
let output = OutputClass::new(
Some(device.device().clone()),
device.host(),
channels,
sample_rate,
buffer_size,
);
EngineOutput { device, output }
})
.collect();
let names: Vec<String> = built_outputs
.iter()
.map(|output| output.device.name())
.collect();
let built_inputs = inputs
.into_iter()
.map(|device| {
let routes = resolve_routes(&device, &names)?;
let mut input = InputClass::new(
Some(device.device().clone()),
device.host(),
sample_rate,
buffer_size,
|_captured: &[S]| {},
);
if device.routing().is_some() {
let devices = routes
.iter()
.map(|index| built_outputs[*index].device.device().clone())
.collect();
input.set_routing(Some(devices));
}
Ok(EngineInput {
device,
input,
routes,
})
})
.collect::<Result<Vec<_>, Error>>()?;
Ok(AudioEngine {
inputs: built_inputs,
outputs: built_outputs,
sample_rate,
buffer_size,
plugins,
})
}
pub fn inputs(&self) -> &[EngineInput<S>] {
&self.inputs
}
pub fn inputs_mut(&mut self) -> &mut [EngineInput<S>] {
&mut self.inputs
}
pub fn outputs(&self) -> &[EngineOutput<S>] {
&self.outputs
}
pub fn outputs_mut(&mut self) -> &mut [EngineOutput<S>] {
&mut self.outputs
}
pub fn sample_rate(&self) -> SampleRate {
self.sample_rate
}
pub fn buffer_size(&self) -> Option<i32> {
self.buffer_size
}
pub fn plugins(&self) -> &[Plugin] {
&self.plugins
}
pub fn apply_input_plugins(&mut self, index: usize, buffer: &mut [S]) -> Result<(), Error> {
let input = self
.inputs
.get_mut(index)
.ok_or_else(|| unknown(index, "input"))?;
let channels = input.input.channels();
for plugin in input.device.plugins_mut() {
plugin.apply(buffer, channels)?;
}
Ok(())
}
pub fn apply_output_plugins(&mut self, index: usize, buffer: &mut [S]) -> Result<(), Error> {
let output = self
.outputs
.get_mut(index)
.ok_or_else(|| unknown(index, "output"))?;
let channels = output.output.channels();
for plugin in output.device.plugins_mut() {
plugin.apply(buffer, channels)?;
}
Ok(())
}
pub fn apply_engine_plugins(
&mut self,
buffer: &mut [S],
channels: u16,
) -> Result<(), Error> {
for plugin in &mut self.plugins {
plugin.apply(buffer, channels)?;
}
Ok(())
}
pub fn apply_plugins(
&mut self,
index: usize,
to: usize,
buffer: &mut [S],
channels: u16,
) -> Result<(), Error> {
self.apply_input_plugins(index, buffer)?;
self.apply_engine_plugins(buffer, channels)?;
self.apply_output_plugins(to, buffer)
}
}
impl<S: SampleType> std::fmt::Debug for EngineInput<S> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("EngineInput")
.field("device", &self.device.name())
.field("channels", &self.input.channels())
.field("plugins", &self.device.plugins().len())
.field("routes", &self.routes)
.finish()
}
}
impl<S: SampleType> std::fmt::Debug for EngineOutput<S> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("EngineOutput")
.field("device", &self.device.name())
.field("channels", &self.output.channels())
.field("plugins", &self.device.plugins().len())
.finish()
}
}
impl<S: SampleType> std::fmt::Debug for AudioEngine<S> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("AudioEngine")
.field("sample_rate", &self.sample_rate)
.field("buffer_size", &self.buffer_size)
.field("inputs", &self.inputs)
.field("outputs", &self.outputs)
.field("plugins", &self.plugins.len())
.finish()
}
}
fn resolve_routes(device: &AtomeDevice, outputs: &[String]) -> Result<Vec<usize>, Error> {
let Some(routing) = device.routing() else {
return Ok((0..outputs.len()).collect());
};
routing
.iter()
.map(|wanted| {
outputs
.iter()
.position(|name| name == wanted)
.ok_or_else(|| {
Error::with_message(
ErrorKind::InvalidInput,
format!(
"{} is routed to {wanted:?}, which is not one of the outputs",
device.name()
),
)
})
})
.collect()
}
fn unknown(index: usize, what: &str) -> Error {
Error::with_message(
ErrorKind::InvalidInput,
format!("no {what} at index {index}"),
)
}