unnieversal/crates/baseplug/src/smooth.rs

195 lines
4.0 KiB
Rust

use std::fmt;
use std::ops;
use std::slice;
use num_traits::Float;
const SETTLE: f32 = 0.00001f32;
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum SmoothStatus {
Inactive,
Active,
Deactivating
}
impl SmoothStatus {
#[inline]
fn is_active(&self) -> bool {
self != &SmoothStatus::Inactive
}
}
pub struct SmoothOutput<'a, T> {
pub values: &'a [T],
pub status: SmoothStatus
}
impl<'a, T> SmoothOutput<'a, T> {
#[inline]
pub fn is_smoothing(&self) -> bool {
self.status.is_active()
}
}
impl<'a, T, I> ops::Index<I> for SmoothOutput<'a, T>
where I: slice::SliceIndex<[T]>
{
type Output = I::Output;
#[inline]
fn index(&self, idx: I) -> &I::Output {
&self.values[idx]
}
}
pub struct Smooth<T: Float> {
output: [T; crate::MAX_BLOCKSIZE],
input: T,
status: SmoothStatus,
a: T,
b: T,
last_output: T
}
impl<T> Smooth<T>
where T: Float + fmt::Display
{
pub fn new(input: T) -> Self {
Self {
status: SmoothStatus::Inactive,
input,
output: [input; crate::MAX_BLOCKSIZE],
a: T::one(),
b: T::zero(),
last_output: input
}
}
pub fn reset(&mut self, val: T)
{
*self = Self {
a: self.a,
b: self.b,
..Self::new(val)
};
}
pub fn set(&mut self, val: T) {
self.input = val;
self.status = SmoothStatus::Active;
}
#[inline]
pub fn dest(&self) -> T {
self.input
}
#[inline]
pub fn output(&self) -> SmoothOutput<T> {
SmoothOutput {
values: &self.output,
status: self.status
}
}
#[inline]
pub fn current_value(&self) -> SmoothOutput<T> {
SmoothOutput {
values: slice::from_ref(&self.last_output),
status: self.status
}
}
pub fn update_status_with_epsilon(&mut self, epsilon: T) -> SmoothStatus {
let status = self.status;
match status {
SmoothStatus::Active => {
if (self.input - self.output[0]).abs() < epsilon {
self.reset(self.input);
self.status = SmoothStatus::Deactivating;
}
},
SmoothStatus::Deactivating =>
self.status = SmoothStatus::Inactive,
_ => ()
};
self.status
}
pub fn process(&mut self, nframes: usize) {
if self.status != SmoothStatus::Active {
return
}
let nframes = nframes.min(crate::MAX_BLOCKSIZE);
let input = self.input * self.a;
self.output[0] = input + (self.last_output * self.b);
for i in 1..nframes {
self.output[i] = input + (self.output[i - 1] * self.b);
}
self.last_output = self.output[nframes - 1];
}
#[inline]
pub fn is_active(&self) -> bool {
self.status.is_active()
}
}
impl Smooth<f32> {
pub fn set_speed_ms(&mut self, sample_rate: f32, ms: f32) {
self.b = (-1.0f32 / (ms * (sample_rate / 1000.0f32))).exp();
self.a = 1.0f32 - self.b;
}
#[inline]
pub fn update_status(&mut self) -> SmoothStatus {
self.update_status_with_epsilon(SETTLE)
}
}
impl<T> From<T> for Smooth<T>
where T: Float + fmt::Display
{
fn from(val: T) -> Self {
Self::new(val)
}
}
impl<T, I> ops::Index<I> for Smooth<T>
where I: slice::SliceIndex<[T]>,
T: Float
{
type Output = I::Output;
#[inline]
fn index(&self, idx: I) -> &I::Output {
&self.output[idx]
}
}
impl<T> fmt::Debug for Smooth<T>
where T: Float + fmt::Debug
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(concat!("Smooth<", stringify!(T), ">"))
.field("output[0]", &self.output[0])
.field("input", &self.input)
.field("status", &self.status)
.field("last_output", &self.last_output)
.finish()
}
}