rawdio/effects/waveshaper/
waveshaper_node.rs

1use super::{parameters::*, waveshaper_processor::WaveshaperProcessor};
2use crate::{commands::Id, graph::DspNode, parameter::*, prelude::*, utility::create_parameters};
3
4/// A node that will distort the input signal using a specified function
5///
6/// # Parameters
7/// - overdrive
8/// - mix
9pub struct Waveshaper {
10    /// The node to connect to the audio graph
11    pub node: GraphNode,
12
13    params: Parameters,
14}
15
16impl DspNode for Waveshaper {
17    fn get_parameters_mut(&mut self) -> &mut Parameters {
18        &mut self.params
19    }
20}
21
22impl Waveshaper {
23    /// Create a waveshaper that uses the mathematical tanh function to shape the
24    /// signal
25    pub fn tanh(context: &dyn Context, channel_count: usize) -> Self {
26        let shaper = |input: f32| {
27            const CONSTANT: f32 = 2.0;
28            CONSTANT * (input / CONSTANT).tanh()
29        };
30
31        Self::new(context, channel_count, &shaper)
32    }
33
34    /// Create a waveshaper that uses a soft saturation function to shape the
35    /// input
36    pub fn soft_saturator(context: &dyn Context, channel_count: usize, threshold: Level) -> Self {
37        let threshold = threshold.as_linear() as f32;
38        assert!(0.0 <= threshold);
39        assert!(threshold <= 1.0);
40
41        let shaper = move |input: f32| {
42            if input < threshold {
43                input
44            } else {
45                threshold
46                    + (input - threshold)
47                        / (1.0 + ((input - threshold) / (1.0 - threshold)).powf(2.0))
48            }
49        };
50
51        Self::new(context, channel_count, &shaper)
52    }
53
54    /// Create a waveshaper that hard clips the signal when it goes over the
55    /// specified threshold
56    pub fn hard_clip(context: &dyn Context, channel_count: usize, threshold: Level) -> Self {
57        let threshold = threshold.as_linear() as f32;
58
59        let shaper = Box::new(move |input: f32| {
60            if input < -threshold {
61                return -threshold;
62            }
63
64            if input > threshold {
65                return threshold;
66            }
67
68            input
69        });
70
71        Self::new(context, channel_count, &shaper)
72    }
73
74    /// Create a new waveshaper using a custom shaper function
75    pub fn new(context: &dyn Context, channel_count: usize, shaper: &dyn Fn(f32) -> f32) -> Self {
76        let id = Id::generate();
77
78        let (params, realtime_params) = create_parameters(
79            id,
80            context,
81            [
82                (
83                    "overdrive",
84                    ParameterRange::new(
85                        OVERDRIVE_PARAMETER_DEFAULT,
86                        OVERDRIVE_PARAMETER_MIN,
87                        OVERDRIVE_PARAMETER_MAX,
88                    ),
89                ),
90                (
91                    "mix",
92                    ParameterRange::new(
93                        MIX_PARAMETER_DEFAULT,
94                        MIX_PARAMETER_MIN,
95                        MIX_PARAMETER_MAX,
96                    ),
97                ),
98            ],
99        );
100
101        let processor = Box::new(WaveshaperProcessor::new(
102            shaper,
103            context.get_sample_rate(),
104            context.maximum_frame_count(),
105        ));
106
107        Self {
108            node: GraphNode::new(
109                id,
110                context,
111                channel_count,
112                channel_count,
113                processor,
114                realtime_params,
115            ),
116            params,
117        }
118    }
119
120    /// Get the overdrive parameter
121    pub fn overdrive(&mut self) -> &mut AudioParameter {
122        self.get_parameter_mut("overdrive")
123    }
124
125    /// Get the mix parameter
126    pub fn mix(&mut self) -> &mut AudioParameter {
127        self.get_parameter_mut("mix")
128    }
129}