Skip to main content

rill_core/traits/
processable.rs

1//! Processable trait for unified audio node processing.
2//!
3//! This module defines the `Processable` trait that unifies `generate`, `process`, and `consume`
4//! into a single `process_block` method, making it easier to build generic audio graphs.
5
6use crate::math::Transcendental;
7use crate::time::ClockTick;
8use crate::traits::ProcessResult;
9
10// ============================================================================
11// ProcessContext
12// ============================================================================
13
14/// Convenience structure that gathers all input buffers for a node.
15///
16/// Nodes write their output directly into their own output port buffers
17/// (accessible via `AudioNode::output_port_mut`), so only input data
18/// is passed through this context.
19pub struct ProcessContext<'a, T: Transcendental, const BUF_SIZE: usize> {
20    /// Current clock tick
21    pub clock: &'a ClockTick,
22    /// Audio input buffers (slice of references to [T; BUF_SIZE])
23    pub audio_inputs: &'a [&'a [T; BUF_SIZE]],
24    /// Control input values (slice of T)
25    pub control_inputs: &'a [T],
26    /// Clock input ticks
27    pub clock_inputs: &'a [ClockTick],
28    /// Feedback input buffers (slice of references to [T; BUF_SIZE])
29    pub feedback_inputs: &'a [&'a [T; BUF_SIZE]],
30}
31
32// ============================================================================
33// Processable Trait
34// ============================================================================
35
36/// Unified trait for processing audio nodes.
37///
38/// This trait is implemented for all `Source`, `Processor`, and `Sink` types,
39/// providing a single method that dispatches to the appropriate underlying
40/// method (`generate`, `process`, or `consume`).
41pub trait Processable<T: Transcendental, const BUF_SIZE: usize> {
42    /// Process a single block of audio.
43    ///
44    /// The default implementation uses the node's category to call the
45    /// appropriate subtrait method. Implementors can override this if they
46    /// need custom behavior.
47    fn process_block(&mut self, ctx: &mut ProcessContext<T, BUF_SIZE>) -> ProcessResult<()>;
48}
49
50// ============================================================================
51// Blanket Implementations for Trait Objects
52// ============================================================================
53
54impl<T, const BUF_SIZE: usize> Processable<T, BUF_SIZE>
55    for Box<dyn crate::traits::Source<T, BUF_SIZE>>
56where
57    T: Transcendental,
58{
59    fn process_block(&mut self, ctx: &mut ProcessContext<T, BUF_SIZE>) -> ProcessResult<()> {
60        self.as_mut()
61            .generate(ctx.clock, ctx.control_inputs, ctx.clock_inputs)
62    }
63}
64
65impl<T, const BUF_SIZE: usize> Processable<T, BUF_SIZE>
66    for Box<dyn crate::traits::Processor<T, BUF_SIZE>>
67where
68    T: Transcendental,
69{
70    fn process_block(&mut self, ctx: &mut ProcessContext<T, BUF_SIZE>) -> ProcessResult<()> {
71        self.as_mut().process(
72            ctx.clock,
73            ctx.audio_inputs,
74            ctx.control_inputs,
75            ctx.clock_inputs,
76            ctx.feedback_inputs,
77        )
78    }
79}
80
81impl<T, const BUF_SIZE: usize> Processable<T, BUF_SIZE>
82    for Box<dyn crate::traits::Sink<T, BUF_SIZE>>
83where
84    T: Transcendental,
85{
86    fn process_block(&mut self, ctx: &mut ProcessContext<T, BUF_SIZE>) -> ProcessResult<()> {
87        self.as_mut().consume(
88            ctx.clock,
89            ctx.audio_inputs,
90            ctx.control_inputs,
91            ctx.clock_inputs,
92            ctx.feedback_inputs,
93        )
94    }
95}
96
97// ============================================================================
98// NodeVariant Enum
99// ============================================================================
100
101/// Enum that holds any kind of audio node.
102pub enum NodeVariant<T: Transcendental, const BUF_SIZE: usize> {
103    Source(Box<dyn crate::traits::Source<T, BUF_SIZE>>),
104    Processor(Box<dyn crate::traits::Processor<T, BUF_SIZE>>),
105    Sink(Box<dyn crate::traits::Sink<T, BUF_SIZE>>),
106}
107
108impl<T: Transcendental, const BUF_SIZE: usize> Processable<T, BUF_SIZE> for NodeVariant<T, BUF_SIZE> {
109    fn process_block(&mut self, ctx: &mut ProcessContext<T, BUF_SIZE>) -> ProcessResult<()> {
110        match self {
111            NodeVariant::Source(src) => src.process_block(ctx),
112            NodeVariant::Processor(proc) => proc.process_block(ctx),
113            NodeVariant::Sink(sink) => sink.process_block(ctx),
114        }
115    }
116}
117
118impl<T: Transcendental, const BUF_SIZE: usize> crate::traits::AudioNode<T, BUF_SIZE>
119    for NodeVariant<T, BUF_SIZE>
120{
121    fn metadata(&self) -> crate::traits::NodeMetadata {
122        match self {
123            NodeVariant::Source(src) => src.metadata(),
124            NodeVariant::Processor(proc) => proc.metadata(),
125            NodeVariant::Sink(sink) => sink.metadata(),
126        }
127    }
128
129    fn init(&mut self, sample_rate: f32) {
130        match self {
131            NodeVariant::Source(src) => src.init(sample_rate),
132            NodeVariant::Processor(proc) => proc.init(sample_rate),
133            NodeVariant::Sink(sink) => sink.init(sample_rate),
134        }
135    }
136
137    fn reset(&mut self) {
138        match self {
139            NodeVariant::Source(src) => src.reset(),
140            NodeVariant::Processor(proc) => proc.reset(),
141            NodeVariant::Sink(sink) => sink.reset(),
142        }
143    }
144
145    fn get_parameter(&self, id: &crate::traits::ParameterId) -> Option<crate::traits::ParamValue> {
146        match self {
147            NodeVariant::Source(src) => src.get_parameter(id),
148            NodeVariant::Processor(proc) => proc.get_parameter(id),
149            NodeVariant::Sink(sink) => sink.get_parameter(id),
150        }
151    }
152
153    fn set_parameter(
154        &mut self,
155        id: &crate::traits::ParameterId,
156        value: crate::traits::ParamValue,
157    ) -> ProcessResult<()> {
158        match self {
159            NodeVariant::Source(src) => src.set_parameter(id, value),
160            NodeVariant::Processor(proc) => proc.set_parameter(id, value),
161            NodeVariant::Sink(sink) => sink.set_parameter(id, value),
162        }
163    }
164
165    fn id(&self) -> crate::traits::NodeId {
166        match self {
167            NodeVariant::Source(src) => src.id(),
168            NodeVariant::Processor(proc) => proc.id(),
169            NodeVariant::Sink(sink) => sink.id(),
170        }
171    }
172
173    fn set_id(&mut self, id: crate::traits::NodeId) {
174        match self {
175            NodeVariant::Source(src) => src.set_id(id),
176            NodeVariant::Processor(proc) => proc.set_id(id),
177            NodeVariant::Sink(sink) => sink.set_id(id),
178        }
179    }
180
181    fn input_port(&self, index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>> {
182        match self {
183            NodeVariant::Source(src) => src.input_port(index),
184            NodeVariant::Processor(proc) => proc.input_port(index),
185            NodeVariant::Sink(sink) => sink.input_port(index),
186        }
187    }
188
189    fn input_port_mut(
190        &mut self,
191        index: usize,
192    ) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>> {
193        match self {
194            NodeVariant::Source(src) => src.input_port_mut(index),
195            NodeVariant::Processor(proc) => proc.input_port_mut(index),
196            NodeVariant::Sink(sink) => sink.input_port_mut(index),
197        }
198    }
199
200    fn output_port(&self, index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>> {
201        match self {
202            NodeVariant::Source(src) => src.output_port(index),
203            NodeVariant::Processor(proc) => proc.output_port(index),
204            NodeVariant::Sink(sink) => sink.output_port(index),
205        }
206    }
207
208    fn output_port_mut(
209        &mut self,
210        index: usize,
211    ) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>> {
212        match self {
213            NodeVariant::Source(src) => src.output_port_mut(index),
214            NodeVariant::Processor(proc) => proc.output_port_mut(index),
215            NodeVariant::Sink(sink) => sink.output_port_mut(index),
216        }
217    }
218
219    fn control_port(&self, index: usize) -> Option<&crate::traits::port::Port<T, BUF_SIZE>> {
220        match self {
221            NodeVariant::Source(src) => src.control_port(index),
222            NodeVariant::Processor(proc) => proc.control_port(index),
223            NodeVariant::Sink(sink) => sink.control_port(index),
224        }
225    }
226
227    fn control_port_mut(
228        &mut self,
229        index: usize,
230    ) -> Option<&mut crate::traits::port::Port<T, BUF_SIZE>> {
231        match self {
232            NodeVariant::Source(src) => src.control_port_mut(index),
233            NodeVariant::Processor(proc) => proc.control_port_mut(index),
234            NodeVariant::Sink(sink) => sink.control_port_mut(index),
235        }
236    }
237
238    fn state(&self) -> &crate::traits::NodeState<T, BUF_SIZE> {
239        match self {
240            NodeVariant::Source(src) => src.state(),
241            NodeVariant::Processor(proc) => proc.state(),
242            NodeVariant::Sink(sink) => sink.state(),
243        }
244    }
245
246    fn state_mut(&mut self) -> &mut crate::traits::NodeState<T, BUF_SIZE> {
247        match self {
248            NodeVariant::Source(src) => src.state_mut(),
249            NodeVariant::Processor(proc) => proc.state_mut(),
250            NodeVariant::Sink(sink) => sink.state_mut(),
251        }
252    }
253}