use crate::building_blocks::envelopes::source_env::*;
use crate::building_blocks::oscillators::*;
use crate::building_blocks::{
EnvelopeSegmentInfo, MonoSource, SampleBuffer, SynthParameterLabel, SynthParameterValue, ValOp,
ValueOrModulator,
};
fn y(din: f32, last_y: f32, g: f32) -> f32 {
(1.0 - g) * din + g * last_y
}
#[derive(Clone, Debug)]
pub struct EnvFollower<const BUFSIZE: usize> {
pub range: f32,
pub min: f32,
pub y_0: f32,
pub ga: f32,
pub gr: f32,
pub pre_gain: f32,
}
impl<const BUFSIZE: usize> MonoSource<BUFSIZE> for EnvFollower<BUFSIZE> {
fn set_parameter(&mut self, _par: SynthParameterLabel, _value: &SynthParameterValue) {}
fn set_modulator(
&mut self,
_par: SynthParameterLabel,
_init: f32,
_modulator: Modulator<BUFSIZE>,
) {
}
fn finish(&mut self) {}
fn reset(&mut self) {}
fn is_finished(&self) -> bool {
false
}
fn get_next_block(
&mut self,
start_sample: usize,
in_buffers: &[SampleBuffer],
) -> [f32; BUFSIZE] {
self.get_next_block_with_input(start_sample, &[1.0; BUFSIZE], in_buffers)
}
fn get_next_block_with_input(
&mut self,
start_sample: usize,
input: &[f32; BUFSIZE],
_in_buffers: &[SampleBuffer],
) -> [f32; BUFSIZE] {
let mut out_buf = [0.0; BUFSIZE];
for s in start_sample..BUFSIZE {
let din = input[s].abs() * self.pre_gain;
let y = y(
din,
self.y_0,
if self.y_0 < din { self.ga } else { self.gr },
);
self.y_0 = y;
out_buf[s] = self.min + y * self.range;
}
out_buf
}
}
#[derive(Clone)]
pub struct Modulator<const BUFSIZE: usize> {
pub source: Box<dyn MonoSource<BUFSIZE> + Sync + Send>,
pub op: ValOp,
pub add: f32,
pub positive: bool,
pub rectify: bool,
}
impl<const BUFSIZE: usize> Modulator<BUFSIZE> {
#[allow(clippy::too_many_arguments)]
pub fn env_follower(
op: ValOp,
min: f32,
range: f32,
pre_gain: f32,
attack: f32,
release: f32,
add: f32,
positive: bool,
rectify: bool,
sr: f32,
) -> Modulator<BUFSIZE> {
let env_f = EnvFollower {
range,
min,
pre_gain,
y_0: 0.0,
ga: f32::exp(-1.0 / (sr * attack)),
gr: f32::exp(-1.0 / (sr * release)),
};
Modulator {
source: Box::new(env_f),
op,
add,
positive,
rectify,
}
}
#[allow(clippy::too_many_arguments)]
pub fn lfo(
op: ValOp,
freq: ValueOrModulator<BUFSIZE>,
eff_phase: f32,
amp: ValueOrModulator<BUFSIZE>,
add: f32,
positive: bool,
rectify: bool,
sr: f32,
) -> Modulator<BUFSIZE> {
let mut src_osc = SineOsc::new(5.0, 1.0, sr);
src_osc.set_param_or_modulator(SynthParameterLabel::PitchFrequency, freq);
src_osc.set_param_or_modulator(SynthParameterLabel::OscillatorAmplitude, amp);
src_osc.set_parameter(
SynthParameterLabel::OscillatorPhaseEffective,
&SynthParameterValue::ScalarF32(eff_phase - add),
);
Modulator {
source: Box::new(src_osc),
op,
add,
positive,
rectify,
}
}
#[allow(clippy::too_many_arguments)]
pub fn lfsaw(
op: ValOp,
freq: ValueOrModulator<BUFSIZE>,
eff_phase: f32,
amp: ValueOrModulator<BUFSIZE>,
add: f32,
positive: bool,
rectify: bool,
sr: f32,
) -> Modulator<BUFSIZE> {
let mut src_osc = LFSaw::new(5.0, 1.0, sr);
src_osc.set_param_or_modulator(SynthParameterLabel::PitchFrequency, freq);
src_osc.set_param_or_modulator(SynthParameterLabel::OscillatorAmplitude, amp);
src_osc.set_parameter(
SynthParameterLabel::OscillatorPhaseEffective,
&SynthParameterValue::ScalarF32(eff_phase - add),
);
Modulator {
source: Box::new(src_osc),
op,
add,
positive,
rectify,
}
}
#[allow(clippy::too_many_arguments)]
pub fn lfrsaw(
op: ValOp,
freq: ValueOrModulator<BUFSIZE>,
eff_phase: f32,
amp: ValueOrModulator<BUFSIZE>,
add: f32,
positive: bool,
rectify: bool,
sr: f32,
) -> Modulator<BUFSIZE> {
let mut src_osc = LFRSaw::new(5.0, 1.0, sr);
src_osc.set_param_or_modulator(SynthParameterLabel::PitchFrequency, freq);
src_osc.set_param_or_modulator(SynthParameterLabel::OscillatorAmplitude, amp);
src_osc.set_parameter(
SynthParameterLabel::OscillatorPhaseEffective,
&SynthParameterValue::ScalarF32(eff_phase - add),
);
Modulator {
source: Box::new(src_osc),
op,
add,
positive,
rectify,
}
}
#[allow(clippy::too_many_arguments)]
pub fn lftri(
op: ValOp,
freq: ValueOrModulator<BUFSIZE>,
eff_phase: f32,
amp: ValueOrModulator<BUFSIZE>,
add: f32,
positive: bool,
rectify: bool,
sr: f32,
) -> Modulator<BUFSIZE> {
let mut src_osc = LFTri::new(5.0, 1.0, sr);
src_osc.set_param_or_modulator(SynthParameterLabel::PitchFrequency, freq);
src_osc.set_param_or_modulator(SynthParameterLabel::OscillatorAmplitude, amp);
src_osc.set_parameter(
SynthParameterLabel::OscillatorPhaseEffective,
&SynthParameterValue::ScalarF32(eff_phase - add),
);
Modulator {
source: Box::new(src_osc),
op,
add,
positive,
rectify,
}
}
#[allow(clippy::too_many_arguments)]
pub fn lfsquare(
op: ValOp,
freq: ValueOrModulator<BUFSIZE>,
pw: f32,
amp: ValueOrModulator<BUFSIZE>,
add: f32,
positive: bool,
rectify: bool,
sr: f32,
) -> Modulator<BUFSIZE> {
let mut src_osc = LFSquare::new(5.0, pw, 1.0, sr);
src_osc.set_param_or_modulator(SynthParameterLabel::PitchFrequency, freq);
src_osc.set_param_or_modulator(SynthParameterLabel::OscillatorAmplitude, amp);
Modulator {
source: Box::new(src_osc),
op,
add,
positive,
rectify,
}
}
pub fn lin_ramp(op: ValOp, from: f32, to: f32, time: f32, sr: f32) -> Modulator<BUFSIZE> {
Modulator {
source: Box::new(LinearRamp::new(from, to, time, sr)),
op,
add: 0.0,
positive: false,
rectify: false,
}
}
pub fn log_ramp(op: ValOp, from: f32, to: f32, time: f32, sr: f32) -> Modulator<BUFSIZE> {
Modulator {
source: Box::new(LogRamp::new(from, to, time, sr)),
op,
add: 0.0,
positive: false,
rectify: false,
}
}
pub fn exp_ramp(op: ValOp, from: f32, to: f32, time: f32, sr: f32) -> Modulator<BUFSIZE> {
Modulator {
source: Box::new(ExpRamp::new(from, to, time, sr)),
op,
add: 0.0,
positive: false,
rectify: false,
}
}
pub fn multi_point_envelope(
op: ValOp,
segments: Vec<EnvelopeSegmentInfo>,
loop_env: bool,
sr: f32,
) -> Modulator<BUFSIZE> {
Modulator {
source: Box::new(MultiPointEnvelope::new(segments, loop_env, sr)),
op,
add: 0.0,
positive: false,
rectify: false,
}
}
pub fn process(
&mut self,
original_value: f32,
start_sample: usize,
in_buffers: &[SampleBuffer],
) -> [f32; BUFSIZE] {
if self.positive {
match self.op {
ValOp::Add => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| f32::max(original_value + x + self.add, 0.0001)),
ValOp::Subtract => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| f32::max(original_value - x + self.add, 0.0001)),
ValOp::Multiply => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| f32::max(original_value * x + self.add, 0.0001)),
ValOp::Divide => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| f32::max(original_value / x + self.add, 0.0001)),
ValOp::Replace => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| f32::max(x + self.add, 0.0001)),
}
} else if self.rectify {
match self.op {
ValOp::Add => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| (original_value + x + self.add).abs()),
ValOp::Subtract => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| (original_value - x + self.add).abs()),
ValOp::Multiply => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| (original_value * x + self.add).abs()),
ValOp::Divide => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| (original_value / x + self.add).abs()),
ValOp::Replace => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| (x + self.add).abs()),
}
} else {
match self.op {
ValOp::Add => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| original_value + x + self.add),
ValOp::Subtract => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| original_value - x + self.add),
ValOp::Multiply => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| original_value * x + self.add),
ValOp::Divide => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| original_value / x + self.add),
ValOp::Replace => self
.source
.get_next_block(start_sample, in_buffers)
.map(|x| x + self.add),
}
}
}
pub fn process_with_input(
&mut self,
original_value: f32,
start_sample: usize,
input: &[f32; BUFSIZE],
in_buffers: &[SampleBuffer],
) -> [f32; BUFSIZE] {
if self.positive {
match self.op {
ValOp::Add => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| f32::max(original_value + x + self.add, 0.0001)),
ValOp::Subtract => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| f32::max(original_value - x + self.add, 0.0001)),
ValOp::Multiply => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| f32::max(original_value * x + self.add, 0.0001)),
ValOp::Divide => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| f32::max(original_value / x + self.add, 0.0001)),
ValOp::Replace => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| f32::max(x + self.add, 0.0001)),
}
} else if self.rectify {
match self.op {
ValOp::Add => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| (original_value + x + self.add).abs()),
ValOp::Subtract => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| (original_value - x + self.add).abs()),
ValOp::Multiply => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| (original_value * x + self.add).abs()),
ValOp::Divide => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| (original_value / x + self.add).abs()),
ValOp::Replace => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| (x + self.add).abs()),
}
} else {
match self.op {
ValOp::Add => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| original_value + x + self.add),
ValOp::Subtract => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| original_value - x + self.add),
ValOp::Multiply => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| original_value * x + self.add),
ValOp::Divide => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| original_value / x + self.add),
ValOp::Replace => self
.source
.get_next_block_with_input(start_sample, input, in_buffers)
.map(|x| x + self.add),
}
}
}
}