devalang_wasm/engine/audio/
synth.rs1pub mod types;
3
4use std::f32::consts::PI;
5
6pub fn oscillator_sample(waveform: &str, frequency: f32, time: f32) -> f32 {
8 let phase = 2.0 * PI * frequency * time;
9
10 match waveform {
11 "sine" => phase.sin(),
12
13 "square" => {
14 if phase.sin() >= 0.0 {
15 1.0
16 } else {
17 -1.0
18 }
19 }
20
21 "saw" => {
22 2.0 * (frequency * time - (frequency * time + 0.5).floor())
24 }
25
26 "triangle" => {
27 (2.0 * (2.0 * (frequency * time).fract() - 1.0)).abs() * 2.0 - 1.0
29 }
30
31 _ => 0.0, }
33}
34
35pub fn adsr_envelope(
38 sample_index: usize,
39 attack_samples: usize,
40 decay_samples: usize,
41 sustain_samples: usize,
42 release_samples: usize,
43 sustain_level: f32,
44) -> f32 {
45 let attack_end = attack_samples;
46 let decay_end = attack_samples + decay_samples;
47 let sustain_end = attack_samples + decay_samples + sustain_samples;
48 let release_end = attack_samples + decay_samples + sustain_samples + release_samples;
49
50 if sample_index < attack_end && attack_samples > 0 {
51 let progress = sample_index as f32 / attack_samples.max(1) as f32;
53 progress
54 } else if sample_index < decay_end && decay_samples > 0 {
55 let decay_progress = (sample_index - attack_end) as f32 / decay_samples.max(1) as f32;
57 1.0 - (1.0 - sustain_level) * decay_progress
58 } else if sample_index < sustain_end {
59 sustain_level
61 } else if sample_index < release_end && release_samples > 0 {
62 let release_progress = (sample_index - sustain_end) as f32 / release_samples.max(1) as f32;
64 sustain_level * (1.0 - release_progress).max(0.0)
65 } else {
66 0.0
68 }
69}
70
71pub fn time_to_samples(time_seconds: f32, sample_rate: u32) -> usize {
73 (time_seconds * sample_rate as f32) as usize
74}
75
76pub fn midi_to_frequency(midi_note: u8) -> f32 {
78 440.0 * 2.0_f32.powf((midi_note as f32 - 69.0) / 12.0)
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn test_oscillator_sine() {
87 let sample = oscillator_sample("sine", 440.0, 0.0);
88 assert!((sample - 0.0).abs() < 0.01);
89 }
90
91 #[test]
92 fn test_oscillator_square() {
93 let sample = oscillator_sample("square", 440.0, 0.0);
94 assert!((sample - 1.0).abs() < 0.01);
95 }
96
97 #[test]
98 fn test_adsr_attack() {
99 let envelope = adsr_envelope(500, 1000, 500, 1000, 500, 0.7);
100 assert!(envelope > 0.4 && envelope < 0.6); }
102
103 #[test]
104 fn test_midi_to_frequency() {
105 let a4 = midi_to_frequency(69);
106 assert!((a4 - 440.0).abs() < 0.1);
107
108 let c4 = midi_to_frequency(60);
109 assert!((c4 - 261.63).abs() < 0.5);
110 }
111}