use rill_core::{
buffer::DelayLine,
math::Transcendental,
traits::{SignalNode, NodeCategory, NodeMetadata, NodeState, Processor},
ClockTick, NodeId, ParamValue, ParameterId, Port, ProcessError, ProcessResult,
};
const MAX_DELAY_SECONDS: f32 = 0.5;
const MAX_SAMPLE_RATE: f32 = 48_000.0;
const MAX_DELAY_SAMPLES: usize = (MAX_DELAY_SECONDS * MAX_SAMPLE_RATE) as usize;
pub struct Delay<T: Transcendental, const BUF_SIZE: usize> {
id: NodeId,
metadata: NodeMetadata,
inputs: Vec<Port<T, BUF_SIZE>>,
outputs: Vec<Port<T, BUF_SIZE>>,
controls: Vec<Port<T, BUF_SIZE>>,
state: NodeState<T, BUF_SIZE>,
pub delay_time: f32,
delay_samples: usize,
pub feedback: f32,
pub mix: f32,
delay_line: DelayLine<T, MAX_DELAY_SAMPLES>,
sample_rate: f32,
}
impl<T: Transcendental, const BUF_SIZE: usize> Delay<T, BUF_SIZE> {
pub fn new(sample_rate: f32) -> Self {
let metadata = NodeMetadata::new("Delay", NodeCategory::Processor);
let mut inputs = Vec::new();
let mut outputs = Vec::new();
inputs.push(Port::input(NodeId(0), 0, "signal_in"));
outputs.push(Port::output(NodeId(0), 0, "signal_out"));
let delay_time = 0.5;
let delay_samples = (delay_time * sample_rate) as usize;
let mut delay_line = DelayLine::new(sample_rate);
delay_line.set_delay_samples(delay_samples);
Self {
id: NodeId(0),
metadata,
inputs,
outputs,
controls: Vec::new(),
state: NodeState::new(sample_rate),
delay_time,
delay_samples,
feedback: 0.3,
mix: 0.5,
delay_line,
sample_rate,
}
}
pub fn with_params(sample_rate: f32, delay_time: f32, feedback: f32, mix: f32) -> Self {
let mut instance = Self::new(sample_rate);
instance.set_delay_time(delay_time);
instance.set_feedback(feedback);
instance.set_mix(mix);
instance
}
pub fn set_delay_time(&mut self, time: f32) {
self.delay_time = time.clamp(0.01, MAX_DELAY_SECONDS);
self.update_delay_samples();
}
pub fn set_feedback(&mut self, fb: f32) {
self.feedback = fb.clamp(0.0, 0.99);
}
pub fn set_mix(&mut self, mix: f32) {
self.mix = mix.clamp(0.0, 1.0);
}
fn update_delay_samples(&mut self) {
self.delay_samples = (self.delay_time * self.sample_rate) as usize;
if self.delay_samples >= MAX_DELAY_SAMPLES {
self.delay_samples = MAX_DELAY_SAMPLES - 1;
}
self.delay_line.set_delay_samples(self.delay_samples);
}
pub fn process_sample(&mut self, input: T) -> T {
let delayed = self.delay_line.read_delayed(self.delay_samples);
let dry = input;
let wet = delayed;
let mix = T::from_f32(self.mix);
let one_minus_mix = T::ONE - mix;
let output = dry.mul(one_minus_mix).add(wet.mul(mix));
let feedback = T::from_f32(self.feedback);
let write_sample = input.add(delayed.mul(feedback));
self.delay_line.write(write_sample);
output
}
}
impl<T: Transcendental, const BUF_SIZE: usize> SignalNode<T, BUF_SIZE> for Delay<T, BUF_SIZE> {
fn node_type_id(&self) -> rill_core::NodeTypeId
where
Self: 'static + Sized,
{
rill_core::NodeTypeId::of::<Self>()
}
fn id(&self) -> NodeId {
self.id
}
fn set_id(&mut self, id: NodeId) {
self.id = id;
}
fn metadata(&self) -> NodeMetadata {
self.metadata.clone()
}
fn init(&mut self, sample_rate: f32) {
self.sample_rate = sample_rate;
self.update_delay_samples();
self.delay_line.clear();
}
fn reset(&mut self) {
self.state.sample_pos = 0;
self.state.blocks_processed = 0;
self.delay_line.clear();
}
fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
let name = id.as_str();
match name {
"delay_time" => Some(ParamValue::Float(self.delay_time)),
"feedback" => Some(ParamValue::Float(self.feedback)),
"mix" => Some(ParamValue::Float(self.mix)),
_ => None,
}
}
fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
let name = id.as_str();
if let Some(v) = value.as_f32() {
match name {
"delay_time" => {
self.set_delay_time(v);
Ok(())
}
"feedback" => {
self.set_feedback(v);
Ok(())
}
"mix" => {
self.set_mix(v);
Ok(())
}
_ => Err(ProcessError::parameter(format!(
"Unknown parameter: {}",
name
))),
}
} else {
Err(ProcessError::parameter("Expected float value"))
}
}
fn input_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
self.inputs.get(index)
}
fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
self.inputs.get_mut(index)
}
fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
self.outputs.get(index)
}
fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
self.outputs.get_mut(index)
}
fn control_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
self.controls.get(index)
}
fn control_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
self.controls.get_mut(index)
}
fn num_inputs(&self) -> usize {
self.inputs.len()
}
fn num_outputs(&self) -> usize {
self.outputs.len()
}
fn state(&self) -> &NodeState<T, BUF_SIZE> {
&self.state
}
fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
&mut self.state
}
}
impl<T: Transcendental, const BUF_SIZE: usize> Processor<T, BUF_SIZE> for Delay<T, BUF_SIZE> {
fn process(
&mut self,
_clock: &ClockTick,
_signal_inputs: &[&[T; BUF_SIZE]],
_control_inputs: &[T],
_clock_inputs: &[ClockTick],
_feedback_inputs: &[&[T; BUF_SIZE]],
) -> ProcessResult<()> {
let input_buf = *self.inputs[0].buffer.as_array();
let mut temp = [T::ZERO; BUF_SIZE];
for i in 0..BUF_SIZE {
temp[i] = self.process_sample(input_buf[i]);
}
*self.outputs[0].buffer.as_mut_array() = temp;
Ok(())
}
fn latency(&self) -> usize {
0
}
}