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 is_faded_out(&self) -> bool {
105 !self.audible.load(Ordering::Acquire)
106 }
107
108 pub fn period(&self) -> std::time::Duration {
111 std::time::Duration::from_micros(self.period_us.load(Ordering::Relaxed) as u64)
112 }
113
114 pub fn set_sleep_gain(&self, gain: f32, snap: bool) {
117 if snap {
118 self.sleep_snap.store(true, Ordering::Release);
119 }
120 self.sleep_gain
121 .store(gain.clamp(0.0, 1.0).to_bits(), Ordering::Release);
122 }
123}
124
125pub struct Fader {
131 control: Arc<FadeControl>,
132 pos: usize,
135 len: usize,
136 quick_step: usize,
138 sample_rate: f64,
139 sleep: f32,
142 sleep_target: u32,
143 sleep_step: f32,
144 glide: f32,
145}
146
147impl Fader {
148 pub fn new(control: Arc<FadeControl>, sample_rate: f64) -> Self {
149 let len = ((sample_rate * FADE_SECONDS) as usize).max(1);
150 let quick = ((sample_rate * QUICK_FADE.as_secs_f64()) as usize).max(1);
151 Self {
152 control,
153 pos: len,
154 len,
155 quick_step: len.div_ceil(quick),
156 sample_rate,
157 sleep: 1.0,
158 sleep_target: 1.0f32.to_bits(),
159 sleep_step: 0.0,
160 glide: ((sample_rate * SLEEP_GLIDE_SECONDS) as f32).max(1.0),
161 }
162 }
163
164 pub fn readable(&mut self, wanted: usize, channels: usize) -> usize {
170 let frames = wanted / channels.max(1);
171 if frames > 0 {
172 let us = (frames as f64 * 1e6 / self.sample_rate) as u32;
173 self.control.period_us.store(us, Ordering::Relaxed);
174 }
175 if self.control.from_silence.swap(false, Ordering::AcqRel) {
176 self.pos = 0;
177 }
178 if self.control.audible.load(Ordering::Acquire) {
179 return wanted;
180 }
181 if self.pos == 0 {
182 self.control.silent.store(true, Ordering::Release);
183 }
184 wanted.min(self.pos.div_ceil(self.step()) * channels.max(1))
185 }
186
187 fn step(&self) -> usize {
189 if self.control.quick.load(Ordering::Relaxed) {
190 self.quick_step
191 } else {
192 1
193 }
194 }
195
196 pub fn hold(&mut self) {
200 if !self.control.audible.load(Ordering::Acquire) {
201 self.pos = 0;
202 self.control.silent.store(true, Ordering::Release);
203 }
204 }
205
206 fn follow_sleep(&mut self) {
208 let bits = self.control.sleep_gain.load(Ordering::Acquire);
209 let snap = self.control.sleep_snap.swap(false, Ordering::AcqRel);
210 if bits == self.sleep_target && !snap {
211 return;
212 }
213 self.sleep_target = bits;
214 let target = f32::from_bits(bits);
215 if snap {
216 self.sleep = target;
217 self.sleep_step = 0.0;
218 } else {
219 self.sleep_step = (target - self.sleep) / self.glide;
220 }
221 }
222
223 pub fn apply(&mut self, samples: &mut [f32], channels: usize) {
225 self.ramp(samples, channels);
226 if self.control.muted.load(Ordering::Relaxed) {
227 samples.fill(0.0);
228 }
229 }
230
231 fn ramp(&mut self, samples: &mut [f32], channels: usize) {
232 self.follow_sleep();
233 let rising = self.control.audible.load(Ordering::Acquire);
234 let pausing = !((rising && self.pos == self.len) || (!rising && self.pos == 0));
235 let sleeping = self.sleep != 1.0 || self.sleep_step != 0.0;
236 if !pausing && !sleeping {
237 return;
238 }
239 let target = f32::from_bits(self.sleep_target);
240 let step = self.step();
241 for frame in samples.chunks_mut(channels.max(1)) {
242 if pausing {
243 self.pos = if rising {
244 (self.pos + step).min(self.len)
245 } else {
246 self.pos.saturating_sub(step)
247 };
248 }
249 if self.sleep_step != 0.0 {
250 self.sleep += self.sleep_step;
251 if (self.sleep_step > 0.0 && self.sleep >= target)
252 || (self.sleep_step < 0.0 && self.sleep <= target)
253 {
254 self.sleep = target;
255 self.sleep_step = 0.0;
256 }
257 }
258 let level = self.pos as f32 / self.len as f32;
261 let gain = level * level * self.sleep;
262 for s in frame {
263 *s *= gain;
264 }
265 }
266 if !rising && self.pos == 0 {
267 self.control.silent.store(true, Ordering::Release);
268 }
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 const RATE: f64 = 1000.0; fn render(fader: &mut Fader, frames: usize) -> Vec<f32> {
279 let wanted = fader.readable(frames * 2, 2);
280 let mut out = vec![1.0f32; wanted];
281 fader.apply(&mut out, 2);
282 out
283 }
284
285 #[test]
286 fn a_fade_out_over_silence_is_silent_at_once() {
287 let control = FadeControl::new();
288 let mut fader = Fader::new(control.clone(), RATE);
289 fader.hold();
290 assert!(!control.is_silent(), "holding at full level fades nothing");
291
292 control.fade_out();
293 fader.hold();
294 assert!(control.is_silent());
295 assert_eq!(
296 fader.readable(64, 2),
297 0,
298 "nothing of the track plays after the pause"
299 );
300 }
301
302 #[test]
303 fn full_level_passes_samples_through() {
304 let control = FadeControl::new();
305 let mut fader = Fader::new(control, RATE);
306 let out = render(&mut fader, 64);
307 assert_eq!(out.len(), 128);
308 assert!(out.iter().all(|s| *s == 1.0));
309 }
310
311 #[test]
312 fn muted_reads_on_and_plays_silence() {
313 let control = FadeControl::new();
314 control.muted.store(true, Ordering::Release);
315 let mut fader = Fader::new(control, RATE);
316 let out = render(&mut fader, 64);
317 assert_eq!(out.len(), 128, "the track moves on as if heard");
318 assert!(out.iter().all(|s| *s == 0.0));
319 }
320
321 #[test]
322 fn fade_out_stops_reading_at_silence() {
323 let control = FadeControl::new();
324 let mut fader = Fader::new(control.clone(), RATE);
325 control.fade_out();
326
327 let first = render(&mut fader, 100);
328 assert_eq!(first.len(), 200);
329 assert!(first.windows(2).all(|w| w[1] <= w[0]), "gain only falls");
330 assert!(!control.is_silent());
331
332 let second = render(&mut fader, 100);
333 assert_eq!(second.len(), 100, "only the rest of the ramp is read");
334 assert_eq!(*second.last().unwrap(), 0.0);
335 assert!(control.is_silent());
336
337 assert!(
338 render(&mut fader, 100).is_empty(),
339 "nothing read once silent"
340 );
341 }
342
343 #[test]
344 fn fade_in_from_stopped_starts_at_silence() {
345 let control = FadeControl::new();
346 let mut fader = Fader::new(control.clone(), RATE);
347 control.fade_in(true);
348
349 let out = render(&mut fader, 200);
350 assert!(out[0] < 0.01);
351 assert!(out.windows(2).all(|w| w[1] >= w[0]), "gain only rises");
352 assert_eq!(*out.last().unwrap(), 1.0);
353 }
354
355 #[test]
358 fn a_sleep_gain_glides_and_full_level_is_untouched() {
359 let control = FadeControl::new();
360 let mut fader = Fader::new(control.clone(), RATE);
361 assert!(render(&mut fader, 10).iter().all(|s| *s == 1.0));
362
363 control.set_sleep_gain(0.5, false);
364 let glide = render(&mut fader, 100); assert!(glide.windows(2).all(|w| w[1] <= w[0]), "only falls");
366 assert!(glide[0] < 1.0 && glide[0] > 0.99, "no step: {}", glide[0]);
367 assert!((glide.last().unwrap() - 0.5).abs() < 1e-4);
368 assert!(
369 render(&mut fader, 10)
370 .iter()
371 .all(|s| (*s - 0.5).abs() < 1e-6)
372 );
373
374 control.set_sleep_gain(1.0, true);
375 assert!(
376 render(&mut fader, 10).iter().all(|s| *s == 1.0),
377 "snapped back"
378 );
379 }
380
381 #[test]
382 fn resume_mid_fade_turns_around() {
383 let control = FadeControl::new();
384 let mut fader = Fader::new(control.clone(), RATE);
385 control.fade_out();
386 let down = render(&mut fader, 50);
387 control.fade_in(false);
388 let up = render(&mut fader, 10);
389 assert!(up[0] > *down.last().unwrap(), "rises from where it was");
390 assert!(!control.is_silent());
391 }
392
393 #[test]
399 fn a_quick_fade_out_and_in_has_no_step() {
400 let rate = 48000.0;
401 let sine = |phase: f64, n: usize| {
402 (0..n)
403 .map(|i| (phase + std::f64::consts::TAU * 1000.0 * i as f64 / rate).sin() as f32)
404 .collect::<Vec<f32>>()
405 };
406 let callback = |fader: &mut Fader, source: &[f32], at: &mut usize, frames: usize| {
409 let take = fader.readable(frames, 1);
410 let mut out = source[*at..*at + take].to_vec();
411 *at += take;
412 fader.apply(&mut out, 1);
413 out
414 };
415
416 let old = FadeControl::new();
417 let mut fader = Fader::new(old.clone(), rate);
418 let playing = sine(0.0, 48000);
419 let mut at = 0;
420 let mut heard = callback(&mut fader, &playing, &mut at, 492);
423 old.fade_out_quickly();
424 let mut tail = Vec::new();
425 for _ in 0..20 {
426 tail.extend(callback(&mut fader, &playing, &mut at, 512));
427 if old.is_silent() {
428 break;
429 }
430 }
431 assert!(old.is_silent());
432 let quick = (rate * QUICK_FADE.as_secs_f64()) as usize;
433 assert!(tail.len() <= quick + 1, "faded in {} frames", tail.len());
434 heard.extend(tail);
435
436 let new = FadeControl::new();
437 let mut fader = Fader::new(new.clone(), rate);
438 new.fade_in_quickly(true);
439 let next = sine(2.9, 2048);
440 heard.extend(callback(&mut fader, &next, &mut 0, 2048));
441
442 let natural = (std::f64::consts::TAU * 1000.0 / rate) as f32 * 1.05;
444 let worst = heard
445 .windows(2)
446 .map(|w| (w[1] - w[0]).abs())
447 .fold(0.0, f32::max);
448 assert!(
449 worst <= natural,
450 "a step of {worst}, more than the sine's own {natural}"
451 );
452
453 new.fade_out();
456 let pause = callback(&mut fader, &playing, &mut 0, 48000);
457 assert_eq!(pause.len(), (rate * FADE_SECONDS) as usize);
458 }
459
460 #[test]
463 fn the_callback_reports_its_period() {
464 for (frames, rate) in [(512usize, 44100.0), (1024, 48000.0)] {
465 let control = FadeControl::new();
466 let mut fader = Fader::new(control.clone(), rate);
467 assert_eq!(control.period(), std::time::Duration::ZERO);
468 fader.readable(frames * 2, 2);
469 let want = frames as f64 / rate;
470 assert!(
471 (control.period().as_secs_f64() - want).abs() < 2e-6,
472 "{frames} frames"
473 );
474 }
475 }
476}