From: Kostya Shishkov Date: Wed, 28 Jan 2026 17:57:14 +0000 (+0100) Subject: nihav_core/soundcvt: improve calculate_remix_matrix() X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=2ee7e892ef93a07bcbb7b424914cc150e32b4e54;p=nihav.git nihav_core/soundcvt: improve calculate_remix_matrix() --- diff --git a/nihav-core/src/soundcvt/mod.rs b/nihav-core/src/soundcvt/mod.rs index 242e96a..4a6afd9 100644 --- a/nihav-core/src/soundcvt/mod.rs +++ b/nihav-core/src/soundcvt/mod.rs @@ -576,10 +576,17 @@ fn is_stereo(chmap: &NAChannelMap) -> bool { /// Calculates matrix of remixing coefficients for converting input channel layout into destination one. pub fn calculate_remix_matrix(src: &NAChannelMap, dst: &NAChannelMap) -> Vec { + // common case: stereo -> mono downmix if is_stereo(src) && dst.num_channels() == 1 && (dst.get_channel(0) == NAChannelType::L || dst.get_channel(0) == NAChannelType::C) { return vec![0.5, 0.5]; } + // common case: mono -> stereo upmix + if src.num_channels() == 1 && is_stereo(dst) && + (src.get_channel(0) == NAChannelType::L || src.get_channel(0) == NAChannelType::C) { + return vec![1.0, 1.0]; + } + // still rather common case: 5.x/6.x -> stereo downmix if src.num_channels() >= 5 && is_stereo(dst) { let src_nch = src.num_channels(); let mut mat = vec![0.0f32; src_nch * 2]; @@ -596,7 +603,67 @@ pub fn calculate_remix_matrix(src: &NAChannelMap, dst: &NAChannelMap) -> Vec C + // use L+R -> C in this case instead as some files have nothing in centre channel + if src.num_channels() > 2 && dst.num_channels() == 1 && dst.get_channel(0) == NAChannelType::C { + if let (Some(lidx), Some(ridx)) = (src.find_channel_id(NAChannelType::L), src.find_channel_id(NAChannelType::R)) { + let mut remix = vec![0.0; src.num_channels()]; + remix[lidx] = SQRT_2 / 2.0; + remix[ridx] = SQRT_2 / 2.0; + return remix; + } + } + // generic possible case: target includes all channels from source + let dst_ch = dst.num_channels(); + let mut remix = vec![0.0; src.num_channels() * dst_ch]; + let mut mapped = vec![false; dst_ch]; + + for (&sch, mix_coeffs) in src.iter().zip(remix.chunks_exact_mut(dst_ch)) { + for ((coef, &dch), mapped_ch) in mix_coeffs.iter_mut().zip(dst.iter()) + .zip(mapped.iter_mut()) { + if dch == sch && !*mapped_ch { + *coef = 1.0; + *mapped_ch = true; + } + } + } + if !mapped.contains(&false) { + return remix; + } + // try to fill gaps by mono <-> stereo conversions + for (didx, (&dch, mapped_ch)) in dst.iter().zip(mapped.iter_mut()).enumerate() { + if *mapped_ch { + continue; + } + if let Some((lch, rch)) = dch.get_pair() { + if let (Some(lidx), Some(ridx)) = (src.find_channel_id(lch), src.find_channel_id(rch)) { + remix[lidx * dst_ch + didx] = SQRT_2 / 2.0; + remix[ridx * dst_ch + didx] = SQRT_2 / 2.0; + *mapped_ch = true; + } + } + } + for (&sch, mix_coef) in src.iter().zip(remix.chunks_exact_mut(dst_ch)) { + if let Some((lch, rch)) = sch.get_pair() { + if let (Some(lidx), Some(ridx)) = (dst.find_channel_id(lch), src.find_channel_id(rch)) { + if !mapped[lidx] && !mapped[ridx] { + mix_coef[lidx] = 1.0; + mix_coef[ridx] = 1.0; + mapped[lidx] = true; + mapped[ridx] = true; + } + } + } + } + if !mapped.contains(&true) { + print!("failed to find any map from"); + for ch in src.iter() { print!(" {ch}"); } + print!(" to"); + for ch in dst.iter() { print!(" {ch}"); } + println!(); unimplemented!(); + } + remix } #[cfg(test)]