Skip to main content

rill_router/eq/
node.rs

1//! Processor nodes for integration with rill-core signal graphs.
2
3use rill_core::{
4    Node, NodeCategory, NodeId, NodeMetadata, NodeState, ParamValue, ParameterId, Port,
5    ProcessError, ProcessResult, Processor, Transcendental,
6};
7use rill_core_dsp::filters::{Biquad, FilterParams, FilterType};
8
9use super::{BandType, FilterFactory, GraphicEq, ParametricEq};
10
11/// Default factory that creates `Biquad<f32>` filters.
12#[derive(Debug, Clone, Default)]
13pub struct BiquadFactory;
14
15impl FilterFactory<Biquad<f32>> for BiquadFactory {
16    fn create_filter(
17        &self,
18        filter_type: FilterType,
19        frequency: f32,
20        q: f32,
21        gain_db: f32,
22    ) -> Biquad<f32> {
23        let params = FilterParams {
24            filter_type,
25            cutoff: frequency,
26            q,
27            gain_db,
28        };
29        Biquad::new(params)
30    }
31}
32
33/// Parametric equalizer processor node for signal graphs.
34pub struct ParametricEqProcessor<T: Transcendental, const BUF_SIZE: usize> {
35    /// Node identifier
36    id: NodeId,
37    /// Node metadata
38    metadata: NodeMetadata,
39    /// Input ports
40    inputs: Vec<Port<T, BUF_SIZE>>,
41    /// Output ports
42    outputs: Vec<Port<T, BUF_SIZE>>,
43    /// Control ports
44    controls: Vec<Port<T, BUF_SIZE>>,
45    /// Node state
46    state: NodeState<T, BUF_SIZE>,
47    /// Inner parametric equalizer (works with f32)
48    eq: ParametricEq<Biquad<f32>, BiquadFactory>,
49    /// Output gain (linear)
50    pub output_gain: f32,
51    /// Number of bands
52    num_bands: usize,
53}
54
55impl<T: Transcendental, const BUF_SIZE: usize> ParametricEqProcessor<T, BUF_SIZE> {
56    /// Creates a new parametric equalizer processor with default parameters.
57    pub fn new(sample_rate: f32, num_bands: usize) -> Self {
58        let metadata = NodeMetadata::new("ParametricEqProcessor", NodeCategory::Processor);
59
60        let mut inputs = Vec::new();
61        let mut outputs = Vec::new();
62
63        // Create one audio input and one audio output
64        inputs.push(Port::input(NodeId(0), 0, "signal_in"));
65        outputs.push(Port::output(NodeId(0), 0, "signal_out"));
66
67        let factory = BiquadFactory;
68        let mut eq = ParametricEq::new(factory, num_bands, sample_rate);
69        eq.init(sample_rate);
70
71        Self {
72            id: NodeId(0),
73            metadata,
74            inputs,
75            outputs,
76            controls: Vec::new(),
77            state: NodeState::new(sample_rate),
78            eq,
79            output_gain: 1.0,
80            num_bands,
81        }
82    }
83
84    /// Set parameters for a specific band.
85    pub fn set_band(
86        &mut self,
87        index: usize,
88        frequency: f32,
89        q: f32,
90        gain_db: f32,
91    ) -> Result<(), rill_core::Error> {
92        self.eq.set_band(index, frequency, q, gain_db)?;
93        Ok(())
94    }
95
96    /// Set band type.
97    pub fn set_band_type(
98        &mut self,
99        index: usize,
100        band_type: BandType,
101    ) -> Result<(), rill_core::Error> {
102        self.eq.set_band_type(index, band_type)?;
103        Ok(())
104    }
105
106    /// Enable/disable band.
107    pub fn set_band_enabled(
108        &mut self,
109        index: usize,
110        enabled: bool,
111    ) -> Result<(), rill_core::Error> {
112        self.eq.set_band_enabled(index, enabled)?;
113        Ok(())
114    }
115
116    /// Set output gain (linear).
117    pub fn set_output_gain(&mut self, gain: f32) {
118        self.output_gain = gain.clamp(0.0, 4.0);
119        self.eq.set_output_gain(self.output_gain);
120    }
121
122    /// Get number of bands.
123    pub fn num_bands(&self) -> usize {
124        self.num_bands
125    }
126
127    /// Get reference to inner equalizer.
128    pub fn eq(&self) -> &ParametricEq<Biquad<f32>, BiquadFactory> {
129        &self.eq
130    }
131
132    /// Get mutable reference to inner equalizer.
133    pub fn eq_mut(&mut self) -> &mut ParametricEq<Biquad<f32>, BiquadFactory> {
134        &mut self.eq
135    }
136}
137
138impl<T: Transcendental, const BUF_SIZE: usize> Node<T, BUF_SIZE>
139    for ParametricEqProcessor<T, BUF_SIZE>
140{
141    fn node_type_id(&self) -> rill_core::NodeTypeId
142    where
143        Self: 'static + Sized,
144    {
145        rill_core::NodeTypeId::of::<Self>()
146    }
147
148    fn id(&self) -> NodeId {
149        self.id
150    }
151
152    fn set_id(&mut self, id: NodeId) {
153        self.id = id;
154        // Update port IDs? Ports store node ID, but they are created with NodeId(0).
155        // For simplicity, we ignore for now.
156    }
157
158    fn metadata(&self) -> NodeMetadata {
159        self.metadata.clone()
160    }
161
162    fn init(&mut self, sample_rate: f32) {
163        self.state.sample_rate = sample_rate;
164        self.eq.init(sample_rate);
165    }
166
167    fn reset(&mut self) {
168        self.state.sample_pos = 0;
169        self.state.blocks_processed = 0;
170        self.eq.reset();
171    }
172
173    fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
174        let name = id.as_str();
175        if name == "output_gain" {
176            return Some(ParamValue::Float(self.output_gain));
177        }
178
179        // Parse band parameter: band_<index>_<field>
180        let parts: Vec<&str> = name.split('_').collect();
181        if parts.len() >= 3 && parts[0] == "band" {
182            if let Ok(index) = parts[1].parse::<usize>() {
183                if index < self.num_bands {
184                    let field = parts[2];
185                    match field {
186                        "freq" => {
187                            return self.eq.get_band_frequency(index).map(ParamValue::Float);
188                        }
189                        "q" => {
190                            return self.eq.get_band_q(index).map(ParamValue::Float);
191                        }
192                        "gain" => {
193                            return self.eq.get_band_gain(index).map(ParamValue::Float);
194                        }
195                        "enabled" => {
196                            return self.eq.get_band_enabled(index).map(ParamValue::Bool);
197                        }
198                        _ => {}
199                    }
200                }
201            }
202        }
203
204        None
205    }
206
207    fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
208        let name = id.as_str();
209        if name == "output_gain" {
210            if let Some(v) = value.as_f32() {
211                self.set_output_gain(v);
212                Ok(())
213            } else {
214                Err(ProcessError::parameter("Expected float value"))
215            }
216        } else {
217            // Parse band parameter
218            let parts: Vec<&str> = name.split('_').collect();
219            if parts.len() >= 3 && parts[0] == "band" {
220                if let Ok(index) = parts[1].parse::<usize>() {
221                    if index >= self.num_bands {
222                        return Err(ProcessError::parameter(format!(
223                            "Band index {} out of range",
224                            index
225                        )));
226                    }
227                    let field = parts[2];
228                    match field {
229                        "freq" => {
230                            if let Some(v) = value.as_f32() {
231                                self.eq
232                                    .set_band(
233                                        index,
234                                        v,
235                                        self.eq.get_band_q(index).unwrap_or(1.0),
236                                        self.eq.get_band_gain(index).unwrap_or(0.0),
237                                    )
238                                    .map_err(|e| ProcessError::parameter(e.to_string()))?;
239                                Ok(())
240                            } else {
241                                Err(ProcessError::parameter("Expected float value"))
242                            }
243                        }
244                        "q" => {
245                            if let Some(v) = value.as_f32() {
246                                self.eq
247                                    .set_band(
248                                        index,
249                                        self.eq.get_band_frequency(index).unwrap_or(1000.0),
250                                        v,
251                                        self.eq.get_band_gain(index).unwrap_or(0.0),
252                                    )
253                                    .map_err(|e| ProcessError::parameter(e.to_string()))?;
254                                Ok(())
255                            } else {
256                                Err(ProcessError::parameter("Expected float value"))
257                            }
258                        }
259                        "gain" => {
260                            if let Some(v) = value.as_f32() {
261                                self.eq
262                                    .set_band(
263                                        index,
264                                        self.eq.get_band_frequency(index).unwrap_or(1000.0),
265                                        self.eq.get_band_q(index).unwrap_or(1.0),
266                                        v,
267                                    )
268                                    .map_err(|e| ProcessError::parameter(e.to_string()))?;
269                                Ok(())
270                            } else {
271                                Err(ProcessError::parameter("Expected float value"))
272                            }
273                        }
274                        "enabled" => {
275                            if let Some(b) = value.as_bool() {
276                                self.eq
277                                    .set_band_enabled(index, b)
278                                    .map_err(|e| ProcessError::parameter(e.to_string()))?;
279                                Ok(())
280                            } else {
281                                Err(ProcessError::parameter("Expected boolean value"))
282                            }
283                        }
284                        _ => Err(ProcessError::parameter(format!(
285                            "Unknown band field: {}",
286                            field
287                        ))),
288                    }
289                } else {
290                    Err(ProcessError::parameter(format!(
291                        "Invalid band index: {}",
292                        parts[1]
293                    )))
294                }
295            } else {
296                Err(ProcessError::parameter(format!(
297                    "Unknown parameter: {}",
298                    name
299                )))
300            }
301        }
302    }
303
304    fn input_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
305        self.inputs.get(index)
306    }
307
308    fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
309        self.inputs.get_mut(index)
310    }
311
312    fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
313        self.outputs.get(index)
314    }
315
316    fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
317        self.outputs.get_mut(index)
318    }
319
320    fn control_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
321        self.controls.get(index)
322    }
323
324    fn control_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
325        self.controls.get_mut(index)
326    }
327
328    fn num_inputs(&self) -> usize {
329        self.inputs.len()
330    }
331
332    fn num_outputs(&self) -> usize {
333        self.outputs.len()
334    }
335
336    fn num_signal_inputs(&self) -> usize {
337        self.inputs.len()
338    }
339
340    fn num_signal_outputs(&self) -> usize {
341        self.outputs.len()
342    }
343
344    fn state(&self) -> &NodeState<T, BUF_SIZE> {
345        &self.state
346    }
347
348    fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
349        &mut self.state
350    }
351}
352
353impl<T: Transcendental, const BUF_SIZE: usize> Processor<T, BUF_SIZE>
354    for ParametricEqProcessor<T, BUF_SIZE>
355{
356    fn process(
357        &mut self,
358        _ctx: &rill_core::RenderContext,
359        _signal_inputs: &[&[T; BUF_SIZE]],
360        _control_inputs: &[T],
361        _clock_inputs: &[rill_core::RenderContext],
362        _feedback_inputs: &[&[T; BUF_SIZE]],
363    ) -> ProcessResult<()> {
364        let input_buf = *self.inputs[0].read();
365        let output_buf = self.outputs[0].write();
366
367        let mut input_f32 = [0.0f32; BUF_SIZE];
368        for (dest, &src) in input_f32.iter_mut().zip(input_buf.iter()) {
369            *dest = src.to_f32();
370        }
371
372        let mut output_f32 = [0.0f32; BUF_SIZE];
373        self.eq.process_block(&input_f32, &mut output_f32);
374
375        for (dest, &src) in output_buf.iter_mut().zip(output_f32.iter()) {
376            *dest = T::from_f32(src);
377        }
378
379        Ok(())
380    }
381
382    fn latency(&self) -> usize {
383        0
384    }
385}
386
387/// Graphic equalizer processor node for signal graphs.
388pub struct GraphicEqProcessor<T: Transcendental, const BUF_SIZE: usize> {
389    /// Node identifier
390    id: NodeId,
391    /// Node metadata
392    metadata: NodeMetadata,
393    /// Input ports
394    inputs: Vec<Port<T, BUF_SIZE>>,
395    /// Output ports
396    outputs: Vec<Port<T, BUF_SIZE>>,
397    /// Control ports
398    controls: Vec<Port<T, BUF_SIZE>>,
399    /// Node state
400    state: NodeState<T, BUF_SIZE>,
401    /// Inner graphic equalizer (works with f32)
402    eq: GraphicEq<Biquad<f32>>,
403    /// Output gain (linear)
404    pub output_gain: f32,
405    /// Number of bands
406    num_bands: usize,
407}
408
409impl<T: Transcendental, const BUF_SIZE: usize> GraphicEqProcessor<T, BUF_SIZE> {
410    /// Creates a new graphic equalizer processor with ISO 1/3 octave bands.
411    pub fn new_third_octave(sample_rate: f32) -> Self {
412        let metadata = NodeMetadata::new("GraphicEqProcessor", NodeCategory::Processor);
413
414        let mut inputs = Vec::new();
415        let mut outputs = Vec::new();
416
417        inputs.push(Port::input(NodeId(0), 0, "signal_in"));
418        outputs.push(Port::output(NodeId(0), 0, "signal_out"));
419
420        let factory = BiquadFactory;
421        let mut eq = GraphicEq::new_third_octave(factory, sample_rate);
422        eq.init(sample_rate);
423
424        let num_bands = eq.num_bands();
425
426        Self {
427            id: NodeId(0),
428            metadata,
429            inputs,
430            outputs,
431            controls: Vec::new(),
432            state: NodeState::new(sample_rate),
433            eq,
434            output_gain: 1.0,
435            num_bands,
436        }
437    }
438
439    /// Creates a new graphic equalizer processor with custom frequencies.
440    pub fn with_frequencies(frequencies: Vec<f32>, sample_rate: f32) -> Self {
441        let metadata = NodeMetadata::new("GraphicEqProcessor", NodeCategory::Processor);
442
443        let mut inputs = Vec::new();
444        let mut outputs = Vec::new();
445
446        inputs.push(Port::input(NodeId(0), 0, "signal_in"));
447        outputs.push(Port::output(NodeId(0), 0, "signal_out"));
448
449        let factory = BiquadFactory;
450        let mut eq = GraphicEq::with_frequencies(factory, frequencies, sample_rate);
451        eq.init(sample_rate);
452
453        let num_bands = eq.num_bands();
454
455        Self {
456            id: NodeId(0),
457            metadata,
458            inputs,
459            outputs,
460            controls: Vec::new(),
461            state: NodeState::new(sample_rate),
462            eq,
463            output_gain: 1.0,
464            num_bands,
465        }
466    }
467
468    /// Set gain for a specific band (in dB).
469    pub fn set_band_gain(&mut self, index: usize, gain_db: f32) -> Result<(), rill_core::Error> {
470        self.eq.set_band_gain(index, gain_db)?;
471        Ok(())
472    }
473
474    /// Enable/disable band.
475    pub fn set_band_enabled(
476        &mut self,
477        index: usize,
478        enabled: bool,
479    ) -> Result<(), rill_core::Error> {
480        self.eq.set_band_enabled(index, enabled)?;
481        Ok(())
482    }
483
484    /// Set output gain (linear).
485    pub fn set_output_gain(&mut self, gain: f32) {
486        self.output_gain = gain.clamp(0.0, 4.0);
487        self.eq.set_output_gain(self.output_gain);
488    }
489
490    /// Get number of bands.
491    pub fn num_bands(&self) -> usize {
492        self.num_bands
493    }
494
495    /// Get reference to inner equalizer.
496    pub fn eq(&self) -> &GraphicEq<Biquad<f32>> {
497        &self.eq
498    }
499
500    /// Get mutable reference to inner equalizer.
501    pub fn eq_mut(&mut self) -> &mut GraphicEq<Biquad<f32>> {
502        &mut self.eq
503    }
504}
505
506impl<T: Transcendental, const BUF_SIZE: usize> Node<T, BUF_SIZE>
507    for GraphicEqProcessor<T, BUF_SIZE>
508{
509    fn node_type_id(&self) -> rill_core::NodeTypeId
510    where
511        Self: 'static + Sized,
512    {
513        rill_core::NodeTypeId::of::<Self>()
514    }
515
516    fn id(&self) -> NodeId {
517        self.id
518    }
519
520    fn set_id(&mut self, id: NodeId) {
521        self.id = id;
522    }
523
524    fn metadata(&self) -> NodeMetadata {
525        self.metadata.clone()
526    }
527
528    fn init(&mut self, sample_rate: f32) {
529        self.state.sample_rate = sample_rate;
530        self.eq.init(sample_rate);
531    }
532
533    fn reset(&mut self) {
534        self.state.sample_pos = 0;
535        self.state.blocks_processed = 0;
536        self.eq.reset();
537    }
538
539    fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
540        let name = id.as_str();
541        if name == "output_gain" {
542            return Some(ParamValue::Float(self.output_gain));
543        }
544
545        // Parse band parameter: band_<index>_<field>
546        let parts: Vec<&str> = name.split('_').collect();
547        if parts.len() >= 3 && parts[0] == "band" {
548            if let Ok(index) = parts[1].parse::<usize>() {
549                if index < self.num_bands {
550                    let field = parts[2];
551                    match field {
552                        "gain" => {
553                            return self.eq.get_band_gain(index).map(ParamValue::Float);
554                        }
555                        "enabled" => {
556                            return self.eq.get_band_enabled(index).map(ParamValue::Bool);
557                        }
558                        _ => {}
559                    }
560                }
561            }
562        }
563
564        None
565    }
566
567    fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
568        let name = id.as_str();
569        if name == "output_gain" {
570            if let Some(v) = value.as_f32() {
571                self.set_output_gain(v);
572                Ok(())
573            } else {
574                Err(ProcessError::parameter("Expected float value"))
575            }
576        } else {
577            // Parse band parameter
578            let parts: Vec<&str> = name.split('_').collect();
579            if parts.len() >= 3 && parts[0] == "band" {
580                if let Ok(index) = parts[1].parse::<usize>() {
581                    if index >= self.num_bands {
582                        return Err(ProcessError::parameter(format!(
583                            "Band index {} out of range",
584                            index
585                        )));
586                    }
587                    let field = parts[2];
588                    match field {
589                        "gain" => {
590                            if let Some(v) = value.as_f32() {
591                                self.eq
592                                    .set_band_gain(index, v)
593                                    .map_err(|e| ProcessError::parameter(e.to_string()))?;
594                                Ok(())
595                            } else {
596                                Err(ProcessError::parameter("Expected float value"))
597                            }
598                        }
599                        "enabled" => {
600                            if let Some(b) = value.as_bool() {
601                                self.eq
602                                    .set_band_enabled(index, b)
603                                    .map_err(|e| ProcessError::parameter(e.to_string()))?;
604                                Ok(())
605                            } else {
606                                Err(ProcessError::parameter("Expected boolean value"))
607                            }
608                        }
609                        _ => Err(ProcessError::parameter(format!(
610                            "Unknown band field: {}",
611                            field
612                        ))),
613                    }
614                } else {
615                    Err(ProcessError::parameter(format!(
616                        "Invalid band index: {}",
617                        parts[1]
618                    )))
619                }
620            } else {
621                Err(ProcessError::parameter(format!(
622                    "Unknown parameter: {}",
623                    name
624                )))
625            }
626        }
627    }
628
629    fn input_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
630        self.inputs.get(index)
631    }
632
633    fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
634        self.inputs.get_mut(index)
635    }
636
637    fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
638        self.outputs.get(index)
639    }
640
641    fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
642        self.outputs.get_mut(index)
643    }
644
645    fn control_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
646        self.controls.get(index)
647    }
648
649    fn control_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
650        self.controls.get_mut(index)
651    }
652
653    fn num_inputs(&self) -> usize {
654        self.inputs.len()
655    }
656
657    fn num_outputs(&self) -> usize {
658        self.outputs.len()
659    }
660
661    fn num_signal_inputs(&self) -> usize {
662        self.inputs.len()
663    }
664
665    fn num_signal_outputs(&self) -> usize {
666        self.outputs.len()
667    }
668
669    fn state(&self) -> &NodeState<T, BUF_SIZE> {
670        &self.state
671    }
672
673    fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
674        &mut self.state
675    }
676}
677
678impl<T: Transcendental, const BUF_SIZE: usize> Processor<T, BUF_SIZE>
679    for GraphicEqProcessor<T, BUF_SIZE>
680{
681    fn process(
682        &mut self,
683        _ctx: &rill_core::RenderContext,
684        _signal_inputs: &[&[T; BUF_SIZE]],
685        _control_inputs: &[T],
686        _clock_inputs: &[rill_core::RenderContext],
687        _feedback_inputs: &[&[T; BUF_SIZE]],
688    ) -> ProcessResult<()> {
689        let input_buf = *self.inputs[0].read();
690        let output_buf = self.outputs[0].write();
691
692        let mut input_f32 = [0.0f32; BUF_SIZE];
693        for (dest, &src) in input_f32.iter_mut().zip(input_buf.iter()) {
694            *dest = src.to_f32();
695        }
696
697        let mut output_f32 = [0.0f32; BUF_SIZE];
698        self.eq.process_block(&input_f32, &mut output_f32);
699
700        for (dest, &src) in output_buf.iter_mut().zip(output_f32.iter()) {
701            *dest = T::from_f32(src);
702        }
703
704        Ok(())
705    }
706
707    fn latency(&self) -> usize {
708        0
709    }
710}