use cubecl::server::Handle;
use dspk_core::{DspResult, HaloWindow, RecordingSource, SampleFormat, WindowLoader};
use dspk_ops::pipeline::PipelineWorkspace;
pub trait WindowAligner {
fn align(&mut self, window: &Handle, at: &HaloWindow, samples: usize) -> Handle;
}
pub struct AlignerChain<'a>(pub Vec<Box<dyn WindowAligner + 'a>>);
impl WindowAligner for AlignerChain<'_> {
fn align(&mut self, window: &Handle, at: &HaloWindow, samples: usize) -> Handle {
let mut handle = window.clone();
for step in &mut self.0 {
handle = step.align(&handle, at, samples);
}
handle
}
}
pub struct DeviceWindows<'a> {
loader: WindowLoader<'a>,
stored: Option<SampleFormat>,
workspace: PipelineWorkspace<f32>,
aligner: Option<Box<dyn WindowAligner + 'a>>,
}
impl<'a> DeviceWindows<'a> {
pub fn new(source: &'a dyn RecordingSource, mut workspace: PipelineWorkspace<f32>, aligner: Option<Box<dyn WindowAligner + 'a>>) -> Self {
let info = source.info();
let probe = |format: SampleFormat| source.read_stored(&[0], 0..1, &mut vec![0u8; format.bytes()]).is_ok();
let stored = (info.format != SampleFormat::F32 && info.samples > 0 && info.channel_count() > 0 && probe(info.format))
.then_some(info.format);
if stored.is_some() {
let (gains, offsets): (Vec<f32>, Vec<f32>) = info.channels.iter().map(|c| (c.gain, c.offset)).unzip();
workspace.set_stored_scaling(&gains, &offsets);
}
Self { loader: WindowLoader::new(source), stored, workspace, aligner }
}
pub fn push_aligner(&mut self, step: Box<dyn WindowAligner + 'a>) {
self.aligner = Some(match self.aligner.take() {
Some(first) => Box::new(AlignerChain(vec![first, step])),
None => step,
});
}
pub fn for_each_while(&mut self, windows: &[HaloWindow], mut f: impl FnMut(&HaloWindow, &Handle) -> DspResult<bool>) -> DspResult<()> {
let Self { loader, stored, workspace, aligner } = self;
let mut finish = |window: &HaloWindow, handle: Handle| {
let handle = match aligner.as_mut() {
Some(aligner) => aligner.align(&handle, window, window.read_len()),
None => handle,
};
f(window, &handle)
};
match *stored {
Some(format) => loader.stream_stored_owned_while(windows, |window, bytes| {
let handle = workspace.process_owned_stored_chunk_in_vram(bytes, format, window.read_len())?;
finish(window, handle)
}),
None => loader.stream_owned_while(windows, |window, raw| {
let handle = workspace.process_owned_chunk_in_vram(raw, window.read_len());
finish(window, handle)
}),
}
}
}
pub fn spread_windows(total: u64, count: usize, len: u64, halos: (u64, u64)) -> Vec<HaloWindow> {
let len = len.clamp(1, total);
let n = count.max(1) as u64;
(0..n)
.map(|i| {
let start = if n > 1 { i * (total - len) / (n - 1) } else { 0 };
HaloWindow::around(i as usize, start..start + len, halos.0, halos.1, total)
})
.collect()
}
pub fn snippet_fits(s: usize, before: usize, after: usize, len: usize) -> bool {
s >= before && s + after <= len
}