embedded_audio/synth/
wavetable.rs1use crate::fixed::{Phase, hz_to_phase_inc, lerp_i8, phase_index};
2
3#[derive(Debug, Clone, Copy)]
5pub struct WavetableVoice<'a> {
6 table: &'a [u8],
7 phase: Phase,
8 phase_inc: u32,
9 active: bool,
10}
11
12impl<'a> WavetableVoice<'a> {
13 pub const fn new(table: &'a [u8]) -> Self {
15 Self {
16 table,
17 phase: 0,
18 phase_inc: 0,
19 active: false,
20 }
21 }
22
23 pub const fn sine() -> Self {
24 Self::new(&SINE_TABLE)
25 }
26
27 pub const fn triangle() -> Self {
28 Self::new(&TRIANGLE_TABLE)
29 }
30
31 pub const fn saw() -> Self {
32 Self::new(&SAW_TABLE)
33 }
34
35 pub const fn square() -> Self {
36 Self::new(&SQUARE_TABLE)
37 }
38
39 pub fn start(&mut self, freq_hz: u32, sample_rate_hz: u32) {
40 self.phase = 0;
41 self.phase_inc = hz_to_phase_inc(freq_hz, sample_rate_hz);
42 self.active = true;
43 }
44
45 pub fn stop(&mut self) {
46 self.active = false;
47 }
48
49 pub fn is_active(&self) -> bool {
50 self.active
51 }
52
53 pub fn next_sample(&mut self) -> Option<i8> {
54 if !self.active || self.table.len() < 256 {
55 return None;
56 }
57 let idx = phase_index(self.phase);
58 let frac = (self.phase >> 16) as u8;
59 let a = self.table[idx as usize] as i8;
60 let b = self.table[idx.wrapping_add(1) as usize] as i8;
61 let sample = lerp_i8(a, b, frac);
62 self.phase = self.phase.wrapping_add(self.phase_inc);
63 Some(sample)
64 }
65}
66
67pub static SINE_TABLE: [u8; 256] = generate_sine_table();
69pub static TRIANGLE_TABLE: [u8; 256] = generate_triangle_table();
71pub static SAW_TABLE: [u8; 256] = generate_saw_table();
73pub static SQUARE_TABLE: [u8; 256] = generate_square_table();
75pub static PULSE_25_TABLE: [u8; 256] = generate_pulse_25_table();
77
78const fn generate_sine_table() -> [u8; 256] {
79 let mut t = [0u8; 256];
80 let mut i = 0;
81 while i < 256 {
82 let x = i as i32;
83 let y = if x < 128 { x * 2 } else { (255 - x) * 2 };
84 let quad = (y * (255 - y)) >> 8;
85 let s = if x < 128 { quad } else { -quad };
86 let u = s + 128;
87 t[i] = if u < 0 {
88 0
89 } else if u > 255 {
90 255
91 } else {
92 u as u8
93 };
94 i += 1;
95 }
96 t
97}
98
99const fn generate_triangle_table() -> [u8; 256] {
100 let mut t = [0u8; 256];
101 let mut i = 0;
102 while i < 256 {
103 let val = if i < 64 {
104 128 + i * 2
105 } else if i < 192 {
106 255 - (i - 64) * 2
107 } else {
108 (i - 192) * 2
109 };
110 t[i] = val as u8;
111 i += 1;
112 }
113 t
114}
115
116const fn generate_saw_table() -> [u8; 256] {
117 let mut t = [0u8; 256];
118 let mut i = 0;
119 while i < 256 {
120 t[i] = i as u8;
121 i += 1;
122 }
123 t
124}
125
126const fn generate_square_table() -> [u8; 256] {
127 let mut t = [0u8; 256];
128 let mut i = 0;
129 while i < 256 {
130 t[i] = if i < 128 { 255 } else { 0 };
131 i += 1;
132 }
133 t
134}
135
136const fn generate_pulse_25_table() -> [u8; 256] {
137 let mut t = [0u8; 256];
138 let mut i = 0;
139 while i < 256 {
140 t[i] = if i < 64 { 255 } else { 0 };
141 i += 1;
142 }
143 t
144}
145
146pub fn generate_wavetable_fixed<F: Fn(u8) -> i8>(f: F) -> [u8; 256] {
148 let mut table = [0u8; 256];
149 for (i, slot) in table.iter_mut().enumerate() {
150 let sample = f(i as u8);
151 *slot = (sample as i16 + 128).clamp(0, 255) as u8;
152 }
153 table
154}