1use std::sync::Arc;
2use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
3
4const FADE_SECONDS: f64 = 0.15;
9
10pub const QUICK_FADE: std::time::Duration = std::time::Duration::from_millis(15);
14
15const SLEEP_GLIDE_SECONDS: f64 = 0.1;
19
20#[derive(Default)]
23pub struct FadeControl {
24 audible: AtomicBool,
26 from_silence: AtomicBool,
29 silent: AtomicBool,
32 sleep_gain: AtomicU32,
35 sleep_snap: AtomicBool,
37 muted: AtomicBool,
39 quick: AtomicBool,
42 period_us: AtomicU32,
45}
46
47impl FadeControl {
48 pub fn new() -> Arc<Self> {
49 Arc::new(Self {
50 audible: AtomicBool::new(true),
51 sleep_gain: AtomicU32::new(1.0f32.to_bits()),
52 ..Default::default()
53 })
54 }
55
56 pub fn for_output() -> Arc<Self> {
58 let control = Self::new();
59 if crate::config::Config::cached().playback.muted {
60 log::info!("audio: muted (playback.muted)");
61 control.muted.store(true, Ordering::Release);
62 }
63 control
64 }
65
66 pub fn fade_out(&self) {
67 self.quick.store(false, Ordering::Release);
68 self.audible.store(false, Ordering::Release);
69 }
70
71 pub fn fade_out_quickly(&self) {
73 self.quick.store(true, Ordering::Release);
74 self.audible.store(false, Ordering::Release);
75 }
76
77 pub fn fade_in(&self, from_silence: bool) {
80 self.quick.store(false, Ordering::Release);
81 self.rise(from_silence);
82 }
83
84 pub fn fade_in_quickly(&self, from_silence: bool) {
86 self.quick.store(true, Ordering::Release);
87 self.rise(from_silence);
88 }
89
90 fn rise(&self, from_silence: bool) {
91 self.silent.store(false, Ordering::Release);
92 if from_silence {
93 self.from_silence.store(true, Ordering::Release);
94 }
95 self.audible.store(true, Ordering::Release);
96 }
97
98 pub fn is_silent(&self) -> bool {
99 self.silent.load(Ordering::Acquire)
100 }
101
102 pub fn period(&self) -> std::time::Duration {
105 std::time::Duration::from_micros(self.period_us.load(Ordering::Relaxed) as u64)
106 }
107
108 pub fn set_sleep_gain(&self, gain: f32, snap: bool) {
111 if snap {
112 self.sleep_snap.store(true, Ordering::Release);
113 }
114 self.sleep_gain
115 .store(gain.clamp(0.0, 1.0).to_bits(), Ordering::Release);
116 }
117}
118
119pub struct Fader {
125 control: Arc<FadeControl>,
126 pos: usize,
129 len: usize,
130 quick_step: usize,
132 sample_rate: f64,
133 sleep: f32,
136 sleep_target: u32,
137 sleep_step: f32,
138 glide: f32,
139}
140
141impl Fader {
142 pub fn new(control: Arc<FadeControl>, sample_rate: f64) -> Self {
143 let len = ((sample_rate * FADE_SECONDS) as usize).max(1);
144 let quick = ((sample_rate * QUICK_FADE.as_secs_f64()) as usize).max(1);
145 Self {
146 control,
147 pos: len,
148 len,
149 quick_step: len.div_ceil(quick),
150 sample_rate,
151 sleep: 1.0,
152 sleep_target: 1.0f32.to_bits(),
153 sleep_step: 0.0,
154 glide: ((sample_rate * SLEEP_GLIDE_SECONDS) as f32).max(1.0),
155 }
156 }
157
158 pub fn readable(&mut self, wanted: usize, channels: usize) -> usize {
164 let frames = wanted / channels.max(1);
165 if frames > 0 {
166 let us = (frames as f64 * 1e6 / self.sample_rate) as u32;
167 self.control.period_us.store(us, Ordering::Relaxed);
168 }
169 if self.control.from_silence.swap(false, Ordering::AcqRel) {
170 self.pos = 0;
171 }
172 if self.control.audible.load(Ordering::Acquire) {
173 return wanted;
174 }
175 if self.pos == 0 {
176 self.control.silent.store(true, Ordering::Release);
177 }
178 wanted.min(self.pos.div_ceil(self.step()) * channels.max(1))
179 }
180
181 fn step(&self) -> usize {
183 if self.control.quick.load(Ordering::Relaxed) {
184 self.quick_step
185 } else {
186 1
187 }
188 }
189
190 pub fn hold(&mut self) {
194 if !self.control.audible.load(Ordering::Acquire) {
195 self.pos = 0;
196 self.control.silent.store(true, Ordering::Release);
197 }
198 }
199
200 fn follow_sleep(&mut self) {
202 let bits = self.control.sleep_gain.load(Ordering::Acquire);
203 let snap = self.control.sleep_snap.swap(false, Ordering::AcqRel);
204 if bits == self.sleep_target && !snap {
205 return;
206 }
207 self.sleep_target = bits;
208 let target = f32::from_bits(bits);
209 if snap {
210 self.sleep = target;
211 self.sleep_step = 0.0;
212 } else {
213 self.sleep_step = (target - self.sleep) / self.glide;
214 }
215 }
216
217 pub fn apply(&mut self, samples: &mut [f32], channels: usize) {
219 self.ramp(samples, channels);
220 if self.control.muted.load(Ordering::Relaxed) {
221 samples.fill(0.0);
222 }
223 }
224
225 fn ramp(&mut self, samples: &mut [f32], channels: usize) {
226 self.follow_sleep();
227 let rising = self.control.audible.load(Ordering::Acquire);
228 let pausing = !((rising && self.pos == self.len) || (!rising && self.pos == 0));
229 let sleeping = self.sleep != 1.0 || self.sleep_step != 0.0;
230 if !pausing && !sleeping {
231 return;
232 }
233 let target = f32::from_bits(self.sleep_target);
234 let step = self.step();
235 for frame in samples.chunks_mut(channels.max(1)) {
236 if pausing {
237 self.pos = if rising {
238 (self.pos + step).min(self.len)
239 } else {
240 self.pos.saturating_sub(step)
241 };
242 }
243 if self.sleep_step != 0.0 {
244 self.sleep += self.sleep_step;
245 if (self.sleep_step > 0.0 && self.sleep >= target)
246 || (self.sleep_step < 0.0 && self.sleep <= target)
247 {
248 self.sleep = target;
249 self.sleep_step = 0.0;
250 }
251 }
252 let level = self.pos as f32 / self.len as f32;
255 let gain = level * level * self.sleep;
256 for s in frame {
257 *s *= gain;
258 }
259 }
260 if !rising && self.pos == 0 {
261 self.control.silent.store(true, Ordering::Release);
262 }
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 const RATE: f64 = 1000.0; fn render(fader: &mut Fader, frames: usize) -> Vec<f32> {
273 let wanted = fader.readable(frames * 2, 2);
274 let mut out = vec![1.0f32; wanted];
275 fader.apply(&mut out, 2);
276 out
277 }
278
279 #[test]
280 fn a_fade_out_over_silence_is_silent_at_once() {
281 let control = FadeControl::new();
282 let mut fader = Fader::new(control.clone(), RATE);
283 fader.hold();
284 assert!(!control.is_silent(), "holding at full level fades nothing");
285
286 control.fade_out();
287 fader.hold();
288 assert!(control.is_silent());
289 assert_eq!(
290 fader.readable(64, 2),
291 0,
292 "nothing of the track plays after the pause"
293 );
294 }
295
296 #[test]
297 fn full_level_passes_samples_through() {
298 let control = FadeControl::new();
299 let mut fader = Fader::new(control, RATE);
300 let out = render(&mut fader, 64);
301 assert_eq!(out.len(), 128);
302 assert!(out.iter().all(|s| *s == 1.0));
303 }
304
305 #[test]
306 fn muted_reads_on_and_plays_silence() {
307 let control = FadeControl::new();
308 control.muted.store(true, Ordering::Release);
309 let mut fader = Fader::new(control, RATE);
310 let out = render(&mut fader, 64);
311 assert_eq!(out.len(), 128, "the track moves on as if heard");
312 assert!(out.iter().all(|s| *s == 0.0));
313 }
314
315 #[test]
316 fn fade_out_stops_reading_at_silence() {
317 let control = FadeControl::new();
318 let mut fader = Fader::new(control.clone(), RATE);
319 control.fade_out();
320
321 let first = render(&mut fader, 100);
322 assert_eq!(first.len(), 200);
323 assert!(first.windows(2).all(|w| w[1] <= w[0]), "gain only falls");
324 assert!(!control.is_silent());
325
326 let second = render(&mut fader, 100);
327 assert_eq!(second.len(), 100, "only the rest of the ramp is read");
328 assert_eq!(*second.last().unwrap(), 0.0);
329 assert!(control.is_silent());
330
331 assert!(
332 render(&mut fader, 100).is_empty(),
333 "nothing read once silent"
334 );
335 }
336
337 #[test]
338 fn fade_in_from_stopped_starts_at_silence() {
339 let control = FadeControl::new();
340 let mut fader = Fader::new(control.clone(), RATE);
341 control.fade_in(true);
342
343 let out = render(&mut fader, 200);
344 assert!(out[0] < 0.01);
345 assert!(out.windows(2).all(|w| w[1] >= w[0]), "gain only rises");
346 assert_eq!(*out.last().unwrap(), 1.0);
347 }
348
349 #[test]
352 fn a_sleep_gain_glides_and_full_level_is_untouched() {
353 let control = FadeControl::new();
354 let mut fader = Fader::new(control.clone(), RATE);
355 assert!(render(&mut fader, 10).iter().all(|s| *s == 1.0));
356
357 control.set_sleep_gain(0.5, false);
358 let glide = render(&mut fader, 100); assert!(glide.windows(2).all(|w| w[1] <= w[0]), "only falls");
360 assert!(glide[0] < 1.0 && glide[0] > 0.99, "no step: {}", glide[0]);
361 assert!((glide.last().unwrap() - 0.5).abs() < 1e-4);
362 assert!(
363 render(&mut fader, 10)
364 .iter()
365 .all(|s| (*s - 0.5).abs() < 1e-6)
366 );
367
368 control.set_sleep_gain(1.0, true);
369 assert!(
370 render(&mut fader, 10).iter().all(|s| *s == 1.0),
371 "snapped back"
372 );
373 }
374
375 #[test]
376 fn resume_mid_fade_turns_around() {
377 let control = FadeControl::new();
378 let mut fader = Fader::new(control.clone(), RATE);
379 control.fade_out();
380 let down = render(&mut fader, 50);
381 control.fade_in(false);
382 let up = render(&mut fader, 10);
383 assert!(up[0] > *down.last().unwrap(), "rises from where it was");
384 assert!(!control.is_silent());
385 }
386
387 #[test]
393 fn a_quick_fade_out_and_in_has_no_step() {
394 let rate = 48000.0;
395 let sine = |phase: f64, n: usize| {
396 (0..n)
397 .map(|i| (phase + std::f64::consts::TAU * 1000.0 * i as f64 / rate).sin() as f32)
398 .collect::<Vec<f32>>()
399 };
400 let callback = |fader: &mut Fader, source: &[f32], at: &mut usize, frames: usize| {
403 let take = fader.readable(frames, 1);
404 let mut out = source[*at..*at + take].to_vec();
405 *at += take;
406 fader.apply(&mut out, 1);
407 out
408 };
409
410 let old = FadeControl::new();
411 let mut fader = Fader::new(old.clone(), rate);
412 let playing = sine(0.0, 48000);
413 let mut at = 0;
414 let mut heard = callback(&mut fader, &playing, &mut at, 492);
417 old.fade_out_quickly();
418 let mut tail = Vec::new();
419 for _ in 0..20 {
420 tail.extend(callback(&mut fader, &playing, &mut at, 512));
421 if old.is_silent() {
422 break;
423 }
424 }
425 assert!(old.is_silent());
426 let quick = (rate * QUICK_FADE.as_secs_f64()) as usize;
427 assert!(tail.len() <= quick + 1, "faded in {} frames", tail.len());
428 heard.extend(tail);
429
430 let new = FadeControl::new();
431 let mut fader = Fader::new(new.clone(), rate);
432 new.fade_in_quickly(true);
433 let next = sine(2.9, 2048);
434 heard.extend(callback(&mut fader, &next, &mut 0, 2048));
435
436 let natural = (std::f64::consts::TAU * 1000.0 / rate) as f32 * 1.05;
438 let worst = heard
439 .windows(2)
440 .map(|w| (w[1] - w[0]).abs())
441 .fold(0.0, f32::max);
442 assert!(
443 worst <= natural,
444 "a step of {worst}, more than the sine's own {natural}"
445 );
446
447 new.fade_out();
450 let pause = callback(&mut fader, &playing, &mut 0, 48000);
451 assert_eq!(pause.len(), (rate * FADE_SECONDS) as usize);
452 }
453
454 #[test]
457 fn the_callback_reports_its_period() {
458 for (frames, rate) in [(512usize, 44100.0), (1024, 48000.0)] {
459 let control = FadeControl::new();
460 let mut fader = Fader::new(control.clone(), rate);
461 assert_eq!(control.period(), std::time::Duration::ZERO);
462 fader.readable(frames * 2, 2);
463 let want = frames as f64 / rate;
464 assert!(
465 (control.period().as_secs_f64() - want).abs() < 2e-6,
466 "{frames} frames"
467 );
468 }
469 }
470}