1use crate::delay::Delay;
4use rill_core::{
5 buffer::DelayLine,
6 math::Transcendental,
7 traits::{Node, NodeCategory, NodeMetadata, NodeState, Processor},
8 NodeId, ParamValue, ParameterId, Port, ProcessError, ProcessResult, RenderContext,
9};
10
11const MAX_LOOKAHEAD_TIME: f32 = 0.01;
13const MAX_SAMPLE_RATE: f32 = 192_000.0;
15const MAX_LOOKAHEAD_SAMPLES: usize = (MAX_LOOKAHEAD_TIME * MAX_SAMPLE_RATE) as usize;
17const ANALYSIS_BUF_SIZE: usize = MAX_LOOKAHEAD_SAMPLES * 2;
19
20pub struct Limiter<T: Transcendental, const BUF_SIZE: usize> {
22 id: NodeId,
24 metadata: NodeMetadata,
26 inputs: Vec<Port<T, BUF_SIZE>>,
28 outputs: Vec<Port<T, BUF_SIZE>>,
30 controls: Vec<Port<T, BUF_SIZE>>,
32 state: NodeState<T, BUF_SIZE>,
34 delay: Delay<T, BUF_SIZE>,
36 analysis_buffer: DelayLine<T, ANALYSIS_BUF_SIZE>,
38 threshold_db: f32,
40 threshold_linear: T,
42 output_gain: f32,
44 attack: f32,
46 release: f32,
48 lookahead: f32,
50 lookahead_samples: usize,
52 current_gain: f32,
54 attack_coeff: f32,
56 release_coeff: f32,
58 sample_rate: f32,
60 position: usize,
62 init_buffer: Vec<T>,
64 initializing: bool,
66 warming_up: bool,
68}
69
70impl<T: Transcendental, const BUF_SIZE: usize> Limiter<T, BUF_SIZE> {
71 pub fn new(
73 sample_rate: f32,
74 threshold_db: f32,
75 attack: f32,
76 release: f32,
77 output_gain: f32,
78 ) -> Self {
79 let threshold_db = threshold_db.clamp(-60.0, 0.0);
80 let threshold_linear = T::from_f32(10.0_f32.powf(threshold_db / 20.0));
81
82 let attack = attack.clamp(0.001, 0.1);
83 let release = release.clamp(0.01, 1.0);
84
85 let attack_coeff = (-1.0 / (attack * sample_rate)).exp();
86 let release_coeff = (-1.0 / (release * sample_rate)).exp();
87
88 let lookahead = 0.005; let lookahead_samples = (lookahead * sample_rate) as usize;
90
91 let delay = Delay::with_params(sample_rate, lookahead, 0.0, 1.0);
93
94 let analysis_buffer = DelayLine::new(sample_rate);
96
97 let init_buffer = Vec::with_capacity(lookahead_samples);
99
100 let metadata = NodeMetadata::new("Limiter", NodeCategory::Processor);
101 let mut inputs = Vec::new();
102 let mut outputs = Vec::new();
103 inputs.push(Port::input(NodeId(0), 0, "signal_in"));
104 outputs.push(Port::output(NodeId(0), 0, "signal_out"));
105
106 Self {
107 id: NodeId(0),
108 metadata,
109 inputs,
110 outputs,
111 controls: Vec::new(),
112 state: NodeState::new(sample_rate),
113 delay,
114 analysis_buffer,
115 threshold_db,
116 threshold_linear,
117 output_gain: output_gain.clamp(0.0, 2.0),
118 attack,
119 release,
120 lookahead,
121 lookahead_samples,
122 current_gain: 1.0,
123 attack_coeff,
124 release_coeff,
125 sample_rate,
126 position: 0,
127 init_buffer,
128 initializing: true,
129 warming_up: false,
130 }
131 }
132
133 pub fn process_sample(&mut self, input: T) -> T {
135 self.position += 1;
136
137 self.analysis_buffer.write(input);
139
140 let delayed = self.delay.process_sample(input);
142
143 if self.initializing {
145 self.init_buffer.push(input);
147
148 if self.position >= self.lookahead_samples {
150 self.initializing = false;
151 self.warming_up = true;
152
153 self.delay.reset();
155
156 }
159
160 return input;
162 }
163
164 if self.warming_up {
166 if self.position < self.lookahead_samples * 2 {
168 if self.position - self.lookahead_samples <= self.init_buffer.len() {
170 let idx = self.position - self.lookahead_samples - 1;
171 if idx < self.init_buffer.len() {
172 let sample = self.init_buffer[idx];
173 let _ = self.delay.process_sample(sample);
174 }
175 }
176
177 if self.position >= self.lookahead_samples * 2 - 1 {
179 self.warming_up = false;
180 }
182
183 return input;
184 }
185 }
186
187 let mut max_amp = T::ZERO;
190 for offset in 0..self.lookahead_samples {
191 let sample = self.analysis_buffer.read_delayed(offset);
192 let abs_sample = sample.abs();
193 if abs_sample > max_amp {
194 max_amp = abs_sample;
195 }
196 }
197
198 let target_gain = if max_amp > self.threshold_linear {
200 self.threshold_linear.div(max_amp).to_f32()
201 } else {
202 1.0
203 };
204
205 if target_gain < self.current_gain {
207 self.current_gain =
208 self.current_gain * self.attack_coeff + target_gain * (1.0 - self.attack_coeff);
209 } else {
210 self.current_gain =
211 self.current_gain * self.release_coeff + target_gain * (1.0 - self.release_coeff);
212 }
213
214 let output = delayed.mul(T::from_f32(self.current_gain * self.output_gain));
216
217 output.clamp(T::from_f32(-2.0), T::from_f32(2.0))
224 }
225
226 pub fn process_block(&mut self, input: &[T], output: &mut [T]) {
228 for i in 0..input.len().min(output.len()) {
229 output[i] = self.process_sample(input[i]);
230 }
231 }
232
233 pub fn current_gain(&self) -> f32 {
235 self.current_gain
236 }
237
238 pub fn lookahead_samples(&self) -> usize {
240 self.lookahead_samples
241 }
242
243 pub fn set_threshold(&mut self, db: f32) {
245 self.threshold_db = db.clamp(-60.0, 0.0);
246 self.threshold_linear = T::from_f32(10.0_f32.powf(self.threshold_db / 20.0));
247 }
248
249 pub fn set_attack(&mut self, attack: f32) {
251 self.attack = attack.clamp(0.001, 0.1);
252 self.attack_coeff = (-1.0 / (self.attack * self.sample_rate)).exp();
253 }
254
255 pub fn set_release(&mut self, release: f32) {
257 self.release = release.clamp(0.01, 1.0);
258 self.release_coeff = (-1.0 / (self.release * self.sample_rate)).exp();
259 }
260
261 pub fn set_lookahead(&mut self, lookahead: f32) {
263 self.lookahead = lookahead.clamp(0.0, 0.01);
264 self.lookahead_samples = (self.lookahead * self.sample_rate) as usize;
265 self.delay.set_delay_time(lookahead);
266 self.analysis_buffer.clear();
267 self.current_gain = 1.0;
268 self.position = 0;
269 self.init_buffer.clear();
270 self.initializing = true;
271 self.warming_up = false;
272 }
273
274 pub fn reset(&mut self) {
276 self.current_gain = 1.0;
277 self.position = 0;
278 self.init_buffer.clear();
279 self.initializing = true;
280 self.warming_up = false;
281 self.delay.reset();
282 self.analysis_buffer.clear();
283 }
284
285 pub fn force_ready(&mut self) {
287 if self.initializing || self.warming_up {
288 for _ in 0..self.lookahead_samples * 2 {
290 let test_val = T::from_f32(0.1);
291 self.analysis_buffer.write(test_val);
292 let _ = self.delay.process_sample(test_val);
293 }
294 self.initializing = false;
295 self.warming_up = false;
296 self.position = self.lookahead_samples * 2;
297 }
299 }
300}
301
302impl<T: Transcendental, const BUF_SIZE: usize> Node<T, BUF_SIZE> for Limiter<T, BUF_SIZE> {
303 fn node_type_id(&self) -> rill_core::NodeTypeId
304 where
305 Self: 'static + Sized,
306 {
307 rill_core::NodeTypeId::of::<Self>()
308 }
309
310 fn id(&self) -> NodeId {
311 self.id
312 }
313
314 fn set_id(&mut self, id: NodeId) {
315 self.id = id;
316 }
317
318 fn metadata(&self) -> NodeMetadata {
319 self.metadata.clone()
320 }
321
322 fn init(&mut self, sample_rate: f32) {
323 self.sample_rate = sample_rate;
324 self.attack_coeff = (-1.0 / (self.attack * sample_rate)).exp();
325 self.release_coeff = (-1.0 / (self.release * sample_rate)).exp();
326
327 self.lookahead_samples = (self.lookahead * sample_rate) as usize;
328 self.analysis_buffer = DelayLine::new(sample_rate);
329 self.current_gain = 1.0;
330 self.position = 0;
331 self.init_buffer.clear();
332 self.initializing = true;
333 self.warming_up = false;
334
335 self.delay.init(sample_rate);
336 self.delay.set_delay_time(self.lookahead);
337 }
338
339 fn reset(&mut self) {
340 self.state.sample_pos = 0;
341 self.state.blocks_processed = 0;
342 Limiter::reset(self);
343 }
344
345 fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
346 let name = id.as_str();
347 match name {
348 "threshold" => Some(ParamValue::Float(self.threshold_db)),
349 "attack" => Some(ParamValue::Float(self.attack)),
350 "release" => Some(ParamValue::Float(self.release)),
351 "output_gain" => Some(ParamValue::Float(self.output_gain)),
352 "lookahead" => Some(ParamValue::Float(self.lookahead)),
353 "current_gain" => Some(ParamValue::Float(self.current_gain)),
354 _ => None,
355 }
356 }
357
358 fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
359 let name = id.as_str();
360 if let Some(v) = value.as_f32() {
361 match name {
362 "threshold" => {
363 self.set_threshold(v);
364 Ok(())
365 }
366 "attack" => {
367 self.set_attack(v);
368 Ok(())
369 }
370 "release" => {
371 self.set_release(v);
372 Ok(())
373 }
374 "output_gain" => {
375 self.output_gain = v.clamp(0.0, 2.0);
376 Ok(())
377 }
378 "lookahead" => {
379 self.set_lookahead(v);
380 Ok(())
381 }
382 _ => Err(ProcessError::parameter(format!(
383 "Unknown parameter: {}",
384 name
385 ))),
386 }
387 } else {
388 Err(ProcessError::parameter("Expected float value"))
389 }
390 }
391
392 fn input_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
393 self.inputs.get(index)
394 }
395
396 fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
397 self.inputs.get_mut(index)
398 }
399
400 fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
401 self.outputs.get(index)
402 }
403
404 fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
405 self.outputs.get_mut(index)
406 }
407
408 fn control_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
409 self.controls.get(index)
410 }
411
412 fn control_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
413 self.controls.get_mut(index)
414 }
415
416 fn num_inputs(&self) -> usize {
417 self.inputs.len()
418 }
419
420 fn num_outputs(&self) -> usize {
421 self.outputs.len()
422 }
423
424 fn num_signal_inputs(&self) -> usize {
425 self.inputs.len()
426 }
427
428 fn num_signal_outputs(&self) -> usize {
429 self.outputs.len()
430 }
431
432 fn state(&self) -> &NodeState<T, BUF_SIZE> {
433 &self.state
434 }
435
436 fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
437 &mut self.state
438 }
439}
440
441impl<T: Transcendental, const BUF_SIZE: usize> Processor<T, BUF_SIZE> for Limiter<T, BUF_SIZE> {
442 fn process(
443 &mut self,
444 _ctx: &RenderContext,
445 _signal_inputs: &[&[T; BUF_SIZE]],
446 _control_inputs: &[T],
447 _clock_inputs: &[RenderContext],
448 _feedback_inputs: &[&[T; BUF_SIZE]],
449 ) -> ProcessResult<()> {
450 for i in 0..BUF_SIZE {
451 let sample = self.inputs[0].read()[i];
452 self.outputs[0].write()[i] = self.process_sample(sample);
453 }
454 self.state.advance();
455 Ok(())
456 }
457
458 fn latency(&self) -> usize {
459 0
460 }
461}