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 hold(&mut self) {
96 if !self.control.audible.load(Ordering::Acquire) {
97 self.pos = 0;
98 self.control.silent.store(true, Ordering::Release);
99 }
100 }
101
102 pub fn apply(&mut self, samples: &mut [f32], channels: usize) {
104 let rising = self.control.audible.load(Ordering::Acquire);
105 if (rising && self.pos == self.len) || (!rising && self.pos == 0) {
106 return;
107 }
108 for frame in samples.chunks_mut(channels.max(1)) {
109 self.pos = if rising {
110 (self.pos + 1).min(self.len)
111 } else {
112 self.pos.saturating_sub(1)
113 };
114 let level = self.pos as f32 / self.len as f32;
117 let gain = level * level;
118 for s in frame {
119 *s *= gain;
120 }
121 }
122 if !rising && self.pos == 0 {
123 self.control.silent.store(true, Ordering::Release);
124 }
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 const RATE: f64 = 1000.0; fn render(fader: &mut Fader, frames: usize) -> Vec<f32> {
135 let wanted = fader.readable(frames * 2, 2);
136 let mut out = vec![1.0f32; wanted];
137 fader.apply(&mut out, 2);
138 out
139 }
140
141 #[test]
142 fn a_fade_out_over_silence_is_silent_at_once() {
143 let control = FadeControl::new();
144 let mut fader = Fader::new(control.clone(), RATE);
145 fader.hold();
146 assert!(!control.is_silent(), "holding at full level fades nothing");
147
148 control.fade_out();
149 fader.hold();
150 assert!(control.is_silent());
151 assert_eq!(
152 fader.readable(64, 2),
153 0,
154 "nothing of the track plays after the pause"
155 );
156 }
157
158 #[test]
159 fn full_level_passes_samples_through() {
160 let control = FadeControl::new();
161 let mut fader = Fader::new(control, RATE);
162 let out = render(&mut fader, 64);
163 assert_eq!(out.len(), 128);
164 assert!(out.iter().all(|s| *s == 1.0));
165 }
166
167 #[test]
168 fn fade_out_stops_reading_at_silence() {
169 let control = FadeControl::new();
170 let mut fader = Fader::new(control.clone(), RATE);
171 control.fade_out();
172
173 let first = render(&mut fader, 100);
174 assert_eq!(first.len(), 200);
175 assert!(first.windows(2).all(|w| w[1] <= w[0]), "gain only falls");
176 assert!(!control.is_silent());
177
178 let second = render(&mut fader, 100);
179 assert_eq!(second.len(), 100, "only the rest of the ramp is read");
180 assert_eq!(*second.last().unwrap(), 0.0);
181 assert!(control.is_silent());
182
183 assert!(
184 render(&mut fader, 100).is_empty(),
185 "nothing read once silent"
186 );
187 }
188
189 #[test]
190 fn fade_in_from_stopped_starts_at_silence() {
191 let control = FadeControl::new();
192 let mut fader = Fader::new(control.clone(), RATE);
193 control.fade_in(true);
194
195 let out = render(&mut fader, 200);
196 assert!(out[0] < 0.01);
197 assert!(out.windows(2).all(|w| w[1] >= w[0]), "gain only rises");
198 assert_eq!(*out.last().unwrap(), 1.0);
199 }
200
201 #[test]
202 fn resume_mid_fade_turns_around() {
203 let control = FadeControl::new();
204 let mut fader = Fader::new(control.clone(), RATE);
205 control.fade_out();
206 let down = render(&mut fader, 50);
207 control.fade_in(false);
208 let up = render(&mut fader, 10);
209 assert!(up[0] > *down.last().unwrap(), "rises from where it was");
210 assert!(!control.is_silent());
211 }
212}