use crate::error::AnalysisError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChannelMixMode {
Mono,
MidSide,
Dominant,
Center,
}
pub fn stereo_to_mono(
left: &[f32],
right: &[f32],
mode: ChannelMixMode,
) -> Result<Vec<f32>, AnalysisError> {
if left.len() != right.len() {
return Err(AnalysisError::InvalidInput(
format!("Left and right channels must have same length: left={}, right={}",
left.len(), right.len())
));
}
if left.is_empty() {
return Ok(Vec::new());
}
log::debug!("Converting stereo to mono: {} samples using {:?}", left.len(), mode);
let mono = match mode {
ChannelMixMode::Mono => {
left.iter()
.zip(right.iter())
.map(|(&l, &r)| (l + r) * 0.5)
.collect()
}
ChannelMixMode::MidSide => {
left.iter()
.zip(right.iter())
.map(|(&l, &r)| (l + r) * 0.5)
.collect()
}
ChannelMixMode::Dominant => {
left.iter()
.zip(right.iter())
.map(|(&l, &r)| {
let abs_l = l.abs();
let abs_r = r.abs();
if abs_l >= abs_r {
l
} else {
r
}
})
.collect()
}
ChannelMixMode::Center => {
left.iter()
.zip(right.iter())
.map(|(&l, &r)| (l + r) * 0.5)
.collect()
}
};
Ok(mono)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stereo_to_mono_simple_average() {
let left = vec![1.0f32, 0.5f32, 0.0f32];
let right = vec![0.0f32, 0.5f32, 1.0f32];
let mono = stereo_to_mono(&left, &right, ChannelMixMode::Mono).unwrap();
assert_eq!(mono.len(), 3);
assert!((mono[0] - 0.5).abs() < 1e-6); assert!((mono[1] - 0.5).abs() < 1e-6); assert!((mono[2] - 0.5).abs() < 1e-6); }
#[test]
fn test_stereo_to_mono_midside() {
let left = vec![1.0f32, 0.5f32];
let right = vec![0.0f32, 0.5f32];
let mono = stereo_to_mono(&left, &right, ChannelMixMode::MidSide).unwrap();
let mono_simple = stereo_to_mono(&left, &right, ChannelMixMode::Mono).unwrap();
assert_eq!(mono, mono_simple);
}
#[test]
fn test_stereo_to_mono_center() {
let left = vec![1.0f32, 0.5f32];
let right = vec![0.0f32, 0.5f32];
let mono = stereo_to_mono(&left, &right, ChannelMixMode::Center).unwrap();
let mono_simple = stereo_to_mono(&left, &right, ChannelMixMode::Mono).unwrap();
assert_eq!(mono, mono_simple);
}
#[test]
fn test_stereo_to_mono_dominant() {
let left = vec![1.0f32, 0.3f32, -0.8f32];
let right = vec![0.2f32, 0.9f32, -0.1f32];
let mono = stereo_to_mono(&left, &right, ChannelMixMode::Dominant).unwrap();
assert_eq!(mono.len(), 3);
assert!((mono[0] - 1.0).abs() < 1e-6);
assert!((mono[1] - 0.9).abs() < 1e-6);
assert!((mono[2] - (-0.8)).abs() < 1e-6);
}
#[test]
fn test_stereo_to_mono_dominant_tie() {
let left = vec![0.5f32, -0.5f32];
let right = vec![0.5f32, 0.5f32];
let mono = stereo_to_mono(&left, &right, ChannelMixMode::Dominant).unwrap();
assert_eq!(mono.len(), 2);
assert!((mono[0] - 0.5).abs() < 1e-6);
assert!((mono[1] - (-0.5)).abs() < 1e-6);
}
#[test]
fn test_stereo_to_mono_different_lengths() {
let left = vec![1.0f32, 0.5f32];
let right = vec![0.0f32];
let result = stereo_to_mono(&left, &right, ChannelMixMode::Mono);
assert!(result.is_err());
}
#[test]
fn test_stereo_to_mono_empty() {
let left = vec![];
let right = vec![];
let mono = stereo_to_mono(&left, &right, ChannelMixMode::Mono).unwrap();
assert!(mono.is_empty());
}
#[test]
fn test_stereo_to_mono_large_input() {
let len = 44100 * 30; let left: Vec<f32> = (0..len).map(|i| (i as f32 / 1000.0).sin()).collect();
let right: Vec<f32> = (0..len).map(|i| (i as f32 / 1000.0).cos()).collect();
let mono = stereo_to_mono(&left, &right, ChannelMixMode::Mono).unwrap();
assert_eq!(mono.len(), len);
for i in 0..100.min(len) {
let expected = (left[i] + right[i]) * 0.5;
assert!((mono[i] - expected).abs() < 1e-6,
"Mismatch at index {}: expected {}, got {}", i, expected, mono[i]);
}
}
#[test]
fn test_stereo_to_mono_all_modes() {
let left = vec![0.8f32, 0.2f32, -0.5f32];
let right = vec![0.2f32, 0.8f32, -0.3f32];
let mono = stereo_to_mono(&left, &right, ChannelMixMode::Mono).unwrap();
let midside = stereo_to_mono(&left, &right, ChannelMixMode::MidSide).unwrap();
let center = stereo_to_mono(&left, &right, ChannelMixMode::Center).unwrap();
let dominant = stereo_to_mono(&left, &right, ChannelMixMode::Dominant).unwrap();
assert_eq!(mono, midside);
assert_eq!(mono, center);
assert_ne!(mono, dominant);
assert!((dominant[0] - 0.8).abs() < 1e-6); assert!((dominant[1] - 0.8).abs() < 1e-6); assert!((dominant[2] - (-0.5)).abs() < 1e-6); }
}