use super::frame::{ChannelMode, JointStereoMode};
pub struct StereoProcessor {
intensity_scale: [f32; 576],
ms_state: bool,
}
impl Default for StereoProcessor {
fn default() -> Self {
Self::new()
}
}
impl StereoProcessor {
#[must_use]
pub fn new() -> Self {
Self {
intensity_scale: [1.0; 576],
ms_state: false,
}
}
pub fn process(&mut self, left: &mut [f32], right: &mut [f32], mode: ChannelMode) {
match mode {
ChannelMode::Stereo => {
}
ChannelMode::JointStereo(joint_mode) => {
self.process_joint_stereo(left, right, joint_mode);
}
ChannelMode::DualChannel => {
}
ChannelMode::Mono => {
right.copy_from_slice(left);
}
}
}
fn process_joint_stereo(&mut self, left: &mut [f32], right: &mut [f32], mode: JointStereoMode) {
let len = left.len().min(right.len());
if mode.ms_stereo {
self.decode_ms_stereo(left, right, len);
}
if mode.intensity {
self.decode_intensity_stereo(left, right, len, mode.bound);
}
}
fn decode_ms_stereo(&mut self, left: &mut [f32], right: &mut [f32], len: usize) {
const SQRT2_2: f32 = 0.707_106_78;
for i in 0..len {
let mid = left[i];
let side = right[i];
left[i] = (mid + side) * SQRT2_2;
right[i] = (mid - side) * SQRT2_2;
}
self.ms_state = true;
}
fn decode_intensity_stereo(
&mut self,
left: &mut [f32],
right: &mut [f32],
len: usize,
bound: u8,
) {
let bound = bound as usize;
if bound < len {
for i in bound..len {
let scale = self.intensity_scale[i];
right[i] = left[i] * scale;
}
}
}
pub fn set_intensity_scale(&mut self, scales: &[f32]) {
let len = scales.len().min(self.intensity_scale.len());
self.intensity_scale[..len].copy_from_slice(&scales[..len]);
}
pub fn reset(&mut self) {
self.intensity_scale = [1.0; 576];
self.ms_state = false;
}
#[must_use]
pub const fn is_ms_active(&self) -> bool {
self.ms_state
}
}
#[must_use]
pub fn intensity_scale_from_position(pos: u8, is_right: bool) -> f32 {
const INTENSITY_TABLE: [f32; 14] = [
0.0,
0.211_324_87,
0.366_025_4,
0.5,
0.633_974_6,
0.788_675_13,
1.0,
1.0,
0.788_675_13,
0.633_974_6,
0.5,
0.366_025_4,
0.211_324_87,
0.0,
];
if pos >= 7 {
let idx = (pos as usize).min(13);
if is_right {
INTENSITY_TABLE[idx]
} else {
INTENSITY_TABLE[13 - idx]
}
} else {
let idx = pos as usize;
if is_right {
INTENSITY_TABLE[13 - idx]
} else {
INTENSITY_TABLE[idx]
}
}
}
pub fn apply_stereo_width(left: &mut [f32], right: &mut [f32], width: f32) {
debug_assert!(left.len() == right.len());
let width = width.max(0.0);
for (l, r) in left.iter_mut().zip(right.iter_mut()) {
let mid = (*l + *r) * 0.5;
let side = (*l - *r) * 0.5;
*l = mid + side * width;
*r = mid - side * width;
}
}
pub fn downmix_to_mono(left: &[f32], right: &[f32], output: &mut [f32]) {
debug_assert!(left.len() == right.len());
debug_assert!(output.len() >= left.len());
for (i, (&l, &r)) in left.iter().zip(right.iter()).enumerate() {
output[i] = (l + r) * 0.5;
}
}
pub fn upmix_to_stereo(input: &[f32], left: &mut [f32], right: &mut [f32]) {
debug_assert!(left.len() == right.len());
debug_assert!(input.len() <= left.len());
let len = input.len();
left[..len].copy_from_slice(&input[..len]);
right[..len].copy_from_slice(&input[..len]);
}
pub fn apply_balance(left: &mut [f32], right: &mut [f32], balance: f32) {
debug_assert!(left.len() == right.len());
let balance = balance.clamp(-1.0, 1.0);
let left_scale = if balance > 0.0 { 1.0 - balance } else { 1.0 };
let right_scale = if balance < 0.0 { 1.0 + balance } else { 1.0 };
for l in left.iter_mut() {
*l *= left_scale;
}
for r in right.iter_mut() {
*r *= right_scale;
}
}
pub fn swap_channels(left: &mut [f32], right: &mut [f32]) {
debug_assert!(left.len() == right.len());
for (l, r) in left.iter_mut().zip(right.iter_mut()) {
std::mem::swap(l, r);
}
}
#[must_use]
pub fn is_mostly_mono(left: &[f32], right: &[f32], threshold: f32) -> bool {
debug_assert!(left.len() == right.len());
let mut diff_sum = 0.0f32;
let mut total_sum = 0.0f32;
for (&l, &r) in left.iter().zip(right.iter()) {
diff_sum += (l - r).abs();
total_sum += (l.abs() + r.abs()) * 0.5;
}
if total_sum == 0.0 {
return true;
}
(diff_sum / total_sum) < threshold
}
#[must_use]
pub fn calculate_separation(left: &[f32], right: &[f32]) -> f32 {
debug_assert!(left.len() == right.len());
let mut correlation = 0.0f32;
let mut left_energy = 0.0f32;
let mut right_energy = 0.0f32;
for (&l, &r) in left.iter().zip(right.iter()) {
correlation += l * r;
left_energy += l * l;
right_energy += r * r;
}
let denominator = (left_energy * right_energy).sqrt();
if denominator == 0.0 {
return 0.0;
}
(correlation / denominator).clamp(-1.0, 1.0)
}