Skip to main content

rill_core/traits/
algorithm.rs

1//! Algorithm trait — the unified per-port and DSP processing primitive.
2//!
3//! All processing in the Rill graph is defined by `Algorithm` implementations.
4//! Every port's `run_action()` delegates to its `Algorithm::process()`.
5//! Low-level DSP primitives (filters, generators) also implement `Algorithm`.
6
7use crate::math::Transcendental;
8use crate::time::ClockTick;
9use crate::traits::ProcessResult;
10
11// ============================================================================
12// ActionContext
13// ============================================================================
14
15/// Context provided to an [`Algorithm`] during processing.
16pub struct ActionContext<'a> {
17    /// Current clock tick providing sample-accurate timing.
18    pub tick: &'a ClockTick,
19}
20
21impl<'a> ActionContext<'a> {
22    /// Create a new action context from a clock tick reference.
23    pub fn new(tick: &'a ClockTick) -> Self {
24        Self { tick }
25    }
26}
27
28// ============================================================================
29// Algorithm Metadata
30// ============================================================================
31
32/// Functional category of an algorithm (for introspection / UI).
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub enum AlgorithmCategory {
35    /// Signal generator (oscillator, noise, etc.).
36    Generator,
37    /// Signal filter (biquad, SVF, etc.).
38    Filter,
39    /// Signal effect (delay, distortion, etc.).
40    Effect,
41    /// Signal analyzer (meter, scope, etc.).
42    Analyzer,
43    /// Utility / helper (smoother, mapper, etc.).
44    Utility,
45}
46
47impl AlgorithmCategory {
48    /// Human-readable name
49    pub const fn name(&self) -> &'static str {
50        match self {
51            Self::Generator => "generator",
52            Self::Filter => "filter",
53            Self::Effect => "effect",
54            Self::Analyzer => "analyzer",
55            Self::Utility => "utility",
56        }
57    }
58}
59
60/// Descriptive metadata for an [`Algorithm`] implementation.
61#[derive(Debug, Clone)]
62pub struct AlgorithmMetadata {
63    /// Short name (e.g. "Biquad", "OnePole", "ParamSmoother").
64    pub name: &'static str,
65    /// Functional category.
66    pub category: AlgorithmCategory,
67    /// One-line description.
68    pub description: &'static str,
69    /// Author name.
70    pub author: &'static str,
71    /// Version string.
72    pub version: &'static str,
73}
74
75impl AlgorithmMetadata {
76    /// Minimal default metadata with `Utility` category and empty fields.
77    pub const fn empty() -> Self {
78        Self {
79            name: "",
80            category: AlgorithmCategory::Utility,
81            description: "",
82            author: "",
83            version: "",
84        }
85    }
86}
87
88// ============================================================================
89// Algorithm Trait
90// ============================================================================
91
92/// Unified processing primitive for ports and DSP blocks.
93///
94/// Every port in the graph owns an optional `Box<dyn Algorithm>`. When present,
95/// the port's `run_action()` calls `Algorithm::process()` to fill its buffer.
96///
97/// Low-level DSP components (filters, generators, effects) also implement this
98/// trait directly, making them usable both inside the graph and standalone.
99///
100/// # Required methods
101/// - `process()` — the main per-block processing entry point.
102/// - `reset()` — restore initial state.
103///
104/// # Optional methods
105/// - `init()` — configure sample rate.
106/// - `apply_command()` — receive a real-time parameter value from the control
107///   path (called between samples by the graph driver).
108/// - `metadata()` — return descriptive info (defaults to empty).
109pub trait Algorithm<T: Transcendental>: Send + Sync {
110    /// Process one block of signal.
111    ///
112    /// # Arguments
113    /// * `input`  — Signal data from upstream (empty when the port is
114    ///   unconnected, or `None` for source ports / control output ports).
115    /// * `output` — Buffer to fill with processed data.
116    /// * `ctx`    — Processing context (clock tick, block position, etc.).
117    fn process(
118        &mut self,
119        input: Option<&[T]>,
120        output: &mut [T],
121        ctx: &ActionContext,
122    ) -> ProcessResult<()>;
123
124    /// Receive a real-time command value from the control path.
125    ///
126    /// Called by the graph driver between `process()` calls when a
127    /// `SetParameter` targets this port. The algorithm should store the
128    /// value and apply it (possibly smoothed) on the next `process()`.
129    ///
130    /// Default: no-op.
131    fn apply_command(&mut self, _value: T) {}
132
133    /// Initialise the algorithm with a sample rate.
134    ///
135    /// Called once when the node is added to the graph. Available for
136    /// coefficient pre-computation.
137    ///
138    /// Default: no-op.
139    fn init(&mut self, _sample_rate: f32) {}
140
141    /// Reset the algorithm to its initial state.
142    ///
143    /// Called when the owning node is reset, or when feedback delay lines
144    /// need clearing.
145    fn reset(&mut self);
146
147    /// Descriptive metadata (defaults to empty).
148    fn metadata(&self) -> AlgorithmMetadata {
149        AlgorithmMetadata::empty()
150    }
151}