use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize, Copy, Clone, Debug)]
pub struct EnvelopePoint {
pub frame: usize,
pub value: f32,
}
impl EnvelopePoint {
pub fn lerp(a: &EnvelopePoint, b: &EnvelopePoint, pos: usize) -> f32 {
if pos <= a.frame {
a.value
} else if pos >= b.frame {
b.value
} else {
let p: f32 = (pos - a.frame) as f32 / (b.frame - a.frame) as f32;
a.value * (1.0 - p) + b.value * p
}
}
}
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
pub struct Envelope {
pub enabled: bool,
pub point: Vec<EnvelopePoint>,
pub sustain_enabled: bool,
pub sustain_start_point: usize,
pub sustain_end_point: usize,
pub loop_enabled: bool,
pub loop_start_point: usize,
pub loop_end_point: usize,
}
impl Envelope {
pub fn is_valid(&self) -> bool {
self.point.len() >= 2
&& self.point.len() <= 12
&& self.sustain_start_point < self.point.len()
&& self.sustain_end_point < self.point.len()
&& self.loop_start_point < self.point.len()
&& self.loop_end_point < self.point.len()
&& self.loop_start_point <= self.loop_end_point
&& self.sustain_start_point <= self.sustain_end_point
}
pub fn loop_in_sustain(&self, frame: usize) -> usize {
if self.sustain_enabled
&& self.sustain_end_point < self.point.len()
&& self.sustain_start_point < self.point.len()
{
let sustain_end = self.point[self.sustain_end_point].frame;
if frame > sustain_end {
return self.point[self.sustain_start_point].frame;
}
}
frame
}
pub fn loop_in_loop(&self, frame: usize) -> usize {
if self.loop_enabled
&& self.loop_end_point < self.point.len()
&& self.loop_start_point < self.point.len()
{
let loop_end = self.point[self.loop_end_point].frame;
if frame > loop_end {
return self.point[self.loop_start_point].frame;
}
}
frame
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn lerp_at_boundaries() {
let a = EnvelopePoint {
frame: 0,
value: 0.0,
};
let b = EnvelopePoint {
frame: 10,
value: 1.0,
};
assert_eq!(EnvelopePoint::lerp(&a, &b, 0), 0.0);
assert_eq!(EnvelopePoint::lerp(&a, &b, 10), 1.0);
assert!((EnvelopePoint::lerp(&a, &b, 5) - 0.5).abs() < 1e-5);
}
#[test]
fn lerp_before_and_after() {
let a = EnvelopePoint {
frame: 5,
value: 0.0,
};
let b = EnvelopePoint {
frame: 15,
value: 1.0,
};
assert_eq!(EnvelopePoint::lerp(&a, &b, 0), 0.0); assert_eq!(EnvelopePoint::lerp(&a, &b, 20), 1.0); }
#[test]
fn loop_in_sustain_empty_points_no_panic() {
let e = Envelope {
sustain_enabled: true,
sustain_start_point: 5,
sustain_end_point: 10,
point: vec![], ..Default::default()
};
assert_eq!(e.loop_in_sustain(100), 100);
}
#[test]
fn loop_in_loop_empty_points_no_panic() {
let e = Envelope {
loop_enabled: true,
loop_start_point: 0,
loop_end_point: 5,
point: vec![], ..Default::default()
};
assert_eq!(e.loop_in_loop(100), 100);
}
#[test]
fn loop_in_sustain_wraps() {
let e = Envelope {
sustain_enabled: true,
sustain_start_point: 1,
sustain_end_point: 2,
point: vec![
EnvelopePoint {
frame: 0,
value: 0.0,
},
EnvelopePoint {
frame: 10,
value: 0.5,
},
EnvelopePoint {
frame: 20,
value: 1.0,
},
],
..Default::default()
};
assert_eq!(e.loop_in_sustain(15), 15); assert_eq!(e.loop_in_sustain(25), 10); }
}