1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! Typed engine diagnostics; presentation owns their translated text.
use crate::input::Peq;
#[derive(Debug, Clone, PartialEq)]
pub enum Note {
LiftCapped {
from_ms: f64,
taps: usize,
lift_ms: f64,
},
SubFilters(Vec<(String, crate::setup::SubFilter)>),
LowProtect {
role: String,
rolloff_hz: f64,
hp_hz: Option<f64>,
limited: bool,
reduced: usize,
cuts: usize,
},
RcLimit {
hz: f64,
auto: bool,
schroeder_hz: Option<f64>,
basis: secs_types::calibration::RcHiBasis,
factor: u8,
band_hz: Option<f64>,
band_t60_s: Option<f64>,
positions: Option<usize>,
},
RcBands {
bands: Vec<(f64, f64)>,
},
/// ADR 0025, one per main when `lf_align` is on and the mode has FIR
/// taps. `arrival_ms` is the median LF arrival left after correction
/// (positive = later than HF); `limited` means the pull clamp cut the
/// requested correction by more than 1 ms somewhere in the band.
LfAlign {
role: String,
strength: f64,
arrival_ms: Option<f64>,
limited: bool,
},
/// ADR 0025, one per main when `mode_decay` is on. Each mode is
/// `(hz, measured T60 s, cut depth dB)` with the depth positive; an empty
/// list means no mode qualified.
ModeDecay {
role: String,
target_t60_s: f64,
modes: Vec<(f64, f64, f64)>,
},
/// ADR 0030, one per channel whose FIR realizes excess phase when
/// `hf_phase` is on. `rotation_deg` is the wrapped rotation unwound above
/// the band, `None` when less than an octave is left below 0.45·SR;
/// `limited` means the `[−5 ms, lift]` clamp kept part of it.
HfPhase {
role: String,
rotation_deg: Option<f64>,
limited: bool,
},
Latency {
latency_ms: f64,
lift_ms: f64,
main_extra_ms: f64,
},
PhaseStrength(Vec<(String, f64)>),
PhaseBand(Vec<(String, Option<f64>)>),
// RC (P09)
LongModeBoost {
cap_db: f64,
modes: Vec<(f64, f64)>,
},
GdCuts {
role: String,
cuts: Vec<Peq>,
},
// BA (P10)
BaCost(f64),
BaBusPass {
first: f64,
second: f64,
kept_first: bool,
},
BaManual,
LayoutFallback(String),
Layout(String),
TwoWay {
roles: Vec<String>,
gain_db: f64,
xo_hz: f64,
},
BusGainAnchored(f64),
Mimo(MimoNote),
}
/// Semantic diagnostics in producer order; never translated presentation text.
#[derive(Debug, Clone, PartialEq)]
pub enum MimoNote {
InsufficientPositions {
measured: usize,
},
ExcludedSupports(Vec<String>),
SupportRanges(Vec<(String, Option<(f64, f64)>)>),
GuardReduced {
role: String,
gain_db: f64,
},
GuardDisabled {
role: String,
},
SupportLimit {
max_supports: usize,
},
FirBudget {
support_count: usize,
taps_total: usize,
},
}
#[cfg(test)]
impl MimoNote {
// Only the oracle adapter renders the legacy diagnostic strings.
pub(crate) fn oracle_text(&self) -> String {
match self {
Self::InsufficientPositions { measured } => {
format!("측정 위치 {measured}곳 — MIMO 는 3곳 이상일 때만")
}
Self::ExcludedSupports(roles) => format!("서포트 제외(사용자): {}", roles.join(", ")),
Self::SupportRanges(ranges) => format!(
"서포트 대역 {}",
ranges
.iter()
.map(|(role, range)| match range {
Some((lo, hi)) => format!("{role} {lo:.0}–{hi:.0}"),
None => format!("{role} 제외"),
})
.collect::<Vec<_>>()
.join(", ")
),
Self::GuardReduced { role, gain_db } => {
format!("{role}: 대역 늦은 에너지 악화로 서포트 {gain_db:.0} dB")
}
Self::GuardDisabled { role } => format!("{role}: MIMO 끔(악화)"),
Self::SupportLimit { max_supports } => format!("입력당 서포트 ≤ {max_supports}"),
Self::FirBudget {
support_count,
taps_total,
} => {
let digits = taps_total.to_string();
let grouped = digits
.chars()
.enumerate()
.fold(String::new(), |mut out, (i, ch)| {
if i > 0 && (digits.len() - i).is_multiple_of(3) {
out.push(',');
}
out.push(ch);
out
});
format!("FIR 서포트 {support_count}개 · 총 {grouped}탭(48 kHz)")
}
}
}
}