use std::f32::consts::TAU;
use crate::{
context::AudioContext,
msg::{NodeMessage, RtValue},
node::{Inputs, Node},
ports::{PortBuilder, Ports},
};
#[derive(Clone, Debug)]
pub struct OnePole {
a: f32,
chans: usize,
state: Vec<f32>,
ports: Ports,
}
impl OnePole {
pub fn new(cutoff: f32, chans: usize, sr: usize) -> Self {
let a = get_a_from_cutoff(sr as f32, cutoff);
Self {
a,
chans,
state: vec![0.0; chans],
ports: PortBuilder::default()
.audio_in(chans)
.audio_in_named(&["cutoff"])
.audio_out(chans)
.build(),
}
}
}
impl Node for OnePole {
fn process(&mut self, _: &mut AudioContext, inputs: &Inputs, outputs: &mut [&mut [f32]]) {
for c in 0..self.chans {
if let Some(input_buf) = inputs[c] {
let chan_out = &mut outputs[c];
let mut prev_sample = self.state[c];
for (ins, outs) in input_buf.iter().zip(chan_out.iter_mut()) {
let val = ins * (1.0 - self.a) + prev_sample * self.a;
*outs = val;
prev_sample = val;
}
self.state[c] = prev_sample;
}
}
}
fn handle_msg(&mut self, msg: NodeMessage) {
if let NodeMessage::SetParam(inner) = msg
&& let ("a", RtValue::F32(val)) = (inner.param_name, inner.value)
{
self.a = val
}
}
fn ports(&self) -> &Ports {
&self.ports
}
}
fn get_a_from_cutoff(sr: f32, fc: f32) -> f32 {
let x = TAU * fc / sr;
let pole = x / (x + 1.0);
1.0 - pole
}