use crate::fixed::fixed::{Q15, Q8_8};
use crate::fixed::units::{PitchDelta, RetrigMul};
#[inline]
pub const fn lfo_speed_from_nibble_64(speed_nibble: u8) -> Q8_8 {
Q8_8::from_ratio((speed_nibble & 0x0F) as i16, 64)
}
#[inline]
pub const fn lfo_depth_from_nibble_16(depth_nibble: u8) -> Q15 {
Q15::from_ratio((depth_nibble & 0x0F) as i32, 16)
}
#[inline]
pub const fn it_vibrato_depth_from_nibble_4(depth_nibble: u8) -> PitchDelta {
PitchDelta::from_ratio((depth_nibble & 0x0F) as i16, 4)
}
#[inline]
pub const fn it_panbrello_depth_from_nibble_4(depth_nibble: u8) -> Q15 {
Q15::from_ratio((depth_nibble & 0x0F) as i32, 4)
}
#[inline]
pub const fn vibrato_depth_from_nibble_16(depth_nibble: u8) -> PitchDelta {
PitchDelta::from_ratio((depth_nibble & 0x0F) as i16, 16)
}
#[inline]
pub const fn retrig_mul_ft2(nibble: u8) -> RetrigMul {
match nibble {
6 => RetrigMul::from_ratio(11, 16),
7 => RetrigMul::from_ratio(1, 2),
0xE => RetrigMul::from_ratio(3, 2),
0xF => RetrigMul::from_ratio(2, 1),
_ => RetrigMul::UNITY,
}
}
#[inline]
pub const fn retrig_mul_s3m(nibble: u8) -> RetrigMul {
match nibble {
6 => RetrigMul::from_ratio(5, 8),
7 => RetrigMul::from_ratio(1, 2),
0xE => RetrigMul::from_ratio(3, 2),
0xF => RetrigMul::from_ratio(2, 1),
_ => RetrigMul::UNITY,
}
}
#[inline]
pub const fn retrig_volume_delta_q15(nibble: u8) -> Q15 {
match nibble {
1 => Q15::from_ratio(-1, 64),
2 => Q15::from_ratio(-2, 64),
3 => Q15::from_ratio(-4, 64),
4 => Q15::from_ratio(-8, 64),
5 => Q15::from_ratio(-16, 64),
9 => Q15::from_ratio(1, 64),
0xA => Q15::from_ratio(2, 64),
0xB => Q15::from_ratio(4, 64),
0xC => Q15::from_ratio(8, 64),
0xD => Q15::from_ratio(16, 64),
_ => Q15::ZERO,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lfo_speed_nibble_quantization() {
let mut prev = i16::MIN;
for n in 0..16 {
let q = lfo_speed_from_nibble_64(n);
assert!(q.raw() > prev);
prev = q.raw();
}
}
#[test]
fn it_panbrello_saturates_at_full_swing() {
assert_eq!(it_panbrello_depth_from_nibble_4(0), Q15::ZERO);
assert_eq!(it_panbrello_depth_from_nibble_4(4), Q15::ONE);
assert_eq!(it_panbrello_depth_from_nibble_4(15), Q15::ONE);
}
#[test]
fn retrig_mul_ft2_known_values() {
assert_eq!(retrig_mul_ft2(7), RetrigMul::from_ratio(1, 2));
assert_eq!(retrig_mul_ft2(0xF), RetrigMul::from_ratio(2, 1));
}
#[test]
fn retrig_volume_delta_signs() {
for (neg, pos) in [(1u8, 9u8), (2, 0xA), (3, 0xB), (4, 0xC), (5, 0xD)] {
assert_eq!(
retrig_volume_delta_q15(neg).raw(),
-retrig_volume_delta_q15(pos).raw()
);
}
assert_eq!(retrig_volume_delta_q15(0), Q15::ZERO);
}
}