mod knot;
pub use knot::*;
mod tests;
use crate::{
math::lerp,
prelude::{KnotValue, LoopKind},
Vec,
};
use core::cmp::Ordering;
use super::EnvelopePreset;
const SAFETY_EPSILON: f32 = f32::EPSILON * 2.0;
#[derive(Debug, Clone, PartialEq)]
pub struct Envelope<T>
where
T: KnotValue,
{
pub knots: Vec<Knot<T>>,
pub loop_kind: LoopKind,
release: bool,
release_time: Option<f32>,
release_loop_pos: f32,
head: usize,
}
impl<T> Default for Envelope<T>
where
T: KnotValue,
{
fn default() -> Self {
let len = 2;
let mut knots = Vec::new();
for i in 0..len {
let time = i as f32 / (len - 1) as f32;
knots.push(Knot {
time,
value: (1.0 - time).into(),
interpolation: Interpolation::Linear,
});
}
Self {
knots,
head: 0,
release: false,
release_time: None,
release_loop_pos: 0.0,
loop_kind: LoopKind::None,
}
}
}
impl<T, V> From<&[Knot<T>]> for Envelope<V>
where
T: KnotValue + Into<V>,
V: KnotValue
{
fn from(source: &[Knot<T>]) -> Self {
let len = source.len();
let mut knots = Vec::<Knot<V>>::new();
for i in 0..len {
let knot = source[i];
knots.push( Knot{
time: knot.time,
value: knot.value.into(),
interpolation: knot.interpolation,
});
}
knots.sort_by(|a, b| match a.partial_cmp(b) {
Some(comp) => comp,
None => Ordering::Equal,
});
Self {
knots,
loop_kind: LoopKind::None,
release: false,
release_time: None,
release_loop_pos: 0.0,
head: 0,
}
}
}
impl<T> From<EnvelopePreset<T>> for Envelope<T>
where
T: 'static + KnotValue,
{
fn from(value: EnvelopePreset<T>) -> Self {
Self::from(value.knots)
.offset_values(value.value_offset)
.scale_values(value.value_scale)
.scale_time(value.time_scale)
.set_loop(value.loop_kind)
}
}
impl<T> Envelope<T>
where
T: KnotValue,
{
pub fn len(&self) -> usize {
self.knots.len()
}
pub fn offset_values(mut self, offset: f32) -> Self {
for knot in &mut self.knots {
*knot = knot.offset(offset.into());
}
self
}
pub fn scale_values(mut self, factor: f32) -> Self {
for knot in &mut self.knots {
*knot = knot.scale_value(factor.into());
}
self
}
pub fn scale_time(mut self, factor: f32) -> Self {
for knot in &mut self.knots {
*knot = knot.scale_time(factor);
}
self
}
pub fn set_loop(mut self, kind: LoopKind) -> Self {
self.loop_kind = kind;
self
}
pub fn sort_by_time(&mut self) {
self.knots.sort_by(|a, b| match a.partial_cmp(b) {
Some(comp) => comp,
None => Ordering::Equal,
});
}
pub fn reset(&mut self) {
self.head = 0;
self.release = false;
self.release_time = None;
self.release_loop_pos = 0.0;
}
pub fn release(&mut self) {
self.release = true;
}
pub fn peek(&mut self, time: f32) -> f32 {
let first_knot = self.knots[0];
if time <= first_knot.time {
return first_knot.value.into();
}
let last_knot = self.knots[self.knots.len() - 1];
let get_loop_time = |loop_in: u8, loop_out: u8| -> (f32, f32) {
let knot_in = self.knots.get(loop_in as usize);
let time_in = if let Some(knot) = knot_in {
knot.time
} else {
0.0
};
let knot_out = self.knots.get(loop_out as usize);
let time_out = if let Some(knot) = knot_out {
knot.time
} else {
last_knot.time
};
(time_in, time_out)
};
let mut get_release_time = |time_in: f32, loop_pos: f32| {
if self.release {
if self.release_time.is_none() {
if time >= time_in {
self.release_time = Some(time);
self.release_loop_pos = loop_pos;
}
}
}
};
match self.loop_kind {
LoopKind::None => {
if time >= last_knot.time {
return last_knot.value.into();
}
return self.peek_within_time_range(time, 1.0);
}
LoopKind::Repeat => {
if time == last_knot.time {
return last_knot.value.into();
}
if time > last_knot.time {
self.head = 0;
let normal_t = get_loop_position_f32(time, first_knot.time, last_knot.time);
return self.peek_within_time_range(normal_t, 1.0);
}
self.peek_within_time_range(time, 1.0)
}
LoopKind::Echo {
loop_in,
loop_out,
decay,
} => {
let decay: f32 = decay.into();
let (time_in, time_out) = get_loop_time(loop_in, loop_out);
let loop_pos = get_loop_position_f32(time, time_in, time_out);
get_release_time(time_in, loop_pos);
if let Some(release_time) = self.release_time {
let local_time = self.release_loop_pos + (time - release_time);
if local_time > last_knot.time {
self.head = 0;
let normal_t =
get_loop_position_f32(local_time, first_knot.time, last_knot.time);
let iteration = get_iteration(local_time, first_knot.time, last_knot.time);
let attenuation = 1.0 - (1.0 - (decay / iteration));
return self.peek_within_time_range(normal_t, attenuation);
}
return self.peek_within_time_range(local_time, 1.0);
} else {
self.peek_within_time_range(loop_pos, 1.0)
}
}
LoopKind::LoopPoints { loop_in, loop_out } => {
let (time_in, time_out) = get_loop_time(loop_in, loop_out);
let loop_pos = get_loop_position_f32(time, time_in, time_out);
get_release_time(time_in, loop_pos);
if let Some(release_time) = self.release_time {
let local_time = self.release_loop_pos + (time - release_time);
if local_time > last_knot.time {
return last_knot.value.into();
}
return self.peek_within_time_range(local_time, 1.0);
} else {
self.peek_within_time_range(loop_pos, 1.0)
}
}
}
}
fn peek_within_time_range(&mut self, time: f32, attenuation: f32) -> f32 {
let last_index = self.knots.len() - 1;
if self.head >= last_index {
{
#[cfg(debug_assertions)]
{
unreachable!();
}
#[cfg(not(debug_assertions))]
{
return 0.0;
}
}
}
let mut current = self.knots[self.head];
let mut next = self.knots[self.head + 1];
if time < current.time || time > next.time {
loop {
if time < current.time {
#[cfg(debug_assertions)]
{
if self.head == 0 {
unreachable!()
}
}
self.head -= 1;
current = self.knots[self.head];
next = self.knots[self.head + 1];
} else if time > next.time {
#[cfg(debug_assertions)]
{
if self.head == last_index {
unreachable!()
}
}
self.head += 1;
current = self.knots[self.head];
next = self.knots[self.head + 1];
}
if time >= current.time && time <= next.time {
break;
}
}
}
match current.interpolation {
Interpolation::Linear => {
let local_time = time - current.time;
let next_time = next.time - current.time;
let x = local_time / next_time;
lerp(current.value, next.value, x) * attenuation
}
Interpolation::Step => {
current.value.into() * attenuation
}
}
}
}
pub(crate) fn get_loop_position_f32(t: f32, loop_in: f32, loop_out: f32) -> f32 {
if t > loop_out {
let diff = t - loop_out;
let width = loop_out - loop_in;
if width < SAFETY_EPSILON {
return loop_out;
}
return (diff % width) + loop_in;
}
t
}
fn get_iteration(local_time: f32, first_time: f32, last_time: f32) -> f32 {
if last_time > first_time {
let delta = last_time - first_time;
let iteration = (local_time - first_time) / delta;
libm::floorf(iteration)
} else {
libm::floorf(local_time / last_time)
}
}