Skip to main content

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/// This is 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    /// Applies plain parameter values in `param_specs()` order.
42    ///
43    /// This is used by host/plugin bridges to populate runtime processor params
44    /// from external automation/UI state. Implementations may ignore unknown or
45    /// missing values.
46    fn apply_plain_values(&mut self, _values: &[f32]) {}
47}
48
49/// Specification for a single processor parameter.
50#[derive(Debug, Clone)]
51pub struct ParamSpec {
52    /// Display name of the parameter (e.g., "Frequency").
53    pub name: &'static str,
54
55    /// ID suffix for this parameter (e.g., "frequency").
56    /// Full ID will be prefixed with processor name: "my_filter_frequency"
57    pub id_suffix: &'static str,
58
59    /// Value range for this parameter.
60    pub range: ParamRange,
61
62    /// Default value.
63    pub default: f64,
64
65    /// Unit string (e.g., "dB", "Hz", "%").
66    pub unit: &'static str,
67
68    /// Optional group name for UI organization (e.g., "Input", "Processing", "Output").
69    pub group: Option<&'static str>,
70}
71
72/// Parameter value range definition.
73#[derive(Debug, Clone)]
74pub enum ParamRange {
75    /// Linear range from min to max.
76    Linear { min: f64, max: f64 },
77
78    /// Skewed range with exponential/logarithmic scaling.
79    /// `factor > 1.0` produces logarithmic-style skew; `factor < 1.0` produces exponential-style skew.
80    Skewed { min: f64, max: f64, factor: f64 },
81
82    /// Integer stepped range (for enums, switches).
83    Stepped { min: i32, max: i32 },
84
85    /// Enumerated parameter with named variants.
86    ///
87    /// Index 0 corresponds to the first variant, 1 to the second, etc.
88    Enum { variants: &'static [&'static str] },
89}
90
91/// Unit type has no parameters.
92impl ProcessorParams for () {
93    fn param_specs() -> &'static [ParamSpec] {
94        &[]
95    }
96}
97
98/// Trait for user-implemented DSP processors.
99///
100/// Implement this trait to define custom audio processing logic.
101/// All methods must be real-time safe (no allocations, locks, or syscalls).
102///
103/// # Example
104///
105/// ```rust,no_run
106/// use wavecraft_dsp::{ParamRange, ParamSpec, Processor, ProcessorParams, Transport};
107///
108/// #[derive(Default)]
109/// struct MyGainParams {
110///     level: f32,
111/// }
112///
113/// impl ProcessorParams for MyGainParams {
114///     fn param_specs() -> &'static [ParamSpec] {
115///         &[ParamSpec {
116///             name: "Level",
117///             id_suffix: "level",
118///             range: ParamRange::Linear { min: 0.0, max: 2.0 },
119///             default: 1.0,
120///             unit: "x",
121///             group: None,
122///         }]
123///     }
124/// }
125///
126/// struct MyGain {
127///     sample_rate: f32,
128/// }
129///
130/// impl Processor for MyGain {
131///     type Params = MyGainParams;
132///
133///     fn process(
134///         &mut self,
135///         buffer: &mut [&mut [f32]],
136///         _transport: &Transport,
137///         params: &Self::Params,
138///     ) {
139///         for channel in buffer.iter_mut() {
140///             for sample in channel.iter_mut() {
141///                 *sample *= params.level;
142///             }
143///         }
144///     }
145/// }
146/// ```
147pub trait Processor: Send + 'static {
148    /// Associated parameter type for this processor.
149    ///
150    /// Use `()` for processors with no parameters, or define a struct
151    /// with `#[derive(ProcessorParams)]`.
152    type Params: ProcessorParams + Default + Send + Sync + 'static;
153
154    /// Process a buffer of audio samples.
155    ///
156    /// The buffer is provided as a slice of mutable slices, one per channel.
157    /// Modify samples in-place to apply your DSP effect.
158    ///
159    /// # Arguments
160    /// * `buffer` - Audio channels as `[L, R, ...]` where each channel is `[samples]`
161    /// * `transport` - Playback timing information
162    /// * `params` - Current parameter values
163    ///
164    /// # Real-Time Safety
165    /// This method is called on the audio thread. It MUST be real-time safe:
166    /// - No allocations (`Vec::push`, `String`, `Box::new`)
167    /// - No locks (`Mutex`, `RwLock`)
168    /// - No syscalls (file I/O, logging, network)
169    /// - No panics (use `debug_assert!` only)
170    fn process(&mut self, buffer: &mut [&mut [f32]], transport: &Transport, params: &Self::Params);
171
172    /// Called when the sample rate changes.
173    ///
174    /// Use this to update internal state that depends on sample rate
175    /// (e.g., filter coefficients, delay line sizes).
176    ///
177    /// # Arguments
178    /// * `sample_rate` - New sample rate in Hz (e.g., 44100.0, 48000.0)
179    ///
180    /// # Default
181    /// No-op by default. Override if your processor needs sample rate.
182    fn set_sample_rate(&mut self, _sample_rate: f32) {}
183
184    /// Reset processor state.
185    ///
186    /// Called when the host stops playback or when the user resets the plugin.
187    /// Use this to clear delay lines, reset filters, etc.
188    ///
189    /// # Default
190    /// No-op by default. Override if your processor maintains state.
191    fn reset(&mut self) {}
192}