pub const BLOCK_FRAMES: usize = 32;
#[derive(Debug, Clone)]
pub struct BlockBuf {
data: Vec<f32>,
channels: usize,
}
impl BlockBuf {
pub fn new(channels: usize) -> Self {
assert!(channels > 0, "BlockBuf requires at least one channel");
Self {
data: vec![0.0; channels * BLOCK_FRAMES],
channels,
}
}
#[inline]
pub fn channels(&self) -> usize {
self.channels
}
#[inline]
pub fn channel(&self, ch: usize) -> &[f32] {
debug_assert!(ch < self.channels);
&self.data[ch * BLOCK_FRAMES..(ch + 1) * BLOCK_FRAMES]
}
#[inline]
pub fn channel_mut(&mut self, ch: usize) -> &mut [f32] {
debug_assert!(ch < self.channels);
&mut self.data[ch * BLOCK_FRAMES..(ch + 1) * BLOCK_FRAMES]
}
#[inline]
pub fn clear(&mut self) {
self.data.fill(0.0);
}
pub fn fill_deinterleaved(&mut self, input: &[f32], frames: usize) {
debug_assert!(frames <= BLOCK_FRAMES);
debug_assert!(input.len() >= frames * self.channels);
let channels = self.channels;
for ch in 0..channels {
let dst = &mut self.data[ch * BLOCK_FRAMES..(ch + 1) * BLOCK_FRAMES];
for (f, sample) in dst.iter_mut().enumerate().take(frames) {
*sample = input[f * channels + ch];
}
dst[frames..].fill(0.0);
}
}
pub fn write_interleaved(&self, out: &mut [f32], frames: usize) {
debug_assert!(frames <= BLOCK_FRAMES);
debug_assert!(out.len() >= frames * self.channels);
let channels = self.channels;
for ch in 0..channels {
let src = &self.data[ch * BLOCK_FRAMES..(ch + 1) * BLOCK_FRAMES];
for (f, &sample) in src.iter().enumerate().take(frames) {
out[f * channels + ch] = sample;
}
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct OnsetEvent {
pub stage_frame: f64,
pub strength: f32,
pub beat: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct StageCtx<'a> {
pub embedded_rate: f64,
pub embedded_rate_slope: f64,
pub onsets: &'a [OnsetEvent],
pub modulation_hold: bool,
pub has_artifact: bool,
}
pub trait Stage: Send {
fn process(&mut self, block: &mut BlockBuf, ctx: &StageCtx<'_>);
fn latency_frames(&self) -> usize;
fn reset(&mut self);
fn prime(&mut self, history: &[f32]) {
let _ = history;
self.reset();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn block_roundtrip_interleave() {
let mut block = BlockBuf::new(2);
let input: Vec<f32> = (0..BLOCK_FRAMES * 2).map(|i| i as f32).collect();
block.fill_deinterleaved(&input, BLOCK_FRAMES);
assert_eq!(block.channel(0)[0], 0.0);
assert_eq!(block.channel(1)[0], 1.0);
assert_eq!(block.channel(0)[1], 2.0);
let mut out = vec![0.0; BLOCK_FRAMES * 2];
block.write_interleaved(&mut out, BLOCK_FRAMES);
assert_eq!(out, input);
}
#[test]
fn partial_fill_zero_pads() {
let mut block = BlockBuf::new(1);
block.fill_deinterleaved(&[1.0; 8], 8);
assert_eq!(&block.channel(0)[..8], &[1.0; 8]);
assert_eq!(&block.channel(0)[8..], &[0.0; BLOCK_FRAMES - 8]);
}
}