1use std::sync::Arc;
2use std::sync::atomic::{AtomicBool, Ordering};
3
4const FADE_SECONDS: f64 = 0.15;
9
10#[derive(Default)]
13pub struct FadeControl {
14 audible: AtomicBool,
16 from_silence: AtomicBool,
19 silent: AtomicBool,
22}
23
24impl FadeControl {
25 pub fn new() -> Arc<Self> {
26 Arc::new(Self {
27 audible: AtomicBool::new(true),
28 ..Default::default()
29 })
30 }
31
32 pub fn fade_out(&self) {
33 self.audible.store(false, Ordering::Release);
34 }
35
36 pub fn fade_in(&self, from_silence: bool) {
39 self.silent.store(false, Ordering::Release);
40 if from_silence {
41 self.from_silence.store(true, Ordering::Release);
42 }
43 self.audible.store(true, Ordering::Release);
44 }
45
46 pub fn is_silent(&self) -> bool {
47 self.silent.load(Ordering::Acquire)
48 }
49}
50
51pub struct Fader {
57 control: Arc<FadeControl>,
58 pos: usize,
61 len: usize,
62}
63
64impl Fader {
65 pub fn new(control: Arc<FadeControl>, sample_rate: f64) -> Self {
66 let len = ((sample_rate * FADE_SECONDS) as usize).max(1);
67 Self {
68 control,
69 pos: len,
70 len,
71 }
72 }
73
74 pub fn readable(&mut self, wanted: usize, channels: usize) -> usize {
80 if self.control.from_silence.swap(false, Ordering::AcqRel) {
81 self.pos = 0;
82 }
83 if self.control.audible.load(Ordering::Acquire) {
84 return wanted;
85 }
86 if self.pos == 0 {
87 self.control.silent.store(true, Ordering::Release);
88 }
89 wanted.min(self.pos * channels.max(1))
90 }
91
92 pub fn apply(&mut self, samples: &mut [f32], channels: usize) {
94 let rising = self.control.audible.load(Ordering::Acquire);
95 if (rising && self.pos == self.len) || (!rising && self.pos == 0) {
96 return;
97 }
98 for frame in samples.chunks_mut(channels.max(1)) {
99 self.pos = if rising {
100 (self.pos + 1).min(self.len)
101 } else {
102 self.pos.saturating_sub(1)
103 };
104 let level = self.pos as f32 / self.len as f32;
107 let gain = level * level;
108 for s in frame {
109 *s *= gain;
110 }
111 }
112 if !rising && self.pos == 0 {
113 self.control.silent.store(true, Ordering::Release);
114 }
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 const RATE: f64 = 1000.0; fn render(fader: &mut Fader, frames: usize) -> Vec<f32> {
125 let wanted = fader.readable(frames * 2, 2);
126 let mut out = vec![1.0f32; wanted];
127 fader.apply(&mut out, 2);
128 out
129 }
130
131 #[test]
132 fn full_level_passes_samples_through() {
133 let control = FadeControl::new();
134 let mut fader = Fader::new(control, RATE);
135 let out = render(&mut fader, 64);
136 assert_eq!(out.len(), 128);
137 assert!(out.iter().all(|s| *s == 1.0));
138 }
139
140 #[test]
141 fn fade_out_stops_reading_at_silence() {
142 let control = FadeControl::new();
143 let mut fader = Fader::new(control.clone(), RATE);
144 control.fade_out();
145
146 let first = render(&mut fader, 100);
147 assert_eq!(first.len(), 200);
148 assert!(first.windows(2).all(|w| w[1] <= w[0]), "gain only falls");
149 assert!(!control.is_silent());
150
151 let second = render(&mut fader, 100);
152 assert_eq!(second.len(), 100, "only the rest of the ramp is read");
153 assert_eq!(*second.last().unwrap(), 0.0);
154 assert!(control.is_silent());
155
156 assert!(
157 render(&mut fader, 100).is_empty(),
158 "nothing read once silent"
159 );
160 }
161
162 #[test]
163 fn fade_in_from_stopped_starts_at_silence() {
164 let control = FadeControl::new();
165 let mut fader = Fader::new(control.clone(), RATE);
166 control.fade_in(true);
167
168 let out = render(&mut fader, 200);
169 assert!(out[0] < 0.01);
170 assert!(out.windows(2).all(|w| w[1] >= w[0]), "gain only rises");
171 assert_eq!(*out.last().unwrap(), 1.0);
172 }
173
174 #[test]
175 fn resume_mid_fade_turns_around() {
176 let control = FadeControl::new();
177 let mut fader = Fader::new(control.clone(), RATE);
178 control.fade_out();
179 let down = render(&mut fader, 50);
180 control.fade_in(false);
181 let up = render(&mut fader, 10);
182 assert!(up[0] > *down.last().unwrap(), "rises from where it was");
183 assert!(!control.is_silent());
184 }
185}