Skip to main content

rill_core/traits/
node.rs

1//! Core node traits for the Rill ecosystem
2//!
3//! Defines the fundamental building blocks of the signal graph:
4//! - `SignalNode`: Base trait for all nodes
5//! - `Source`: Active generator (has no inputs)
6//! - `Processor`: Passive processor (has inputs and outputs)
7//! - `Sink`: Active consumer (has no outputs)
8
9use crate::queues::signal::SetParameter;
10use crate::time::ClockTick;
11use crate::traits::param::{ParamMetadata, ParamValue, ParameterId};
12use crate::traits::ProcessResult;
13use std::any::TypeId;
14use std::fmt;
15
16// ============================================================================
17// Node Identification
18// ============================================================================
19
20/// Unique identifier for a node in the graph
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub struct NodeId(pub u32);
23
24impl NodeId {
25    /// Create a new node ID
26    pub const fn new(id: u32) -> Self {
27        Self(id)
28    }
29
30    /// Get the inner value
31    pub const fn inner(&self) -> u32 {
32        self.0
33    }
34}
35
36impl fmt::Display for NodeId {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        write!(f, "Node({})", self.0)
39    }
40}
41
42impl From<u32> for NodeId {
43    fn from(id: u32) -> Self {
44        Self(id)
45    }
46}
47
48// ============================================================================
49// Node Category
50// ============================================================================
51
52/// Category of a node (for UI/organization)
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54pub enum NodeCategory {
55    /// Source nodes (generators)
56    Source,
57
58    /// Processor nodes (effects, filters)
59    Processor,
60
61    /// Sink nodes (outputs)
62    Sink,
63
64    /// Utility nodes (routing, mixing)
65    Utility,
66
67    /// Analyzer nodes (meters, scopes)
68    Analyzer,
69
70    /// Sequencer nodes (pattern generators)
71    Sequencer,
72}
73
74impl NodeCategory {
75    /// Get the name of the category
76    pub const fn name(&self) -> &'static str {
77        match self {
78            Self::Source => "source",
79            Self::Processor => "processor",
80            Self::Sink => "sink",
81            Self::Utility => "utility",
82            Self::Analyzer => "analyzer",
83            Self::Sequencer => "sequencer",
84        }
85    }
86}
87
88impl fmt::Display for NodeCategory {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(f, "{}", self.name())
91    }
92}
93
94// ============================================================================
95// Node Type ID
96// ============================================================================
97
98/// Type identifier for a node (for downcasting)
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
100pub struct NodeTypeId(TypeId);
101
102impl NodeTypeId {
103    /// Create a new node type ID from a type
104    pub fn of<T: 'static + ?Sized>() -> Self {
105        Self(TypeId::of::<T>())
106    }
107
108    /// Get the inner TypeId
109    pub fn as_type_id(&self) -> TypeId {
110        self.0
111    }
112}
113
114// ============================================================================
115// Node Metadata
116// ============================================================================
117
118/// Metadata about a node
119#[derive(Debug, Clone)]
120pub struct NodeMetadata {
121    /// Name of the node
122    pub name: String,
123
124    /// Canonical type name used for serialization / factory lookup
125    /// (e.g. `Some("rill/sine_osc")`). When `None`, [`name`] is used instead.
126    pub type_name: Option<String>,
127
128    /// Category of the node
129    pub category: NodeCategory,
130
131    /// Description of what the node does
132    pub description: String,
133
134    /// Author of the node
135    pub author: String,
136
137    /// Version of the node
138    pub version: String,
139
140    /// Number of signal input ports
141    pub signal_inputs: usize,
142
143    /// Number of signal output ports
144    pub signal_outputs: usize,
145
146    /// Number of control input ports
147    pub control_inputs: usize,
148
149    /// Number of control output ports
150    pub control_outputs: usize,
151
152    /// Number of clock input ports
153    pub clock_inputs: usize,
154
155    /// Number of clock output ports
156    pub clock_outputs: usize,
157
158    /// Number of feedback ports
159    pub feedback_ports: usize,
160
161    /// Parameters exposed by the node
162    pub parameters: Vec<ParamMetadata>,
163}
164
165impl NodeMetadata {
166    /// Create new node metadata with minimal info
167    pub fn new(name: &str, category: NodeCategory) -> Self {
168        Self {
169            type_name: None,
170            name: name.to_string(),
171            category,
172            description: String::new(),
173            author: String::new(),
174            version: String::new(),
175            signal_inputs: 0,
176            signal_outputs: 0,
177            control_inputs: 0,
178            control_outputs: 0,
179            clock_inputs: 0,
180            clock_outputs: 0,
181            feedback_ports: 0,
182            parameters: Vec::new(),
183        }
184    }
185}
186
187// ============================================================================
188// Node State
189// ============================================================================
190
191/// State of a node during processing
192/// State of a node during processing
193#[derive(Debug, Clone)]
194pub struct NodeState<T: crate::math::Transcendental, const BUF_SIZE: usize> {
195    /// Current sample position
196    pub sample_pos: u64,
197
198    /// Number of processed blocks
199    pub blocks_processed: u64,
200
201    /// Sample rate
202    pub sample_rate: f32,
203
204    /// Whether the node is active
205    pub active: bool,
206
207    /// Internal phase (for generators)
208    pub phase: T,
209}
210
211impl<T: crate::math::Transcendental, const BUF_SIZE: usize> NodeState<T, BUF_SIZE> {
212    /// Create new node state
213    pub fn new(sample_rate: f32) -> Self {
214        Self {
215            sample_pos: 0,
216            blocks_processed: 0,
217            sample_rate,
218            active: true,
219            phase: T::ZERO,
220        }
221    }
222
223    /// Advance state by one block
224    pub fn advance(&mut self) {
225        self.sample_pos += BUF_SIZE as u64;
226        self.blocks_processed += 1;
227    }
228
229    /// Get current time in seconds
230    pub fn current_time_seconds(&self) -> f64 {
231        self.sample_pos as f64 / self.sample_rate as f64
232    }
233
234    /// Reset state
235    pub fn reset(&mut self) {
236        self.sample_pos = 0;
237        self.blocks_processed = 0;
238        self.phase = T::ZERO;
239    }
240}
241
242// ============================================================================
243// SignalNode Trait (Base for all nodes)
244// ============================================================================
245
246/// Base trait for all audio nodes
247///
248/// This trait provides the fundamental operations that every node must implement:
249/// - Port counting
250/// - Parameter access
251/// - Initialization and reset
252///
253/// The actual processing is split into specialized traits:
254/// - `Source` for generators
255/// - `Processor` for processors with inputs/outputs
256/// - `Sink` for consumers
257pub trait SignalNode<T: crate::math::Transcendental, const BUF_SIZE: usize>: Send + Sync {
258    /// Get node metadata
259    fn metadata(&self) -> NodeMetadata;
260
261    /// Get the node's type ID
262    fn node_type_id(&self) -> NodeTypeId
263    where
264        Self: 'static + Sized,
265    {
266        NodeTypeId::of::<Self>()
267    }
268
269    /// Initialize the node with a sample rate
270    fn init(&mut self, sample_rate: f32);
271
272    /// Reset the node to its initial state
273    fn reset(&mut self);
274
275    /// Get the value of a parameter
276    fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue>;
277
278    /// Set the value of a parameter
279    fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()>;
280
281    /// Apply a `SetParameter` command to this node.
282    ///
283    /// Routes the command to the appropriate port based on `cmd.port`.
284    /// Falls back to `set_parameter()` when the port is not found.
285    fn apply_set_parameter(&mut self, cmd: &SetParameter) -> ProcessResult<()> {
286        use crate::traits::port::{PortDirection, PortType};
287        let value = T::from_f32(cmd.value);
288        let port = match cmd.port.port_type() {
289            PortType::Control => self.control_port_mut(cmd.port.index() as usize),
290            PortType::Signal => match cmd.port.direction() {
291                PortDirection::Input => self.input_port_mut(cmd.port.index() as usize),
292                PortDirection::Output => self.output_port_mut(cmd.port.index() as usize),
293            },
294            PortType::Param => self.input_port_mut(cmd.port.index() as usize),
295            PortType::Clock | PortType::Feedback => None,
296        };
297        match port {
298            Some(p) => {
299                p.set_value(value);
300                Ok(())
301            }
302            None => self.set_parameter(&cmd.parameter, ParamValue::Float(cmd.value)),
303        }
304    }
305
306    /// Get node ID
307    fn id(&self) -> NodeId;
308
309    /// Set node ID
310    fn set_id(&mut self, id: NodeId);
311
312    /// Get input port by index
313    fn input_port(&self, index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>>;
314
315    /// Get mutable input port by index
316    fn input_port_mut(
317        &mut self,
318        index: usize,
319    ) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>>;
320
321    /// Get output port by index
322    fn output_port(&self, index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>>;
323
324    /// Get mutable output port by index
325    fn output_port_mut(
326        &mut self,
327        index: usize,
328    ) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>>;
329
330    /// Get control port by index
331    fn control_port(&self, index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>>;
332
333    /// Get mutable control port by index
334    fn control_port_mut(
335        &mut self,
336        index: usize,
337    ) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>>;
338
339    /// Get node state
340    fn state(&self) -> &NodeState<T, BUF_SIZE>;
341
342    /// Get mutable node state
343    fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE>;
344
345    // ========================================================================
346    // Port Counting (with defaults)
347    // ========================================================================
348
349    /// Number of signal input ports
350    fn num_signal_inputs(&self) -> usize {
351        0
352    }
353
354    /// Number of signal output ports
355    fn num_signal_outputs(&self) -> usize {
356        0
357    }
358
359    /// Number of control input ports
360    fn num_control_inputs(&self) -> usize {
361        0
362    }
363
364    /// Number of control output ports
365    fn num_control_outputs(&self) -> usize {
366        0
367    }
368
369    /// Number of clock input ports
370    fn num_clock_inputs(&self) -> usize {
371        0
372    }
373
374    /// Number of clock output ports
375    fn num_clock_outputs(&self) -> usize {
376        0
377    }
378
379    /// Number of feedback ports
380    fn num_feedback_ports(&self) -> usize {
381        0
382    }
383
384    /// Total number of input ports
385    fn num_inputs(&self) -> usize {
386        self.num_signal_inputs()
387            + self.num_control_inputs()
388            + self.num_clock_inputs()
389            + self.num_feedback_ports()
390    }
391
392    /// Total number of output ports
393    fn num_outputs(&self) -> usize {
394        self.num_signal_outputs() + self.num_control_outputs() + self.num_clock_outputs()
395    }
396}
397
398// ============================================================================
399// Source Trait (Active generators)
400// ============================================================================
401
402/// Active source of signals
403///
404/// Sources generate audio from internal state. They have no audio inputs,
405/// but may have control and clock inputs for modulation.
406pub trait Source<T: crate::math::Transcendental, const BUF_SIZE: usize>: SignalNode<T, BUF_SIZE> {
407    /// Generate the next block of audio
408    ///
409    /// # Arguments
410    /// * `clock` - Current clock tick
411    /// * `control_inputs` - Control signal values (one per control input)
412    /// * `clock_inputs` - Clock signal values (one per clock input)
413    ///
414    /// The source writes output samples into its own output port buffers,
415    /// accessible via `self.output_port_mut(index)`.
416    fn generate(
417        &mut self,
418        clock: &ClockTick,
419        control_inputs: &[T],
420        clock_inputs: &[ClockTick],
421    ) -> ProcessResult<()>;
422
423    /// Number of audio outputs (default 1)
424    fn num_signal_outputs(&self) -> usize {
425        1
426    }
427
428    /// Number of control inputs (default 0)
429    fn num_control_inputs(&self) -> usize {
430        0
431    }
432
433    /// Number of clock inputs (default 0)
434    fn num_clock_inputs(&self) -> usize {
435        0
436    }
437}
438
439// ============================================================================
440// Processor Trait (Passive processors)
441// ============================================================================
442
443/// Passive processor of signals
444///
445/// Processors transform input signals into output signals.
446/// They have audio inputs and outputs, and may have control and clock ports.
447pub trait Processor<T: crate::math::Transcendental, const BUF_SIZE: usize>:
448    SignalNode<T, BUF_SIZE>
449{
450    /// Process a block of audio
451    ///
452    /// # Arguments
453    /// * `clock` - Current clock tick
454    /// * `signal_inputs` - Audio input buffers (one per audio input)
455    /// * `control_inputs` - Control signal values (one per control input)
456    /// * `clock_inputs` - Clock signal values (one per clock input)
457    /// * `feedback_inputs` - Feedback values from previous blocks (one per feedback port)
458    ///
459    /// The processor writes output samples into its own output port buffers,
460    /// accessible via `self.output_port_mut(index)`.
461    fn process(
462        &mut self,
463        clock: &ClockTick,
464        signal_inputs: &[&[T; BUF_SIZE]],
465        control_inputs: &[T],
466        clock_inputs: &[ClockTick],
467        feedback_inputs: &[&[T; BUF_SIZE]],
468    ) -> ProcessResult<()>;
469
470    /// Latency in samples (for delay compensation)
471    fn latency(&self) -> usize {
472        0
473    }
474}
475
476// ============================================================================
477// Sink Trait (Active consumers)
478// ============================================================================
479
480/// Active sink of signals
481///
482/// Sinks consume audio and send it to external destinations.
483/// They have no audio outputs, but may have control and clock ports.
484pub trait Sink<T: crate::math::Transcendental, const BUF_SIZE: usize>: SignalNode<T, BUF_SIZE> {
485    /// Consume a block of audio
486    ///
487    /// # Arguments
488    /// * `clock` - Current clock tick
489    /// * `signal_inputs` - Audio input buffers (one per audio input)
490    /// * `control_inputs` - Control signal values (one per control input)
491    /// * `clock_inputs` - Clock signal values (one per clock input)
492    /// * `feedback_inputs` - Feedback values from previous blocks
493    fn consume(
494        &mut self,
495        clock: &ClockTick,
496        signal_inputs: &[&[T; BUF_SIZE]],
497        control_inputs: &[T],
498        clock_inputs: &[ClockTick],
499        feedback_inputs: &[&[T; BUF_SIZE]],
500    ) -> ProcessResult<()>;
501}
502
503// ============================================================================
504// Tests
505// ============================================================================
506
507#[cfg(test)]
508mod tests {
509    use super::*;
510    use crate::math::Transcendental;
511
512    struct TestNode;
513
514    impl<T: Transcendental, const BUF_SIZE: usize> SignalNode<T, BUF_SIZE> for TestNode {
515        fn metadata(&self) -> NodeMetadata {
516            NodeMetadata {
517                name: "Test".to_string(),
518                type_name: None,
519                category: NodeCategory::Utility,
520                description: "Test node".to_string(),
521                author: "Rill".to_string(),
522                version: "1.0".to_string(),
523                signal_inputs: 0,
524                signal_outputs: 0,
525                control_inputs: 0,
526                control_outputs: 0,
527                clock_inputs: 0,
528                clock_outputs: 0,
529                feedback_ports: 0,
530                parameters: vec![],
531            }
532        }
533
534        fn init(&mut self, _sample_rate: f32) {}
535        fn reset(&mut self) {}
536        fn get_parameter(&self, _id: &ParameterId) -> Option<ParamValue> {
537            None
538        }
539        fn set_parameter(&mut self, _id: &ParameterId, _value: ParamValue) -> ProcessResult<()> {
540            Ok(())
541        }
542
543        fn id(&self) -> NodeId {
544            NodeId(0)
545        }
546        fn set_id(&mut self, _id: NodeId) {}
547
548        fn input_port(&self, _index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>> {
549            None
550        }
551        fn input_port_mut(
552            &mut self,
553            _index: usize,
554        ) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>> {
555            None
556        }
557        fn output_port(&self, _index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>> {
558            None
559        }
560        fn output_port_mut(
561            &mut self,
562            _index: usize,
563        ) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>> {
564            None
565        }
566        fn control_port(&self, _index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>> {
567            None
568        }
569        fn control_port_mut(
570            &mut self,
571            _index: usize,
572        ) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>> {
573            None
574        }
575
576        fn state(&self) -> &NodeState<T, BUF_SIZE> {
577            unimplemented!()
578        }
579
580        fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
581            unimplemented!()
582        }
583    }
584
585    #[test]
586    fn test_node_id() {
587        let id = NodeId::new(42);
588        assert_eq!(id.inner(), 42);
589        assert_eq!(format!("{}", id), "Node(42)");
590    }
591
592    #[test]
593    fn test_node_category() {
594        assert_eq!(NodeCategory::Source.name(), "source");
595        assert_eq!(NodeCategory::Processor.name(), "processor");
596        assert_eq!(NodeCategory::Sink.name(), "sink");
597        assert_eq!(NodeCategory::Utility.name(), "utility");
598    }
599
600    #[test]
601    fn test_node_metadata_new() {
602        let metadata = NodeMetadata::new("Test", NodeCategory::Source);
603        assert_eq!(metadata.name, "Test");
604        assert_eq!(metadata.category, NodeCategory::Source);
605    }
606
607    #[test]
608    fn test_node_state() {
609        let mut state = NodeState::<f32, 64>::new(44100.0);
610        assert_eq!(state.sample_pos, 0);
611        assert_eq!(state.sample_rate, 44100.0);
612
613        state.advance();
614        assert_eq!(state.sample_pos, 64);
615        assert_eq!(state.blocks_processed, 1);
616
617        state.reset();
618        assert_eq!(state.sample_pos, 0);
619        assert_eq!(state.blocks_processed, 0);
620    }
621}