wavecraft_dsp/traits.rs
1//! Core DSP traits for user-implemented audio processors.
2//!
3//! This module defines the primary extension points for users building plugins
4//! with Wavecraft. The `Processor` trait is the main interface for custom DSP code.
5
6/// Transport information for timing-aware DSP.
7///
8/// Provides context about playback state, tempo, and position.
9#[derive(Debug, Clone, Copy, Default)]
10pub struct Transport {
11 /// Current tempo in BPM (beats per minute).
12 pub tempo: Option<f64>,
13
14 /// Current playback position in samples.
15 pub pos_samples: i64,
16
17 /// True if the host is playing.
18 pub playing: bool,
19}
20
21/// Trait for defining processor parameters.
22///
23/// This trait provides metadata about a processor's parameters,
24/// enabling automatic UI generation and nih-plug integration.
25///
26/// Typically implemented via `#[derive(ProcessorParams)]` rather than manually.
27pub trait ProcessorParams: Default + Send + Sync + 'static {
28 /// Returns the parameter specifications for this processor.
29 fn param_specs() -> &'static [ParamSpec];
30
31 /// Builds parameter values initialized from each [`ParamSpec::default`].
32 ///
33 /// By default this falls back to `Self::default()`. Implementations should
34 /// override this when struct field defaults differ from declared
35 /// `param_specs()` defaults (common with `#[derive(Default)]` on numeric
36 /// fields, which initializes them to zero).
37 fn from_param_defaults() -> Self {
38 Self::default()
39 }
40}
41
42/// Specification for a single parameter.
43#[derive(Debug, Clone)]
44pub struct ParamSpec {
45 /// Display name of the parameter (e.g., "Frequency").
46 pub name: &'static str,
47
48 /// ID suffix for this parameter (e.g., "frequency").
49 /// Full ID will be prefixed with processor name: "my_filter_frequency"
50 pub id_suffix: &'static str,
51
52 /// Value range for this parameter.
53 pub range: ParamRange,
54
55 /// Default value.
56 pub default: f64,
57
58 /// Unit string (e.g., "dB", "Hz", "%").
59 pub unit: &'static str,
60
61 /// Optional group name for UI organization (e.g., "Input", "Processing", "Output").
62 pub group: Option<&'static str>,
63}
64
65/// Parameter value range definition.
66#[derive(Debug, Clone)]
67pub enum ParamRange {
68 /// Linear range from min to max.
69 Linear { min: f64, max: f64 },
70
71 /// Skewed range with exponential/logarithmic scaling.
72 /// Factor > 1.0 = logarithmic, factor < 1.0 = exponential.
73 Skewed { min: f64, max: f64, factor: f64 },
74
75 /// Integer stepped range (for enums, switches).
76 Stepped { min: i32, max: i32 },
77
78 /// Enumerated parameter with named variants.
79 ///
80 /// Index 0 corresponds to the first variant, 1 to the second, etc.
81 Enum { variants: &'static [&'static str] },
82}
83
84/// Unit type has no parameters.
85impl ProcessorParams for () {
86 fn param_specs() -> &'static [ParamSpec] {
87 &[]
88 }
89}
90
91/// Trait for user-implemented DSP processors.
92///
93/// Implement this trait to define custom audio processing logic.
94/// All methods must be real-time safe (no allocations, locks, or syscalls).
95///
96/// # Example
97///
98/// ```rust,no_run
99/// use wavecraft_dsp::{ParamRange, ParamSpec, Processor, ProcessorParams, Transport};
100///
101/// #[derive(Default)]
102/// struct MyGainParams {
103/// level: f32,
104/// }
105///
106/// impl ProcessorParams for MyGainParams {
107/// fn param_specs() -> &'static [ParamSpec] {
108/// &[ParamSpec {
109/// name: "Level",
110/// id_suffix: "level",
111/// range: ParamRange::Linear { min: 0.0, max: 2.0 },
112/// default: 1.0,
113/// unit: "x",
114/// group: None,
115/// }]
116/// }
117/// }
118///
119/// struct MyGain {
120/// sample_rate: f32,
121/// }
122///
123/// impl Processor for MyGain {
124/// type Params = MyGainParams;
125///
126/// fn process(
127/// &mut self,
128/// buffer: &mut [&mut [f32]],
129/// _transport: &Transport,
130/// params: &Self::Params,
131/// ) {
132/// for channel in buffer.iter_mut() {
133/// for sample in channel.iter_mut() {
134/// *sample *= params.level;
135/// }
136/// }
137/// }
138/// }
139/// ```
140pub trait Processor: Send + 'static {
141 /// Associated parameter type for this processor.
142 ///
143 /// Use `()` for processors with no parameters, or define a struct
144 /// with `#[derive(ProcessorParams)]`.
145 type Params: ProcessorParams + Default + Send + Sync + 'static;
146
147 /// Process a buffer of audio samples.
148 ///
149 /// The buffer is provided as a slice of mutable slices, one per channel.
150 /// Modify samples in-place to apply your DSP effect.
151 ///
152 /// # Arguments
153 /// * `buffer` - Audio channels as `[L, R, ...]` where each channel is `[samples]`
154 /// * `transport` - Playback timing information
155 /// * `params` - Current parameter values
156 ///
157 /// # Real-Time Safety
158 /// This method is called on the audio thread. It MUST be real-time safe:
159 /// - No allocations (`Vec::push`, `String`, `Box::new`)
160 /// - No locks (`Mutex`, `RwLock`)
161 /// - No syscalls (file I/O, logging, network)
162 /// - No panics (use `debug_assert!` only)
163 fn process(&mut self, buffer: &mut [&mut [f32]], transport: &Transport, params: &Self::Params);
164
165 /// Called when the sample rate changes.
166 ///
167 /// Use this to update internal state that depends on sample rate
168 /// (e.g., filter coefficients, delay line sizes).
169 ///
170 /// # Arguments
171 /// * `sample_rate` - New sample rate in Hz (e.g., 44100.0, 48000.0)
172 ///
173 /// # Default
174 /// No-op by default. Override if your processor needs sample rate.
175 fn set_sample_rate(&mut self, _sample_rate: f32) {}
176
177 /// Reset processor state.
178 ///
179 /// Called when the host stops playback or when the user resets the plugin.
180 /// Use this to clear delay lines, reset filters, etc.
181 ///
182 /// # Default
183 /// No-op by default. Override if your processor maintains state.
184 fn reset(&mut self) {}
185}