use std::cmp::max;
use halfband::fir::{Downsampler16, Upsampler16};
use crate::{
context::AudioContext,
executor::MAX_ARITY,
node::{Inputs, LegatoNode, Node},
ports::Ports,
};
const OVERSAMPLE_K: usize = 2;
#[derive(Debug)]
pub struct Oversampler2X {
node: LegatoNode,
upsamplers: Box<[Upsampler16]>,
downsamplers: Box<[Downsampler16]>,
upsampled: Box<[f32]>,
node_outputs: Box<[f32]>,
chans: usize,
}
impl Oversampler2X {
pub fn new(node: LegatoNode, buffer_size: usize) -> Self {
let ports = node.get_node().ports();
let chans = max(ports.audio_in.len(), ports.audio_out.len());
let upsamplers = (0..chans)
.map(|_| Upsampler16::default())
.collect::<Vec<Upsampler16>>()
.into();
let downsamplers = (0..chans)
.map(|_| Downsampler16::default())
.collect::<Vec<Downsampler16>>()
.into();
Self {
node,
upsamplers,
downsamplers,
upsampled: vec![0.0; buffer_size * OVERSAMPLE_K * chans].into(),
node_outputs: vec![0.0; buffer_size * OVERSAMPLE_K * chans].into(),
chans,
}
}
}
impl Clone for Oversampler2X {
fn clone(&self) -> Self {
let upsamplers = (0..self.chans)
.map(|_| Upsampler16::default())
.collect::<Vec<Upsampler16>>()
.into();
let downsamplers = (0..self.chans)
.map(|_| Downsampler16::default())
.collect::<Vec<Downsampler16>>()
.into();
Self {
node: self.node.clone(),
upsamplers,
downsamplers,
upsampled: self.upsampled.clone(),
node_outputs: self.node_outputs.clone(),
chans: self.chans,
}
}
}
impl Node for Oversampler2X {
fn process(&mut self, ctx: &mut AudioContext, inputs: &Inputs, outputs: &mut [&mut [f32]]) {
let cfg = ctx.get_config();
let block_size = cfg.block_size;
let sample_rate = cfg.sample_rate;
assert!(self.upsampled.len() == self.chans * block_size * OVERSAMPLE_K);
let mut node_inputs: [Option<&[f32]>; MAX_ARITY] = [None; MAX_ARITY];
let mut has_inputs: [bool; MAX_ARITY] = [false; MAX_ARITY];
for (c, chan_in_outer) in inputs.iter().enumerate() {
if let Some(chan_in) = chan_in_outer {
has_inputs[c] = true;
let start = block_size * OVERSAMPLE_K * c;
let end = start + block_size * OVERSAMPLE_K;
let chan_mut = &mut self.upsampled[start..end];
self.upsamplers[c].process_block(chan_in, chan_mut);
}
}
for (c, (input_chan, has_input_chan)) in node_inputs
.iter_mut()
.zip(has_inputs.iter())
.take(self.chans)
.enumerate()
{
if *has_input_chan {
let start = block_size * OVERSAMPLE_K * c;
let end = start + block_size * OVERSAMPLE_K;
let chan_in = &self.upsampled[start..end];
*input_chan = Some(chan_in);
}
}
self.node_outputs.fill(0.0);
let mut node_outputs_raw = slice_node_ports_mut(
&mut self.node_outputs,
0,
block_size * OVERSAMPLE_K,
self.chans,
);
let outputs_for_node = &mut node_outputs_raw[..self.chans];
ctx.set_block_size(block_size * OVERSAMPLE_K);
ctx.set_sample_rate(sample_rate * OVERSAMPLE_K);
self.node
.get_node_mut()
.process(ctx, inputs, outputs_for_node);
ctx.set_block_size(block_size);
ctx.set_sample_rate(sample_rate);
for c in 0..self.chans {
let downsampler = &mut self.downsamplers[c];
let chan_out = &mut outputs[c];
downsampler.process_block(outputs_for_node[c], chan_out);
}
}
fn ports(&self) -> &Ports {
self.node.get_node().ports()
}
}
#[inline(always)]
fn slice_node_ports_mut(
buffer: &mut [f32],
offset: usize,
block_size: usize,
chans: usize,
) -> [&mut [f32]; MAX_ARITY] {
let end = (block_size * chans) + offset;
let node_buffer = &mut buffer[offset..end];
let mut chunks = node_buffer.chunks_exact_mut(block_size);
std::array::from_fn(|_| chunks.next().unwrap_or_default())
}