minidsp_protocol/
fixed_point.rs1use 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 & 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 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 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}