1use crate::channel::Ramp;
2use crate::sound::Rng;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum Wave {
6 Sine,
7 Square,
8 Saw,
9 White,
10 Pink,
11}
12
13pub(crate) struct Synth {
14 wave: Wave,
15 pub(crate) frequency: Ramp,
16 phase: f64,
17 rng: Rng,
18 pink: [f32; 7],
19}
20
21impl Synth {
22 pub(crate) fn new(wave: Wave, frequency: f32, seed: u64) -> Self {
23 Self {
24 wave,
25 frequency: Ramp::new(frequency.max(0.0)),
26 phase: 0.0,
27 rng: Rng::new(seed),
28 pink: [0.0; 7],
29 }
30 }
31
32 pub(crate) fn next(&mut self, ratio: f32, rate: u32) -> f32 {
33 let rate = f64::from(rate.max(1));
34 let hz = (f64::from(self.frequency.next()) * f64::from(ratio)).clamp(0.0, rate * 0.45);
35 let dt = hz / rate;
36 let phase = self.phase;
37 let sample = match self.wave {
38 Wave::Sine => f64::from(sine(phase)),
39 Wave::White => f64::from(self.rng.signed()),
40 Wave::Pink => f64::from(self.pink_sample()),
41 Wave::Saw => 2.0 * phase - 1.0 - poly_blep(phase, dt),
42 Wave::Square => {
43 let raw = if phase < 0.5 { 1.0 } else { -1.0 };
44 raw + poly_blep(phase, dt) - poly_blep((phase + 0.5).fract(), dt)
45 }
46 };
47 self.phase += dt;
48 if self.phase >= 1.0 {
49 self.phase -= 1.0;
50 }
51 sample as f32
52 }
53}
54
55impl Synth {
56 fn pink_sample(&mut self) -> f32 {
57 let white = self.rng.signed();
58 let b = &mut self.pink;
59 b[0] = 0.99886 * b[0] + white * 0.055_517_9;
60 b[1] = 0.99332 * b[1] + white * 0.075_075_9;
61 b[2] = 0.96900 * b[2] + white * 0.153_852;
62 b[3] = 0.86650 * b[3] + white * 0.310_485_6;
63 b[4] = 0.55000 * b[4] + white * 0.532_952_2;
64 b[5] = -0.7616 * b[5] - white * 0.016_898;
65 let sum = b[0] + b[1] + b[2] + b[3] + b[4] + b[5] + b[6] + white * 0.5362;
66 b[6] = white * 0.115_926;
67 sum * PINK_SCALE
68 }
69}
70
71const PINK_SCALE: f32 = 0.11;
72
73const SINE_STEPS: usize = 2048;
74
75static SINE: [f32; SINE_STEPS + 1] = sine_table();
76
77const fn sine_table() -> [f32; SINE_STEPS + 1] {
78 let mut table = [0.0f32; SINE_STEPS + 1];
79 let mut i = 0;
80 while i <= SINE_STEPS {
81 table[i] = taylor_sine(std::f64::consts::TAU * i as f64 / SINE_STEPS as f64) as f32;
82 i += 1;
83 }
84 table
85}
86
87const fn taylor_sine(x: f64) -> f64 {
88 let pi = std::f64::consts::PI;
89 let x = if x > 1.5 * pi {
90 x - 2.0 * pi
91 } else if x > 0.5 * pi {
92 pi - x
93 } else {
94 x
95 };
96 let square = x * x;
97 let mut term = x;
98 let mut sum = x;
99 let mut n = 1;
100 while n < 14 {
101 term = -term * square / ((2 * n) * (2 * n + 1)) as f64;
102 sum += term;
103 n += 1;
104 }
105 sum
106}
107
108pub(crate) fn sine(phase: f64) -> f32 {
109 let x = phase * SINE_STEPS as f64;
110 let index = (x as usize).min(SINE_STEPS - 1);
111 let frac = (x - index as f64) as f32;
112 let a = SINE[index];
113 a + (SINE[index + 1] - a) * frac
114}
115
116fn poly_blep(t: f64, dt: f64) -> f64 {
117 if dt <= 0.0 {
118 return 0.0;
119 }
120 if t < dt {
121 let x = t / dt;
122 x + x - x * x - 1.0
123 } else if t > 1.0 - dt {
124 let x = (t - 1.0) / dt;
125 x * x + x + x + 1.0
126 } else {
127 0.0
128 }
129}