Skip to main content

secs_engine/
setup.rs

1//! Constructor normalisation, in upstream dictionary order.
2//! Upstream: `CorrectionEngine.__init__` (`correction_engine.py:418–489`).
3
4use crate::grid::SR_F;
5use crate::input::{BandType, EngineConfig, LayoutKind, Peq, SubManual, SubMode};
6
7#[derive(Debug, Clone, PartialEq)]
8pub struct SubFilter {
9    pub hp_hz: f64,
10    pub hp_type: BandType,
11    pub lp_hz: f64,
12    pub lp_type: BandType,
13    pub peq: Vec<Peq>,
14}
15
16#[derive(Debug, Clone, PartialEq)]
17pub struct Layout {
18    pub kind: LayoutKind,
19    pub rear_trim_ms: f64,
20    pub members: Vec<String>,
21}
22
23#[derive(Debug, Clone)]
24pub struct Setup {
25    pub sub_manual: Vec<(String, SubManual)>,
26    pub sub_filters: Vec<(String, SubFilter)>,
27    pub layouts: Vec<(String, Layout)>,
28    pub lift_ms: f64,
29    pub lift_capped_from_ms: Option<f64>,
30    pub max_latency_ms: f64,
31    pub rc_lo: f64,
32    pub rc_hi_manual: Option<f64>,
33    pub rc_hi: f64,
34    pub rc_bands: Vec<(f64, f64)>,
35    /// ADR 0025 switches; the agreement probe clears both.
36    pub lf_align: bool,
37    pub mode_decay: bool,
38    /// ADR 0030; the agreement probe also clears this switch.
39    pub hf_phase: bool,
40    /// ADR 0034; unlike the phase switches, the agreement probe keeps this on.
41    pub dip_limit: bool,
42    pub peq_bands: usize,
43    pub input_hp_hz: Option<f64>,
44}
45
46impl Setup {
47    pub fn new(config: &EngineConfig) -> Self {
48        let sub_manual: Vec<_> = config
49            .sub_manual
50            .iter()
51            .filter(|(r, _)| config.subs.contains(r))
52            .cloned()
53            .collect();
54        let mut sub_filters: Vec<_> = sub_manual
55            .iter()
56            .filter_map(|(r, m)| {
57                let peq: Vec<_> = m.peq.iter().copied().filter(|b| b.2 != 0.0).collect();
58                (m.hp_hz > 0.0 || m.lp_hz > 0.0 || !peq.is_empty()).then(|| {
59                    (
60                        r.clone(),
61                        SubFilter {
62                            hp_hz: m.hp_hz,
63                            hp_type: m.hp_type,
64                            lp_hz: m.lp_hz,
65                            lp_type: m.lp_type,
66                            peq,
67                        },
68                    )
69                })
70            })
71            .collect();
72        let mut layouts = Vec::new();
73        for (name, layout) in &config.bundle_layouts {
74            let members: Vec<_> = config
75                .subs
76                .iter()
77                .filter(|r| {
78                    sub_manual.iter().any(|(role, m)| {
79                        role == *r
80                            && m.mode == SubMode::Bundle
81                            && (if m.bundle.is_empty() { "A" } else { &m.bundle }) == name
82                    })
83                })
84                .cloned()
85                .collect();
86            let Some(first) = members.first() else {
87                continue;
88            };
89            let lead = sub_filters
90                .iter()
91                .find(|(r, _)| r == first)
92                .map(|(_, f)| f.clone());
93            for r in &members[1..] {
94                if let Some(filter) = &lead {
95                    if let Some((_, value)) = sub_filters.iter_mut().find(|(role, _)| role == r) {
96                        *value = filter.clone();
97                    } else {
98                        sub_filters.push((r.clone(), filter.clone()));
99                    }
100                } else {
101                    sub_filters.retain(|(role, _)| role != r);
102                }
103            }
104            layouts.push((
105                name.clone(),
106                Layout {
107                    kind: layout.kind,
108                    rear_trim_ms: layout.rear_trim_ms,
109                    members,
110                },
111            ));
112        }
113        let cap_ms = 0.7 * config.taps as f64 / SR_F * 1000.0;
114        let capped = config.lift_ms > cap_ms;
115        let lift_ms = if capped { cap_ms } else { config.lift_ms };
116        let max_latency_ms = config.max_latency_ms.map_or(lift_ms + 15.0, |v| {
117            if capped {
118                v - (config.lift_ms - cap_ms)
119            } else {
120                v
121            }
122        });
123        Self {
124            sub_manual,
125            sub_filters,
126            layouts,
127            lift_ms,
128            lift_capped_from_ms: capped.then_some(config.lift_ms),
129            max_latency_ms,
130            rc_lo: config.rc_range.0,
131            rc_hi_manual: config.rc_range.1,
132            rc_hi: config.rc_range.1.filter(|v| *v != 0.0).unwrap_or(500.0),
133            rc_bands: config.rc_bands.clone(),
134            lf_align: config.lf_align,
135            mode_decay: config.mode_decay,
136            hf_phase: config.hf_phase,
137            dip_limit: config.dip_limit,
138            peq_bands: config.peq_bands.unwrap_or(16),
139            input_hp_hz: config.input_hp_hz,
140        }
141    }
142}