use crate::Curve;
use log::warn;
use math_audio_dsp::analysis::compute_average_response;
use std::collections::HashMap;
#[allow(dead_code)]
pub(super) fn split_curve_at_frequency(curve: &Curve, crossover_freq: f64) -> (Curve, Curve) {
let split_idx = curve
.freq
.iter()
.position(|&f| f >= crossover_freq)
.unwrap_or(curve.freq.len());
let overlap_points = 3; let low_end = (split_idx + overlap_points).min(curve.freq.len());
let high_start = split_idx.saturating_sub(overlap_points);
let low_curve = Curve {
freq: curve.freq.slice(ndarray::s![..low_end]).to_owned(),
spl: curve.spl.slice(ndarray::s![..low_end]).to_owned(),
phase: curve
.phase
.as_ref()
.map(|p| p.slice(ndarray::s![..low_end]).to_owned()),
};
let high_curve = Curve {
freq: curve.freq.slice(ndarray::s![high_start..]).to_owned(),
spl: curve.spl.slice(ndarray::s![high_start..]).to_owned(),
phase: curve
.phase
.as_ref()
.map(|p| p.slice(ndarray::s![high_start..]).to_owned()),
};
(low_curve, high_curve)
}
#[allow(dead_code)]
pub(super) fn compute_lr24_crossover_responses(
frequencies: &ndarray::Array1<f64>,
crossover_freq: f64,
sample_rate: f64,
) -> (
Vec<num_complex::Complex<f64>>,
Vec<num_complex::Complex<f64>>,
) {
use math_audio_iir_fir::{Biquad, BiquadFilterType};
let q = std::f64::consts::FRAC_1_SQRT_2;
let lp1 = Biquad::new(
BiquadFilterType::Lowpass,
crossover_freq,
sample_rate,
q,
0.0,
);
let lp2 = Biquad::new(
BiquadFilterType::Lowpass,
crossover_freq,
sample_rate,
q,
0.0,
);
let hp1 = Biquad::new(
BiquadFilterType::Highpass,
crossover_freq,
sample_rate,
q,
0.0,
);
let hp2 = Biquad::new(
BiquadFilterType::Highpass,
crossover_freq,
sample_rate,
q,
0.0,
);
let mut lp_resp = Vec::with_capacity(frequencies.len());
let mut hp_resp = Vec::with_capacity(frequencies.len());
for &freq in frequencies.iter() {
let lp1_resp = lp1.complex_response(freq);
let lp2_resp = lp2.complex_response(freq);
let lp_total = lp1_resp * lp2_resp;
let hp1_resp = hp1.complex_response(freq);
let hp2_resp = hp2.complex_response(freq);
let hp_total = hp1_resp * hp2_resp;
lp_resp.push(lp_total);
hp_resp.push(hp_total);
}
(lp_resp, hp_resp)
}
pub(super) fn check_group_consistency(
group_name: &str,
channels: &[String],
channel_means: &HashMap<String, f64>,
curves: &HashMap<String, Curve>,
) {
if channels.len() < 2 {
return;
}
let mut means = Vec::new();
for ch in channels {
if let Some(&mean) = channel_means.get(ch) {
means.push((ch, mean));
}
}
for i in 0..means.len() {
for j in i + 1..means.len() {
let (ch1, m1) = means[i];
let (ch2, m2) = means[j];
let diff = (m1 - m2).abs();
if diff > 3.0 {
warn!(
"Speaker group '{}' has significant difference: range SPL between '{}' and '{}' is {:.1} dB (> 3.0 dB threshold).",
group_name, ch1, ch2, diff
);
}
}
}
for i in 0..channels.len() {
for j in i + 1..channels.len() {
let ch1 = &channels[i];
let ch2 = &channels[j];
if let (Some(curve1), Some(curve2)) = (curves.get(ch1), curves.get(ch2)) {
check_octave_consistency(group_name, ch1, ch2, curve1, curve2);
}
}
}
}
pub(super) fn check_octave_consistency(
group_name: &str,
ch1: &str,
ch2: &str,
curve1: &Curve,
curve2: &Curve,
) {
let octave_centers = [
31.25, 62.5, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0,
];
for ¢er in &octave_centers {
let f_min = center / 2.0_f64.sqrt();
let f_max = center * 2.0_f64.sqrt();
let start_freq = f_min.max(curve1.freq[0]).max(curve2.freq[0]);
let end_freq = f_max
.min(curve1.freq[curve1.freq.len() - 1])
.min(curve2.freq[curve2.freq.len() - 1]);
if end_freq <= start_freq * 1.1 {
continue; }
let freqs1_f32: Vec<f32> = curve1.freq.iter().map(|&f| f as f32).collect();
let spl1_f32: Vec<f32> = curve1.spl.iter().map(|&s| s as f32).collect();
let freqs2_f32: Vec<f32> = curve2.freq.iter().map(|&f| f as f32).collect();
let spl2_f32: Vec<f32> = curve2.spl.iter().map(|&s| s as f32).collect();
let range = Some((start_freq as f32, end_freq as f32));
let avg1 = compute_average_response(&freqs1_f32, &spl1_f32, range);
let avg2 = compute_average_response(&freqs2_f32, &spl2_f32, range);
let diff = (avg1 - avg2).abs() as f64;
if diff > 6.0 {
warn!(
"Speaker group '{}' has significant difference: octave around {:.0} Hz between '{}' and '{}' differs by {:.1} dB (> 6.0 dB threshold).",
group_name, center, ch1, ch2, diff
);
}
}
}