use super::{Signal, eval_value};
use crate::dsl::{NoiseColor, Shape, SuperWave, Value};
use crate::dsp::Rng;
use std::f32::consts::TAU;
pub(super) fn dust_signal(density: f32, decay: f32, n: usize, sr: u32, rng: &mut Rng) -> Signal {
let srf = sr as f32;
let p = (density / srf).clamp(0.0, 1.0); let g = if decay > 0.0 {
(-1.0 / (decay * srf)).exp()
} else {
0.0
};
let mut y = 0.0f32;
(0..n)
.map(|_| {
let imp = if rng.unit() < p { rng.bi() } else { 0.0 };
y = imp + g * y;
y
})
.collect()
}
pub(crate) fn poly_blep(mut t: f32, dt: f32) -> f32 {
if dt <= 0.0 {
return 0.0;
}
if t < dt {
t /= dt;
t + t - t * t - 1.0
} else if t > 1.0 - dt {
t = (t - 1.0) / dt;
t * t + t + t + 1.0
} else {
0.0
}
}
pub(crate) fn osc(shape: Shape, phase: f32) -> f32 {
match shape {
Shape::Sine => (TAU * phase).sin(),
Shape::Square => {
if phase < 0.5 {
1.0
} else {
-1.0
}
}
Shape::Triangle => {
if phase < 0.5 {
4.0 * phase - 1.0
} else {
3.0 - 4.0 * phase
}
}
Shape::Saw => 2.0 * phase - 1.0,
}
}
pub(super) fn impact_signal(hardness: f32, velocity: f32, n: usize, sr: u32) -> Signal {
let h = hardness.clamp(0.0, 1.0);
let v = velocity.clamp(0.0, 1.0);
let width_s = 0.008 * (1.0 - h) + 0.0003 * h;
let w = ((width_s * sr as f32).round() as usize).max(1);
let norm = v / (0.5 * w as f32);
let mut out = vec![0.0f32; n];
for (i, o) in out.iter_mut().enumerate().take(w.min(n)) {
let phase = (i as f32 + 0.5) / w as f32;
*o = norm * 0.5 * (1.0 - (TAU * phase).cos());
}
out
}
pub(super) fn osc_signal(freq: &Value, n: usize, sr: u32, wave: impl Fn(f32) -> f32) -> Signal {
let f = eval_value(freq, n, sr);
let srf = sr as f32;
let mut phase = 0.0f32;
let mut out = Vec::with_capacity(n);
for &fi in f.iter() {
out.push(wave(phase));
phase += fi.max(0.0) / srf;
phase -= phase.floor();
}
out
}
pub(super) fn square_signal(freq: &Value, duty: &Value, n: usize, sr: u32) -> Signal {
let f = eval_value(freq, n, sr);
let d = eval_value(duty, n, sr);
let srf = sr as f32;
let mut phase = 0.0f32;
let mut out = Vec::with_capacity(n);
for i in 0..n {
let duty = d[i].clamp(0.01, 0.99);
let dt = f[i].max(0.0) / srf;
let mut v = if phase < duty { 1.0 } else { -1.0 };
v += poly_blep(phase, dt);
v -= poly_blep((phase - duty + 1.0).fract(), dt);
out.push(v);
phase += dt;
phase -= phase.floor();
}
out
}
pub(super) fn saw_signal(freq: &Value, n: usize, sr: u32) -> Signal {
let f = eval_value(freq, n, sr);
let srf = sr as f32;
let mut phase = 0.0f32;
let mut out = Vec::with_capacity(n);
for &fi in f.iter() {
let dt = fi.max(0.0) / srf;
out.push((2.0 * phase - 1.0) - poly_blep(phase, dt));
phase += dt;
phase -= phase.floor();
}
out
}
pub(super) fn tri_signal(freq: &Value, n: usize, sr: u32) -> Signal {
let f = eval_value(freq, n, sr);
let srf = sr as f32;
let mut phase = 0.0f32;
let mut tri = 0.0f32;
let mut out = Vec::with_capacity(n);
for &fi in f.iter() {
let dt = fi.max(0.0) / srf;
let mut sq = if phase < 0.5 { 1.0 } else { -1.0 };
sq += poly_blep(phase, dt);
sq -= poly_blep((phase + 0.5).fract(), dt);
tri = tri * 0.9995 + 4.0 * dt * sq;
out.push(tri);
phase += dt;
phase -= phase.floor();
}
out
}
pub(super) fn super_signal(
wave: SuperWave,
freq: &Value,
voices: u32,
detune_cents: f32,
n: usize,
sr: u32,
) -> Signal {
let f = eval_value(freq, n, sr);
let srf = sr as f32;
let v = voices.clamp(1, 16);
let mut out = vec![0.0f32; n];
for k in 0..v {
let cents = if v == 1 {
0.0
} else {
-detune_cents + 2.0 * detune_cents * (k as f32 / (v as f32 - 1.0))
};
let ratio = 2f32.powf(cents / 1200.0);
let mut phase = k as f32 / v as f32; for (i, o) in out.iter_mut().enumerate() {
let dt = (f[i].max(0.0) * ratio) / srf;
let s = match wave {
SuperWave::Sawtooth => (2.0 * phase - 1.0) - poly_blep(phase, dt),
SuperWave::Square => {
let mut sq = if phase < 0.5 { 1.0 } else { -1.0 };
sq += poly_blep(phase, dt);
sq -= poly_blep((phase + 0.5).fract(), dt);
sq
}
};
*o += s;
phase += dt;
phase -= phase.floor();
}
}
let scale = 1.0 / v as f32;
for o in out.iter_mut() {
*o *= scale;
}
out
}
pub(super) fn noise_signal(color: NoiseColor, n: usize, rng: &mut Rng) -> Signal {
match color {
NoiseColor::White => (0..n).map(|_| rng.bi()).collect(),
NoiseColor::Pink => {
let (mut b0, mut b1, mut b2, mut b3, mut b4, mut b5, mut b6) =
(0.0f32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
(0..n)
.map(|_| {
let w = rng.bi();
b0 = 0.99886 * b0 + w * 0.0555179;
b1 = 0.99332 * b1 + w * 0.0750759;
b2 = 0.96900 * b2 + w * 0.153_852;
b3 = 0.86650 * b3 + w * 0.3104856;
b4 = 0.55000 * b4 + w * 0.5329522;
b5 = -0.7616 * b5 - w * 0.0168980;
let out = b0 + b1 + b2 + b3 + b4 + b5 + b6 + w * 0.5362;
b6 = w * 0.115926;
out * 0.11
})
.collect()
}
NoiseColor::Brown => {
let mut last = 0.0f32;
(0..n)
.map(|_| {
last = (last + 0.02 * rng.bi()) * 0.998;
(last * 8.0).clamp(-1.0, 1.0)
})
.collect()
}
}
}
pub(super) fn fm_signal(freq: &Value, ratio: f32, index: &Value, n: usize, sr: u32) -> Signal {
let f = eval_value(freq, n, sr);
let idx = eval_value(index, n, sr);
let srf = sr as f32;
let (mut cph, mut mph) = (0.0f32, 0.0f32);
let mut out = Vec::with_capacity(n);
for i in 0..n {
let m = idx[i] * (TAU * mph).sin();
out.push((TAU * cph + m).sin());
let fi = f[i].max(0.0);
cph += fi / srf;
cph -= cph.floor();
mph += (fi * ratio) / srf;
mph -= mph.floor();
}
out
}