sim_lib_audio_dsp/
modulation.rs1use std::f32::consts::TAU;
2
3use sim_lib_audio_graph_core::{PrepareConfig, ProcessBlock, Processor};
4
5use crate::{
6 common::{input_sample, output_channels, prepare_channels},
7 delay::DelayLine,
8};
9
10#[derive(Clone, Debug, PartialEq)]
13pub struct ModulatedDelayProcessor {
14 base_delay_seconds: f32,
15 depth_seconds: f32,
16 rate_hz: f32,
17 feedback: f32,
18 wet: f32,
19 dry: f32,
20 sample_rate_hz: f32,
21 phase: f32,
22 lines: Vec<DelayLine>,
23}
24
25impl ModulatedDelayProcessor {
26 pub fn new(base_delay_ms: f32, depth_ms: f32, rate_hz: f32) -> Self {
29 Self {
30 base_delay_seconds: (base_delay_ms / 1000.0).max(0.0),
31 depth_seconds: (depth_ms / 1000.0).max(0.0),
32 rate_hz: rate_hz.max(0.0),
33 feedback: 0.0,
34 wet: 0.5,
35 dry: 0.5,
36 sample_rate_hz: 48_000.0,
37 phase: 0.0,
38 lines: Vec::new(),
39 }
40 }
41
42 pub fn with_feedback(mut self, feedback: f32) -> Self {
44 self.feedback = feedback.clamp(-0.99, 0.99);
45 self
46 }
47
48 pub fn with_mix(mut self, dry: f32, wet: f32) -> Self {
50 self.dry = dry;
51 self.wet = wet;
52 self
53 }
54
55 fn max_delay_samples(&self) -> usize {
56 ((self.base_delay_seconds + self.depth_seconds) * self.sample_rate_hz).ceil() as usize + 2
57 }
58
59 fn current_delay_samples(&self) -> f32 {
60 let lfo = self.phase.sin() * 0.5 + 0.5;
61 (self.base_delay_seconds + self.depth_seconds * lfo) * self.sample_rate_hz
62 }
63
64 fn advance_phase(&mut self) {
65 if self.sample_rate_hz > 0.0 {
66 self.phase = (self.phase + TAU * self.rate_hz / self.sample_rate_hz).rem_euclid(TAU);
67 }
68 }
69}
70
71impl Processor for ModulatedDelayProcessor {
72 fn prepare(&mut self, cfg: PrepareConfig) {
73 self.sample_rate_hz = cfg.sample_rate_hz as f32;
74 let line = DelayLine::new(self.max_delay_samples());
75 prepare_channels(&mut self.lines, cfg.out_channels as usize, line);
76 }
77
78 fn reset(&mut self) {
79 self.phase = 0.0;
80 for line in &mut self.lines {
81 line.reset();
82 }
83 }
84
85 fn process(&mut self, block: &mut ProcessBlock<'_>) {
86 let channels = output_channels(block);
87 if self.lines.len() < channels {
88 let max_delay_samples = self.max_delay_samples();
89 self.lines
90 .resize_with(channels, || DelayLine::new(max_delay_samples));
91 }
92 let frames = block.frames as usize;
93 for frame in 0..frames {
94 let delay = self.current_delay_samples();
95 for channel in 0..channels {
96 let input = input_sample(block, channel, frame);
97 let line = &mut self.lines[channel];
98 let delayed = line.read(delay);
99 line.push(input + delayed * self.feedback);
100 block.out_audio[channel][frame] = input * self.dry + delayed * self.wet;
101 }
102 self.advance_phase();
103 }
104 }
105}
106
107#[derive(Clone, Debug, PartialEq)]
109pub struct Chorus {
110 inner: ModulatedDelayProcessor,
111}
112
113impl Chorus {
114 pub fn new(rate_hz: f32, depth_ms: f32) -> Self {
116 Self {
117 inner: ModulatedDelayProcessor::new(18.0, depth_ms, rate_hz).with_mix(0.65, 0.35),
118 }
119 }
120}
121
122impl Processor for Chorus {
123 fn prepare(&mut self, cfg: PrepareConfig) {
124 self.inner.prepare(cfg);
125 }
126
127 fn reset(&mut self) {
128 self.inner.reset();
129 }
130
131 fn process(&mut self, block: &mut ProcessBlock<'_>) {
132 self.inner.process(block);
133 }
134}
135
136#[derive(Clone, Debug, PartialEq)]
138pub struct Flanger {
139 inner: ModulatedDelayProcessor,
140}
141
142impl Flanger {
143 pub fn new(rate_hz: f32, depth_ms: f32, feedback: f32) -> Self {
145 Self {
146 inner: ModulatedDelayProcessor::new(2.5, depth_ms, rate_hz)
147 .with_feedback(feedback)
148 .with_mix(0.55, 0.45),
149 }
150 }
151}
152
153impl Processor for Flanger {
154 fn prepare(&mut self, cfg: PrepareConfig) {
155 self.inner.prepare(cfg);
156 }
157
158 fn reset(&mut self) {
159 self.inner.reset();
160 }
161
162 fn process(&mut self, block: &mut ProcessBlock<'_>) {
163 self.inner.process(block);
164 }
165}
166
167#[derive(Clone, Debug, PartialEq)]
169pub struct Vibrato {
170 inner: ModulatedDelayProcessor,
171}
172
173impl Vibrato {
174 pub fn new(rate_hz: f32, depth_ms: f32) -> Self {
176 Self {
177 inner: ModulatedDelayProcessor::new(depth_ms, depth_ms, rate_hz).with_mix(0.0, 1.0),
178 }
179 }
180}
181
182impl Processor for Vibrato {
183 fn prepare(&mut self, cfg: PrepareConfig) {
184 self.inner.prepare(cfg);
185 }
186
187 fn reset(&mut self) {
188 self.inner.reset();
189 }
190
191 fn process(&mut self, block: &mut ProcessBlock<'_>) {
192 self.inner.process(block);
193 }
194}