use std::time::Duration;
#[derive(PartialEq, Clone, Copy)]
pub struct Fader {
length: Duration,
running_time: Duration,
volume_from: f32,
volume_to: f32,
is_running: bool,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum FadeDirection {
In,
Out,
}
fn lerp(t: f32, a: f32, b: f32) -> f32 { (1.0 - t) * a + t * b }
impl Fader {
pub fn fade(length: Duration, volume_from: f32, volume_to: f32) -> Self {
Self {
length,
running_time: Duration::default(),
volume_from,
volume_to,
is_running: true,
}
}
pub fn fade_in(time: Duration, volume_to: f32) -> Self { Self::fade(time, 0.0, volume_to) }
pub fn fade_out(time: Duration, volume_from: f32) -> Self { Self::fade(time, volume_from, 0.0) }
pub fn update_target_volume(&mut self, volume: f32) {
match self.direction() {
FadeDirection::In => {
self.volume_to = volume;
},
FadeDirection::Out => {
if self.get_volume() > volume {
self.volume_from = volume;
}
},
}
}
pub fn direction(&self) -> FadeDirection {
if self.volume_to < self.volume_from {
FadeDirection::Out
} else {
FadeDirection::In
}
}
pub fn update(&mut self, dt: Duration) {
if self.is_running {
self.running_time += dt;
if self.running_time >= self.length {
self.running_time = self.length;
self.is_running = false;
}
}
}
pub fn get_volume(&self) -> f32 {
lerp(
self.running_time.as_nanos() as f32 / self.length.as_nanos() as f32,
self.volume_from,
self.volume_to,
)
}
pub fn is_finished(&self) -> bool { self.running_time >= self.length || !self.is_running }
}
impl Default for Fader {
fn default() -> Self {
Self {
length: Duration::default(),
running_time: Duration::default(),
volume_from: 0.0,
volume_to: 1.0,
is_running: false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fade_direction_in() {
let fader = Fader::fade_in(Duration::from_secs(10), 0.0);
assert_eq!(fader.direction(), FadeDirection::In);
}
#[test]
fn fade_direction_out() {
let fader = Fader::fade_out(Duration::from_secs(10), 1.0);
assert_eq!(fader.direction(), FadeDirection::Out);
}
#[test]
fn fade_out_completes() {
let mut fader = Fader::fade_out(Duration::from_secs(10), 1.0);
fader.update(Duration::from_secs(10));
assert_eq!(fader.get_volume(), 0.0);
assert!(fader.is_finished());
}
#[test]
fn update_target_volume_fading_out_when_currently_above() {
let mut fader = Fader::fade_out(Duration::from_secs(20), 1.0);
fader.update(Duration::from_millis(100));
fader.update_target_volume(0.4);
fader.update(Duration::from_millis(100));
assert!(fader.get_volume() < 0.4)
}
#[test]
fn update_target_volume_fading_out_when_currently_below() {
let mut fader = Fader::fade_out(Duration::from_secs(10), 0.8);
fader.update(Duration::from_secs(9));
fader.update_target_volume(1.0);
fader.update(Duration::from_millis(100));
assert!(fader.get_volume() < 0.2);
}
#[test]
fn update_target_volume_fading_in_when_currently_above() {
let mut fader = Fader::fade_in(Duration::from_secs(10), 1.0);
fader.update(Duration::from_secs(9));
fader.update_target_volume(0.4);
fader.update(Duration::from_secs(1));
assert_eq!(fader.get_volume(), 0.4);
}
#[test]
fn update_target_volume_fading_in_when_currently_below() {
let mut fader = Fader::fade_in(Duration::from_secs(20), 1.0);
fader.update(Duration::from_millis(100));
fader.update_target_volume(0.4);
assert_eq!(fader.volume_to, 0.4);
}
}