use crate::cli::PeqModel;
use crate::param_utils;
#[derive(Clone, Copy)]
pub struct SpacingConstraintData {
pub min_spacing_oct: f64,
pub peq_model: PeqModel,
}
fn compute_min_octave_spacing(xs: &[f64], peq_model: PeqModel, min_freq_hz: f64) -> f64 {
let n = param_utils::num_filters(xs, peq_model);
if n <= 1 {
return f64::INFINITY;
}
let mut min_dist = f64::INFINITY;
for i in 0..n {
let fi = param_utils::freq_from_log10_clamped(
param_utils::get_filter_params(xs, i, peq_model).freq,
min_freq_hz,
);
for j in (i + 1)..n {
let fj = param_utils::freq_from_log10_clamped(
param_utils::get_filter_params(xs, j, peq_model).freq,
min_freq_hz,
);
let d_oct = (fj / fi).log2().abs();
min_dist = min_dist.min(d_oct);
}
}
min_dist
}
pub fn constraint_spacing(
x: &[f64],
_grad: Option<&mut [f64]>,
data: &mut SpacingConstraintData,
) -> f64 {
if data.min_spacing_oct <= 0.0 {
return 0.0;
}
let min_dist = compute_min_octave_spacing(x, data.peq_model, 1e-6);
if min_dist.is_finite() {
data.min_spacing_oct - min_dist
} else {
0.0
}
}
pub fn viol_spacing_from_xs(xs: &[f64], peq_model: PeqModel, min_spacing_oct: f64) -> f64 {
if min_spacing_oct <= 0.0 {
return 0.0;
}
let min_dist = compute_min_octave_spacing(xs, peq_model, 1e-6);
if min_dist.is_finite() {
(min_spacing_oct - min_dist).max(0.0)
} else {
0.0
}
}