use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
use crate::random::Random;
pub fn midi_to_hz(midi_note: f64) -> f64 {
440.0 * 2_f64.powf((midi_note - 69.0) / 12.0)
}
fn linspace(start: f32, end: f32, num: usize) -> Vec<f32> {
match num {
0 => vec![],
1 => vec![start],
_ => (0..num)
.map(|i| start + (end - start) * (i as f32 / (num - 1) as f32))
.collect(),
}
}
pub fn square(sample_rate: f64, freq: f64, duration: f64, amp: f32, duty: f64) -> Vec<f32> {
let n = (sample_rate * duration).floor() as usize;
let threshold = (2.0 * duty - 1.0) as f32;
let two_pi_f = 2.0 * core::f64::consts::PI * freq;
(0..n)
.map(|i| {
let t = i as f64 / sample_rate;
let s = (two_pi_f * t).sin() as f32;
let x = s - threshold;
let sign = if x > 0.0 {
1.0
} else if x < 0.0 {
-1.0
} else {
0.0
};
amp * sign
})
.collect()
}
pub fn triangle(sample_rate: f64, freq: f64, duration: f64, amp: f32) -> Vec<f32> {
let n = (sample_rate * duration).floor() as usize;
let two_pi = 2.0 * core::f64::consts::PI;
let coef = amp as f64 * (2.0 / core::f64::consts::PI);
(0..n)
.map(|i| {
let t = i as f64 / sample_rate;
let s = (two_pi * freq * t).sin();
(coef * s.asin()) as f32
})
.collect()
}
pub fn noise_burst(
sample_rate: f64,
random: &mut Box<dyn Random>,
duration: f64,
amp: f32,
) -> Vec<f32> {
let n = (sample_rate * duration).floor() as usize;
(0..n)
.map(|_| amp * (random.next_float() * 2.0 - 1.0))
.collect()
}
pub fn sine(sample_rate: f64, freq: f64, duration: f64, amp: f32) -> Vec<f32> {
let n = (sample_rate * duration).floor() as usize;
let two_pi_f = 2.0 * core::f64::consts::PI * freq;
(0..n)
.map(|i| amp * (two_pi_f * (i as f64 / sample_rate)).sin() as f32)
.collect()
}
pub fn fm_sine(
sample_rate: f64,
freq: f64,
duration: f64,
amp: f32,
mod_ratio: f64,
mod_index: f64,
) -> Vec<f32> {
let n = (sample_rate * duration).floor() as usize;
let two_pi = 2.0 * core::f64::consts::PI;
(0..n)
.map(|i| {
let t = i as f64 / sample_rate;
let modulator = mod_index * (two_pi * freq * mod_ratio * t).sin();
amp * (two_pi * freq * t + modulator).sin() as f32
})
.collect()
}
pub fn karplus_strong(sample_rate: f64, freq: f64, duration: f64, amp: f32) -> Vec<f32> {
let n = (sample_rate * duration).floor() as usize;
if n == 0 {
return Vec::new();
}
let period = ((sample_rate / freq).round() as usize).max(2);
let mut lcg: u32 = (period as u32).wrapping_mul(2654435761);
let mut buf: Vec<f32> = (0..period)
.map(|_| {
lcg = lcg.wrapping_mul(1664525).wrapping_add(1013904223);
(lcg >> 15) as f32 / 32767.0 - 1.0
})
.collect();
let mut out = Vec::with_capacity(n);
let mut pos = 0usize;
for _ in 0..n {
let prev = (pos + period - 1) % period;
let avg = (buf[pos] + buf[prev]) * 0.5;
buf[pos] = avg;
out.push(avg * amp);
pos = (pos + 1) % period;
}
out
}
pub fn vibrato_sine(
sample_rate: f64,
freq: f64,
duration: f64,
amp: f32,
vib_rate: f64,
vib_depth: f64,
) -> Vec<f32> {
let n = (sample_rate * duration).floor() as usize;
let two_pi = 2.0 * core::f64::consts::PI;
let sr_recip = 1.0 / sample_rate;
(0..n)
.scan(0.0f64, move |phase, i| {
let t = i as f64 * sr_recip;
let inst_freq = freq * (1.0 + vib_depth * (two_pi * vib_rate * t).sin());
*phase += two_pi * inst_freq * sr_recip;
Some(amp * (*phase).sin() as f32)
})
.collect()
}
pub fn pitch_sweep_sine(
sample_rate: f64,
freq_start: f64,
freq_end: f64,
duration: f64,
amp: f32,
decay_rate: f64,
) -> Vec<f32> {
let n = (sample_rate * duration).floor() as usize;
let two_pi = 2.0 * core::f64::consts::PI;
let sr_recip = 1.0 / sample_rate;
(0..n)
.scan(0.0f64, move |phase, i| {
let t = i as f64 * sr_recip;
let freq = freq_end + (freq_start - freq_end) * (-decay_rate * t).exp();
*phase += two_pi * freq * sr_recip;
Some(amp * (*phase).sin() as f32)
})
.collect()
}
pub fn envelope(
sample_rate: f64,
samples: &[f32],
attack: f64,
decay: f64,
sustain: f32,
release: f64,
) -> Vec<f32> {
let n = samples.len();
let a = (attack * sample_rate).floor() as usize;
let a = a.min(n);
let d = (decay * sample_rate).floor() as usize;
let d = d.min(n.saturating_sub(a));
let r = (release * sample_rate).floor() as usize;
let r = r.min(n);
let s_len = n.saturating_sub(a + d + r);
let mut env: Vec<f32> = Vec::new();
env.extend(linspace(0.0, 1.0, a));
env.extend(linspace(1.0, sustain, d));
env.extend(core::iter::repeat(sustain).take(s_len));
env.extend(linspace(sustain, 0.0, r));
while env.len() < n {
env.push(0.0);
}
env.truncate(n);
samples
.iter()
.zip(env.iter())
.map(|(&s, &e)| s * e)
.collect()
}