1#![warn(missing_docs)]
2
3mod filters;
24use filters::{Mode, TPTOnePoleStereo};
25
26const SAMPLE_RATE: f32 = 44_100.0;
27
28pub struct Settings {
30 pub delay_time: f32,
32
33 pub output_level: f32,
35
36 pub feedback: f32,
39
40 pub ping_pong: bool,
42
43 pub width: f32,
46
47 pub phase_reverse: bool,
49
50 pub lowpass_filter: f64,
52
53 pub highpass_filter: f64,
55
56 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
83pub struct Delay {
87 pub settings: Settings,
89 state: State,
90}
91
92impl Delay {
93 pub fn new(settings: Settings) -> Self {
95 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 pub fn process(&mut self, input: &[f32], output: &mut [f32]) {
118 let mut input_index = 0;
119 let mut output_index = 0;
120
121 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 let delay_sample = (
130 delay_sample.0 * self.settings.feedback,
131 delay_sample.1 * self.settings.feedback,
132 );
133
134 let delay_sample = match self.settings.phase_reverse {
136 true => (-delay_sample.0, -delay_sample.1),
137 false => delay_sample,
138 };
139
140 let delay_sample = self.state.lowpass_filter.process(delay_sample);
142 let delay_sample = self.state.highpass_filter.process(delay_sample);
143
144 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 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 let delay_sample = (
174 delay_sample.0 * self.settings.output_level,
175 delay_sample.1 * self.settings.output_level,
176 );
177
178 output[output_index * 2] = delay_sample.0;
180 output[output_index * 2 + 1] = delay_sample.1;
181
182 input_index += 1;
184 output_index += 1;
185
186 self.state.delay_buffer_index =
188 (self.state.delay_buffer_index + 1) % self.state.delay_buffer.len();
189 }
190 }
191}