use std::fmt;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use crate::config::AudioConfig;
use rill_core::io::{IoDriver, IoResult};
use rill_core::time::ClockTick;
#[derive(Copy, Clone)]
struct CbSlot(usize);
impl CbSlot {
fn new() -> Self {
Self(Box::into_raw(Box::new(None::<Box<dyn FnMut(&ClockTick)>>)) as usize)
}
unsafe fn set(&self, cb: Box<dyn FnMut(&ClockTick)>) {
(*(self.0 as *mut Option<Box<dyn FnMut(&ClockTick)>>)) = Some(cb);
}
unsafe fn call(&mut self, tick: &ClockTick) {
if let Some(ref mut cb) = *(self.0 as *mut Option<Box<dyn FnMut(&ClockTick)>>) {
cb(tick);
}
}
unsafe fn take_box(&self) {
let taken = (*(self.0 as *mut Option<Box<dyn FnMut(&ClockTick)>>)).take();
drop(taken);
}
}
pub struct NullBackend {
config: AudioConfig,
cb: CbSlot,
is_running: bool,
xruns: u32,
}
impl fmt::Debug for NullBackend {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NullBackend")
.field("config", &self.config)
.field("is_running", &self.is_running)
.field("xruns", &self.xruns)
.finish()
}
}
impl NullBackend {
pub fn new(config: AudioConfig) -> Self {
Self {
config,
cb: CbSlot::new(),
is_running: false,
xruns: 0,
}
}
}
impl IoDriver for NullBackend {
fn set_process_callback(&self, cb: Box<dyn FnMut(&ClockTick)>) {
unsafe {
self.cb.set(cb);
}
}
fn run(&self, _running: Arc<AtomicBool>) -> IoResult<()> {
let mut tick = ClockTick::new(
0,
self.config.buffer_size,
self.config.sample_rate as f32,
"null".into(),
);
tick.speed_ratio = 1.0;
unsafe {
let mut_ref: *mut CbSlot = &self.cb as *const CbSlot as *mut CbSlot;
(*mut_ref).call(&tick);
}
Ok(())
}
fn stop(&self) -> IoResult<()> {
Ok(())
}
}
impl Drop for NullBackend {
fn drop(&mut self) {
unsafe {
self.cb.take_box();
}
}
}