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