Skip to content

Commit e322763

Browse files
perf: various optimizations
Signed-off-by: Maika Namuo <httpworldview@gmail.com>
1 parent 76e4794 commit e322763

4 files changed

Lines changed: 146 additions & 145 deletions

File tree

‎src/dsp.rs‎

Lines changed: 66 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ pub struct AudioBlock<'a> {
125125
pub sample_rate: f32,
126126
pub positions: [ChannelPosition; MAX_AUDIO_CHANNELS],
127127
stereo: [[f32; 2]; MAX_AUDIO_CHANNELS],
128+
stereo_channels: usize,
128129
}
129130

130131
fn stereo_indices(channels: usize, positions: [ChannelPosition; MAX_AUDIO_CHANNELS]) -> [usize; 2] {
@@ -207,17 +208,26 @@ impl<'a> AudioBlock<'a> {
207208
positions: [ChannelPosition; MAX_AUDIO_CHANNELS],
208209
) -> Self {
209210
let channels = channels.clamp(1, MAX_AUDIO_CHANNELS);
211+
let stereo_channels = (2..channels.min(samples.len()))
212+
.rfind(|&channel| {
213+
samples[channel..]
214+
.iter()
215+
.step_by(channels)
216+
.any(|sample| sample.to_bits() != 0)
217+
})
218+
.map_or(channels.min(2), |channel| channel + 1);
210219
Self {
211220
samples,
212221
channels,
213222
sample_rate: sanitize_sample_rate(sample_rate),
214223
positions,
215224
stereo: stereo_matrix(channels, positions),
225+
stereo_channels,
216226
}
217227
}
218228

219-
pub fn stereo_matrix(&self) -> &[[f32; 2]; MAX_AUDIO_CHANNELS] {
220-
&self.stereo
229+
pub fn stereo_matrix(&self) -> &[[f32; 2]] {
230+
&self.stereo[..self.stereo_channels]
221231
}
222232

223233
pub fn frame_count(&self) -> usize {
@@ -230,83 +240,96 @@ impl<'a> AudioBlock<'a> {
230240
}
231241

232242
#[derive(Debug, Clone, Copy)]
233-
struct CompensatedSum {
234-
sum: f64,
235-
correction: f64,
243+
struct CompensatedPair {
244+
sums: [f64; 2],
245+
corrections: [f64; 2],
236246
}
237247

238-
impl CompensatedSum {
248+
impl CompensatedPair {
239249
const ZERO: Self = Self {
240-
sum: 0.0,
241-
correction: 0.0,
250+
sums: [0.0; 2],
251+
corrections: [0.0; 2],
242252
};
243253

244254
// Kahan-Babuska-Neumaier compensated addition.
245-
fn add(&mut self, value: f64) {
246-
let next = self.sum + value;
247-
self.correction += if self.sum.abs() >= value.abs() {
248-
(self.sum - next) + value
255+
fn add(&mut self, index: usize, value: f64) {
256+
let next = self.sums[index] + value;
257+
self.corrections[index] += if self.sums[index].abs() >= value.abs() {
258+
(self.sums[index] - next) + value
249259
} else {
250-
(value - next) + self.sum
260+
(value - next) + self.sums[index]
251261
};
252-
self.sum = next;
262+
self.sums[index] = next;
263+
}
264+
265+
fn add_both(&mut self, value: f64) {
266+
(0..2).for_each(|index| self.add(index, value));
267+
}
268+
269+
fn refresh(&mut self) {
270+
(self.sums, self.corrections) = ([self.sums[1], 0.0], [self.corrections[1], 0.0]);
253271
}
254272

255273
fn value(self) -> f64 {
256-
self.sum + self.correction
274+
self.sums[0] + self.corrections[0]
257275
}
258276
}
259277

260278
/// Running means for several values over one or more independently sized windows.
261279
/// All windows share the ring sized for the longest duration.
262280
#[derive(Debug)]
263-
pub struct WindowedMeans<const VALUES: usize, const WINDOWS: usize> {
264-
buffer: Box<[[f64; VALUES]]>,
281+
pub struct WindowedMeans<const VALUES: usize, const WINDOWS: usize, T = f64> {
282+
buffer: Box<[[T; VALUES]]>,
265283
capacities: [usize; WINDOWS],
266-
sums: [[CompensatedSum; VALUES]; WINDOWS],
267-
// Periodic full-window sums bound compensation error without rescanning the ring.
268-
refresh_sums: [[CompensatedSum; VALUES]; WINDOWS],
284+
sums: [[CompensatedPair; VALUES]; WINDOWS],
269285
refresh_counts: [usize; WINDOWS],
270286
head: usize,
271287
count: usize,
272288
}
273289

274-
impl<const VALUES: usize, const WINDOWS: usize> WindowedMeans<VALUES, WINDOWS> {
290+
impl<const VALUES: usize, const WINDOWS: usize, T> WindowedMeans<VALUES, WINDOWS, T>
291+
where
292+
T: Copy + From<f32> + Into<f64>,
293+
{
275294
pub fn new(capacities: [usize; WINDOWS]) -> Self {
276295
let capacities = capacities.map(|capacity| capacity.max(1));
277296
let len = capacities.iter().copied().max().unwrap_or(1);
278297
Self {
279-
buffer: vec![[0.0; VALUES]; len].into_boxed_slice(),
298+
buffer: vec![[T::from(0.0); VALUES]; len].into_boxed_slice(),
280299
capacities,
281-
sums: [[CompensatedSum::ZERO; VALUES]; WINDOWS],
282-
refresh_sums: [[CompensatedSum::ZERO; VALUES]; WINDOWS],
300+
sums: [[CompensatedPair::ZERO; VALUES]; WINDOWS],
283301
refresh_counts: [0; WINDOWS],
284302
head: 0,
285303
count: 0,
286304
}
287305
}
288306

289-
pub fn push<T: Copy + Into<f64>>(&mut self, values: [T; VALUES]) {
290-
let values = values.map(|value| {
291-
let value = value.into();
292-
if value.is_finite() { value } else { 0.0 }
307+
pub fn push(&mut self, mut values: [T; VALUES]) {
308+
let mapped: [f64; VALUES] = std::array::from_fn(|index| {
309+
let value = values[index].into();
310+
if value.is_finite() {
311+
value
312+
} else {
313+
values[index] = T::from(0.0);
314+
0.0
315+
}
293316
});
294317
let len = self.buffer.len();
295318
for window in 0..WINDOWS {
296319
let capacity = self.capacities[window];
297320
let old =
298321
(self.count >= capacity).then(|| self.buffer[(self.head + len - capacity) % len]);
299322
for value in 0..VALUES {
300-
self.sums[window][value].add(values[value]);
323+
self.sums[window][value].add_both(mapped[value]);
301324
if let Some(old) = old {
302-
self.sums[window][value].add(-old[value]);
325+
self.sums[window][value].add(0, -old[value].into());
303326
}
304-
self.refresh_sums[window][value].add(values[value]);
305327
}
306328
self.refresh_counts[window] += 1;
307329
if self.refresh_counts[window] == capacity {
308-
self.sums[window] = self.refresh_sums[window];
309-
self.refresh_sums[window] = [CompensatedSum::ZERO; VALUES];
330+
self.sums[window]
331+
.iter_mut()
332+
.for_each(CompensatedPair::refresh);
310333
self.refresh_counts[window] = 0;
311334
}
312335
}
@@ -315,18 +338,18 @@ impl<const VALUES: usize, const WINDOWS: usize> WindowedMeans<VALUES, WINDOWS> {
315338
self.count = (self.count + 1).min(len);
316339
}
317340

341+
pub fn with_leading_zeros(capacities: [usize; WINDOWS], count: usize) -> Self {
342+
let mut means = Self::new(capacities);
343+
means.head = count % means.buffer.len();
344+
means.count = count.min(means.buffer.len());
345+
means.refresh_counts = means.capacities.map(|capacity| count % capacity);
346+
means
347+
}
348+
318349
pub fn mean(&self, window: usize) -> [f64; VALUES] {
319350
let count = self.count.min(self.capacities[window]).max(1);
320351
std::array::from_fn(|value| self.sums[window][value].value() / count as f64)
321352
}
322-
323-
pub fn clear(&mut self) {
324-
self.sums = [[CompensatedSum::ZERO; VALUES]; WINDOWS];
325-
self.refresh_sums = [[CompensatedSum::ZERO; VALUES]; WINDOWS];
326-
self.refresh_counts = [0; WINDOWS];
327-
self.head = 0;
328-
self.count = 0;
329-
}
330353
}
331354

332355
#[derive(Debug, Clone, Copy)]
@@ -584,29 +607,11 @@ mod tests {
584607
let mut unsupported = [Unknown; MAX_AUDIO_CHANNELS];
585608
unsupported[..2].copy_from_slice(&[LowFrequency, Aux(0)]);
586609
assert_eq!(
587-
&AudioBlock::with_positions(&[], 2, 48_000.0, unsupported).stereo_matrix()[..2],
610+
AudioBlock::with_positions(&[], 8, 48_000.0, unsupported).stereo_matrix(),
588611
&[[1.0, 0.0], [0.0, 1.0]]
589612
);
590613
}
591614

592-
#[test]
593-
fn running_means_clear_without_reallocating_or_replaying_old_values() {
594-
let mut means = WindowedMeans::<1, 2>::new([2, 4]);
595-
for value in [1.0, 2.0, 3.0, 4.0] {
596-
means.push([value]);
597-
}
598-
let storage = means.buffer.as_ptr();
599-
means.clear();
600-
assert_eq!(means.mean(0), [0.0]);
601-
assert_eq!(means.mean(1), [0.0]);
602-
assert_eq!(means.buffer.as_ptr(), storage);
603-
604-
means.push([10.0]);
605-
means.push([20.0]);
606-
assert_eq!(means.mean(0), [15.0]);
607-
assert_eq!(means.mean(1), [15.0]);
608-
}
609-
610615
#[test]
611616
fn running_means_sanitize_non_finite_values_without_poisoning_state() {
612617
let mut means = WindowedMeans::<1, 1>::new([1]);
@@ -632,7 +637,7 @@ mod tests {
632637
}
633638
assert_eq!(means.mean(0), [1.0]);
634639

635-
means.clear();
640+
let mut means = WindowedMeans::<1, 1>::new([2]);
636641
for value in [1.0e100, 2.0, 1.0e-100, 1.0e-100] {
637642
means.push([value]);
638643
}

0 commit comments

Comments
 (0)