use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use crate::time::ClockTick;
pub type IoResult<T> = Result<T, String>;
pub trait IoControl {
fn write_data(&self, data: &[u8]) -> usize;
}
pub trait IoDriver: Send + Sync {
fn set_process_callback(&self, cb: Box<dyn FnMut(&ClockTick)>);
fn run(&self, running: Arc<AtomicBool>) -> IoResult<()>;
fn stop(&self) -> IoResult<()>;
fn as_control(&self) -> Option<&dyn IoControl> {
None
}
}
pub trait IoCapture: Send + Sync {
fn read_input(&self, channel: usize, dst: &mut [f32]) -> usize;
fn num_input_channels(&self) -> usize;
}
pub trait IoPlayback: Send + Sync {
fn write_output(&self, channel: usize, src: &[f32]) -> usize;
fn num_output_channels(&self) -> usize;
}
pub trait IoBackend: IoDriver {}
impl<T: IoDriver> IoBackend for T {}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
struct TestBackend {
reg: AtomicU8,
}
impl IoDriver for TestBackend {
fn set_process_callback(&self, _cb: Box<dyn FnMut(&ClockTick)>) {}
fn run(&self, _: Arc<AtomicBool>) -> IoResult<()> {
Ok(())
}
fn stop(&self) -> IoResult<()> {
Ok(())
}
fn as_control(&self) -> Option<&dyn IoControl> {
Some(self)
}
}
impl IoControl for TestBackend {
fn write_data(&self, data: &[u8]) -> usize {
if let Some(&v) = data.first() {
self.reg.store(v, Ordering::Relaxed);
}
1
}
}
#[test]
fn test_iocontrol_write_data() {
let b = TestBackend {
reg: AtomicU8::new(0),
};
let ctrl = b.as_control().unwrap();
ctrl.write_data(&[42]);
assert_eq!(b.reg.load(Ordering::Relaxed), 42);
}
#[test]
fn test_iocontrol_default_returns_none() {
struct NoControl;
impl IoDriver for NoControl {
fn set_process_callback(&self, _cb: Box<dyn FnMut(&ClockTick)>) {}
fn run(&self, _: Arc<AtomicBool>) -> IoResult<()> {
Ok(())
}
fn stop(&self) -> IoResult<()> {
Ok(())
}
}
let b = NoControl;
assert!(b.as_control().is_none());
}
}