use super::*;
pub(crate) mod detail {
use super::*;
pub use crate::fixedmath::I3F29 as EnvSignalFxP;
#[derive(Eq, PartialEq, Clone, Copy, Default)]
pub enum EnvMode {
#[default]
Release,
Attack,
Decay,
}
pub trait EnvType<T: DspFormatBase>: Copy + Default + From<T::Scalar> + PartialOrd {
fn to_scalar(self) -> T::Scalar;
}
impl EnvType<i16> for EnvSignalFxP {
fn to_scalar(self) -> ScalarFxP {
ScalarFxP::saturating_from_num(self)
}
}
impl<T: crate::Float + Send> EnvType<T> for T
where
T: From<crate::IScalarFxP> + From<crate::NoteFxP>,
{
fn to_scalar(self) -> Self {
self
}
}
pub trait EnvOps: crate::DspFormatBase {
const SIGNAL_MIN: Self::EnvSignal;
const SIGNAL_MAX: Self::EnvSignal;
const ATTACK_THRESHOLD: Self::EnvSignal;
const ADR_DEFAULT: Self::EnvParam;
fn calc_env(
context: &Self::Context,
setpoint: Self::EnvSignal,
setpoint_old: Self::EnvSignal,
last: Self::EnvSignal,
rise_time: Self::EnvParam,
) -> Self::EnvSignal;
}
}
use detail::{EnvMode, EnvSignalFxP, EnvType};
#[derive(Clone)]
pub struct EnvParams<T: DspFormatBase> {
pub attack: T::EnvParam,
pub decay: T::EnvParam,
pub sustain: T::Scalar,
pub release: T::EnvParam,
}
impl<T: DspFormatBase + detail::EnvOps> Default for EnvParams<T> {
fn default() -> Self {
Self {
attack: T::ADR_DEFAULT,
decay: T::ADR_DEFAULT,
sustain: T::Scalar::one(),
release: T::ADR_DEFAULT,
}
}
}
impl<T: DspFloat> From<&EnvParams<i16>> for EnvParams<T> {
fn from(value: &EnvParams<i16>) -> Self {
EnvParams::<T> {
attack: value.attack.to_num(),
decay: value.decay.to_num(),
sustain: value.sustain.to_num(),
release: value.release.to_num(),
}
}
}
#[derive(Clone, Default)]
pub struct Env<T: DspFormatBase + detail::EnvOps> {
setpoint: T::EnvSignal,
signal: T::EnvSignal,
mode: EnvMode,
}
impl<T: DspFormat> Device<T> for Env<T> {
type Input = bool;
type Params = EnvParams<T>;
type Output = T::Scalar;
fn next(&mut self, context: &T::Context, gate: bool, params: EnvParams<T>) -> T::Scalar {
let setpoint_old = self.setpoint;
if !gate {
self.mode = EnvMode::Release;
self.setpoint = T::SIGNAL_MIN;
} else if self.mode == EnvMode::Release {
self.mode = EnvMode::Attack;
self.setpoint = T::SIGNAL_MAX;
} else if self.mode == EnvMode::Attack && self.signal > T::ATTACK_THRESHOLD {
self.mode = EnvMode::Decay;
}
let rise = match self.mode {
EnvMode::Attack => params.attack,
EnvMode::Decay => {
self.setpoint = params.sustain.into();
params.decay
}
EnvMode::Release => params.release,
};
self.signal = T::calc_env(context, self.setpoint, setpoint_old, self.signal, rise);
self.signal.to_scalar()
}
}
impl<T: DspFloat> detail::EnvOps for T {
const SIGNAL_MIN: T = T::ZERO;
const SIGNAL_MAX: T = T::ONE;
const ATTACK_THRESHOLD: T = T::POINT_NINE_EIGHT;
const ADR_DEFAULT: T = T::POINT_ONE;
fn calc_env(context: &Context<T>, setpoint: T, setpoint_old: T, last: T, rise_time: T) -> T {
let k = rise_time * (context.sample_rate / T::TWO) + T::ONE;
let pro = setpoint_old + setpoint - last - last;
let delta = pro / k;
last + delta
}
}
impl detail::EnvOps for i16 {
const ATTACK_THRESHOLD: EnvSignalFxP = EnvSignalFxP::lit("0.98");
const SIGNAL_MAX: EnvSignalFxP = EnvSignalFxP::lit("0x0.FFFC");
const SIGNAL_MIN: EnvSignalFxP = EnvSignalFxP::lit("0x0.0004");
const ADR_DEFAULT: EnvParamFxP = EnvParamFxP::lit("0.1");
fn calc_env(
context: &ContextFxP,
setpoint: EnvSignalFxP,
setpoint_old: EnvSignalFxP,
last: EnvSignalFxP,
rise_time: EnvParamFxP,
) -> EnvSignalFxP {
use crate::fixedmath::{one_over_one_plus, I2F14, U16F0};
let sr = U16F0::from_bits(context.sample_rate.value() >> 1);
let k = rise_time.wide_mul(sr);
let (gain, shift) = one_over_one_plus(k);
let pro = I2F14::saturating_from_num(setpoint_old + setpoint - last.unwrapped_shl(1));
let delta = pro.wide_mul_unsigned(gain).unwrapped_shr(shift);
last + delta
}
}