minidsp_protocol/
fixed_point.rs

1use std::fmt::{self, Display};
2
3#[derive(Clone, Copy)]
4pub struct FixedPoint(u32);
5
6impl FixedPoint {
7    pub fn from_db(db: f32) -> Self {
8        FixedPoint::from_f32(10f64.powf(db as f64 / 20f64) as f32)
9    }
10
11    pub fn to_db(&self) -> f32 {
12        let linear = self.to_f32() as f64;
13        let db = (20f64 * linear.log10()) as f32;
14        (db * 100f32).round() / 100f32
15    }
16
17    pub fn from_u32(val: u32) -> Self {
18        Self(val)
19    }
20
21    pub fn from_f32(val: f32) -> Self {
22        let encoded = val as f64 * (1 << 23) as f64;
23        let encoded = encoded + ((1 << 27) as f64);
24        let encoded = encoded as i64 as u32;
25        let mut encoded = encoded ^ 0x0800_0000;
26
27        // if encoded & 0x0800_0000 != 0 {
28        //     encoded |= 0xF000_0000;
29        // }
30
31        if encoded & 0xF000_0000 == 0xF000_0000 {
32            encoded &= 0x0FFF_FFFF
33        }
34
35        Self(encoded)
36    }
37
38    pub fn to_f32(&self) -> f32 {
39        let val = self.0 ^ 0x0800_0000;
40        let sub = (val.wrapping_sub(1 << 27)) as i32 as f32;
41        let decoded = sub / ((1 << 23) as f32);
42        decoded as f32
43    }
44
45    pub fn to_u32(&self) -> u32 {
46        self.0
47    }
48}
49
50impl Default for FixedPoint {
51    fn default() -> Self {
52        Self::from_u32(0)
53    }
54}
55
56impl From<f32> for FixedPoint {
57    fn from(val: f32) -> Self {
58        Self::from_f32(val)
59    }
60}
61
62impl From<FixedPoint> for f32 {
63    fn from(fp: FixedPoint) -> Self {
64        fp.to_f32()
65    }
66}
67
68impl Display for FixedPoint {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        write!(f, "{}", self.to_f32())
71    }
72}
73
74impl fmt::Debug for FixedPoint {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        f.debug_tuple("FixedPoint").field(&self.to_f32()).finish()
77    }
78}
79
80#[cfg(test)]
81mod test {
82    use crate::FixedPoint;
83
84    #[test]
85    fn test_codec() {
86        use super::*;
87
88        let values: &[(f32, u32)] = &[
89            (-64., 0xe0_00_00_00),
90            (0.05, 0x00_06_66_66),
91            (0.025, 0x00_03_33_33),
92            (0.0125, 0x00_01_99_99),
93            (0.125, 0x00_10_00_00),
94            (0.0625, 0x00_08_00_00),
95            (1.0, 0x00_80_00_00),
96            (-1.0, 0x0f_80_00_00),
97            (-0.05, 0x0f_f9_99_99),
98            (-0.025, 0x0f_fc_cc_cc),
99            (-0.0125, 0x0f_fe_66_66),
100            (-0.125, 0x0f_f0_00_00),
101            (-0.062, 0x0f_f8_10_62),
102            (64., 0x20_00_00_00),
103            (128., 0x40_00_00_00),
104            (-128., 0xc0_00_00_00),
105            (-111., 0xd8_80_00_00),
106        ];
107
108        for &(val, hex) in values {
109            let enc = FixedPoint::from(val).to_u32();
110            let dec = FixedPoint::from_u32(enc).to_f32();
111
112            // println!("val={} hex={:#x?} enc={:#x?} dec={}", val, hex, enc, dec);
113            assert!(
114                (hex as i32 - enc as i32).abs() < 2,
115                "{:x} and {:x} differ too much",
116                hex,
117                enc
118            );
119            assert!(
120                (val - dec).abs() < 1e-5,
121                "{} and {} differ too much",
122                val,
123                dec
124            )
125        }
126    }
127
128    #[test]
129    fn test_db() {
130        let values: &[(f32, u32)] = &[
131            (12.0, 0x01_fd_93_c1),
132            (6.0, 0x00_ff_64_c1),
133            (3.1, 0x00_b6_e5_ff),
134            (1.0, 0x00_8f_9e_4c),
135            (0.5, 0x00_87_95_a0),
136            (0.3, 0x00_84_7f_89),
137            (-0.3, 0x00_7b_a7_8e),
138            (-0.5, 0x00_78_d6_fc),
139            (-3.1, 0x00_59_94_6c),
140            (-6.0, 0x00_40_26_e7),
141            (-12.0, 0x00_20_26_f3),
142            (-72.0, 0x00_00_08_3b),
143        ];
144
145        for &(val, hex) in values.iter() {
146            let enc = FixedPoint::from_db(val).0;
147            let dec = FixedPoint::from_u32(hex).to_db();
148
149            // println!(
150            //     "val={} hex={:x?} enc={:x?} dec={}",
151            //     val,
152            //     hex,
153            //     enc,
154            //     dec
155            // );
156
157            let enc_delta = ((enc as i64) - (hex as i64)).abs();
158            let dec_delta = (val - dec).abs();
159            assert!(enc_delta < 2 && dec_delta < 1e-5);
160        }
161    }
162}