use crate::audio::io::AudioIO;
use crate::render_plan::{PlanSlot, RenderPlan};
use crate::state::StateSlot;
use arc_swap::ArcSwap;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::JoinHandle;
use std::time::Duration;
#[derive(Clone, Default)]
pub struct HwPorts {
pub ins: Vec<Arc<AudioIO>>,
pub outs: Vec<Arc<AudioIO>>,
pub buffer_size: usize,
}
pub struct PlanBuilder {
dirty: Arc<AtomicBool>,
quit: Arc<AtomicBool>,
thread: std::thread::Thread,
join: Option<JoinHandle<()>>,
}
impl PlanBuilder {
pub fn spawn(
state: Arc<StateSlot>,
hw: Arc<ArcSwap<HwPorts>>,
slot: Arc<PlanSlot>,
collector: basedrop::Collector,
) -> Self {
let dirty = Arc::new(AtomicBool::new(true));
let quit = Arc::new(AtomicBool::new(false));
let join = std::thread::spawn({
let dirty = dirty.clone();
let quit = quit.clone();
move || builder_loop(state, hw, slot, collector, dirty, quit)
});
Self {
dirty,
quit,
thread: join.thread().clone(),
join: Some(join),
}
}
pub fn mark_dirty(&self) {
self.dirty.store(true, Ordering::Release);
self.thread.unpark();
}
}
impl Drop for PlanBuilder {
fn drop(&mut self) {
self.quit.store(true, Ordering::Release);
self.thread.unpark();
if let Some(join) = self.join.take() {
let _ = join.join();
}
}
}
fn builder_loop(
state: Arc<StateSlot>,
hw: Arc<ArcSwap<HwPorts>>,
slot: Arc<PlanSlot>,
mut collector: basedrop::Collector,
dirty: Arc<AtomicBool>,
quit: Arc<AtomicBool>,
) {
loop {
std::thread::park_timeout(Duration::from_millis(10));
if quit.load(Ordering::Acquire) {
drop(slot);
collector.collect();
let _ = collector.try_cleanup();
break;
}
if !dirty.swap(false, Ordering::AcqRel) {
continue;
}
let HwPorts {
ins,
outs,
buffer_size,
} = hw.load_full().as_ref().clone();
let state = state.load_full();
let plan = RenderPlan::compile(&state, &ins, &outs, buffer_size.max(1));
slot.store(Arc::new(basedrop::Owned::new(&collector.handle(), plan)));
collector.collect();
}
}