pub mod types;
use std::f32::consts::PI;
pub fn oscillator_sample(waveform: &str, frequency: f32, time: f32) -> f32 {
let phase = 2.0 * PI * frequency * time;
match waveform {
"sine" => phase.sin(),
"square" => {
if phase.sin() >= 0.0 {
1.0
} else {
-1.0
}
}
"saw" => {
2.0 * (frequency * time - (frequency * time + 0.5).floor())
}
"triangle" => {
(2.0 * (2.0 * (frequency * time).fract() - 1.0)).abs() * 2.0 - 1.0
}
_ => 0.0, }
}
pub fn adsr_envelope(
sample_index: usize,
attack_samples: usize,
decay_samples: usize,
sustain_samples: usize,
release_samples: usize,
sustain_level: f32,
) -> f32 {
let attack_end = attack_samples;
let decay_end = attack_samples + decay_samples;
let sustain_end = attack_samples + decay_samples + sustain_samples;
let release_end = attack_samples + decay_samples + sustain_samples + release_samples;
if sample_index < attack_end && attack_samples > 0 {
let progress = sample_index as f32 / attack_samples.max(1) as f32;
progress
} else if sample_index < decay_end && decay_samples > 0 {
let decay_progress = (sample_index - attack_end) as f32 / decay_samples.max(1) as f32;
1.0 - (1.0 - sustain_level) * decay_progress
} else if sample_index < sustain_end {
sustain_level
} else if sample_index < release_end && release_samples > 0 {
let release_progress = (sample_index - sustain_end) as f32 / release_samples.max(1) as f32;
sustain_level * (1.0 - release_progress).max(0.0)
} else {
0.0
}
}
pub fn time_to_samples(time_seconds: f32, sample_rate: u32) -> usize {
(time_seconds * sample_rate as f32) as usize
}
pub fn midi_to_frequency(midi_note: u8) -> f32 {
440.0 * 2.0_f32.powf((midi_note as f32 - 69.0) / 12.0)
}
#[cfg(test)]
#[path = "test_synth.rs"]
mod tests;