Recover analyzer DSP and realtime pipeline corrections

This commit is contained in:
Mikei386
2026-07-21 19:57:52 +02:00
parent e499bac928
commit 91aeccb938
27 changed files with 3410 additions and 924 deletions
+60 -25
View File
@@ -51,20 +51,20 @@ const CONFIG = {
REALTIME_BAR_DECAY_DB_PER_S: 20,
RTA_ENGINE: 'iir', // 'fft' | 'iir'
RTA_FREQ_RANGE: 'norm', // 'norm' | 'lf'
RTA_BPO_MODE: '1_6',
RTA_BPO_MODE: '1_3',
RTA_WEIGHTING: 'z', // 'z' | 'a' | 'c'
RTA_DISPLAY_GAIN_FFT_DB: 20,
RTA_DISPLAY_GAIN_IIR_DB: 20,
RTA_BAR_LAYOUT: 'rtw', // 'iec' | 'rtw'
RTA_BAR_BASE_COLOR: '#ffe066',
HEADER_TEXT_COLOR: '#ffe066',
RTA_INTEGRATION: 'fast', // 'impulse' | 'fast' | 'slow' | 'peak'
RTA_INTEGRATION: 'fast', // 'impulse' | 'fast' | 'medium' | 'slow' | 'average' | 'peak'
RTA_BALLISTICS_MODE: 'average', // 'average' | 'peak' | 'both'
RTA_PEAK_HOLD_MODE: 'auto', // 'off' | 'auto' | 'manual'
RTA_PEAK_HOLD_SEC: 2,
RTA_PEAK_HOLD_SEC: 2.5,
RTA_PEAK_DECAY_DB_PER_S: 20,
RTA_DISPLAY_HOLD_SEC: 0,
RTA_IIR_ORDER: 4,
RTA_IIR_ORDER: 6,
RTA_IIR_TAU_FAST: 0.12,
RTA_IIR_TAU_SLOW: 1.0,
SPECTRO_GAMMA: 0.4,
@@ -156,20 +156,19 @@ const CONFIG = {
PPM_DIN_TOP: +5,
PPM_DIN_BOTTOM: -50,
PPM_DIN_RED_START: 0,
// DIN PPM attack and decay times: 5 ms integration and 20 dB in 1.7 s
// (≈11.8 dB/s) return, per IEC 60268‑10 Type I.
PPM_DIN_ATTACK_MS: 5,
// Fixed RTW/DIN profile: 10 ms integration and 20 dB return in 1.5 s.
PPM_DIN_ATTACK_MS: 10,
// Non-normative: when enabled, PPM DIN attack becomes instant (no integration).
// Useful to compensate perceived meter lag caused by device / pipeline latency.
PPM_DIN_FAST_ATTACK: false,
PPM_DIN_DECAY_DB_PER_S: 11.8,
PPM_DIN_DECAY_DB_PER_S: 20 / 1.5,
// Hold time for DIN PPM: verlängert auf 1000 ms (1 s), damit der Peak‑Hold
// auch bei kurzen Transienten deutlich sichtbar bleibt. Gemäß
// Rundfunkpraxis werden Peak‑Hold‑Striche häufig 1–2 Sekunden gehalten.
PPM_DIN_HOLD_MS: 1000,
// Decay rate for the DIN PPM hold. When the held peak decays, it uses
// the same slope as the main meter (≈11.8 dB/s).
PPM_DIN_HOLD_DECAY_DB_PER_S: 11.8,
// the same slope as the main meter (≈13.33 dB/s).
PPM_DIN_HOLD_DECAY_DB_PER_S: 20 / 1.5,
// Offsets für die PPM-Meter (DIN/EBU-spezifisch). Die EBU/DIN Offsets
// ersetzen den früheren globalen PPM_OFFSET.
@@ -178,16 +177,18 @@ const CONFIG = {
PPM_REF_DBFS_PEAK_FOR_0_DBU: -15.0,
// Ballistic parameters for the EBU PPM (Type IIb). Attack ~10 ms and
// decay 24 dB in 2.8 s (≈8.6 dB/s), per IEC 60268‑10 Type II. A short
// decay 24 dB in 2.8 s (≈8.57 dB/s), per EBU Tech 3205-E Type IIb. A short
// peak-hold (~750 ms) helps visualise transients. Hold decay matches
// the main decay rate.
PPM_EBU_ATTACK_MS: 10,
PPM_EBU_DECAY_DB_PER_S: 8.6,
PPM_EBU_DECAY_DB_PER_S: 24 / 2.8,
PPM_EBU_HOLD_MS: 750,
PPM_EBU_HOLD_DECAY_DB_PER_S: 8.6,
PPM_EBU_HOLD_DECAY_DB_PER_S: 24 / 2.8,
CORR_SMOOTH: 0.85,
CORR_ZERO_ON_SILENCE: false,
CORR_SILENCE_THRESHOLD_RMS_DBFS: -75,
CORR_RESPONSE_S: 1.0,
CORR_RESET_TOKEN: 0,
XY_POINTS: 1024,
XY_STYLE: 'lines',
XY_SILENCE_GATE_ENABLED: true,
@@ -198,6 +199,7 @@ const CONFIG = {
GONIO_MANUAL_GAIN_DB: -5,
GONIO_AGC_ENABLED: false,
GONIO_LINE_FADE_MS: 50,
GONIO_PERSISTENCE_MODE: 'fast',
PHASE_DISPLAY_GAIN_DB: 0,
PHASE_AGC_ENABLED: false,
PHASE_TRAIL_ENABLED: true,
@@ -599,6 +601,8 @@ const PHOENIX_GLOBAL_OPTION_KEYS = Object.freeze([
'CLOCK_LED_COLOR',
'HEADER_TEXT_COLOR',
'RTA_BAR_BASE_COLOR',
'SPECTRO_GAMMA',
'SPECTRO_SCROLL_MODE',
'PEAK_HISTORY_SCROLL_MODE',
'PEAK_HISTORY_FILL_ENABLED',
'PEAK_HISTORY_FILL_INVERT',
@@ -774,6 +778,14 @@ function loadConfig(opts = {}) {
if (typeof saved.PPM_DIN_DECAY_DB_PER_S === 'number') CONFIG.PPM_DIN_DECAY_DB_PER_S = saved.PPM_DIN_DECAY_DB_PER_S;
if (typeof saved.PPM_DIN_HOLD_MS === 'number') CONFIG.PPM_DIN_HOLD_MS = saved.PPM_DIN_HOLD_MS;
if (CONFIG.RTA_BAR_LAYOUT === 'classic') CONFIG.RTA_BAR_LAYOUT = 'rtw';
if (CONFIG.RTA_BAR_LAYOUT === 'rtw') {
CONFIG.RTA_ENGINE = 'iir';
CONFIG.RTA_BPO_MODE = '1_3';
CONFIG.RTA_IIR_ORDER = 6;
CONFIG.RTA_FREQ_RANGE = 'norm';
const hold = Number(CONFIG.RTA_PEAK_HOLD_SEC);
CONFIG.RTA_PEAK_HOLD_SEC = hold >= 3.25 ? 4.0 : 2.5;
}
if (!Number.isFinite(CONFIG.DBFS_TOP)) CONFIG.DBFS_TOP = CONFIG_DEFAULTS.DBFS_TOP;
if (!Number.isFinite(CONFIG.DBFS_BOTTOM)) CONFIG.DBFS_BOTTOM = CONFIG_DEFAULTS.DBFS_BOTTOM;
// Clamp to sensible bounds while keeping the user-selected display range.
@@ -827,6 +839,14 @@ function loadConfig(opts = {}) {
CONFIG.LR_FRACTIONAL_DELAY_SAMPLES = Math.max(-1.5, Math.min(1.5, Number(CONFIG.LR_FRACTIONAL_DELAY_SAMPLES)));
CONFIG.PHOENIX_BASE_URL = normalizePhoenixBaseUrlForCurrentClient(CONFIG.PHOENIX_BASE_URL || CONFIG_DEFAULTS.PHOENIX_BASE_URL || DEFAULT_PHOENIX_BASE_URL);
CONFIG.PPM_DIN_FAST_ATTACK = !!CONFIG.PPM_DIN_FAST_ATTACK;
// Migrate stale profiles: detector ballistics are standards profiles,
// not user-adjustable display preferences.
CONFIG.PPM_DIN_ATTACK_MS = 10;
CONFIG.PPM_DIN_DECAY_DB_PER_S = 20 / 1.5;
CONFIG.PPM_DIN_HOLD_DECAY_DB_PER_S = 20 / 1.5;
CONFIG.PPM_EBU_ATTACK_MS = 10;
CONFIG.PPM_EBU_DECAY_DB_PER_S = 24 / 2.8;
CONFIG.PPM_EBU_HOLD_DECAY_DB_PER_S = 24 / 2.8;
CONFIG.HIFI_PEAK_ALIGNMENT = (CONFIG.HIFI_PEAK_ALIGNMENT === 'ppm_din_zero' || CONFIG.HIFI_PEAK_ALIGNMENT === 'dbfs_zero')
? 'ppm_din_zero'
: 'ppm_din_minus5';
@@ -969,6 +989,10 @@ function buildPhoenixGlobalConfigPayload() {
if (raw === 'stopwatch') return 'stopwatch';
return fallback;
};
const normalizeSpectroScroll = (value) => {
const number = Number(value);
return [0.5, 1, 2, 4, 6].includes(number) ? number : 1;
};
const normalizePeakHistoryScroll = (value) => {
const allowed = new Set([0.5, 1, 2, 4, 6]);
const num = Number(value);
@@ -990,11 +1014,11 @@ function buildPhoenixGlobalConfigPayload() {
monoInput: !!CONFIG.MONO_INPUT,
lrFractionalDelayEnabled: !!CONFIG.LR_FRACTIONAL_DELAY_ENABLED,
lrFractionalDelaySamples: Number.isFinite(CONFIG.LR_FRACTIONAL_DELAY_SAMPLES) ? CONFIG.LR_FRACTIONAL_DELAY_SAMPLES : (CONFIG_DEFAULTS.LR_FRACTIONAL_DELAY_SAMPLES ?? 0.996),
ppmDinAttackMs: Number.isFinite(CONFIG.PPM_DIN_ATTACK_MS) ? CONFIG.PPM_DIN_ATTACK_MS : (CONFIG_DEFAULTS.PPM_DIN_ATTACK_MS ?? 5),
ppmDinDecayDbPerS: Number.isFinite(CONFIG.PPM_DIN_DECAY_DB_PER_S) ? CONFIG.PPM_DIN_DECAY_DB_PER_S : (CONFIG_DEFAULTS.PPM_DIN_DECAY_DB_PER_S ?? 11.8),
ppmDinAttackMs: 10,
ppmDinDecayDbPerS: 20 / 1.5,
ppmDinFastAttack: !!CONFIG.PPM_DIN_FAST_ATTACK,
ppmEbuAttackMs: Number.isFinite(CONFIG.PPM_EBU_ATTACK_MS) ? CONFIG.PPM_EBU_ATTACK_MS : (CONFIG_DEFAULTS.PPM_EBU_ATTACK_MS ?? 10),
ppmEbuDecayDbPerS: Number.isFinite(CONFIG.PPM_EBU_DECAY_DB_PER_S) ? CONFIG.PPM_EBU_DECAY_DB_PER_S : (CONFIG_DEFAULTS.PPM_EBU_DECAY_DB_PER_S ?? 8.6),
ppmEbuAttackMs: 10,
ppmEbuDecayDbPerS: 24 / 2.8,
lufsIWindowMin: Number.isFinite(CONFIG.LUFS_I_WINDOW_MIN) ? CONFIG.LUFS_I_WINDOW_MIN : (CONFIG_DEFAULTS.LUFS_I_WINDOW_MIN ?? 4),
lufsINormEnabled: !!CONFIG.LUFS_I_NORM_ENABLED,
ppmDinLoudnessBoxes: !!CONFIG.PPM_DIN_LOUDNESS_BOXES,
@@ -1016,6 +1040,8 @@ function buildPhoenixGlobalConfigPayload() {
clockLedColor: normalizeColor(CONFIG.CLOCK_LED_COLOR, CONFIG_DEFAULTS.CLOCK_LED_COLOR || '#ff0000'),
headerTextColor: normalizeColor(CONFIG.HEADER_TEXT_COLOR, CONFIG_DEFAULTS.HEADER_TEXT_COLOR || '#ffe066'),
rtaBarBaseColor: normalizeColor(CONFIG.RTA_BAR_BASE_COLOR, CONFIG_DEFAULTS.RTA_BAR_BASE_COLOR || '#ffe066'),
spectroGamma: Number.isFinite(CONFIG.SPECTRO_GAMMA) ? Math.max(0.3, Math.min(1.2, CONFIG.SPECTRO_GAMMA)) : 0.9,
spectroScrollMode: normalizeSpectroScroll(CONFIG.SPECTRO_SCROLL_MODE),
peakHistoryScrollMode: normalizePeakHistoryScroll(CONFIG.PEAK_HISTORY_SCROLL_MODE),
peakHistoryFillEnabled: !!CONFIG.PEAK_HISTORY_FILL_ENABLED,
peakHistoryFillInvert: !!CONFIG.PEAK_HISTORY_FILL_INVERT,
@@ -1063,6 +1089,10 @@ function applyPhoenixGlobalConfig(payload = {}) {
if (raw === 'stopwatch') return 'stopwatch';
return fallback;
};
const normalizeSpectroScroll = (value) => {
const number = Number(value);
return [0.5, 1, 2, 4, 6].includes(number) ? number : 1;
};
const fft = Number(payload.fftSize);
if (Number.isFinite(fft)) {
CONFIG.FFT_SIZE = (fft === 2048 || fft === 4096 || fft === 8192 || fft === 16384)
@@ -1073,15 +1103,20 @@ function applyPhoenixGlobalConfig(payload = {}) {
const mode = String(payload.rtaBpoMode || '').trim();
CONFIG.RTA_BPO_MODE = (mode === '1_3' || mode === '1_6' || mode === '1_12') ? mode : '1_6';
}
const dinAttack = Number(payload.ppmDinAttackMs);
if (Number.isFinite(dinAttack)) CONFIG.PPM_DIN_ATTACK_MS = Math.max(0.1, Math.min(100, dinAttack));
const dinDecay = Number(payload.ppmDinDecayDbPerS);
if (Number.isFinite(dinDecay)) CONFIG.PPM_DIN_DECAY_DB_PER_S = Math.max(0.1, Math.min(120, dinDecay));
if (CONFIG.RTA_BAR_LAYOUT === 'rtw') {
CONFIG.RTA_ENGINE = 'iir';
CONFIG.RTA_BPO_MODE = '1_3';
CONFIG.RTA_IIR_ORDER = 6;
CONFIG.RTA_FREQ_RANGE = 'norm';
}
const spectroGamma = Number(payload.spectroGamma);
if (Number.isFinite(spectroGamma)) CONFIG.SPECTRO_GAMMA = Math.max(0.3, Math.min(1.2, spectroGamma));
if ('spectroScrollMode' in payload) CONFIG.SPECTRO_SCROLL_MODE = normalizeSpectroScroll(payload.spectroScrollMode);
CONFIG.PPM_DIN_ATTACK_MS = 10;
CONFIG.PPM_DIN_DECAY_DB_PER_S = 20 / 1.5;
if ('ppmDinFastAttack' in payload) CONFIG.PPM_DIN_FAST_ATTACK = !!payload.ppmDinFastAttack;
const ebuAttack = Number(payload.ppmEbuAttackMs);
if (Number.isFinite(ebuAttack)) CONFIG.PPM_EBU_ATTACK_MS = Math.max(0.1, Math.min(100, ebuAttack));
const ebuDecay = Number(payload.ppmEbuDecayDbPerS);
if (Number.isFinite(ebuDecay)) CONFIG.PPM_EBU_DECAY_DB_PER_S = Math.max(0.1, Math.min(120, ebuDecay));
CONFIG.PPM_EBU_ATTACK_MS = 10;
CONFIG.PPM_EBU_DECAY_DB_PER_S = 24 / 2.8;
const lufsWin = Number(payload.lufsIWindowMin);
if (Number.isFinite(lufsWin)) CONFIG.LUFS_I_WINDOW_MIN = Math.max(1, Math.min(10, Math.round(lufsWin)));
if ('lufsINormEnabled' in payload) CONFIG.LUFS_I_NORM_ENABLED = !!payload.lufsINormEnabled;