aych_delay/
lib.rs

1#![warn(missing_docs)]
2
3//! A delay effect modelled after the H-Delay by Waves.
4//!
5//! Example:
6//!
7//! ```rust
8//! use aych_delay::{Delay, Settings};
9//!
10//! let mut delay = Delay::new(Settings {
11//!    delay_time: 166.66,
12//!   feedback: 0.75,
13//!   ..Settings::default()
14//! });
15//!
16//! let input = vec![...];
17//! let mut output = vec![0.0; 512];
18//!
19//! delay.process(&mut input, &mut output);
20//! ```
21//!
22
23mod filters;
24use filters::{Mode, TPTOnePoleStereo};
25
26const SAMPLE_RATE: f32 = 44_100.0;
27
28/// `Settings` contains the parameters for the delay effect.
29pub struct Settings {
30    /// The delay time in milliseconds.
31    pub delay_time: f32,
32
33    /// The output level of the delay effect. 1.0 is unity gain.
34    pub output_level: f32,
35
36    /// The feedback level of the delay effect (Also known as "decay").
37    /// 0.0 is no feedback, 1.0 is infinite feedback.
38    pub feedback: f32,
39
40    /// Whether to use ping-pong delay.
41    pub ping_pong: bool,
42
43    /// The width of the delay effect, when ping-pong is enabled.
44    /// 0.0 is mono, 1.0 is full stereo.
45    pub width: f32,
46
47    /// Whether to reverse the phase of the delayed signal.
48    pub phase_reverse: bool,
49
50    /// The cutoff frequency of the lowpass filter.
51    pub lowpass_filter: f64,
52
53    /// The cutoff frequency of the highpass filter.
54    pub highpass_filter: f64,
55
56    /// The dry/wet mix of the delay effect.
57    pub dry_wet_mix: f32,
58}
59
60impl Default for Settings {
61    fn default() -> Self {
62        Self {
63            delay_time: 250.,
64            output_level: 1.0,
65            feedback: 0.8,
66            ping_pong: true,
67            width: 1.0,
68            phase_reverse: true,
69            lowpass_filter: 5000.0,
70            highpass_filter: 500.0,
71            dry_wet_mix: 0.5,
72        }
73    }
74}
75
76struct State {
77    delay_buffer: Vec<(f32, f32)>,
78    delay_buffer_index: usize,
79    lowpass_filter: TPTOnePoleStereo,
80    highpass_filter: TPTOnePoleStereo,
81}
82
83/// `Delay` is the main struct for the delay effect.
84///
85/// Internally, it maintains a buffer of delayed samples and a set of filters.
86pub struct Delay {
87    /// The current settings for the delay effect.
88    pub settings: Settings,
89    state: State,
90}
91
92impl Delay {
93    /// Creates a new `Delay` instance with the specified settings.
94    pub fn new(settings: Settings) -> Self {
95        // Initialize the delay buffer with the specified delay time.
96        let delay_buffer_size = (settings.delay_time / 1000.0) * SAMPLE_RATE;
97
98        let state = State {
99            delay_buffer: vec![(0.0, 0.0); delay_buffer_size as usize],
100            delay_buffer_index: 0,
101            lowpass_filter: TPTOnePoleStereo::new(
102                Mode::LOWPASS,
103                SAMPLE_RATE as f64,
104                settings.lowpass_filter,
105            ),
106            highpass_filter: TPTOnePoleStereo::new(
107                Mode::HIGHPASS,
108                SAMPLE_RATE as f64,
109                settings.highpass_filter,
110            ),
111        };
112
113        Self { settings, state }
114    }
115
116    /// Processes the input buffer and writes the updated signal to the output buffer.
117    pub fn process(&mut self, input: &[f32], output: &mut [f32]) {
118        let mut input_index = 0;
119        let mut output_index = 0;
120
121        // Convert the input buffer into an array of stereo samples.
122        let input_stereo: Vec<(f32, f32)> = input.chunks(2).map(|c| (c[0], c[1])).collect();
123
124        while input_index < input_stereo.len() && output_index < output.len() {
125            let input_sample = input_stereo[input_index];
126            let delay_sample = self.state.delay_buffer[self.state.delay_buffer_index];
127
128            // Apply feedback by scaling the delay sample by the current feedback level.
129            let delay_sample = (
130                delay_sample.0 * self.settings.feedback,
131                delay_sample.1 * self.settings.feedback,
132            );
133
134            // Apply phase reverse by inverting the phase of the delay sample.
135            let delay_sample = match self.settings.phase_reverse {
136                true => (-delay_sample.0, -delay_sample.1),
137                false => delay_sample,
138            };
139
140            // Apply filtering by convolving the delay sample with the filter coefficients.
141            let delay_sample = self.state.lowpass_filter.process(delay_sample);
142            let delay_sample = self.state.highpass_filter.process(delay_sample);
143
144            // Apply ping-pong by mixing the left and right channels of the delay sample.
145            if self.settings.ping_pong {
146                let width = self.settings.width / 2.0 + 0.5;
147
148                let pp_input = ((input_sample.0) * (1.0 - width), (input_sample.1) * width);
149
150                let pp_delay = (
151                    delay_sample.0 * (1.0 - width) + delay_sample.1 * width,
152                    delay_sample.1 * (1.0 - width) + delay_sample.0 * width,
153                );
154
155                self.state.delay_buffer[self.state.delay_buffer_index] =
156                    (pp_input.0 + pp_delay.0, pp_input.1 + pp_delay.1);
157            } else {
158                self.state.delay_buffer[self.state.delay_buffer_index] = (
159                    input_sample.0 + delay_sample.0,
160                    input_sample.1 + delay_sample.1,
161                );
162            }
163
164            // Mix the dry and wet signals
165            let delay_sample = (
166                (1.0 - self.settings.dry_wet_mix) * input_sample.0
167                    + self.settings.dry_wet_mix * delay_sample.0,
168                (1.0 - self.settings.dry_wet_mix) * input_sample.1
169                    + self.settings.dry_wet_mix * delay_sample.1,
170            );
171
172            // Apply output level by scaling the delayed sample by the current output level.
173            let delay_sample = (
174                delay_sample.0 * self.settings.output_level,
175                delay_sample.1 * self.settings.output_level,
176            );
177
178            // Write the delayed sample to the output buffer.
179            output[output_index * 2] = delay_sample.0;
180            output[output_index * 2 + 1] = delay_sample.1;
181
182            // Increment the input and output buffer indices.
183            input_index += 1;
184            output_index += 1;
185
186            // Increment the delay buffer index and wrap around if necessary.
187            self.state.delay_buffer_index =
188                (self.state.delay_buffer_index + 1) % self.state.delay_buffer.len();
189        }
190    }
191}