use four_cc::FourCC;
use strum::VariantNames;
use crate::{
effect::{Effect, EffectTime},
parameter::{
formatters, EnumParameter, EnumParameterValue, FloatParameter, ParameterValueUpdate,
SmoothedParameterValue,
},
utils::{
buffer::{scale_buffer, InterleavedBufferMut},
db_to_linear,
dsp::filters::dc::{DcFilter, DcFilterMode},
},
Error, Parameter, ParameterScaling,
};
#[derive(
Debug, Default, Copy, Clone, PartialEq, strum::Display, strum::EnumString, strum::VariantNames,
)]
pub enum GainEffectDcFilterMode {
#[default]
Off,
Slow,
Default,
Fast,
}
impl GainEffectDcFilterMode {
fn to_dc_mode(self) -> Option<DcFilterMode> {
match self {
GainEffectDcFilterMode::Off => None,
GainEffectDcFilterMode::Slow => Some(DcFilterMode::Slow),
GainEffectDcFilterMode::Default => Some(DcFilterMode::Default),
GainEffectDcFilterMode::Fast => Some(DcFilterMode::Fast),
}
}
}
pub struct GainEffect {
gain: SmoothedParameterValue,
dc_filter_mode: EnumParameterValue<GainEffectDcFilterMode>,
dc_filters: Vec<DcFilter>,
sample_rate: u32,
channel_count: usize,
}
impl GainEffect {
pub const EFFECT_NAME: &str = "Gain";
const MIN_DB: f32 = -60.0;
const MAX_DB: f32 = 24.0;
pub const GAIN: FloatParameter = FloatParameter::new(
FourCC(*b"gain"),
"Gain",
0.000001..=15.848932, 1.0, )
.with_scaling(ParameterScaling::Decibel(Self::MIN_DB, Self::MAX_DB))
.with_formatter(formatters::GAIN);
pub const DC_FILTER: EnumParameter = EnumParameter::new(
FourCC(*b"dcfm"),
"DC Filter",
GainEffectDcFilterMode::VARIANTS,
GainEffectDcFilterMode::Off as usize,
);
pub fn new() -> Self {
Self {
sample_rate: 0,
channel_count: 0,
gain: SmoothedParameterValue::from_description(Self::GAIN),
dc_filter_mode: EnumParameterValue::from_description(Self::DC_FILTER),
dc_filters: Vec::new(),
}
}
pub fn with_parameters(gain_db: f32, dc_mode: GainEffectDcFilterMode) -> Self {
let mut effect = Self::new();
let gain_linear = db_to_linear(gain_db.clamp(Self::MIN_DB, Self::MAX_DB));
effect.gain.init_value(gain_linear);
effect.dc_filter_mode.set_value(dc_mode);
effect
}
}
impl Default for GainEffect {
fn default() -> Self {
Self::new()
}
}
impl Effect for GainEffect {
fn name(&self) -> &'static str {
Self::EFFECT_NAME
}
fn weight(&self) -> usize {
1
}
fn parameters(&self) -> Vec<&dyn Parameter> {
vec![self.gain.description(), self.dc_filter_mode.description()]
}
fn initialize(
&mut self,
sample_rate: u32,
channel_count: usize,
_max_frames: usize,
) -> Result<(), Error> {
self.sample_rate = sample_rate;
self.channel_count = channel_count;
self.gain.set_sample_rate(sample_rate);
let dc_mode = self
.dc_filter_mode
.value()
.to_dc_mode()
.unwrap_or(DcFilterMode::Default);
self.dc_filters = (0..channel_count)
.map(|_| DcFilter::new(sample_rate, dc_mode))
.collect();
Ok(())
}
fn process(&mut self, mut output: &mut [f32], _time: &EffectTime) {
if self.dc_filter_mode.value() != GainEffectDcFilterMode::Off {
for (filter, channel_iter) in self
.dc_filters
.iter_mut()
.zip(output.channels_mut(self.channel_count))
{
filter.process(channel_iter);
}
}
if self.gain.value_need_ramp() {
for frame in output.frames_mut(self.channel_count) {
let gain = self.gain.next_value();
for sample in frame {
*sample *= gain;
}
}
} else {
let gain = self.gain.target_value();
scale_buffer(output, gain);
}
}
fn process_tail(&self) -> Option<usize> {
if let Some(dc_mode) = self.dc_filter_mode.value().to_dc_mode() {
Some(self.sample_rate as usize / dc_mode.hz() as usize)
} else {
Some(0)
}
}
fn process_parameter_update(
&mut self,
id: FourCC,
value: &ParameterValueUpdate,
) -> Result<(), Error> {
match id {
_ if id == Self::GAIN.id() => {
self.gain.apply_update(value);
Ok(())
}
_ if id == Self::DC_FILTER.id() => {
self.dc_filter_mode.apply_update(value);
if let Some(dsp_mode) = self.dc_filter_mode.value().to_dc_mode() {
for filter in &mut self.dc_filters {
filter.set_mode(dsp_mode, self.sample_rate);
}
} else {
for filter in &mut self.dc_filters {
filter.reset();
}
}
Ok(())
}
_ => Err(Error::ParameterError(format!(
"Unknown parameter: '{id}' for effect '{}'",
self.name()
))),
}
}
}