use cubecl::prelude::*;
use cubecl::server::Handle;
use dspk_core::compute::LaunchGeometry;
use super::kernels::fractional_delay_taps_kernel;
pub use super::kernels::windowed_sinc_weight;
use crate::core::{buffer, DspFloat};
use crate::math::windows::{blackman_window, sinc};
pub fn fractional_delay_taps(shift: f32, radius: usize) -> Vec<f32> {
let r = radius as isize;
let raw: Vec<f32> = (-r..=r)
.map(|k| {
let tau = k as f32 - shift;
sinc(tau) * blackman_window(tau, radius as f32)
})
.collect();
let norm: f32 = raw.iter().sum();
raw.into_iter().map(|w| w / norm).collect()
}
pub fn fractional_delay(row: &[f32], start: usize, shift: f32, radius: usize, out: &mut [f32]) {
assert!(start >= radius && start + out.len() + radius <= row.len(), "fractional-delay taps leave the row");
let taps = fractional_delay_taps(shift, radius);
for (i, o) in out.iter_mut().enumerate() {
let base = start + i - radius;
*o = taps.iter().zip(&row[base..]).map(|(w, x)| w * x).sum();
}
}
pub fn fractional_delay_taps_device<F: DspFloat>(client: &Client, shifts: &Handle, count: usize, radius: usize) -> Handle {
let n = 2 * radius + 1;
let taps = buffer::empty::<F>(client, count * n);
if count > 0 {
let geom = LaunchGeometry::elementwise(client, count);
unsafe {
fractional_delay_taps_kernel::launch::<F>(
client,
geom.cube_count,
geom.cube_dim,
BufferArg::from_raw_parts(shifts.clone(), count),
BufferArg::from_raw_parts(taps.clone(), count * n),
count as u32,
radius as u32,
);
}
}
taps
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn taps_interpolate_a_line_and_pass_integers() {
let taps = fractional_delay_taps(0.0, 5);
assert!((taps[5] - 1.0).abs() < 1e-6 && taps.iter().enumerate().all(|(i, w)| i == 5 || w.abs() < 1e-6));
let row: Vec<f32> = (0..64).map(|t| (t as f32 * 0.1).sin()).collect();
let mut out = vec![0.0f32; 10];
fractional_delay(&row, 20, 0.3, 5, &mut out);
for (i, o) in out.iter().enumerate() {
assert!((o - (((20 + i) as f32 + 0.3) * 0.1).sin()).abs() < 1e-3);
}
}
fn device_taps_match_host(client: &Client) {
let shifts = [-0.5f32, -0.2, 0.0, 0.37];
let taps = buffer::download::<f32>(client, fractional_delay_taps_device::<f32>(client, &buffer::upload(client, &shifts), 4, 5));
for (i, &s) in shifts.iter().enumerate() {
for (d, h) in taps[i * 11..(i + 1) * 11].iter().zip(fractional_delay_taps(s, 5)) {
assert!((d - h).abs() < 1e-5, "shift {s}: {d} vs {h}");
}
}
}
runtime_test!(device_taps_equal_host_taps, device_taps_match_host);
}