pub fn channel_balance_gain(ch_count: usize, channel_idx: usize, balance: f32) -> f32 {
if ch_count == 2 {
let b = balance.clamp(-1.0, 1.0);
if channel_idx == 0 {
(1.0 - b).clamp(0.0, 1.0)
} else {
(1.0 + b).clamp(0.0, 1.0)
}
} else {
1.0
}
}
pub fn output_meter_linear(port_count: usize, gain: f32, balance: f32) -> Vec<f32> {
(0..port_count)
.map(|channel_idx| 0.0 * gain * channel_balance_gain(port_count, channel_idx, balance))
.collect()
}
#[cfg(any(target_os = "macos", target_os = "windows"))]
pub fn output_meter_db(port_count: usize, gain: f32, balance: f32) -> Vec<f32> {
output_meter_linear(port_count, gain, balance)
.into_iter()
.map(|peak| {
if peak <= 1.0e-6 {
-90.0
} else {
(20.0 * peak.log10()).clamp(-90.0, 20.0)
}
})
.collect()
}
pub fn output_meter_linear_from_plan(
plan: &crate::render_plan::RenderPlan,
gain: f32,
balance: f32,
) -> Vec<f32> {
let ch_count = plan.hw_out_map.len();
let mut out = Vec::with_capacity(ch_count);
for (channel_idx, &(buf, _channel)) in plan.hw_out_map.iter().enumerate() {
let balance_gain = channel_balance_gain(ch_count, channel_idx, balance);
let buf = unsafe { plan.buffer(buf) };
out.push(crate::simd::peak_abs(buf) * gain * balance_gain);
}
out
}
#[cfg(test)]
mod tests {
use super::{channel_balance_gain, output_meter_linear};
#[test]
fn channel_balance_gain_is_neutral_for_non_stereo() {
assert_eq!(channel_balance_gain(1, 0, -1.0), 1.0);
assert_eq!(channel_balance_gain(4, 2, 0.75), 1.0);
}
#[test]
fn channel_balance_gain_clamps_full_left_and_right_for_stereo() {
assert_eq!(channel_balance_gain(2, 0, -1.0), 1.0);
assert_eq!(channel_balance_gain(2, 1, -1.0), 0.0);
assert_eq!(channel_balance_gain(2, 0, 1.0), 0.0);
assert_eq!(channel_balance_gain(2, 1, 1.0), 1.0);
}
#[test]
fn output_meter_linear_returns_silent_fallback_meter() {
let meter = output_meter_linear(2, 2.0, 0.5);
assert_eq!(meter, vec![0.0, 0.0]);
}
#[test]
fn output_meter_linear_handles_empty_outputs_and_zero_gain() {
assert!(output_meter_linear(0, 1.0, 0.0).is_empty());
assert_eq!(output_meter_linear(1, 0.0, 9.0), vec![0.0]);
}
#[test]
fn channel_balance_gain_clamps_out_of_range_balance() {
assert_eq!(channel_balance_gain(2, 0, 2.0), 0.0);
assert_eq!(channel_balance_gain(2, 1, 2.0), 1.0);
assert_eq!(channel_balance_gain(2, 0, -2.0), 1.0);
assert_eq!(channel_balance_gain(2, 1, -2.0), 0.0);
}
}