use crate::C;
use crate::input::Allpass;
use crate::measurement::ramp_phase;
use secs_dsp::response::allpass2;
#[derive(Debug, Clone, PartialEq)]
pub struct BaRow {
pub gain_db: f64,
pub delay_ms: f64,
pub polarity: i8,
pub allpass: Vec<Allpass>,
}
pub fn term(h: &[C], f: &[f64], row: &BaRow) -> Vec<C> {
PreparedBaTerm::new(f, row).apply(h)
}
pub(crate) struct PreparedBaTerm {
gain: C,
polarity: C,
delay: Vec<C>,
allpass: Vec<Vec<C>>,
}
impl PreparedBaTerm {
pub(crate) fn new(f: &[f64], row: &BaRow) -> Self {
Self {
gain: C::new(10f64.powf(row.gain_db / 20.0), 0.0),
polarity: C::new(f64::from(row.polarity), 0.0),
delay: f
.iter()
.map(|&f| C::from_polar(1.0, ramp_phase(f, row.delay_ms) * 0.001))
.collect(),
allpass: row
.allpass
.iter()
.map(|&(f0, q)| allpass2(f, f0, q))
.collect(),
}
}
fn apply(&self, h: &[C]) -> Vec<C> {
let mut out: Vec<_> = h
.iter()
.zip(&self.delay)
.map(|(&h, &delay)| h * self.gain * self.polarity * delay)
.collect();
for factor in &self.allpass {
for (v, &a) in out.iter_mut().zip(factor) {
*v *= a;
}
}
out
}
}
pub(crate) fn sub_sum_prepared(hs: &[Vec<C>], terms: &[PreparedBaTerm], bins: usize) -> Vec<C> {
let mut sum = vec![C::new(0.0, 0.0); bins];
for (h, term) in hs.iter().zip(terms) {
for (s, t) in sum.iter_mut().zip(term.apply(h)) {
*s += t;
}
}
sum
}
pub fn sub_sum(terms: &[(&[C], &BaRow)], f: &[f64]) -> Vec<C> {
let mut sum = vec![C::new(0.0, 0.0); f.len()];
for (h, row) in terms {
for (s, t) in sum.iter_mut().zip(term(h, f, row)) {
*s += t;
}
}
sum
}
#[derive(Debug)]
pub struct StageResponses {
pub top_xo: Vec<C>,
pub sub_xo: Option<Vec<C>>,
pub combined: Vec<C>,
pub output: Vec<C>,
}
pub fn stage_responses(
design: &crate::design::Design,
role: &str,
spectra: &crate::measurement::Spectra,
position: u32,
) -> Option<StageResponses> {
use crate::{
grid::{BINS, design_freqs},
measurement::delay_response,
};
use secs_dsp::response::lr4;
let main = design.mains.iter().find(|m| m.role == role)?;
let h = spectra.get(position, role)?;
if h.len() != BINS {
return None;
}
let gain = C::new(10f64.powf(main.gain_db / 20.0), 0.0);
let mut top: Vec<_> = h
.iter()
.zip(delay_response(main.align_delay_ms))
.map(|(h, d)| h * d * gain)
.collect();
if let Some(p) = &main.top_response {
for (h, p) in top.iter_mut().zip(p.iter()) {
*h *= p;
}
}
let sub_xo = if let (Some(xo), Some(sub)) = (main.xo_hz.filter(|x| *x != 0.0), &design.subs) {
let terms: Option<Vec<_>> = sub
.rows
.iter()
.map(|(r, row)| {
let h = spectra.get(position, r)?;
(h.len() == BINS).then_some((h.as_ref(), row))
})
.collect();
let mut sum = sub_sum(&terms?, design_freqs());
if let Some(p) = &sub.bus_response {
for (h, p) in sum.iter_mut().zip(p.iter()) {
*h *= p;
}
}
for (h, p) in top.iter_mut().zip(lr4(design_freqs(), xo, "HP")) {
*h *= p;
}
for (h, p) in sum.iter_mut().zip(lr4(design_freqs(), xo, "LP")) {
*h *= p;
}
Some(sum)
} else {
None
};
let combined: Vec<_> = match &sub_xo {
Some(sub) => top.iter().zip(sub).map(|(m, s)| m + s).collect(),
None => top.clone(),
};
let output = combined
.iter()
.zip(main.response.iter())
.map(|(h, f)| h * f)
.collect();
Some(StageResponses {
top_xo: top,
sub_xo,
combined,
output,
})
}
pub fn input_response(
design: &crate::design::Design,
role: &str,
spectra: &crate::measurement::Spectra,
position: u32,
) -> Option<Vec<C>> {
stage_responses(design, role, spectra, position).map(|s| s.output)
}
pub fn gather<T: Copy>(h: &[T], idx: &[usize]) -> Vec<T> {
idx.iter().map(|&i| h[i]).collect()
}