audio_engine_core/processor/traits.rs
1//! Audio Processor Traits
2//!
3//! Defines the unified interface for all DSP processors in the audio pipeline.
4//! This abstraction enables a composable DSP chain with guaranteed continuity.
5
6/// Processing result status
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ProcessResult {
9 /// Normal processing completed
10 Ok,
11 /// Processor is disabled, signal passed through unchanged
12 Bypassed,
13}
14
15/// Core audio processor trait
16///
17/// All DSP processors must implement this trait to be used in the DspChain.
18/// The trait provides a unified interface for:
19/// - Audio processing
20/// - State reset
21/// - Enable/disable control
22///
23/// # Thread Safety
24///
25/// Implementations must be `Send` because processors are owned by the audio thread.
26/// Parameters should be passed via the snapshot types in `lockfree_params`.
27///
28/// # Example
29///
30/// ```ignore
31/// use crate::processor::traits::{AudioProcessor, ProcessResult};
32///
33/// struct MyProcessor {
34/// enabled: bool,
35/// gain: f64,
36/// }
37///
38/// impl AudioProcessor for MyProcessor {
39/// fn name(&self) -> &'static str { "MyProcessor" }
40///
41/// fn process(&mut self, buffer: &mut [f64], channels: usize) -> ProcessResult {
42/// if !self.enabled {
43/// return ProcessResult::Bypassed;
44/// }
45/// for sample in buffer.iter_mut() {
46/// *sample *= self.gain;
47/// }
48/// ProcessResult::Ok
49/// }
50///
51/// fn reset(&mut self) {}
52/// fn is_enabled(&self) -> bool { self.enabled }
53/// fn set_enabled(&mut self, enabled: bool) { self.enabled = enabled; }
54/// }
55/// ```
56pub trait AudioProcessor: Send {
57 /// Processor name for debugging and logging
58 fn name(&self) -> &'static str;
59
60 /// Process audio samples in-place
61 ///
62 /// # Arguments
63 /// * `buffer` - Interleaved audio samples [L, R, L, R, ...]
64 /// * `channels` - Number of audio channels
65 ///
66 /// # Returns
67 /// Processing result status indicating what happened
68 fn process(&mut self, buffer: &mut [f64], channels: usize) -> ProcessResult;
69
70 /// Reset internal state (filter delay lines, etc.)
71 ///
72 /// Called when:
73 /// - Starting a new track
74 /// - Changing sample rate
75 /// - After gapless track switch
76 fn reset(&mut self);
77
78 /// Check if processor is enabled
79 fn is_enabled(&self) -> bool;
80
81 /// Enable or disable the processor
82 fn set_enabled(&mut self, enabled: bool);
83
84 /// Update sample rate and recalculate internal coefficients if needed.
85 ///
86 /// Default implementation is no-op for processors that are sample-rate agnostic.
87 fn set_sample_rate(&mut self, _sample_rate: f64) {}
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 struct TestProcessor {
95 enabled: bool,
96 gain: f64,
97 }
98
99 impl AudioProcessor for TestProcessor {
100 fn name(&self) -> &'static str {
101 "TestProcessor"
102 }
103
104 fn process(&mut self, buffer: &mut [f64], _channels: usize) -> ProcessResult {
105 if !self.enabled {
106 return ProcessResult::Bypassed;
107 }
108 for sample in buffer.iter_mut() {
109 *sample *= self.gain;
110 }
111 ProcessResult::Ok
112 }
113
114 fn reset(&mut self) {}
115
116 fn is_enabled(&self) -> bool {
117 self.enabled
118 }
119
120 fn set_enabled(&mut self, enabled: bool) {
121 self.enabled = enabled;
122 }
123 }
124
125 #[test]
126 fn test_processor_enabled() {
127 let mut proc = TestProcessor {
128 enabled: true,
129 gain: 0.5,
130 };
131 let mut buffer = vec![1.0, 1.0];
132 let result = proc.process(&mut buffer, 1);
133 assert_eq!(result, ProcessResult::Ok);
134 assert!((buffer[0] - 0.5).abs() < 1e-10);
135 }
136
137 #[test]
138 fn test_processor_bypassed() {
139 let mut proc = TestProcessor {
140 enabled: false,
141 gain: 0.5,
142 };
143 let mut buffer = vec![1.0, 1.0];
144 let result = proc.process(&mut buffer, 1);
145 assert_eq!(result, ProcessResult::Bypassed);
146 assert!((buffer[0] - 1.0).abs() < 1e-10);
147 }
148}