cute_dsp/
envelopes.rs

1//! LFOs and envelope generators
2//!
3//! This module provides LFOs, envelopes, and filters for manipulating them.
4
5#![allow(unused_imports)]
6
7#[cfg(feature = "std")]
8use std::{vec::Vec, marker::PhantomData, f32::consts::E};
9
10#[cfg(not(feature = "std"))]
11use core::{marker::PhantomData, f32::consts::E};
12
13#[cfg(all(not(feature = "std"), feature = "alloc"))]
14use alloc::vec::Vec;
15
16use num_traits::Float;
17
18/// ADSR (Attack, Decay, Sustain, Release) envelope generator.
19///
20/// This is a classic four-stage envelope commonly used in synthesizers and audio processing.
21/// It generates values from 0 to 1 based on the gate signal and time parameters.
22///
23/// # Stages
24///
25/// - **Attack**: Ramps from 0 to 1 over the attack time
26/// - **Decay**: Ramps from 1 to sustain level over the decay time
27/// - **Sustain**: Holds at a constant level while the gate is open
28/// - **Release**: Ramps from current level to 0 after the gate closes
29///
30/// # Example
31///
32/// ```ignore
33/// let mut adsr = Adsr::new(44100.0); // 44.1kHz sample rate
34/// adsr.set_times(0.01, 0.1, 0.5, 0.2); // Attack, Decay, Sustain level, Release
35///
36/// // Note on
37/// adsr.gate(true);
38/// for _ in 0..44100 {
39///     let sample = adsr.next();
40/// }
41///
42/// // Note off
43/// adsr.gate(false);
44/// for _ in 0..8820 {
45///     let sample = adsr.next();
46/// }
47/// ```
48#[derive(Clone, Debug)]
49pub struct Adsr {
50    // Time parameters (in seconds)
51    attack_time: f32,
52    decay_time: f32,
53    sustain_level: f32,
54    release_time: f32,
55    
56    // State
57    value: f32,
58    gate: bool,
59    stage: AdsrStage,
60    
61    // Increments per sample
62    sample_rate: f32,
63    attack_increment: f32,
64    decay_increment: f32,
65    release_increment: f32,
66}
67
68#[derive(Clone, Copy, Debug, PartialEq)]
69enum AdsrStage {
70    Attack,
71    Decay,
72    Sustain,
73    Release,
74    Done,
75}
76
77impl Adsr {
78    /// Create a new ADSR envelope with the specified sample rate (in Hz)
79    pub fn new(sample_rate: f32) -> Self {
80        let mut adsr = Self {
81            attack_time: 0.01,
82            decay_time: 0.1,
83            sustain_level: 0.7,
84            release_time: 0.2,
85            value: 0.0,
86            gate: false,
87            stage: AdsrStage::Release,
88            sample_rate,
89            attack_increment: 1.0 / (0.01 * sample_rate),
90            decay_increment: (0.7 - 1.0) / (0.1 * sample_rate),
91            release_increment: -0.7 / (0.2 * sample_rate),
92        };
93        adsr.reset();
94        adsr
95    }
96    
97    /// Set all ADSR time parameters (in seconds)
98    ///
99    /// # Arguments
100    ///
101    /// * `attack` - Time to ramp from 0 to 1 (seconds)
102    /// * `decay` - Time to ramp from 1 to sustain level (seconds)
103    /// * `sustain` - Level to hold at (0.0 to 1.0)
104    /// * `release` - Time to ramp from sustain to 0 (seconds)
105    pub fn set_times(&mut self, attack: f32, decay: f32, sustain: f32, release: f32) {
106        self.attack_time = attack.max(0.0001); // Prevent division by zero
107        self.decay_time = decay.max(0.0001);
108        self.sustain_level = sustain.clamp(0.0, 1.0);
109        self.release_time = release.max(0.0001);
110        
111        // Recalculate increments
112        self.attack_increment = 1.0 / (self.attack_time * self.sample_rate);
113        self.decay_increment = (self.sustain_level - 1.0) / (self.decay_time * self.sample_rate);
114        self.release_increment = -self.sustain_level / (self.release_time * self.sample_rate);
115    }
116    
117    /// Set just the attack time (in seconds)
118    pub fn set_attack(&mut self, attack: f32) {
119        self.attack_time = attack.max(0.0001);
120        self.attack_increment = 1.0 / (self.attack_time * self.sample_rate);
121    }
122    
123    /// Set just the decay time (in seconds)
124    pub fn set_decay(&mut self, decay: f32) {
125        self.decay_time = decay.max(0.0001);
126        self.decay_increment = (self.sustain_level - 1.0) / (self.decay_time * self.sample_rate);
127    }
128    
129    /// Set just the sustain level (0.0 to 1.0)
130    pub fn set_sustain(&mut self, sustain: f32) {
131        self.sustain_level = sustain.clamp(0.0, 1.0);
132        self.decay_increment = (self.sustain_level - 1.0) / (self.decay_time * self.sample_rate);
133        self.release_increment = -self.sustain_level / (self.release_time * self.sample_rate);
134    }
135    
136    /// Set just the release time (in seconds)
137    pub fn set_release(&mut self, release: f32) {
138        self.release_time = release.max(0.0001);
139        self.release_increment = -self.sustain_level / (self.release_time * self.sample_rate);
140    }
141    
142    /// Set the gate signal (true = note on, false = note off)
143    pub fn gate(&mut self, gate: bool) {
144        if gate && !self.gate {
145            // Gate opened - start attack from current value
146            self.stage = AdsrStage::Attack;
147        } else if !gate && self.gate {
148            // Gate closed - start release from current value
149            self.stage = AdsrStage::Release;
150        }
151        self.gate = gate;
152    }
153    
154    /// Get the current value without advancing
155    pub fn value(&self) -> f32 {
156        self.value
157    }
158    
159    /// Get the current ADSR stage
160    pub fn stage(&self) -> u32 {
161        match self.stage {
162            AdsrStage::Attack => 0,
163            AdsrStage::Decay => 1,
164            AdsrStage::Sustain => 2,
165            AdsrStage::Release => 3,
166            AdsrStage::Done => 4,
167        }
168    }
169    
170    /// Reset the envelope to zero
171    pub fn reset(&mut self) {
172        self.value = 0.0;
173        self.stage = if self.gate { AdsrStage::Attack } else { AdsrStage::Release };
174    }
175    
176    /// Process and return the next envelope sample
177    pub fn next(&mut self) -> f32 {
178        match self.stage {
179            AdsrStage::Attack => {
180                self.value += self.attack_increment;
181                if self.value >= 1.0 {
182                    self.value = 1.0;
183                    self.stage = AdsrStage::Decay;
184                }
185            }
186            AdsrStage::Decay => {
187                self.value += self.decay_increment;
188                if self.value <= self.sustain_level {
189                    self.value = self.sustain_level;
190                    self.stage = AdsrStage::Sustain;
191                }
192            }
193            AdsrStage::Sustain => {
194                self.value = self.sustain_level;
195                if !self.gate {
196                    self.stage = AdsrStage::Release;
197                }
198            }
199            AdsrStage::Release => {
200                self.value += self.release_increment;
201                if self.value <= 0.0 {
202                    self.value = 0.0;
203                    self.stage = AdsrStage::Done;
204                }
205            }
206            AdsrStage::Done => {
207                self.value = 0.0;
208                if self.gate {
209                    self.stage = AdsrStage::Attack;
210                }
211            }
212        }
213        self.value
214    }
215}
216
217/// An LFO based on cubic segments.
218///
219/// You can randomize the rate and/or the depth. Randomizing the depth past `0.5` means
220/// it no longer neatly alternates sides.
221///
222/// Without randomization, it is approximately sine-like.
223pub struct CubicLfo {
224    ratio: f32,
225    ratio_step: f32,
226    
227    value_from: f32,
228    value_to: f32,
229    value_range: f32,
230    
231    target_low: f32,
232    target_high: f32,
233    target_rate: f32,
234    
235    rate_random: f32,
236    depth_random: f32,
237    
238    fresh_reset: bool,
239    
240    // Random number generation
241    seed: u64,
242}
243
244impl CubicLfo {
245    /// Create a new LFO with a random seed
246    pub fn new() -> Self {
247        let mut lfo = Self {
248            ratio: 0.0,
249            ratio_step: 0.0,
250            value_from: 0.0,
251            value_to: 1.0,
252            value_range: 1.0,
253            target_low: 0.0,
254            target_high: 1.0,
255            target_rate: 0.0,
256            rate_random: 0.5,
257            depth_random: 0.0,
258            fresh_reset: true,
259            seed: 0,
260        };
261        
262        // Use a simple time-based seed
263        #[cfg(feature = "std")]
264        {
265            use std::time::{SystemTime, UNIX_EPOCH};
266            let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default();
267            lfo.seed = now.as_secs() ^ (now.subsec_nanos() as u64);
268        }
269        
270        #[cfg(not(feature = "std"))]
271        {
272            // In no_std environments, use a fixed seed
273            lfo.seed = 12345;
274        }
275        
276        lfo.reset();
277        lfo
278    }
279    
280    /// Create a new LFO with a specific seed
281    pub fn with_seed(seed: u64) -> Self {
282        let mut lfo = Self {
283            ratio: 0.0,
284            ratio_step: 0.0,
285            value_from: 0.0,
286            value_to: 1.0,
287            value_range: 1.0,
288            target_low: 0.0,
289            target_high: 1.0,
290            target_rate: 0.0,
291            rate_random: 0.5,
292            depth_random: 0.0,
293            fresh_reset: true,
294            seed,
295        };
296        lfo.reset();
297        lfo
298    }
299    
300    // Simple xorshift random number generator
301    fn random(&mut self) -> f32 {
302        self.seed ^= self.seed << 13;
303        self.seed ^= self.seed >> 17;
304        self.seed ^= self.seed << 5;
305        (self.seed & 0xFFFFFF) as f32 / 0xFFFFFF as f32
306    }
307    
308    fn random_rate(&mut self) -> f32 {
309        self.target_rate * (E.powf(self.rate_random * (self.random() - 0.5)))
310    }
311    
312    fn random_target(&mut self, previous: f32) -> f32 {
313        let random_offset = self.depth_random * self.random() * (self.target_low - self.target_high);
314        if previous < (self.target_low + self.target_high) * 0.5 {
315            self.target_high + random_offset
316        } else {
317            self.target_low - random_offset
318        }
319    }
320    
321    /// Resets the LFO state, starting with random phase.
322    pub fn reset(&mut self) {
323        self.ratio = self.random();
324        self.ratio_step = self.random_rate();
325        
326        if self.random() < 0.5 {
327            self.value_from = self.target_low;
328            self.value_to = self.target_high;
329        } else {
330            self.value_from = self.target_high;
331            self.value_to = self.target_low;
332        }
333        
334        self.value_range = self.value_to - self.value_from;
335        self.fresh_reset = true;
336    }
337    
338    /// Smoothly updates the LFO parameters.
339    ///
340    /// If called directly after `.reset()`, oscillation will immediately start within the specified range.
341    /// Otherwise, it will remain smooth and fit within the new range after at most one cycle.
342    ///
343    /// The LFO will complete a full oscillation in (approximately) `1/rate` samples.
344    /// `rate_variation` can be any number, but 0-1 is a good range.
345    ///
346    /// `depth_variation` must be in the range [0, 1], where ≤ 0.5 produces random amplitude
347    /// but still alternates up/down.
348    pub fn set(&mut self, low: f32, high: f32, rate: f32, rate_variation: f32, depth_variation: f32) {
349        let rate = rate * 2.0; // We want to go up and down during this period
350        self.target_rate = rate;
351        self.target_low = low.min(high);
352        self.target_high = low.max(high);
353        self.rate_random = rate_variation;
354        self.depth_random = depth_variation.max(0.0).min(1.0);
355        
356        // If we haven't called .next() yet, don't bother being smooth.
357        if self.fresh_reset {
358            return self.reset();
359        }
360        
361        // Only update the current rate if it's outside our new random-variation range
362        let max_random_ratio = E.powf(0.5 * self.rate_random);
363        if self.ratio_step > rate * max_random_ratio || self.ratio_step < rate / max_random_ratio {
364            self.ratio_step = self.random_rate();
365        }
366    }
367    
368    /// Returns the next output sample
369    pub fn next(&mut self) -> f32 {
370        self.fresh_reset = false;
371        let result = self.ratio * self.ratio * (3.0 - 2.0 * self.ratio) * self.value_range + self.value_from;
372        
373        self.ratio += self.ratio_step;
374        while self.ratio >= 1.0 {
375            self.ratio -= 1.0;
376            self.ratio_step = self.random_rate();
377            self.value_from = self.value_to;
378            self.value_to = self.random_target(self.value_from);
379            self.value_range = self.value_to - self.value_from;
380        }
381        
382        result
383    }
384}
385
386/// Variable-width rectangular sum
387pub struct BoxSum<T: Float> {
388    buffer_length: usize,
389    index: usize,
390    buffer: Vec<T>,
391    sum: T,
392    wrap_jump: T,
393}
394
395impl<T: Float> BoxSum<T> {
396    /// Create a new box sum with the specified maximum length
397    pub fn new(max_length: usize) -> Self {
398        let mut result = Self {
399            buffer_length: 0,
400            index: 0,
401            buffer: Vec::new(),
402            sum: T::zero(),
403            wrap_jump: T::zero(),
404        };
405        result.resize(max_length);
406        result
407    }
408    
409    /// Sets the maximum size (and reset contents)
410    pub fn resize(&mut self, max_length: usize) {
411        self.buffer_length = max_length + 1;
412        self.buffer.resize(self.buffer_length, T::zero());
413        self.reset(T::zero());
414    }
415    
416    /// Resets (with an optional "fill" value)
417    pub fn reset(&mut self, value: T) {
418        self.index = 0;
419        self.sum = T::zero();
420        
421        for i in 0..self.buffer.len() {
422            self.buffer[i] = self.sum;
423            self.sum = self.sum + value;
424        }
425        
426        self.wrap_jump = self.sum;
427        self.sum = T::zero();
428    }
429    
430    /// Read a sum of the last `width` samples
431    pub fn read(&self, width: usize) -> T {
432        let mut read_index = self.index as isize - width as isize;
433        let mut result = self.sum;
434        
435        if read_index < 0 {
436            result = result + self.wrap_jump;
437            read_index += self.buffer_length as isize;
438        }
439        
440        result - self.buffer[read_index as usize]
441    }
442    
443    /// Write a new sample
444    pub fn write(&mut self, value: T) {
445        self.index += 1;
446        if self.index == self.buffer_length {
447            self.index = 0;
448            self.wrap_jump = self.sum;
449            self.sum = T::zero();
450        }
451        
452        self.sum = self.sum + value;
453        self.buffer[self.index] = self.sum;
454    }
455    
456    /// Read and write in one operation
457    pub fn read_write(&mut self, value: T, width: usize) -> T {
458        self.write(value);
459        self.read(width)
460    }
461}
462
463/// Rectangular moving average filter (FIR).
464///
465/// A filter of length 1 has order 0 (i.e. does nothing).
466pub struct BoxFilter<T: Float> {
467    box_sum: BoxSum<T>,
468    length: usize,
469    max_length: usize,
470    multiplier: T,
471}
472
473impl<T: Float> BoxFilter<T> {
474    /// Create a new box filter with the specified maximum length
475    pub fn new(max_length: usize) -> Self {
476        let mut result = Self {
477            box_sum: BoxSum::new(max_length),
478            length: 0,
479            max_length: 0,
480            multiplier: T::one(),
481        };
482        result.resize(max_length);
483        result
484    }
485    
486    /// Sets the maximum size (and current size, and resets)
487    pub fn resize(&mut self, max_length: usize) {
488        self.max_length = max_length;
489        self.box_sum.resize(max_length);
490        self.set(max_length);
491    }
492    
493    /// Sets the current size (expanding/allocating only if needed)
494    pub fn set(&mut self, length: usize) {
495        self.length = length;
496        self.multiplier = T::one() / T::from(length).unwrap();
497        
498        if length > self.max_length {
499            self.resize(length);
500        }
501    }
502    
503    /// Resets (with an optional "fill" value)
504    pub fn reset(&mut self, fill: T) {
505        self.box_sum.reset(fill);
506    }
507    
508    /// Process a sample
509    pub fn process(&mut self, v: T) -> T {
510        self.box_sum.read_write(v, self.length) * self.multiplier
511    }
512}
513
514/// FIR filter made from a stack of `BoxFilter`s.
515///
516/// This filter has a non-negative impulse (monotonic step response), making it useful
517/// for smoothing positive-only values.
518pub struct BoxStackFilter<T: Float> {
519    size: usize,
520    layers: Vec<BoxStackLayer<T>>,
521}
522
523struct BoxStackLayer<T: Float> {
524    ratio: f32,
525    length_error: f32,
526    length: usize,
527    filter: BoxFilter<T>,
528}
529
530impl<T: Float> BoxStackFilter<T> {
531    /// Create a new box stack filter with the specified maximum size and number of layers
532    pub fn new(max_size: usize, layers: usize) -> Self {
533        let mut result = Self {
534            size: 0,
535            layers: Vec::new(),
536        };
537        result.resize(max_size, layers);
538        result
539    }
540    
541    /// Returns an optimal set of length ratios (heuristic for larger depths)
542    pub fn optimal_ratios(layer_count: usize) -> Vec<f32> {
543        // Coefficients up to 6, found through numerical search
544        static HARDCODED: [f32; 21] = [
545            1.0, 0.58224186169, 0.41775813831, 0.404078562416, 0.334851475794, 0.261069961789,
546            0.307944914938, 0.27369945234, 0.22913263601, 0.189222996712, 0.248329349789,
547            0.229253789144, 0.201191468123, 0.173033035122, 0.148192357821, 0.205275202874,
548            0.198413552119, 0.178256637764, 0.157821404506, 0.138663023387, 0.121570179349
549        ];
550        
551        if layer_count <= 0 {
552            return Vec::new();
553        } else if layer_count <= 6 {
554            let start = layer_count * (layer_count - 1) / 2;
555            return HARDCODED[start..start + layer_count].to_vec();
556        }
557        
558        let mut result = vec![0.0; layer_count];
559        
560        let inv_n = 1.0 / layer_count as f32;
561        let sqrt_n = (layer_count as f32).sqrt();
562        let p = 1.0 - inv_n;
563        let k = 1.0 + 4.5 / sqrt_n + 0.08 * sqrt_n;
564        
565        let mut sum = 0.0;
566        for i in 0..layer_count {
567            let x = i as f32 * inv_n;
568            let power = -x * (1.0 - p * (-x * k).exp());
569            let length = 2.0f32.powf(power);
570            result[i] = length;
571            sum += length;
572        }
573        
574        let factor = 1.0 / sum;
575        for r in &mut result {
576            *r *= factor;
577        }
578        
579        result
580    }
581    
582    /// Approximate (optimal) bandwidth for a given number of layers
583    pub fn layers_to_bandwidth(layers: usize) -> f32 {
584        1.58 * (layers as f32 + 0.1)
585    }
586    
587    /// Approximate (optimal) peak in the stop-band
588    pub fn layers_to_peak_db(layers: usize) -> f32 {
589        5.0 - layers as f32 * 18.0
590    }
591    
592    /// Sets size using an optimal (heuristic at larger sizes) set of length ratios
593    pub fn resize(&mut self, max_size: usize, layer_count: usize) {
594        self.resize_with_ratios(max_size, Self::optimal_ratios(layer_count));
595    }
596    
597    /// Sets the maximum (and current) impulse response length and explicit length ratios
598    pub fn resize_with_ratios(&mut self, max_size: usize, ratios: Vec<f32>) {
599        self.setup_layers(ratios);
600        
601        for layer in &mut self.layers {
602            layer.filter.resize(0);
603        }
604        
605        self.size = 0;
606        self.set(max_size);
607        self.reset();
608    }
609    
610    fn setup_layers(&mut self, ratios: Vec<f32>) {
611        self.layers.clear();
612        
613        let mut sum = 0.0;
614        for ratio in &ratios {
615            self.layers.push(BoxStackLayer {
616                ratio: *ratio,
617                length_error: 0.0,
618                length: 0,
619                filter: BoxFilter::new(0),
620            });
621            sum += ratio;
622        }
623        
624        let factor = 1.0 / sum;
625        for layer in &mut self.layers {
626            layer.ratio *= factor;
627        }
628    }
629    
630    /// Sets the impulse response length (does not reset if `size` ≤ `max_size`)
631    pub fn set(&mut self, size: usize) {
632        if self.layers.is_empty() {
633            return;
634        }
635        
636        if self.size == size {
637            return;
638        }
639        
640        self.size = size;
641        let order = size - 1;
642        let mut total_order = 0;
643        
644        for layer in &mut self.layers {
645            let layer_order_fractional = layer.ratio * order as f32;
646            let layer_order = layer_order_fractional as usize;
647            layer.length = layer_order + 1;
648            layer.length_error = layer_order as f32 - layer_order_fractional;
649            total_order += layer_order;
650        }
651        
652        // Round some of them up, so the total is correct
653        while total_order < order {
654            let mut min_index = 0;
655            let mut min_error = self.layers[0].length_error;
656            
657            for i in 1..self.layers.len() {
658                if self.layers[i].length_error < min_error {
659                    min_error = self.layers[i].length_error;
660                    min_index = i;
661                }
662            }
663            
664            self.layers[min_index].length += 1;
665            self.layers[min_index].length_error += 1.0;
666            total_order += 1;
667        }
668        
669        for layer in &mut self.layers {
670            layer.filter.set(layer.length);
671        }
672    }
673    
674    /// Resets the filter
675    pub fn reset(&mut self) {
676        for layer in &mut self.layers {
677            layer.filter.reset(T::zero());
678        }
679    }
680    
681    /// Process a sample
682    pub fn process(&mut self, v: T) -> T {
683        let mut result = v;
684        for layer in &mut self.layers {
685            result = layer.filter.process(result);
686        }
687        result
688    }
689}
690
691/// Peak-hold filter.
692///
693/// The size is variable, and can be changed instantly with `.set()`,
694/// or by using `.push()`/`.pop()` in an unbalanced way.
695///
696/// This has complexity O(1) every sample when the length remains constant
697/// (balanced `.push()`/`.pop()`, or using `filter(v)`), and amortised O(1)
698/// complexity otherwise.
699pub struct PeakHold<T: Float> {
700    buffer_mask: usize,
701    buffer: Vec<T>,
702    back_index: isize,
703    middle_start: isize,
704    working_index: isize,
705    middle_end: isize,
706    front_index: isize,
707    front_max: T,
708    working_max: T,
709    middle_max: T,
710}
711
712impl<T: Float> PeakHold<T> {
713    /// Create a new peak-hold filter with the specified maximum length
714    pub fn new(max_length: usize) -> Self {
715        let mut result = Self {
716            buffer_mask: 0,
717            buffer: Vec::new(),
718            back_index: 0,
719            middle_start: 0,
720            working_index: 0,
721            middle_end: 0,
722            front_index: 0,
723            front_max: T::min_value(),
724            working_max: T::min_value(),
725            middle_max: T::min_value(),
726        };
727        result.resize(max_length);
728        result
729    }
730    
731    /// Get the current size of the filter
732    pub fn size(&self) -> usize {
733        (self.front_index - self.back_index) as usize
734    }
735    
736    /// Resize the filter to a new maximum length
737    pub fn resize(&mut self, max_length: usize) {
738        let mut buffer_length = 1;
739        while buffer_length < max_length {
740            buffer_length *= 2;
741        }
742        
743        self.buffer.resize(buffer_length, T::min_value());
744        self.buffer_mask = buffer_length - 1;
745        
746        self.front_index = self.back_index + max_length as isize;
747        self.reset();
748    }
749    
750    /// Reset the filter
751    pub fn reset(&mut self) {
752        let prev_size = self.size();
753        
754        for i in 0..self.buffer.len() {
755            self.buffer[i] = T::min_value();
756        }
757        
758        self.front_max = T::min_value();
759        self.working_max = T::min_value();
760        self.middle_max = T::min_value();
761        
762        self.middle_end = 0;
763        self.working_index = 0;
764        self.front_index = 0;
765        self.middle_start = self.middle_end - (prev_size as isize / 2);
766        self.back_index = self.front_index - prev_size as isize;
767    }
768    
769    /// Sets the size immediately.
770    ///
771    /// Must be `0 <= new_size <= max_length` (see constructor and `.resize()`).
772    ///
773    /// Shrinking doesn't destroy information, and if you expand again (with `preserve_current_peak=false`),
774    /// you will get the same output as before shrinking. Expanding when `preserve_current_peak` is enabled
775    /// is destructive, re-writing its history such that the current output value is unchanged.
776    pub fn set(&mut self, new_size: usize, preserve_current_peak: bool) {
777        while self.size() < new_size {
778            let back_prev_idx = (self.back_index as usize) & self.buffer_mask;
779            let back_prev = self.buffer[back_prev_idx];
780            
781            self.back_index -= 1;
782            
783            let back_idx = (self.back_index as usize) & self.buffer_mask;
784            if preserve_current_peak {
785                self.buffer[back_idx] = back_prev;
786            } else {
787                self.buffer[back_idx] = self.buffer[back_idx].max(back_prev);
788            }
789        }
790        
791        while self.size() > new_size {
792            self.pop();
793        }
794    }
795    
796    /// Push a new value onto the filter
797    pub fn push(&mut self, v: T) {
798        let front_idx = (self.front_index as usize) & self.buffer_mask;
799        self.buffer[front_idx] = v;
800        self.front_index += 1;
801        self.front_max = self.front_max.max(v);
802    }
803    
804    /// Pop a value from the filter
805    pub fn pop(&mut self) {
806        if self.back_index == self.middle_start {
807            // Move along the maximums
808            self.working_max = T::min_value();
809            self.middle_max = self.front_max;
810            self.front_max = T::min_value();
811            
812            let prev_front_length = self.front_index - self.middle_end;
813            let prev_middle_length = self.middle_end - self.middle_start;
814            
815            if prev_front_length <= prev_middle_length + 1 {
816                // Swap over simply
817                self.middle_start = self.middle_end;
818                self.middle_end = self.front_index;
819                self.working_index = self.middle_end;
820            } else {
821                // The front is longer than the middle - only happens if unbalanced
822                // We don't move *all* of the front over, keeping half the surplus in the front
823                let middle_length = (self.front_index - self.middle_start) / 2;
824                self.middle_start = self.middle_end;
825                self.middle_end += middle_length;
826                
827                // Working index is close enough that it will be finished by the time the back is empty
828                let back_length = self.middle_start - self.back_index;
829                let working_length = back_length.min(self.middle_end - self.middle_start);
830                self.working_index = self.middle_start + working_length;
831                
832                // Since the front was not completely consumed, we re-calculate the front's maximum
833                for i in self.middle_end..self.front_index {
834                    let idx = (i as usize) & self.buffer_mask;
835                    self.front_max = self.front_max.max(self.buffer[idx]);
836                }
837                
838                // The index might not start at the end of the working block - compute the last bit immediately
839                for i in (self.working_index..self.middle_end).rev() {
840                    let idx = (i as usize) & self.buffer_mask;
841                    self.buffer[idx] = self.working_max;
842                    self.working_max = self.working_max.max(self.buffer[idx]);
843                }
844            }
845            
846            // Is the new back (previous middle) empty? Only happens if unbalanced
847            if self.back_index == self.middle_start {
848                // swap over again (front's empty, no change)
849                self.working_max = T::min_value();
850                self.middle_max = self.front_max;
851                self.front_max = T::min_value();
852                self.middle_start = self.middle_end;
853                self.working_index = self.middle_end;
854                
855                if self.back_index == self.middle_start {
856                    self.back_index -= 1; // Only happens if you pop from an empty list - fail nicely
857                }
858            }
859            
860            // In case of length 0, when everything points at this value
861            let front_idx = (self.front_index as usize) & self.buffer_mask;
862            self.buffer[front_idx] = T::min_value();
863        }
864        
865        self.back_index += 1;
866        
867        if self.working_index != self.middle_start {
868            self.working_index -= 1;
869            let idx = (self.working_index as usize) & self.buffer_mask;
870            self.buffer[idx] = self.working_max;
871            self.working_max = self.working_max.max(self.buffer[idx]);
872        }
873    }
874    
875    /// Read the current maximum value
876    pub fn read(&self) -> T {
877        let back_idx = (self.back_index as usize) & self.buffer_mask;
878        let back_max = self.buffer[back_idx];
879        back_max.max(self.middle_max).max(self.front_max)
880    }
881    
882    /// Process a sample (push, pop, and read)
883    pub fn process(&mut self, v: T) -> T {
884        self.push(v);
885        self.pop();
886        self.read()
887    }
888}
889
890/// Peak-decay filter with a linear shape and fixed-time return to constant value.
891///
892/// This is equivalent to a `BoxFilter` which resets itself whenever the output
893/// would be less than the input.
894pub struct PeakDecayLinear<T: Float> {
895    peak_hold: PeakHold<T>,
896    value: T,
897    step_multiplier: T,
898}
899
900impl<T: Float> PeakDecayLinear<T> {
901    /// Create a new peak-decay filter with the specified maximum length
902    pub fn new(max_length: usize) -> Self {
903        let mut result = Self {
904            peak_hold: PeakHold::new(max_length),
905            value: T::min_value(),
906            step_multiplier: T::one(),
907        };
908        result.set(max_length as f32);
909        result
910    }
911
912    /// Resize the filter to a new maximum length
913    pub fn resize(&mut self, max_length: usize) {
914        self.peak_hold.resize(max_length);
915        self.reset();
916    }
917
918    /// Set the filter length
919    pub fn set(&mut self, length: f32) {
920        let window_size = length.ceil() as usize;
921        self.peak_hold.set(window_size, true);
922        self.step_multiplier = T::from(1.0 / window_size as f32).unwrap();
923    }
924
925    /// Reset the filter
926    pub fn reset(&mut self) {
927        self.peak_hold.reset();
928        self.set(self.peak_hold.size() as f32);
929        self.value = T::min_value();
930    }
931
932    /// Process a sample
933    pub fn process(&mut self, v: T) -> T {
934        let peak = self.peak_hold.read();
935        self.peak_hold.process(v);
936
937        // Calculate decay step based on peak value and filter length
938        let decay_step = peak * self.step_multiplier;
939        self.value = v.max(self.value - decay_step);
940        self.value
941    }
942}
943
944#[cfg(test)]
945mod tests {
946    use super::*;
947
948    #[test]
949    fn test_cubic_lfo() {
950        let mut lfo = CubicLfo::with_seed(12345);
951
952        // Set parameters
953        lfo.set(0.0, 1.0, 0.1, 0.0, 0.0);
954
955        // Generate some samples
956        let mut samples = Vec::new();
957        for _ in 0..100 {
958            samples.push(lfo.next());
959        }
960
961        // Check that samples are within range
962        for sample in &samples {
963            assert!(*sample >= 0.0 && *sample <= 1.0);
964        }
965
966        // Check that the LFO oscillates (has both high and low values)
967        let min = samples.iter().fold(f32::INFINITY, |a, &b| a.min(b));
968        let max = samples.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b));
969
970        assert!(min < 0.1);
971        assert!(max > 0.9);
972    }
973
974    #[test]
975    fn test_box_filter() {
976        let mut filter = BoxFilter::<f32>::new(10);
977
978        // Set length to 5
979        filter.set(5);
980
981        // Process a step function
982        let mut output = Vec::new();
983        for i in 0..20 {
984            let input = if i >= 10 { 1.0 } else { 0.0 };
985            output.push(filter.process(input));
986        }
987
988        // Check that the output ramps up over 5 samples
989        assert_eq!(output[9], 0.0);
990        assert_eq!(output[10], 0.2);
991        assert_eq!(output[11], 0.4);
992        assert_eq!(output[12], 0.6);
993        assert_eq!(output[13], 0.8);
994        assert_eq!(output[14], 1.0);
995    }
996
997    #[test]
998    fn test_box_stack_filter() {
999        let mut filter = BoxStackFilter::<f32>::new(20, 3);
1000
1001        // Process a step function
1002        let mut output = Vec::new();
1003        for i in 0..40 {
1004            let input = if i >= 20 { 1.0 } else { 0.0 };
1005            output.push(filter.process(input));
1006        }
1007
1008        // Check that the output eventually reaches 1.0
1009        assert!(output[39] > 0.99);
1010
1011        // Check that the transition is smooth (no overshoots)
1012        for i in 1..output.len() {
1013            assert!(output[i] >= output[i-1]);
1014        }
1015    }
1016
1017    fn test_peak_hold() {
1018        let mut filter = PeakHold::<f32>::new(5);
1019
1020        // Process a sequence
1021        let input = vec![0.1, 0.5, 0.3, 0.2, 0.4, 0.1, 0.0];
1022        let mut output = Vec::new();
1023
1024        for &v in &input {
1025            output.push(filter.process(v));
1026        }
1027
1028        // The peak should hold for 5 samples before starting to decay
1029        assert_eq!(output[0], 0.1);  // First sample is the input
1030        assert_eq!(output[1], 0.5);  // Peak value
1031        assert_eq!(output[2], 0.5);  // Hold peak
1032        assert_eq!(output[3], 0.5);  // Hold peak
1033        assert_eq!(output[4], 0.5);  // Hold peak
1034        assert_eq!(output[5], 0.4);  // New peak after window moves
1035        assert_eq!(output[6], 0.4);  // Continue with new peak
1036    }
1037
1038    #[test]
1039    fn test_peak_decay_linear() {
1040        let mut filter = PeakDecayLinear::<f32>::new(10);
1041
1042        // Process a sequence with known values
1043        let input = vec![0.1, 0.5, 0.3, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0];
1044        let mut output = Vec::new();
1045
1046        // Reset filter to ensure clean state
1047        filter.reset();
1048
1049        for &v in &input {
1050            output.push(filter.process(v));
1051        }
1052
1053        // The output should follow a linear decay from peak over 10 samples
1054        let decay_per_sample = 0.5 / 10.0;  // 0.05 per sample
1055
1056        // Check values with appropriate epsilon
1057        assert!((output[0] - 0.1).abs() < 1e-6);  // First sample
1058        assert!((output[1] - 0.5).abs() < 1e-6);  // Peak value
1059        assert!((output[2] - 0.45).abs() < 1e-5,  // After first decay step
1060                "Sample 2 mismatch: expected 0.45, got {}", output[2]);
1061        assert!((output[3] - 0.40).abs() < 1e-5);
1062        assert!((output[4] - 0.35).abs() < 1e-5);
1063        assert!((output[5] - 0.30).abs() < 1e-5);
1064        assert!((output[6] - 0.25).abs() < 1e-5);
1065        assert!((output[7] - 0.20).abs() < 1e-5);
1066        assert!((output[8] - 0.15).abs() < 1e-5);
1067        assert!((output[9] - 0.10).abs() < 1e-5);
1068    }
1069
1070    #[test]
1071    fn test_adsr_basic() {
1072        let sample_rate = 44100.0;
1073        let mut adsr = Adsr::new(sample_rate);
1074        
1075        // Set times: 0.01s attack, 0.1s decay, 0.7 sustain, 0.2s release
1076        adsr.set_times(0.01, 0.1, 0.7, 0.2);
1077        
1078        // Note on
1079        adsr.gate(true);
1080        
1081        // Attack phase: should reach ~1.0
1082        let attack_samples = (0.01 * sample_rate) as usize;
1083        for _ in 0..attack_samples {
1084            adsr.next();
1085        }
1086        assert!(adsr.value() >= 0.99);
1087        
1088        // Decay phase: should reach sustain level (~0.7)
1089        let decay_samples = (0.1 * sample_rate) as usize;
1090        for _ in 0..decay_samples {
1091            adsr.next();
1092        }
1093        assert!((adsr.value() - 0.7).abs() < 0.01);
1094        
1095        // Note off
1096        adsr.gate(false);
1097        
1098        // Release phase: should reach ~0.0
1099        let release_samples = (0.2 * sample_rate) as usize;
1100        for _ in 0..release_samples {
1101            adsr.next();
1102        }
1103        assert!(adsr.value() <= 0.01);
1104    }
1105
1106    #[test]
1107    fn test_adsr_sustain_level() {
1108        let sample_rate = 44100.0;
1109        let mut adsr = Adsr::new(sample_rate);
1110        
1111        adsr.set_times(0.001, 0.001, 0.5, 0.001);
1112        adsr.gate(true);
1113        
1114        // Skip to sustain phase
1115        for _ in 0..100 {
1116            adsr.next();
1117        }
1118        
1119        // Should hold at sustain level
1120        let sustain_val = adsr.next();
1121        assert!((sustain_val - 0.5).abs() < 0.01);
1122    }
1123
1124    #[test]
1125    fn test_adsr_gate_control() {
1126        let sample_rate = 44100.0;
1127        let mut adsr = Adsr::new(sample_rate);
1128        
1129        adsr.set_times(0.01, 0.01, 0.7, 0.01);
1130        
1131        // Initial state should be at zero
1132        assert_eq!(adsr.value(), 0.0);
1133        
1134        // Gate on
1135        adsr.gate(true);
1136        let val1 = adsr.next();
1137        assert!(val1 > 0.0, "Should be attacking");
1138        
1139        // Gate off should start release
1140        adsr.gate(false);
1141        // Skip through to release phase
1142        for _ in 0..1000 {
1143            adsr.next();
1144        }
1145        let val2 = adsr.next();
1146        assert!(val2 >= 0.0 && val2 <= 1.0, "Value should be in range during release");
1147    }
1148}