Skip to content

Commit 28166fb

Browse files
mutexlox-signalpadenot
authored andcommitted
Fix stereo -> mono downmixing.
Rather than summing the two channels, average them. This prevents integer overflow and clipping or needlessly increasing volume.
1 parent f70ef45 commit 28166fb

File tree

3 files changed

+31
-3
lines changed

3 files changed

+31
-3
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ cubeb-backend = "0.13"
1515
float-cmp = "0.6"
1616
libc = "0.2"
1717
mach = "0.3"
18+
num = "0.4.3"
1819
audio-mixer = "0.2"
1920
ringbuf = "0.2.6"
2021
triple_buffer = "5.0.5"

src/backend/buffer_manager.rs

+28-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::os::raw::c_void;
44
use std::slice;
55

66
use cubeb_backend::SampleFormat;
7+
use num::cast::AsPrimitive;
78

89
use super::ringbuf::RingBuffer;
910

@@ -36,12 +37,25 @@ fn drop_first_n_channels_in_place<T: Copy>(
3637
}
3738
}
3839

40+
trait DataType: AsPrimitive<f32> {
41+
fn from_f32(v: f32) -> Self;
42+
}
43+
44+
impl<T: AsPrimitive<f32>> DataType for T
45+
where
46+
f32: AsPrimitive<T>,
47+
{
48+
fn from_f32(v: f32) -> T {
49+
v.as_()
50+
}
51+
}
52+
3953
// It can be that the a stereo microphone is in use, but the user asked for mono input. In this
4054
// particular case, downmix the stereo pair into a mono channel. In all other cases, simply drop
4155
// the remaining channels before appending to the ringbuffer, becauses there is no right or wrong
4256
// way to do this, unlike with the output side, where proper channel matrixing can be done.
4357
// Return the number of valid samples in the buffer.
44-
fn remix_or_drop_channels<T: Copy + std::ops::Add<Output = T>>(
58+
fn remix_or_drop_channels<T: DataType>(
4559
input_channels: usize,
4660
output_channels: usize,
4761
data: &mut [T],
@@ -56,7 +70,8 @@ fn remix_or_drop_channels<T: Copy + std::ops::Add<Output = T>>(
5670
if input_channels == 2 && output_channels == 1 {
5771
let mut read_idx = 0;
5872
for (write_idx, _) in (0..frame_count).enumerate() {
59-
data[write_idx] = data[read_idx] + data[read_idx + 1];
73+
let avg = (data[read_idx].as_() + data[read_idx + 1].as_()) / 2.0;
74+
data[write_idx] = DataType::from_f32(avg);
6075
read_idx += 2;
6176
}
6277
return output_channels * frame_count;
@@ -76,7 +91,7 @@ fn remix_or_drop_channels<T: Copy + std::ops::Add<Output = T>>(
7691
output_channels * frame_count
7792
}
7893

79-
fn process_data<T: Copy + std::ops::Add<Output = T>>(
94+
fn process_data<T: DataType>(
8095
data: *mut c_void,
8196
frame_count: usize,
8297
input_channel_count: usize,
@@ -353,3 +368,13 @@ impl fmt::Debug for BufferManager {
353368
Ok(())
354369
}
355370
}
371+
372+
#[cfg(test)]
373+
mod tests {
374+
use super::*;
375+
#[test]
376+
fn remix_stereo_ints() {
377+
let mut data = [i16::MAX / 2 + 1, i16::MAX / 2 + 1];
378+
assert_eq!(remix_or_drop_channels(2, 1, &mut data, 1), 1);
379+
}
380+
}

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ extern crate cubeb_backend;
1212
extern crate float_cmp;
1313
extern crate mach;
1414

15+
extern crate num;
16+
1517
mod backend;
1618
mod capi;
1719

0 commit comments

Comments
 (0)