use biquad::Q_BUTTERWORTH_F32;
use crate::{
helpers::{db_to_amp, FREQS},
voice::VoiceControlData,
};
use super::{ChannelAudioEvent, ChannelEvent, ControlEvent, VoiceChannel};
pub(crate) struct ValueLerp {
lerp_length: f32,
step: f32,
current: f32,
end: f32,
}
impl ValueLerp {
pub fn new(current: f32, sample_rate: u32) -> Self {
Self {
lerp_length: sample_rate as f32 * 0.01,
step: 0.0,
current,
end: current,
}
}
pub fn set_end(&mut self, end: f32) {
self.step = (end - self.current) / self.lerp_length;
self.end = end;
}
pub fn get_next(&mut self) -> f32 {
if self.end > self.current {
self.current = (self.current + self.step).min(self.end);
} else if self.end < self.current {
self.current = (self.current + self.step).max(self.end);
}
self.current
}
}
pub(super) struct ControlEventData {
selected_lsb: i8,
selected_msb: i8,
pitch_bend_sensitivity_lsb: u8,
pitch_bend_sensitivity_msb: u8,
pitch_bend_sensitivity: f32,
pitch_bend_value: f32,
fine_tune_lsb: u8,
fine_tune_msb: u8,
fine_tune_value: f32,
coarse_tune_value: f32,
pub volume: ValueLerp, pub pan: ValueLerp, pub cutoff: Option<f32>,
pub resonance: Option<f32>,
pub expression: ValueLerp,
}
impl ControlEventData {
pub fn new_defaults(sample_rate: u32) -> Self {
ControlEventData {
selected_lsb: -1,
selected_msb: -1,
pitch_bend_sensitivity_lsb: 0,
pitch_bend_sensitivity_msb: 2,
pitch_bend_sensitivity: 2.0,
pitch_bend_value: 0.0,
fine_tune_lsb: 0,
fine_tune_msb: 0,
fine_tune_value: 0.0,
coarse_tune_value: 0.0,
volume: ValueLerp::new(1.0, sample_rate),
pan: ValueLerp::new(0.5, sample_rate),
cutoff: None,
resonance: None,
expression: ValueLerp::new(1.0, sample_rate),
}
}
}
impl VoiceChannel {
pub fn process_control_event(&mut self, event: ControlEvent) {
match event {
ControlEvent::Raw(controller, value) => match controller {
0x00 => {
self.params.set_bank(value);
}
0x64 => {
self.control_event_data.selected_lsb = value as i8;
}
0x65 => {
self.control_event_data.selected_msb = value as i8;
}
0x06 | 0x26 => {
let (lsb, msb) = {
let data = &self.control_event_data;
(data.selected_lsb, data.selected_msb)
};
if msb == 0 {
match lsb {
0 => {
match controller {
0x06 => {
self.control_event_data.pitch_bend_sensitivity_msb = value
}
0x26 => {
self.control_event_data.pitch_bend_sensitivity_lsb = value
}
_ => (),
}
let sensitivity = {
let data = &self.control_event_data;
(data.pitch_bend_sensitivity_msb as f32)
+ (data.pitch_bend_sensitivity_lsb as f32) / 100.0
};
self.process_control_event(ControlEvent::PitchBendSensitivity(
sensitivity,
))
}
1 => {
match controller {
0x06 => self.control_event_data.fine_tune_msb = value,
0x26 => self.control_event_data.fine_tune_lsb = value,
_ => (),
}
let val: u16 = ((self.control_event_data.fine_tune_msb as u16)
<< 6)
+ self.control_event_data.fine_tune_lsb as u16;
let val = (val as f32 - 4096.0) / 4096.0 * 100.0;
self.process_control_event(ControlEvent::FineTune(val));
}
2 if controller == 0x06 => {
self.process_control_event(ControlEvent::CoarseTune(
value as f32 - 64.0,
))
}
2 => {}
_ => {}
}
}
}
0x07 => {
let vol: f32 = value as f32 / 128.0;
self.control_event_data.volume.set_end(vol);
}
0x0A | 0x08 => {
let pan: f32 = value as f32 / 128.0;
self.control_event_data.pan.set_end(pan);
}
0x0B => {
let expr = value as f32 / 128.0;
self.control_event_data.expression.set_end(expr);
}
0x40 => {
let damper = match value {
0..=63 => false,
64..=127 => true,
_ => false,
};
for key in self.key_voices.iter_mut() {
key.data.set_damper(damper);
}
}
0x47 => {
if value > 64 {
let db = (value as f32 - 64.0) / 2.4;
let value = db_to_amp(db) * Q_BUTTERWORTH_F32;
self.control_event_data.resonance = Some(value);
} else {
self.control_event_data.resonance = None;
}
}
0x48 => {
self.voice_control_data.envelope.release = Some(value);
self.propagate_voice_controls();
}
0x49 => {
self.voice_control_data.envelope.attack = Some(value);
self.propagate_voice_controls();
}
0x4A => {
if value < 64 {
let value = value as usize + 64;
let mut freq = FREQS[value];
if freq > 7000.0 {
let mult = freq / 7000.0 - 1.0;
let mult = mult * 2.36 + 1.0;
freq = mult * 7000.0;
}
self.control_event_data.cutoff = Some(freq);
} else {
self.control_event_data.cutoff = None;
}
}
0x78 if value == 0 => {
self.process_event(ChannelEvent::Audio(ChannelAudioEvent::AllNotesKilled));
}
0x79 if value == 0 => {
self.reset_control();
}
0x7B if value == 0 => {
self.process_event(ChannelEvent::Audio(ChannelAudioEvent::AllNotesOff));
}
_ => {}
},
ControlEvent::PitchBendSensitivity(sensitivity) => {
let pitch_bend = {
let data = &mut self.control_event_data;
data.pitch_bend_sensitivity = sensitivity;
data.pitch_bend_sensitivity * data.pitch_bend_value
};
self.process_control_event(ControlEvent::PitchBend(pitch_bend));
}
ControlEvent::PitchBendValue(value) => {
let pitch_bend = {
let data = &mut self.control_event_data;
data.pitch_bend_value = value;
data.pitch_bend_sensitivity * data.pitch_bend_value
};
self.process_control_event(ControlEvent::PitchBend(pitch_bend));
}
ControlEvent::PitchBend(value) => {
self.control_event_data.pitch_bend_value = value;
self.process_pitch();
}
ControlEvent::FineTune(value) => {
self.control_event_data.fine_tune_value = value;
self.process_pitch();
}
ControlEvent::CoarseTune(value) => {
self.control_event_data.coarse_tune_value = value;
self.process_pitch();
}
}
}
fn process_pitch(&mut self) {
let data = &mut self.control_event_data;
let pitch_bend = data.pitch_bend_value;
let fine_tune = data.fine_tune_value;
let coarse_tune = data.coarse_tune_value;
let combined = pitch_bend + coarse_tune + fine_tune / 100.0;
self.voice_control_data.voice_pitch_multiplier = 2.0f32.powf(combined / 12.0);
self.propagate_voice_controls();
}
pub(super) fn reset_control(&mut self) {
self.control_event_data = ControlEventData::new_defaults(self.stream_params.sample_rate);
self.voice_control_data = VoiceControlData::new_defaults();
self.propagate_voice_controls();
self.control_event_data.cutoff = None;
for key in self.key_voices.iter_mut() {
key.data.set_damper(false);
}
}
pub(super) fn reset_program(&mut self) {
self.params.set_bank(0);
self.params.set_preset(0);
}
}
#[cfg(test)]
mod tests {
use crate::{AudioStreamParams, ChannelCount};
use super::*;
fn new_channel() -> VoiceChannel {
VoiceChannel::new(
Default::default(),
AudioStreamParams::new(48_000, ChannelCount::Stereo),
None,
)
}
#[test]
fn coarse_tune_only_uses_data_entry_msb() {
let mut channel = new_channel();
channel.process_control_event(ControlEvent::Raw(0x65, 0));
channel.process_control_event(ControlEvent::Raw(0x64, 2));
channel.process_control_event(ControlEvent::Raw(0x26, 100));
assert_eq!(channel.control_event_data.coarse_tune_value, 0.0);
channel.process_control_event(ControlEvent::Raw(0x06, 70));
assert_eq!(channel.control_event_data.coarse_tune_value, 6.0);
}
#[test]
fn reset_all_controllers_requires_zero_value() {
let mut channel = new_channel();
channel.process_control_event(ControlEvent::Raw(0x07, 32));
channel.process_control_event(ControlEvent::Raw(0x79, 1));
assert_eq!(channel.control_event_data.volume.end, 32.0 / 128.0);
channel.process_control_event(ControlEvent::Raw(0x79, 0));
assert_eq!(channel.control_event_data.volume.end, 1.0);
}
}