use core::num::NonZeroU32;
#[cfg(not(feature = "std"))]
use num_traits::Float;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::filter::smoothing::f32::{
DEFAULT_SETTLE_EPSILON, DEFAULT_SMOOTH_SECONDS, SmoothingFilter, SmoothingFilterCoeff,
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LinearRange {
pub min: f32,
pub max: f32,
}
impl LinearRange {
pub fn new(min: f32, max: f32) -> Self {
Self { min, max }
}
pub fn clamp(&self, val: f32) -> f32 {
if self.min > self.max {
val.min(self.min).max(self.max)
} else {
val.min(self.max).max(self.min)
}
}
}
impl Default for LinearRange {
fn default() -> Self {
Self { min: 0.0, max: 1.0 }
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NormToFreqRange {
min_hz: f32,
max_hz: f32,
min_log2: f32,
range: f32,
}
impl NormToFreqRange {
pub fn new(min_hz: f32, max_hz: f32) -> Self {
assert!(min_hz < max_hz);
assert_ne!(min_hz, 0.0);
assert_ne!(max_hz, 0.0);
let min_log2 = min_hz.log2();
let range = max_hz.log2() - min_log2;
Self {
min_hz,
max_hz,
min_log2,
range,
}
}
pub fn min_hz(&self) -> f32 {
self.min_hz
}
pub fn max_hz(&self) -> f32 {
self.max_hz
}
pub fn to_hz(&self, normalized: f32) -> f32 {
if normalized <= 0.0 {
return self.min_hz;
}
if normalized >= 1.0 {
return self.max_hz;
}
2.0f32.powf((normalized * self.range) + self.min_log2)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NormToPowRange {
pub exponent: f32,
min: f32,
max: f32,
}
impl NormToPowRange {
pub fn new(min: f32, max: f32, exponent: f32) -> Self {
assert!(min <= max);
Self { exponent, min, max }
}
pub fn min(&self) -> f32 {
self.min
}
pub fn max(&self) -> f32 {
self.max
}
pub fn to_dsp(&self, normalized: f32) -> f32 {
if normalized <= 0.0 {
return self.min;
}
if normalized >= 1.0 {
return self.max;
}
normalized.powf(self.exponent) * (self.max - self.min) + self.min
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SmootherConfig {
pub smooth_seconds: f32,
pub settle_epsilon: f32,
}
impl Default for SmootherConfig {
fn default() -> Self {
Self {
smooth_seconds: DEFAULT_SMOOTH_SECONDS,
settle_epsilon: DEFAULT_SETTLE_EPSILON,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SmoothedParam {
target_value: f32,
target_times_a: f32,
filter: SmoothingFilter,
coeff: SmoothingFilterCoeff,
smooth_secs: f32,
settle_epsilon: f32,
}
impl SmoothedParam {
pub fn new(value: f32, config: SmootherConfig, sample_rate: NonZeroU32) -> Self {
let smooth_secs = config.smooth_seconds.max(0.00001);
let settle_epsilon = config.settle_epsilon.max(f32::EPSILON);
let coeff = SmoothingFilterCoeff::new(sample_rate, smooth_secs);
Self {
target_value: value,
target_times_a: value * coeff.a0,
filter: SmoothingFilter::new(value),
coeff,
smooth_secs,
settle_epsilon,
}
}
pub fn target_value(&self) -> f32 {
self.target_value
}
pub fn set_value(&mut self, value: f32) {
self.target_value = value;
self.target_times_a = value * self.coeff.a0;
}
pub fn settle(&mut self) -> bool {
self.filter.settle(self.target_value, self.settle_epsilon)
}
pub fn is_smoothing(&self) -> bool {
!self.filter.has_settled(self.target_value)
}
pub fn has_settled(&self) -> bool {
self.filter.has_settled(self.target_value)
}
pub fn has_settled_at(&self, value: f32) -> bool {
self.target_value == value && self.filter.has_settled(self.target_value)
}
pub fn has_settled_at_or_below(&self, value: f32) -> bool {
self.target_value <= value && self.filter.has_settled(self.target_value)
}
pub fn reset_to_target(&mut self) {
self.filter = SmoothingFilter::new(self.target_value);
}
#[inline(always)]
pub fn next_smoothed(&mut self) -> f32 {
self.filter
.process_sample_a(self.target_times_a, self.coeff.b1)
}
pub fn process_into_buffer(&mut self, buffer: &mut [f32]) {
if self.is_smoothing() {
self.filter
.process_into_buffer(buffer, self.target_value, self.coeff);
self.filter.settle(self.target_value, self.settle_epsilon);
} else {
buffer.fill(self.target_value);
}
}
pub fn set_smooth_seconds(&mut self, seconds: f32, sample_rate: NonZeroU32) {
self.coeff = SmoothingFilterCoeff::new(sample_rate, seconds);
self.smooth_secs = seconds;
}
pub fn update_sample_rate(&mut self, sample_rate: NonZeroU32) {
self.coeff = SmoothingFilterCoeff::new(sample_rate, self.smooth_secs);
}
}
#[cfg(feature = "alloc")]
pub struct SmoothedParamBuffer {
smoother: SmoothedParam,
buffer: Vec<f32>,
buffer_is_constant: bool,
}
#[cfg(feature = "alloc")]
impl SmoothedParamBuffer {
pub fn new(
value: f32,
config: SmootherConfig,
sample_rate: NonZeroU32,
max_block_frames: usize,
) -> Self {
let mut buffer = Vec::new();
buffer.reserve_exact(max_block_frames);
buffer.resize(max_block_frames, value);
Self {
smoother: SmoothedParam::new(value, config, sample_rate),
buffer,
buffer_is_constant: true,
}
}
pub fn target_value(&self) -> f32 {
self.smoother.target_value()
}
pub fn set_value(&mut self, value: f32) {
self.smoother.set_value(value);
}
pub fn reset(&mut self) {
if self.smoother.is_smoothing() || !self.buffer_is_constant {
self.buffer.fill(self.smoother.target_value);
self.buffer_is_constant = true;
}
self.smoother.reset_to_target();
}
pub fn get_buffer(&mut self, frames: usize) -> (&[f32], bool) {
self.buffer_is_constant = !self.smoother.is_smoothing();
self.smoother
.process_into_buffer(&mut self.buffer[..frames]);
(
&self.buffer[..frames],
self.buffer_is_constant || frames < 2,
)
}
pub fn is_smoothing(&self) -> bool {
self.smoother.is_smoothing()
}
pub fn has_settled(&self) -> bool {
self.smoother.has_settled()
}
pub fn has_settled_at(&self, value: f32) -> bool {
self.smoother.has_settled_at(value)
}
pub fn has_settled_at_or_below(&self, value: f32) -> bool {
self.smoother.has_settled_at_or_below(value)
}
pub fn update_stream(&mut self, sample_rate: NonZeroU32, max_block_frames: usize) {
self.smoother.update_sample_rate(sample_rate);
if self.buffer.len() > max_block_frames {
self.buffer.resize(max_block_frames, 0.0);
} else if self.buffer.len() < max_block_frames {
self.buffer
.reserve_exact(max_block_frames - self.buffer.len());
self.buffer
.resize(max_block_frames, self.smoother.target_value());
}
}
}