1#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Default)]
5#[repr(transparent)]
6pub struct WaveformVal(pub u8);
7
8impl WaveformVal {
9 const MIN_VAL: u8 = u8::MIN;
10 const MAX_VAL: u8 = u8::MAX;
11
12 pub(crate) fn from_f32(val: f32) -> Self {
13 debug_assert!(val >= f32::from(Self::MIN_VAL));
14 let mapped = (val * (f32::from(Self::MAX_VAL) + 1.0)).min(f32::from(Self::MAX_VAL));
15 debug_assert!(mapped >= f32::from(Self::MIN_VAL));
16 debug_assert!(mapped <= f32::from(Self::MAX_VAL));
17 #[expect(clippy::cast_possible_truncation)]
18 #[expect(clippy::cast_sign_loss)]
19 Self(mapped as u8)
20 }
21
22 #[must_use]
23 pub fn to_f32(self) -> f32 {
24 f32::from(self.0) / f32::from(Self::MAX_VAL)
25 }
26
27 #[must_use]
28 pub const fn is_zero(self) -> bool {
29 self.0 == 0
30 }
31}
32
33impl From<WaveformVal> for u8 {
34 fn from(value: WaveformVal) -> Self {
35 value.0
36 }
37}
38
39#[derive(Debug, Clone, Copy, Default)]
40pub struct FilteredWaveformVal {
41 pub all: WaveformVal,
42 pub low: WaveformVal,
43 pub mid: WaveformVal,
44 pub high: WaveformVal,
45}
46
47impl FilteredWaveformVal {
48 #[must_use]
50 pub fn spectral_rgb_color(self) -> (f32, f32, f32) {
51 self.spectral_rgb_color_normalized(0.0)
52 }
53
54 #[must_use]
56 pub fn spectral_rgb_color_all(self) -> (f32, f32, f32) {
57 self.spectral_rgb_color_normalized(self.all.to_f32())
58 }
59
60 #[must_use]
61 fn spectral_rgb_color_normalized(self, max: f32) -> (f32, f32, f32) {
62 let Self {
63 all: _,
64 low,
65 mid,
66 high,
67 } = self;
68 let low = low.to_f32();
69 let mid = mid.to_f32();
70 let high = high.to_f32();
71 let denom = max.max(low).max(mid).max(high);
75 if denom == 0.0 {
76 return (0.0, 0.0, 0.0);
77 }
78 let red = low / denom;
79 let green = mid / denom;
80 let blue = high / denom;
81 (red, green, blue)
82 }
83}
84
85#[derive(Debug, Clone, Copy, Default)]
86pub struct WaveformBin {
87 pub peak: WaveformVal,
89
90 pub energy: WaveformVal,
92}
93
94#[derive(Debug, Clone, Default)]
95pub struct FilteredWaveformBin {
96 pub all: WaveformBin,
97 pub low: WaveformBin,
98 pub mid: WaveformBin,
99 pub high: WaveformBin,
100}
101
102impl FilteredWaveformBin {
103 #[must_use]
105 pub const fn peak(&self) -> FilteredWaveformVal {
106 let Self {
107 all,
108 low,
109 mid,
110 high,
111 } = self;
112 FilteredWaveformVal {
113 all: all.peak,
114 low: low.peak,
115 mid: mid.peak,
116 high: high.peak,
117 }
118 }
119
120 #[must_use]
122 pub const fn energy(&self) -> FilteredWaveformVal {
123 let Self {
124 all,
125 low,
126 mid,
127 high,
128 } = self;
129 FilteredWaveformVal {
130 all: all.energy,
131 low: low.energy,
132 mid: mid.energy,
133 high: high.energy,
134 }
135 }
136
137 #[must_use]
139 pub fn spectral_flatness(&self) -> f32 {
140 let FilteredWaveformVal {
141 all: _,
142 low,
143 mid,
144 high,
145 } = self.energy();
146 let low = low.to_f32();
147 let mid = mid.to_f32();
148 let high = high.to_f32();
149 let arithmetic_mean = (low + mid + high) / 3.0;
150 if arithmetic_mean == 0.0 {
151 return 1.0;
153 }
154 debug_assert!(arithmetic_mean > 0.0);
155 debug_assert!(arithmetic_mean <= 1.0);
156 let geometric_mean = (low * mid * high).powf(1.0 / 3.0);
157 debug_assert!(geometric_mean >= 0.0);
158 debug_assert!(geometric_mean <= 1.0);
159 geometric_mean / arithmetic_mean
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::WaveformVal;
166
167 #[test]
168 fn waveform_val_from_f32() {
169 assert_eq!(
170 WaveformVal::from_f32(0.0),
171 WaveformVal(WaveformVal::MIN_VAL)
172 );
173 assert_eq!(WaveformVal::from_f32(0.25), WaveformVal(64));
174 assert_eq!(WaveformVal::from_f32(0.5), WaveformVal(128));
175 assert_eq!(WaveformVal::from_f32(0.75), WaveformVal(192));
176 assert_eq!(
177 WaveformVal::from_f32(1.0),
178 WaveformVal(WaveformVal::MAX_VAL)
179 );
180 }
181
182 #[test]
183 fn waveform_val_to_from_f32() {
184 for val in WaveformVal::MIN_VAL..=WaveformVal::MAX_VAL {
185 let val = WaveformVal(val);
186 assert_eq!(val, WaveformVal::from_f32(val.to_f32()));
187 }
188 }
189
190 #[test]
191 fn spectral_flatness_one() {
192 for val in WaveformVal::MIN_VAL..=WaveformVal::MAX_VAL {
193 let val = WaveformVal(val);
194 let bin = super::FilteredWaveformBin {
195 all: super::WaveformBin {
196 peak: val,
197 energy: val,
198 },
199 low: super::WaveformBin {
200 peak: val,
201 energy: val,
202 },
203 mid: super::WaveformBin {
204 peak: val,
205 energy: val,
206 },
207 high: super::WaveformBin {
208 peak: val,
209 energy: val,
210 },
211 };
212 let spectral_flatness = bin.spectral_flatness();
213 assert!(spectral_flatness > 0.999_999);
214 }
215 }
216}