shady_audio/bar_processor/
mod.rs1mod config;
2
3use std::{num::NonZero, ops::Range};
4
5use config::BarDistribution;
6pub use config::{BarProcessorConfig, InterpolationVariant};
7use cpal::SampleRate;
8use realfft::num_complex::Complex32;
9use tracing::debug;
10
11use crate::{
12 interpolation::{
13 CubicSplineInterpolation, Interpolater, InterpolationInner, LinearInterpolation,
14 NothingInterpolation, SupportingPoint,
15 },
16 SampleProcessor, MAX_HUMAN_FREQUENCY, MIN_HUMAN_FREQUENCY,
17};
18
19type ChannelInterpolator = InterpolatorCtx;
20type ChannelBars = Box<[f32]>;
21
22struct InterpolatorCtx {
23 interpolator: Box<dyn Interpolater>,
24 supporting_point_fft_ranges: Box<[Range<usize>]>,
25
26 normalize_factor: f32,
27 sensitivity: f32,
28
29 prev: Box<[f32]>,
30 peak: Box<[f32]>,
31 fall: Box<[f32]>,
32 mem: Box<[f32]>,
33}
34
35impl InterpolatorCtx {
36 fn new(config: &BarProcessorConfig, sample_rate: SampleRate, fft_size: usize) -> Self {
37 let (interpolator, supporting_point_fft_ranges) =
38 Self::new_interpolation_data(config, sample_rate, fft_size);
39
40 let peak = vec![0f32; u16::from(config.amount_bars) as usize].into_boxed_slice();
41 let fall = peak.clone();
42 let mem = peak.clone();
43 let prev = peak.clone();
44
45 Self {
46 interpolator,
47 supporting_point_fft_ranges,
48 normalize_factor: 1.,
49 sensitivity: config.sensitivity,
50
51 prev,
52 peak,
53 fall,
54 mem,
55 }
56 }
57
58 fn new_interpolation_data(
60 config: &BarProcessorConfig,
61 sample_rate: SampleRate,
62 sample_len: usize,
63 ) -> (Box<dyn Interpolater>, Box<[Range<usize>]>) {
64 let weights = (0..config.amount_bars.get())
66 .map(|index| exp_fun((index + 1) as f32 / (config.amount_bars.get() + 1) as f32))
67 .collect::<Vec<f32>>();
68 debug!("Weights: {:?}", weights);
69
70 let amount_bins = {
71 let freq_resolution = sample_rate.0 as f32 / sample_len as f32;
72 debug!("Freq resolution: {}", freq_resolution);
73
74 let bin_range = Range {
76 start: ((u16::from(config.freq_range.start) as f32 / freq_resolution) as usize)
77 .max(1),
78 end: (u16::from(config.freq_range.end) as f32 / freq_resolution).ceil() as usize,
79 };
80 debug!("Bin range: {:?}", bin_range);
81 bin_range.len()
82 };
83 debug!("Available bins: {}", amount_bins);
84
85 let (supporting_points, supporting_point_fft_ranges) = {
87 let mut supporting_points = Vec::new();
88 let mut supporting_point_fft_ranges = Vec::new();
89
90 let mut prev_fft_range = 0..0;
91 for (bar_idx, weight) in weights.iter().enumerate() {
92 let end =
93 ((weight / MAX_HUMAN_FREQUENCY as f32) * amount_bins as f32).ceil() as usize;
94
95 let new_fft_range = prev_fft_range.end..end;
96 let is_supporting_point =
97 new_fft_range != prev_fft_range && !new_fft_range.is_empty();
98 if is_supporting_point {
99 supporting_points.push(SupportingPoint { x: bar_idx, y: 0. });
100
101 supporting_point_fft_ranges.push(new_fft_range.clone());
102 }
103
104 prev_fft_range = new_fft_range;
105 }
106
107 match config.bar_distribution {
109 BarDistribution::Uniform => {
110 let step = config.amount_bars.get() as f32 / supporting_points.len() as f32;
111 let supporting_points_len = supporting_points.len();
112 for (idx, supporting_point) in supporting_points
113 [..supporting_points_len.saturating_sub(1)]
114 .iter_mut()
115 .enumerate()
116 {
117 supporting_point.x = (idx as f32 * step) as usize;
118 }
119 }
120 BarDistribution::Natural => {}
121 }
122
123 (supporting_points, supporting_point_fft_ranges)
124 };
125
126 let interpolator: Box<dyn Interpolater> = match config.interpolation {
128 InterpolationVariant::None => NothingInterpolation::boxed(supporting_points),
129 InterpolationVariant::Linear => LinearInterpolation::boxed(supporting_points),
130 InterpolationVariant::CubicSpline => CubicSplineInterpolation::boxed(supporting_points),
131 };
132
133 (interpolator, supporting_point_fft_ranges.into_boxed_slice())
134 }
135
136 fn update_supporting_points(&mut self, fft_out: &[Complex32]) {
137 let mut overshoot = false;
138 let mut is_silent = true;
139
140 let amount_bars = self.amount_bars();
141
142 for (bar_idx, (supporting_point, fft_range)) in self
143 .interpolator
144 .supporting_points_mut()
145 .zip(self.supporting_point_fft_ranges.iter_mut())
146 .enumerate()
147 {
148 let x = supporting_point.x;
149 let prev_magnitude = supporting_point.y;
150 let mut next_magnitude = {
151 let mut raw_bar_val = fft_out[fft_range.clone()]
152 .iter()
153 .map(|out| {
154 let mag = out.norm_sqr();
155 if mag > 0. {
156 is_silent = false;
157 }
158 mag
159 })
160 .max_by(|a, b| a.total_cmp(b))
161 .unwrap();
162
163 raw_bar_val = raw_bar_val.sqrt();
164
165 raw_bar_val
166 * self.normalize_factor
167 * 10f32.powf((x as f32 / amount_bars as f32) - 1.)
168 };
169
170 debug_assert!(!prev_magnitude.is_nan());
171 debug_assert!(!next_magnitude.is_nan());
172
173 if next_magnitude < self.prev[bar_idx] {
175 let grav_mod = 1f32.powf(2.5) * 1.54 / self.sensitivity;
176 next_magnitude = self.peak[bar_idx]
177 * (1. - (self.fall[bar_idx] * self.fall[bar_idx] * grav_mod));
178
179 if next_magnitude < 0. {
180 next_magnitude = 0.;
181 }
182 self.fall[bar_idx] += 0.028;
183 } else {
184 self.peak[bar_idx] = next_magnitude;
185 self.fall[bar_idx] = 0.0;
186 }
187 self.prev[bar_idx] = next_magnitude;
188
189 supporting_point.y = self.mem[bar_idx] * 0.77 + next_magnitude;
190 self.mem[bar_idx] = supporting_point.y;
191
192 if supporting_point.y > 1. {
193 overshoot = true;
194 }
195 }
196
197 if overshoot {
198 self.normalize_factor *= 0.98;
199 } else if !is_silent {
200 self.normalize_factor *= 1.002;
201 }
202 }
203
204 fn amount_bars(&self) -> usize {
205 self.prev.len()
206 }
207}
208
209pub struct BarProcessor {
211 bar_values: Box<[Box<[f32]>]>,
212 channels: Box<[InterpolatorCtx]>,
213
214 config: BarProcessorConfig,
215 sample_rate: SampleRate,
216 sample_len: usize,
217}
218
219impl BarProcessor {
220 pub fn new(processor: &SampleProcessor, config: BarProcessorConfig) -> Self {
224 let sample_rate = processor.sample_rate();
225 let sample_len = processor.fft_size();
226 let amount_channels = processor.amount_channels();
227
228 let (channels, bar_values) =
229 Self::get_channels_and_bar_values(&config, amount_channels, sample_rate, sample_len);
230
231 Self {
232 config,
233 channels,
234 bar_values,
235
236 sample_rate,
237 sample_len,
238 }
239 }
240
241 pub fn process_bars(&mut self, processor: &SampleProcessor) -> &[Box<[f32]>] {
246 for ((channel_idx, channel), fft_ctx) in self
247 .channels
248 .iter_mut()
249 .enumerate()
250 .zip(processor.fft_out().iter())
251 {
252 channel.update_supporting_points(&fft_ctx.fft_out);
253
254 channel
255 .interpolator
256 .interpolate(&mut self.bar_values[channel_idx]);
257 }
258
259 &self.bar_values
260 }
261
262 pub fn config(&self) -> &BarProcessorConfig {
263 &self.config
264 }
265
266 pub fn set_amount_bars(&mut self, amount_bars: NonZero<u16>) {
295 self.config.amount_bars = amount_bars;
296 let amount_channels = self.channels.len();
297
298 let (channels, bar_values) = Self::get_channels_and_bar_values(
299 &self.config,
300 amount_channels,
301 self.sample_rate,
302 self.sample_len,
303 );
304
305 self.channels = channels;
306 self.bar_values = bar_values;
307 }
308
309 fn get_channels_and_bar_values(
310 config: &BarProcessorConfig,
311 amount_channels: usize,
312 sample_rate: SampleRate,
313 sample_len: usize,
314 ) -> (Box<[ChannelInterpolator]>, Box<[ChannelBars]>) {
315 let mut channels = Vec::with_capacity(amount_channels);
316 let bar_values =
317 vec![vec![0f32; config.amount_bars.get() as usize].into_boxed_slice(); amount_channels];
318
319 for _ in 0..amount_channels {
320 channels.push(InterpolatorCtx::new(config, sample_rate, sample_len));
321 }
322
323 (channels.into_boxed_slice(), bar_values.into_boxed_slice())
324 }
325}
326
327fn exp_fun(x: f32) -> f32 {
328 debug_assert!(0. <= x);
329 debug_assert!(x <= 1.);
330
331 let max_mel_value = mel(MAX_HUMAN_FREQUENCY as f32);
332 let min_mel_value = mel(MIN_HUMAN_FREQUENCY as f32);
333
334 let mapped_x = x * (max_mel_value - min_mel_value) + min_mel_value;
336 inv_mel(mapped_x)
337}
338
339fn mel(x: f32) -> f32 {
341 debug_assert!(MIN_HUMAN_FREQUENCY as f32 <= x);
342 debug_assert!(x <= MAX_HUMAN_FREQUENCY as f32);
343
344 2595. * (1. + x / 700.).log10()
345}
346
347fn inv_mel(x: f32) -> f32 {
348 let min_mel_value = mel(MIN_HUMAN_FREQUENCY as f32);
349 let max_mel_value = mel(MAX_HUMAN_FREQUENCY as f32);
350
351 debug_assert!(min_mel_value <= x);
352 debug_assert!(x <= max_mel_value);
353
354 700. * (10f32.powf(x / 2595.) - 1.)
355}