Skip to main content

xsynth_core/channel/
control.rs

1use biquad::Q_BUTTERWORTH_F32;
2
3use crate::{
4    helpers::{db_to_amp, FREQS},
5    voice::VoiceControlData,
6};
7
8use super::{ChannelAudioEvent, ChannelEvent, ControlEvent, VoiceChannel};
9
10pub(crate) struct ValueLerp {
11    lerp_length: f32,
12    step: f32,
13    current: f32,
14    end: f32,
15}
16
17impl ValueLerp {
18    pub fn new(current: f32, sample_rate: u32) -> Self {
19        Self {
20            lerp_length: sample_rate as f32 * 0.01,
21            step: 0.0,
22            current,
23            end: current,
24        }
25    }
26
27    pub fn set_end(&mut self, end: f32) {
28        self.step = (end - self.current) / self.lerp_length;
29        self.end = end;
30    }
31
32    pub fn get_next(&mut self) -> f32 {
33        if self.end > self.current {
34            self.current = (self.current + self.step).min(self.end);
35        } else if self.end < self.current {
36            self.current = (self.current + self.step).max(self.end);
37        }
38        self.current
39    }
40}
41
42pub(super) struct ControlEventData {
43    selected_lsb: i8,
44    selected_msb: i8,
45    pitch_bend_sensitivity_lsb: u8,
46    pitch_bend_sensitivity_msb: u8,
47    pitch_bend_sensitivity: f32,
48    pitch_bend_value: f32,
49    fine_tune_lsb: u8,
50    fine_tune_msb: u8,
51    fine_tune_value: f32,
52    coarse_tune_value: f32,
53    pub volume: ValueLerp, // 0.0 = silent, 1.0 = max volume
54    pub pan: ValueLerp,    // 0.0 = left, 0.5 = center, 1.0 = right
55    pub cutoff: Option<f32>,
56    pub resonance: Option<f32>,
57    pub expression: ValueLerp,
58}
59
60impl ControlEventData {
61    pub fn new_defaults(sample_rate: u32) -> Self {
62        ControlEventData {
63            selected_lsb: -1,
64            selected_msb: -1,
65            pitch_bend_sensitivity_lsb: 0,
66            pitch_bend_sensitivity_msb: 2,
67            pitch_bend_sensitivity: 2.0,
68            pitch_bend_value: 0.0,
69            fine_tune_lsb: 0,
70            fine_tune_msb: 0,
71            fine_tune_value: 0.0,
72            coarse_tune_value: 0.0,
73            volume: ValueLerp::new(1.0, sample_rate),
74            pan: ValueLerp::new(0.5, sample_rate),
75            cutoff: None,
76            resonance: None,
77            expression: ValueLerp::new(1.0, sample_rate),
78        }
79    }
80}
81
82impl VoiceChannel {
83    /// Sends a ControlEvent to the channel.
84    /// See the `ControlEvent` documentation for more information.
85    pub fn process_control_event(&mut self, event: ControlEvent) {
86        match event {
87            ControlEvent::Raw(controller, value) => match controller {
88                0x00 => {
89                    // Bank select
90                    self.params.set_bank(value);
91                }
92                0x64 => {
93                    self.control_event_data.selected_lsb = value as i8;
94                }
95                0x65 => {
96                    self.control_event_data.selected_msb = value as i8;
97                }
98                0x06 | 0x26 => {
99                    let (lsb, msb) = {
100                        let data = &self.control_event_data;
101                        (data.selected_lsb, data.selected_msb)
102                    };
103                    if msb == 0 {
104                        match lsb {
105                            0 => {
106                                // Pitch
107                                match controller {
108                                    0x06 => {
109                                        self.control_event_data.pitch_bend_sensitivity_msb = value
110                                    }
111                                    0x26 => {
112                                        self.control_event_data.pitch_bend_sensitivity_lsb = value
113                                    }
114                                    _ => (),
115                                }
116
117                                let sensitivity = {
118                                    let data = &self.control_event_data;
119                                    (data.pitch_bend_sensitivity_msb as f32)
120                                        + (data.pitch_bend_sensitivity_lsb as f32) / 100.0
121                                };
122
123                                self.process_control_event(ControlEvent::PitchBendSensitivity(
124                                    sensitivity,
125                                ))
126                            }
127                            1 => {
128                                // Fine tune
129                                match controller {
130                                    0x06 => self.control_event_data.fine_tune_msb = value,
131                                    0x26 => self.control_event_data.fine_tune_lsb = value,
132                                    _ => (),
133                                }
134                                let val: u16 = ((self.control_event_data.fine_tune_msb as u16)
135                                    << 6)
136                                    + self.control_event_data.fine_tune_lsb as u16;
137                                let val = (val as f32 - 4096.0) / 4096.0 * 100.0;
138                                self.process_control_event(ControlEvent::FineTune(val));
139                            }
140                            2 if controller == 0x06 => {
141                                // Coarse tune
142                                self.process_control_event(ControlEvent::CoarseTune(
143                                    value as f32 - 64.0,
144                                ))
145                            }
146                            2 => {}
147                            _ => {}
148                        }
149                    }
150                }
151                0x07 => {
152                    // Volume
153                    let vol: f32 = value as f32 / 128.0;
154                    self.control_event_data.volume.set_end(vol);
155                }
156                0x0A | 0x08 => {
157                    // Pan
158                    let pan: f32 = value as f32 / 128.0;
159                    self.control_event_data.pan.set_end(pan);
160                }
161                0x0B => {
162                    // Expression
163                    let expr = value as f32 / 128.0;
164                    self.control_event_data.expression.set_end(expr);
165                }
166                0x40 => {
167                    // Damper / Sustain
168                    let damper = match value {
169                        0..=63 => false,
170                        64..=127 => true,
171                        _ => false,
172                    };
173
174                    for key in self.key_voices.iter_mut() {
175                        key.data.set_damper(damper);
176                    }
177                }
178                0x47 => {
179                    // Resonance
180                    if value > 64 {
181                        let db = (value as f32 - 64.0) / 2.4;
182                        let value = db_to_amp(db) * Q_BUTTERWORTH_F32;
183                        self.control_event_data.resonance = Some(value);
184                    } else {
185                        self.control_event_data.resonance = None;
186                    }
187                }
188                0x48 => {
189                    // Release
190                    self.voice_control_data.envelope.release = Some(value);
191                    self.propagate_voice_controls();
192                }
193                0x49 => {
194                    // Attack
195                    self.voice_control_data.envelope.attack = Some(value);
196                    self.propagate_voice_controls();
197                }
198                0x4A => {
199                    // Cutoff
200                    if value < 64 {
201                        let value = value as usize + 64;
202                        let mut freq = FREQS[value];
203                        if freq > 7000.0 {
204                            // I hate BASS
205                            let mult = freq / 7000.0 - 1.0;
206                            let mult = mult * 2.36 + 1.0;
207                            freq = mult * 7000.0;
208                        }
209                        self.control_event_data.cutoff = Some(freq);
210                    } else {
211                        self.control_event_data.cutoff = None;
212                    }
213                }
214                0x78 if value == 0 => {
215                    // All Sounds Off
216                    self.process_event(ChannelEvent::Audio(ChannelAudioEvent::AllNotesKilled));
217                }
218                0x79 if value == 0 => {
219                    // Reset All Controllers
220                    self.reset_control();
221                }
222                0x7B if value == 0 => {
223                    // All Notes Off
224                    self.process_event(ChannelEvent::Audio(ChannelAudioEvent::AllNotesOff));
225                }
226                _ => {}
227            },
228            ControlEvent::PitchBendSensitivity(sensitivity) => {
229                let pitch_bend = {
230                    let data = &mut self.control_event_data;
231                    data.pitch_bend_sensitivity = sensitivity;
232                    data.pitch_bend_sensitivity * data.pitch_bend_value
233                };
234                self.process_control_event(ControlEvent::PitchBend(pitch_bend));
235            }
236            ControlEvent::PitchBendValue(value) => {
237                let pitch_bend = {
238                    let data = &mut self.control_event_data;
239                    data.pitch_bend_value = value;
240                    data.pitch_bend_sensitivity * data.pitch_bend_value
241                };
242                self.process_control_event(ControlEvent::PitchBend(pitch_bend));
243            }
244            ControlEvent::PitchBend(value) => {
245                self.control_event_data.pitch_bend_value = value;
246                self.process_pitch();
247            }
248            ControlEvent::FineTune(value) => {
249                self.control_event_data.fine_tune_value = value;
250                self.process_pitch();
251            }
252            ControlEvent::CoarseTune(value) => {
253                self.control_event_data.coarse_tune_value = value;
254                self.process_pitch();
255            }
256        }
257    }
258
259    fn process_pitch(&mut self) {
260        let data = &mut self.control_event_data;
261        let pitch_bend = data.pitch_bend_value;
262        let fine_tune = data.fine_tune_value;
263        let coarse_tune = data.coarse_tune_value;
264        let combined = pitch_bend + coarse_tune + fine_tune / 100.0;
265
266        self.voice_control_data.voice_pitch_multiplier = 2.0f32.powf(combined / 12.0);
267        self.propagate_voice_controls();
268    }
269
270    pub(super) fn reset_control(&mut self) {
271        self.control_event_data = ControlEventData::new_defaults(self.stream_params.sample_rate);
272        self.voice_control_data = VoiceControlData::new_defaults();
273        self.propagate_voice_controls();
274
275        self.control_event_data.cutoff = None;
276
277        for key in self.key_voices.iter_mut() {
278            key.data.set_damper(false);
279        }
280    }
281
282    pub(super) fn reset_program(&mut self) {
283        self.params.set_bank(0);
284        self.params.set_preset(0);
285    }
286}
287
288#[cfg(test)]
289mod tests {
290    use crate::{AudioStreamParams, ChannelCount};
291
292    use super::*;
293
294    fn new_channel() -> VoiceChannel {
295        VoiceChannel::new(
296            Default::default(),
297            AudioStreamParams::new(48_000, ChannelCount::Stereo),
298            None,
299        )
300    }
301
302    #[test]
303    fn coarse_tune_only_uses_data_entry_msb() {
304        let mut channel = new_channel();
305
306        channel.process_control_event(ControlEvent::Raw(0x65, 0));
307        channel.process_control_event(ControlEvent::Raw(0x64, 2));
308        channel.process_control_event(ControlEvent::Raw(0x26, 100));
309
310        assert_eq!(channel.control_event_data.coarse_tune_value, 0.0);
311
312        channel.process_control_event(ControlEvent::Raw(0x06, 70));
313
314        assert_eq!(channel.control_event_data.coarse_tune_value, 6.0);
315    }
316
317    #[test]
318    fn reset_all_controllers_requires_zero_value() {
319        let mut channel = new_channel();
320        channel.process_control_event(ControlEvent::Raw(0x07, 32));
321        channel.process_control_event(ControlEvent::Raw(0x79, 1));
322
323        assert_eq!(channel.control_event_data.volume.end, 32.0 / 128.0);
324
325        channel.process_control_event(ControlEvent::Raw(0x79, 0));
326
327        assert_eq!(channel.control_event_data.volume.end, 1.0);
328    }
329}