Fix stereo -> mono downmixing.

Rather than summing the two channels, average them. This prevents
integer overflow and clipping or needlessly increasing volume.
This commit is contained in:
Miriam Zimmerman 2024-10-22 11:15:33 -04:00 коммит произвёл Paul Adenot
Родитель f70ef45e66
Коммит 28166fb5f2
3 изменённых файлов: 31 добавлений и 3 удалений

Просмотреть файл

@ -15,6 +15,7 @@ cubeb-backend = "0.13"
float-cmp = "0.6"
libc = "0.2"
mach = "0.3"
num = "0.4.3"
audio-mixer = "0.2"
ringbuf = "0.2.6"
triple_buffer = "5.0.5"

Просмотреть файл

@ -4,6 +4,7 @@ use std::os::raw::c_void;
use std::slice;
use cubeb_backend::SampleFormat;
use num::cast::AsPrimitive;
use super::ringbuf::RingBuffer;
@ -36,12 +37,25 @@ fn drop_first_n_channels_in_place<T: Copy>(
}
}
trait DataType: AsPrimitive<f32> {
fn from_f32(v: f32) -> Self;
}
impl<T: AsPrimitive<f32>> DataType for T
where
f32: AsPrimitive<T>,
{
fn from_f32(v: f32) -> T {
v.as_()
}
}
// It can be that the a stereo microphone is in use, but the user asked for mono input. In this
// particular case, downmix the stereo pair into a mono channel. In all other cases, simply drop
// the remaining channels before appending to the ringbuffer, becauses there is no right or wrong
// way to do this, unlike with the output side, where proper channel matrixing can be done.
// Return the number of valid samples in the buffer.
fn remix_or_drop_channels<T: Copy + std::ops::Add<Output = T>>(
fn remix_or_drop_channels<T: DataType>(
input_channels: usize,
output_channels: usize,
data: &mut [T],
@ -56,7 +70,8 @@ fn remix_or_drop_channels<T: Copy + std::ops::Add<Output = T>>(
if input_channels == 2 && output_channels == 1 {
let mut read_idx = 0;
for (write_idx, _) in (0..frame_count).enumerate() {
data[write_idx] = data[read_idx] + data[read_idx + 1];
let avg = (data[read_idx].as_() + data[read_idx + 1].as_()) / 2.0;
data[write_idx] = DataType::from_f32(avg);
read_idx += 2;
}
return output_channels * frame_count;
@ -76,7 +91,7 @@ fn remix_or_drop_channels<T: Copy + std::ops::Add<Output = T>>(
output_channels * frame_count
}
fn process_data<T: Copy + std::ops::Add<Output = T>>(
fn process_data<T: DataType>(
data: *mut c_void,
frame_count: usize,
input_channel_count: usize,
@ -353,3 +368,13 @@ impl fmt::Debug for BufferManager {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn remix_stereo_ints() {
let mut data = [i16::MAX / 2 + 1, i16::MAX / 2 + 1];
assert_eq!(remix_or_drop_channels(2, 1, &mut data, 1), 1);
}
}

Просмотреть файл

@ -12,6 +12,8 @@ extern crate cubeb_backend;
extern crate float_cmp;
extern crate mach;
extern crate num;
mod backend;
mod capi;