rill-fft 0.6.0-M2

FFT and frequency-domain signal processing for Rill
Documentation
//! FFT language builtins — spectral gate, spectral delay, and convolver.
#![cfg(feature = "lang")]
use rill_core::math::Transcendental;
use rill_core::traits::algorithm::Algorithm;
use rill_core::traits::{ParamValue, ProcessResult};
use rill_lang::builtin::{BlockBuiltin, BuiltinKind, BuiltinSig, Registry};

fn pv_f32(v: &ParamValue) -> f32 {
    match v {
        ParamValue::Float(f) => *f,
        ParamValue::Int(i) => *i as f32,
        _ => 0.0,
    }
}

struct SpectralGateBuiltin<T: Transcendental> {
    inner: crate::effects::spectral_gate::SpectralGate<T, 64>,
}

impl<T: Transcendental> Algorithm<T> for SpectralGateBuiltin<T> {
    fn process(&mut self, input: Option<&[T]>, output: &mut [T]) -> ProcessResult<()> {
        Algorithm::process(&mut self.inner, input, output)
    }
    fn reset(&mut self) {
        Algorithm::reset(&mut self.inner);
    }
}

impl<T: Transcendental> BlockBuiltin<T> for SpectralGateBuiltin<T> {
    fn set_param(&mut self, index: usize, value: &ParamValue) {
        let v = T::from_f32(pv_f32(value));
        match index {
            0 => self.inner.set_threshold(v),
            1 => self.inner.set_ratio(pv_f32(value)),
            _ => {}
        }
    }
}

struct SpectralDelayBuiltin<T: Transcendental> {
    inner: crate::effects::spectral_delay::SpectralDelay<T, 64, 16>,
}

impl<T: Transcendental> Algorithm<T> for SpectralDelayBuiltin<T> {
    fn process(&mut self, input: Option<&[T]>, output: &mut [T]) -> ProcessResult<()> {
        Algorithm::process(&mut self.inner, input, output)
    }
    fn reset(&mut self) {
        Algorithm::reset(&mut self.inner);
    }
}

impl<T: Transcendental> BlockBuiltin<T> for SpectralDelayBuiltin<T> {
    fn set_param(&mut self, index: usize, value: &ParamValue) {
        let v = pv_f32(value);
        match index {
            0 => self.inner.set_mix(v),
            1 => self.inner.set_feedback(v),
            _ => {}
        }
    }
}

struct ConvolverBuiltin<T: Transcendental> {
    inner: crate::partitioned_conv::PartitionedConvolver<T, 64>,
    ir_gain: f32,
    mix: f32,
}

impl<T: Transcendental> Algorithm<T> for ConvolverBuiltin<T> {
    fn process(&mut self, input: Option<&[T]>, output: &mut [T]) -> ProcessResult<()> {
        match input {
            Some(inp) => {
                self.inner.process(inp, output);
                let gain = T::from_f32(self.ir_gain);
                let mix_gain = T::from_f32(self.mix);
                let dry_gain = T::ONE - mix_gain;
                for i in 0..output.len() {
                    output[i] = inp[i] * dry_gain + output[i] * gain * mix_gain;
                }
                Ok(())
            }
            None => {
                output.fill(T::ZERO);
                Ok(())
            }
        }
    }
    fn reset(&mut self) {}
}

impl<T: Transcendental> BlockBuiltin<T> for ConvolverBuiltin<T> {
    fn set_param(&mut self, index: usize, value: &ParamValue) {
        let v = value.as_f32().unwrap_or(0.0);
        match index {
            0 => {
                self.ir_gain = v.clamp(0.0, 4.0);
            }
            1 => {
                self.mix = v.clamp(0.0, 1.0);
            }
            _ => {}
        }
    }
}

/// Register FFT processing builtins in the rill-lang registry.
pub fn register_fft_builtins<T: Transcendental>(reg: &mut Registry<T>) {
    reg.register_block(
        BuiltinSig::simple("spectralgate", 1, 1, 2, BuiltinKind::Block)
            .with_names(vec!["threshold", "ratio"]),
        |p, _sr| {
            let mut gate = crate::effects::spectral_gate::SpectralGate::<T, 64>::new();
            gate.set_threshold(T::from_f64(p[0]));
            gate.set_ratio(p[1] as f32);
            Box::new(SpectralGateBuiltin { inner: gate })
        },
    );
    reg.register_block(
        BuiltinSig::simple("spectraldelay", 1, 1, 2, BuiltinKind::Block)
            .with_names(vec!["mix", "feedback"]),
        |p, _sr| {
            let mut delay = crate::effects::spectral_delay::SpectralDelay::<T, 64, 16>::new();
            delay.set_mix(p[0] as f32);
            delay.set_feedback(p[1] as f32);
            Box::new(SpectralDelayBuiltin { inner: delay })
        },
    );
    reg.register_block(
        BuiltinSig::simple("convolver", 1, 1, 2, BuiltinKind::Block)
            .with_names(vec!["ir_gain", "mix"]),
        |p, _sr| {
            let ir_gain = p[0] as f32;
            let mix = p[1] as f32;
            let conv = crate::partitioned_conv::PartitionedConvolver::<T, 64>::new(4096);
            Box::new(ConvolverBuiltin {
                inner: conv,
                ir_gain,
                mix,
            })
        },
    );
}