use core::ops::Add;
use fixed::types::extra::{IsLessOrEqual, LeEqU16, LeEqU32, True, Unsigned, U16, U31};
pub use fixed::types::*;
use fixed::{FixedI16, FixedI32, FixedU16, FixedU32};
pub type Sample = I4F12;
pub type USample = U4F12;
pub type Note = U7F9;
pub type SignedNote = I7F9;
pub type Frequency = U14F18; pub type Scalar = U0F16;
const FRAC_16_21: Scalar = Scalar::lit("0x0.c30c"); const FRAC_4_5: Scalar = Scalar::lit("0x0.cccd"); const FRAC_2_3: Scalar = Scalar::lit("0x0.aaab"); const FRAC_8_15: Scalar = Scalar::lit("0x0.8889");
const SMALL_ANGLE_LESS: Sample = Sample::lit("0x0.1");
const FRAC_4LN2_3: Scalar = Scalar::lit("0x0.ec98");
const FREQ_E4: Frequency = Frequency::lit("329.627557");
pub fn scale_fixedfloat<FracA, FracB>(a: FixedU32<FracA>, b: FixedU16<FracB>) -> FixedU32<FracA>
where
FracA: Unsigned + LeEqU32,
FracB: Unsigned + LeEqU16 + Add<U16> + IsLessOrEqual<FracA>,
{
let bbits = FixedU16::<FracB>::INT_NBITS;
let shift = a.leading_zeros();
let a_shifted = U0F32::from_bits(a.unwrapped_shl(shift).to_bits());
let prod = b.wide_mul(U0F16::from_num(a_shifted));
let res = if shift > bbits {
prod.unwrapped_shr(shift - bbits)
} else {
prod.unwrapped_shl(bbits - shift)
};
FixedU32::<FracA>::from_bits(res.to_bits())
}
pub fn widen_i<Frac>(a: FixedI16<Frac>) -> FixedI32<Frac>
where
Frac: Unsigned + LeEqU16 + LeEqU32,
{
FixedI32::<Frac>::from_num(a)
}
fn one_over_one_plus_helper<Frac>(n: FixedU32<Frac>) -> (U1F31, u32)
where
Frac: Unsigned + IsLessOrEqual<U31, Output = True> + LeEqU32,
{
let nbits = FixedU32::<Frac>::INT_NBITS;
let x = n + FixedU32::<Frac>::ONE;
let mut shift = x.leading_zeros();
let mut x_shifted = U1F31::from_bits(x.to_bits()).unwrapped_shl(shift);
shift += 1;
if x_shifted >= U1F31::SQRT_2 {
shift -= 1;
x_shifted = x_shifted.unwrapped_shr(1);
}
(x_shifted, nbits - shift)
}
pub fn one_over_one_plus<Frac>(x: FixedU32<Frac>) -> (U1F15, u32)
where
Frac: Unsigned + IsLessOrEqual<U31, Output = True> + LeEqU32,
{
let (x_shifted, shift) = one_over_one_plus_helper(x);
let x_shifted_trunc = U1F15::from_num(x_shifted);
let x2 = I3F29::from_num(x_shifted_trunc.wide_mul(x_shifted_trunc));
let one_minus_x = I3F29::ONE - I3F29::from_num(x_shifted);
(
U1F15::from_num(x2 + one_minus_x + one_minus_x.unwrapped_shl(1)),
shift,
)
}
pub fn one_over_one_plus_highacc(x: U0F16) -> (U1F15, u32) {
let (x_shifted, shift) = one_over_one_plus_helper(U16F16::from_num(x));
const FIVE_NAR: U3F13 = U3F13::lit("5");
const FIVE: U4F28 = U4F28::lit("5");
const TEN: U4F28 = U4F28::lit("10");
let x_shifted_trunc = U1F15::from_num(x_shifted);
let p1 = x_shifted_trunc.wide_mul(FIVE_NAR - U3F13::from_num(x_shifted_trunc));
let p2 = x_shifted_trunc.wide_mul(U3F13::from_num(TEN - p1));
let p3 = x_shifted_trunc.wide_mul(U3F13::from_num(TEN - p2));
(U1F15::from_num(FIVE - p3), shift)
}
pub fn sin_fixed(x: Sample) -> Sample {
if x.abs() < SMALL_ANGLE_LESS {
return x;
}
let x2 = USample::from_num(x.wide_mul(x));
let c = FRAC_16_21.wide_mul(x2).unwrapped_shr(5);
let c_nested = Scalar::from_num(U4F28::ONE - c);
let b = Scalar::from_num(FRAC_4_5.wide_mul(x2).unwrapped_shr(4));
let b_nested = Scalar::from_num(U16F16::ONE - U16F16::from_num(b.wide_mul(c_nested)));
let a = U2F14::from_num(FRAC_2_3.wide_mul(x2).unwrapped_shr(2));
let a_nested = I1F15::from_num(I2F30::ONE - I2F30::from_num(a.wide_mul(b_nested)));
Sample::from_num(x.wide_mul(a_nested))
}
pub fn cos_fixed(x: Sample) -> Sample {
let x2 = USample::from_num(x.wide_mul(x));
if x.abs() < SMALL_ANGLE_LESS {
return Sample::ONE - Sample::from_num(x2.unwrapped_shr(1));
}
let c = FRAC_8_15.wide_mul(x2).unwrapped_shr(4);
let c_nested = Scalar::from_num(U4F28::ONE - c);
let b = Scalar::from_num(FRAC_2_3.wide_mul(x2).unwrapped_shr(3));
let b_nested = Scalar::from_num(U16F16::ONE - U16F16::from_num(b.wide_mul(c_nested)));
let a_mult_b_nested = x2.wide_mul(b_nested).unwrapped_shr(1);
Sample::ONE - Sample::from_num(a_mult_b_nested)
}
pub fn tan_fixed(x: U0F16) -> U1F15 {
let x2 = x.wide_mul(x);
let x2_over3 = U0F16::from_num(x2).wide_mul(FRAC_2_3).unwrapped_shr(1);
let res_over_x = U1F15::from_num(x2_over3) + U1F15::ONE;
U1F15::from_num(res_over_x.wide_mul(x))
}
fn exp_fixed_small(x: I0F16) -> U2F14 {
let c_nested = I3F29::ONE + I3F29::from_num(x).unwrapped_shr(2);
let b = I0F16::from_num(x.wide_mul_unsigned(FRAC_2_3).unwrapped_shr(1));
let b_nested = I3F29::ONE + I3F13::from_num(c_nested).wide_mul(b);
let a = I3F13::from_num(I3F29::from_num(x).unwrapped_shr(1));
let a_nested = I6F26::ONE + I3F13::from_num(b_nested).wide_mul(a);
U2F14::from_num(x.wide_mul(I3F13::from_num(a_nested)) + I3F29::ONE)
}
pub fn exp_fixed(x: I3F13) -> U8F24 {
const LOOKUP_TABLE: [(Scalar, u32, u32); 8] = [
(Scalar::lit("0x0.f760"), 5, 0),
(Scalar::lit("0x0.a81c"), 3, 0),
(Scalar::lit("0x0.e47c"), 2, 0),
(Scalar::lit("0x0.9b45"), 0, 0),
(Scalar::lit("0x0.d309"), 0, 1),
(Scalar::lit("0x0.8f69"), 0, 3),
(Scalar::lit("0x0.c2eb"), 0, 4),
(Scalar::lit("0x0.8476"), 0, 6),
];
const ONE_HALF: I3F13 = I3F13::lit("0.5");
let x_int = x.int().to_num::<i8>(); let index = (x_int + 4) as usize;
let frac_exp = exp_fixed_small(I0F16::from_num(x.frac() - ONE_HALF));
let (multiplier, right, left) = LOOKUP_TABLE[index];
let retval = U8F56::from_num(multiplier.wide_mul(frac_exp));
U8F24::from_num(retval.unwrapped_shl(left).unwrapped_shr(right))
}
fn note_to_value(note: Note) -> I3F13 {
const C64: I19F13 = I19F13::lit("64");
let note_signed = I19F13::from_num(note) - C64;
I3F13::from_num(note_signed.unwrapped_shr(4))
}
pub fn midi_note_to_frequency(note: Note) -> Frequency {
let note_xform = note_to_value(note);
let power = I3F13::from_num(note_xform.wide_mul_unsigned(FRAC_4LN2_3));
FREQ_E4 * U14F18::from_num(exp_fixed(power))
}
#[cfg(test)]
mod tests {
use super::super::util::calculate_cents;
use super::*;
use fixed::traits::ToFixed;
#[test]
fn const_fraction_correctness() {
assert_eq!(FRAC_16_21, Scalar::from_num(16.0 / 21.0));
assert_eq!(FRAC_4_5, Scalar::from_num(4.0 / 5.0));
assert_eq!(FRAC_2_3, Scalar::from_num(2.0 / 3.0));
assert_eq!(FRAC_8_15, Scalar::from_num(8.0 / 15.0));
}
#[test]
fn sin_zero_and_small_angles() {
let _ = sin_fixed(SMALL_ANGLE_LESS);
assert_eq!(sin_fixed(Sample::ZERO), Sample::ZERO);
}
#[test]
fn sin_fixed_rms_error() {
let numsteps = 2000;
let mut error = 0.0;
for i in 0..=numsteps {
let x = (core::f32::consts::TAU * i as f32) / (numsteps as f32) - core::f32::consts::PI;
let fixed = sin_fixed(Sample::from_num(x));
let float = x.sin();
let this_error = float - fixed.to_num::<f32>();
error += this_error * this_error;
}
error /= numsteps as f32;
error = error.sqrt();
assert!(error < 0.02); }
#[test]
fn sin_slightly_over_bounds_no_overflows() {
let _a = sin_fixed(Sample::lit("3.2"));
let _b = sin_fixed(Sample::lit("-3.2"));
}
#[test]
fn cos_zero_and_small_angles() {
let _ = cos_fixed(SMALL_ANGLE_LESS);
assert_eq!(cos_fixed(Sample::ZERO), Sample::ONE);
}
#[test]
fn cos_fixed_rms_error() {
let numsteps = 2000;
let mut error = 0.0;
for i in 0..=numsteps {
let x = (core::f32::consts::TAU * i as f32) / (numsteps as f32) - core::f32::consts::PI;
let fixed = cos_fixed(Sample::from_num(x));
let float = x.cos();
let this_error = float - fixed.to_num::<f32>();
error += this_error * this_error;
}
error /= numsteps as f32;
error = error.sqrt();
assert!(error < 0.06); }
#[test]
fn cos_slightly_over_bounds_no_overflows() {
let _a = cos_fixed(Sample::lit("3.2"));
let _b = cos_fixed(Sample::lit("-3.2"));
}
#[test]
fn midi_pitch_calculations() {
for i in 0..=127 {
let pitch = 440.0 * f32::powf(2.0, ((i - 69) as f32) / 12.0);
let pitch_fixed = midi_note_to_frequency(i.to_fixed()).to_num::<f32>();
let error = calculate_cents(pitch, pitch_fixed);
assert!(error < 1.0); }
}
}