1use crate::fixed::{Phase, apply_gain_q8, hz_to_phase_inc, phase_index, sin_table};
6
7fn soft_limit(x: f32) -> f32 {
8 x * (27.0 + x * x) / (27.0 + 9.0 * x * x)
9}
10
11fn soft_clip(x: f32) -> f32 {
12 if x < -3.0 {
13 -1.0
14 } else if x > 3.0 {
15 1.0
16 } else {
17 soft_limit(x)
18 }
19}
20
21fn i8_to_f32(sample: i8) -> f32 {
22 sample as f32 / 127.0
23}
24
25fn f32_to_i8(sample: f32) -> i8 {
26 (sample * 127.0).clamp(-128.0, 127.0) as i8
27}
28
29#[derive(Debug, Clone, Copy)]
31pub struct Overdrive {
32 pre_gain: f32,
33 post_gain: f32,
34}
35
36impl Overdrive {
37 pub fn new(drive: f32) -> Self {
41 let mut od = Self {
42 pre_gain: 0.0,
43 post_gain: 1.0,
44 };
45 od.set_drive(drive);
46 od
47 }
48
49 pub fn set_drive(&mut self, drive: f32) {
51 let drive = 2.0 * drive.clamp(0.0, 1.0);
52 let drive_2 = drive * drive;
53 let pre_gain_a = drive * 0.5;
54 let pre_gain_b = drive_2 * drive_2 * drive * 24.0;
55 self.pre_gain = pre_gain_a + (pre_gain_b - pre_gain_a) * drive_2;
56
57 let drive_squashed = drive * (2.0 - drive);
58 self.post_gain = 1.0 / soft_clip(0.33 + drive_squashed * (self.pre_gain - 0.33));
59 }
60
61 pub fn process(&self, input: i8) -> i8 {
63 let pre = self.pre_gain * i8_to_f32(input);
64 f32_to_i8(soft_clip(pre) * self.post_gain)
65 }
66}
67
68fn floor_f32(x: f32) -> f32 {
69 let truncated = x as i32 as f32;
70 if truncated > x {
71 truncated - 1.0
72 } else {
73 truncated
74 }
75}
76
77#[derive(Debug, Clone, Copy)]
80pub struct Wavefolder {
81 gain: f32,
82 offset: f32,
83}
84
85impl Wavefolder {
86 pub const fn new() -> Self {
88 Self {
89 gain: 1.0,
90 offset: 0.0,
91 }
92 }
93
94 pub fn set_gain(&mut self, gain: f32) {
96 self.gain = gain;
97 }
98
99 pub fn set_offset(&mut self, offset: f32) {
101 self.offset = offset;
102 }
103
104 pub fn process(&self, input: i8) -> i8 {
106 let x = (i8_to_f32(input) + self.offset) * self.gain;
107 let fold_count = floor_f32((x + 1.0) * 0.5);
108 let sign = if (fold_count as i64) % 2 == 0 {
109 1.0
110 } else {
111 -1.0
112 };
113 f32_to_i8(sign * (x - 2.0 * fold_count))
114 }
115}
116
117impl Default for Wavefolder {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123#[derive(Debug, Clone, Copy)]
126pub struct Tremolo {
127 phase: Phase,
128 phase_inc: u32,
129 half_depth_q8: u8,
130}
131
132impl Tremolo {
133 pub const fn new() -> Self {
135 Self {
136 phase: 0,
137 phase_inc: 0,
138 half_depth_q8: 127,
139 }
140 }
141
142 pub fn set_freq(&mut self, freq_hz: u32, sample_rate_hz: u32) {
144 self.phase_inc = hz_to_phase_inc(freq_hz, sample_rate_hz);
145 }
146
147 pub fn set_depth_q8(&mut self, depth_q8: u8) {
149 self.half_depth_q8 = depth_q8 / 2;
150 }
151
152 pub fn process(&mut self, input: i8) -> i8 {
154 let lfo = sin_table(phase_index(self.phase)) as i32;
155 self.phase = self.phase.wrapping_add(self.phase_inc);
156
157 let half_depth = self.half_depth_q8 as i32;
158 let dc = 255 - half_depth;
159 let gain_q8 = (dc + (lfo * half_depth) / 127).clamp(0, 255) as u8;
160 apply_gain_q8(input, gain_q8)
161 }
162}
163
164impl Default for Tremolo {
165 fn default() -> Self {
166 Self::new()
167 }
168}