Correct RTA detector timing and peak integration

This commit is contained in:
Mikei386
2026-07-21 21:29:41 +02:00
parent 3478bfb495
commit 9b5250d42b
10 changed files with 312 additions and 112 deletions
+4 -1
View File
@@ -517,6 +517,8 @@ function buildPhoenixMeterPacket(frame) {
rta: rta ? {
engine: String(rta?.engine || 'iir'),
filterbank: String(rta?.filterbank || 'legacy'),
detector: String(rta?.detector || 'average'),
response: String(rta?.response || 'fast'),
bands_avg: Array.isArray(rta?.bands_avg) ? rta.bands_avg : (Array.isArray(rta?.bands) ? rta.bands : []),
bands_peak: Array.isArray(rta?.bands_peak) ? rta.bands_peak : [],
bands: Array.isArray(rta?.bands) ? rta.bands : (Array.isArray(rta?.bands_avg) ? rta.bands_avg : []),
@@ -873,6 +875,7 @@ export function buildRtaRuntimeConfig(CONFIG = {}) {
return {
engine: rtwProfile ? 'iir' : (CONFIG.RTA_ENGINE || 'iir'),
filterbank: CONFIG.RTA_IIR_FILTERBANK === 'butterworth' ? 'butterworth' : 'legacy',
detector: CONFIG.RTA_DETECTOR === 'peak' ? 'peak' : 'average',
fftSize: CONFIG.FFT_SIZE || 4096,
monoInput: !!CONFIG.MONO_INPUT,
lrFractionalDelayEnabled: !!CONFIG.LR_FRACTIONAL_DELAY_ENABLED,
@@ -881,7 +884,7 @@ export function buildRtaRuntimeConfig(CONFIG = {}) {
freqRange: rtwProfile ? 'norm' : (CONFIG.RTA_FREQ_RANGE || 'norm'),
weighting: CONFIG.RTA_WEIGHTING || 'z',
order: rtwProfile ? 6 : (CONFIG.RTA_IIR_ORDER || 6),
tauFast: CONFIG.RTA_IIR_TAU_FAST || 0.12,
tauFast: CONFIG.RTA_IIR_TAU_FAST || 0.125,
tauSlow: CONFIG.RTA_IIR_TAU_SLOW || 1.0,
integration: CONFIG.RTA_INTEGRATION || 'fast',
layout,
+16 -2
View File
@@ -59,14 +59,15 @@ const CONFIG = {
RTA_BAR_LAYOUT: 'rtw', // 'iec' | 'rtw'
RTA_BAR_BASE_COLOR: '#ffe066',
HEADER_TEXT_COLOR: '#ffe066',
RTA_INTEGRATION: 'fast', // 'impulse' | 'fast' | 'medium' | 'slow' | 'average' | 'peak'
RTA_DETECTOR: 'average', // 'average' | 'peak'
RTA_INTEGRATION: 'fast', // 'impulse' | 'fast' | 'medium' | 'slow'
RTA_BALLISTICS_MODE: 'average', // 'average' | 'peak' | 'both'
RTA_PEAK_HOLD_MODE: 'auto', // 'off' | 'auto' | 'manual'
RTA_PEAK_HOLD_SEC: 2.5,
RTA_PEAK_DECAY_DB_PER_S: 20,
RTA_DISPLAY_HOLD_SEC: 0,
RTA_IIR_ORDER: 6,
RTA_IIR_TAU_FAST: 0.12,
RTA_IIR_TAU_FAST: 0.125,
RTA_IIR_TAU_SLOW: 1.0,
SPECTRO_GAMMA: 0.4,
SPECTRO_SCROLL_MODE: 1, // 0.5=Langsam, 1=Normal, 2=Licht, 4=Lächerlich, 6=Wahnsinnig
@@ -788,6 +789,19 @@ function loadConfig(opts = {}) {
CONFIG.RTA_IIR_FILTERBANK = CONFIG.RTA_IIR_FILTERBANK === 'butterworth'
? 'butterworth'
: 'legacy';
if (CONFIG.RTA_INTEGRATION === 'peak') {
CONFIG.RTA_DETECTOR = 'peak';
CONFIG.RTA_INTEGRATION = 'fast';
} else if (CONFIG.RTA_INTEGRATION === 'average') {
CONFIG.RTA_DETECTOR = 'average';
CONFIG.RTA_INTEGRATION = 'fast';
}
CONFIG.RTA_DETECTOR = CONFIG.RTA_DETECTOR === 'peak' ? 'peak' : 'average';
if (!['impulse', 'fast', 'medium', 'slow'].includes(CONFIG.RTA_INTEGRATION)) {
CONFIG.RTA_INTEGRATION = 'fast';
}
CONFIG.RTA_IIR_TAU_FAST = 0.125;
CONFIG.RTA_IIR_TAU_SLOW = 1.0;
if (CONFIG.RTA_BAR_LAYOUT === 'rtw') {
CONFIG.RTA_ENGINE = 'iir';
CONFIG.RTA_IIR_ORDER = 6;
+19 -14
View File
@@ -325,12 +325,12 @@
<small>Filtergüte Ordnung des IIR-Filters</small>
</div>
<div class="opt"><label>IIR Fast τ (s)</label>
<input id="opt_rtaTauFast" type="number" min="0.01" max="1" step="0.01" style="width:110px">
<small>Schnelles Integrationsfenster</small>
<input id="opt_rtaTauFast" type="number" value="0.125" style="width:110px" disabled>
<small>Feste Standardreaktion: 125 ms</small>
</div>
<div class="opt"><label>IIR Slow τ (s)</label>
<input id="opt_rtaTauSlow" type="number" min="0.1" max="5" step="0.1" style="width:110px">
<small>Langsames Integrationsfenster</small>
<input id="opt_rtaTauSlow" type="number" value="1.0" style="width:110px" disabled>
<small>Feste Standardreaktion: 1 Sekunde</small>
</div>
<div class="opt"><label>Frequenzbereich</label>
<select id="opt_rtaFreq" style="width:150px">
@@ -359,24 +359,29 @@
</select>
<small>Pegelgewichtung</small>
</div>
<div class="opt"><label>Integrationszeit</label>
<div class="opt"><label>RTA-Detektor</label>
<select id="opt_rtaDetector" style="width:180px">
<option value="average" selected>Average (RMS)</option>
<option value="peak">Peak (10 ms)</option>
</select>
<small>RTW-Detektortyp: RMS mit gewählter Response oder feste 10-ms-Peakintegration</small>
</div>
<div class="opt"><label>Response</label>
<select id="opt_rtaIntegration" style="width:150px">
<option value="impulse">Impulse</option>
<option value="fast" selected>Fast</option>
<option value="medium">Medium</option>
<option value="slow">Slow</option>
<option value="average">Average</option>
<option value="peak">Peak (10 ms)</option>
</select>
<small>Reaktionsmodus</small>
<small>Average-Ballistik: Impulse 35 ms/1,5 s, Fast 125 ms, Medium 500 ms oder Slow 1 s</small>
</div>
<div class="opt"><label>RTA Ballistics</label>
<div class="opt"><label>RTA-Anzeige</label>
<select id="opt_rtaBallistics" style="width:200px">
<option value="average" selected>Average (RMS)</option>
<option value="peak">Peak</option>
<option value="both">Average + Peak Overlay</option>
<option value="average" selected>Detektorwert</option>
<option value="peak">Peak Hold</option>
<option value="both">Detektorwert + Peak Hold</option>
</select>
<small>Angezeigte Kurven</small>
<small>Auswahl der angezeigten Mess- und Hold-Kurven</small>
</div>
<div class="opt"><label>Hold Mode</label>
<select id="opt_rtaHoldMode" style="width:150px">
@@ -1267,7 +1272,7 @@
<li><strong>Spektrogramm:</strong> Langzeitstillstand und stotterndes Nachholen beseitigt. Inkrementelles Spaltenzeichnen, Worker-ACK, Watchdog/Neustart und begrenztes Überspringen veralteter Spalten ergänzt. 0,5×, 1×, 2×, 4× und 6× besitzen nun eine feste, FFT- und DPI-unabhängige Zeitbasis.</li>
<li><strong>DIN-/EBU-PPM:</strong> Blockunabhängige Quasi-Peak-Detektoren mit bandbegrenzter Interpolation, korrekter Attack- und Rücklaufballistik sowie automatischen Tonburst-, Frequenzgang- und Polaritätstests implementiert. Eine zweite Browser-Anstiegsballistik wurde entfernt.</li>
<li><strong>RTW-RTA:</strong> RTW-Profil verbindlich auf die IIR-Oktavteilband-Filterbank festgelegt. Bis zur Vergleichsmessung am realen RTW-Gerät sind die bisherige Phoenix/RTW-Charakteristik und vollständige Butterworth-Bandpässe sechster Ordnung getrennt auswählbar.</li>
<li><strong>RTA-Bewertung und Integration:</strong> A-, C- und Z-Bewertung sampleweise vor der Filterbank ergänzt. Fast, Medium, Slow, Impulse, Average und Peak rechnen im Leistungsbereich und unabhängig von der ALSA-Periodengröße.</li>
<li><strong>RTA-Detektor und Reaktionszeit:</strong> Average (RMS) und Peak (gleitendes 10-ms-Maximum) sind nun getrennt von Fast, Medium, Slow und Impulse wählbar. Fast wurde auf 125 ms korrigiert; Impulse verwendet 35 ms Anstieg und 1,5 s Rücklauf. Die IIR-Detektoren arbeiten samplegenau und unabhängig von der ALSA-Periodengröße. Alte Einstellungen werden automatisch migriert.</li>
<li><strong>RTA-Auflösung:</strong> Umschaltung zwischen 1/3, 1/6 und 1/12 repariert. Alle drei Auflösungen bleiben im RTW-Profil aktiv und verwenden dessen IIR-Filterbank, Darstellung und Ballistik.</li>
<li><strong>Hardwareparameter:</strong> Tatsächlich von ALSA ausgehandelte Samplerate, Perioden- und Puffergröße werden nun durch DSP, Aufnahme, Status und Browser verwendet; die feste 48-kHz-Annahme wurde entfernt.</li>
<li><strong>Goniometer:</strong> Nur neue XY-Samplepaare werden mit einer periodengrößenunabhängigen mittleren Rate von 60 Hz übertragen. Punktbegrenzung wirkt bereits auf den Transport; begrenzte und wiederverwendbare Spurpuffer reduzieren Speicher- und Zeichenlast.</li>
+17 -4
View File
@@ -287,6 +287,7 @@ function syncUI() {
['opt_rtaLayout', CONFIG.RTA_BAR_LAYOUT || 'iec'],
['opt_rtaBarBaseColor', CONFIG.RTA_BAR_BASE_COLOR || '#ffe066'],
['opt_rtaWeighting', CONFIG.RTA_WEIGHTING || 'z'],
['opt_rtaDetector', CONFIG.RTA_DETECTOR || 'average'],
['opt_rtaIntegration', CONFIG.RTA_INTEGRATION || 'fast'],
['opt_rtaBallistics', CONFIG.RTA_BALLISTICS_MODE || 'average'],
['opt_rtaHoldMode', CONFIG.RTA_PEAK_HOLD_MODE || 'auto'],
@@ -303,7 +304,7 @@ function syncUI() {
['opt_waveColorL', CONFIG.WAVEFORM_COLOR_LEFT || '#00e7ff'],
['opt_waveColorR', CONFIG.WAVEFORM_COLOR_RIGHT || '#ff6b81'],
['opt_waveColorDiff', CONFIG.WAVEFORM_COLOR_DIFF || '#00e7ff'],
['opt_rtaTauFast', CONFIG.RTA_IIR_TAU_FAST ?? 0.12],
['opt_rtaTauFast', CONFIG.RTA_IIR_TAU_FAST ?? 0.125],
['opt_rtaTauSlow', CONFIG.RTA_IIR_TAU_SLOW ?? 1.0],
['opt_rtRenderMode', CONFIG.REALTIME_RENDER_STYLE || 'bars'],
['opt_rtHold', CONFIG.REALTIME_BAR_HOLD_MS],
@@ -418,6 +419,12 @@ function syncUI() {
const iWin = E('opt_lufsIWindowMin');
if (iWin) iWin.disabled = !!CONFIG.LUFS_I_NORM_ENABLED;
const rtaResponse = E('opt_rtaIntegration');
if (rtaResponse) rtaResponse.disabled = CONFIG.RTA_DETECTOR === 'peak';
const rtaTauFast = E('opt_rtaTauFast');
if (rtaTauFast) rtaTauFast.disabled = true;
const rtaTauSlow = E('opt_rtaTauSlow');
if (rtaTauSlow) rtaTauSlow.disabled = true;
const mp3Wrap = E('opt_recMp3BitrateWrap');
if (mp3Wrap) mp3Wrap.style.display = (String(CONFIG.RECORD_OUTPUT_FORMAT || 'wav') === 'mp3') ? '' : 'none';
const mp3Sel = E('opt_recMp3Bitrate');
@@ -1133,8 +1140,14 @@ function wireHandlers(env) {
const allowed = new Set(['z','a','c']);
CONFIG.RTA_WEIGHTING = allowed.has(v) ? v : 'z';
});
h('opt_rtaDetector', v => {
CONFIG.RTA_DETECTOR = v === 'peak' ? 'peak' : 'average';
const response = E('opt_rtaIntegration');
if (response) response.disabled = CONFIG.RTA_DETECTOR === 'peak';
return CONFIG.RTA_DETECTOR;
});
h('opt_rtaIntegration', v => {
const allowed = new Set(['impulse','fast','medium','slow','average','peak']);
const allowed = new Set(['impulse','fast','medium','slow']);
CONFIG.RTA_INTEGRATION = allowed.has(v) ? v : 'fast';
return CONFIG.RTA_INTEGRATION;
});
@@ -1212,8 +1225,8 @@ function wireHandlers(env) {
CONFIG.WAVEFORM_COLOR_DIFF = v;
notifyPhoenixGlobalConfig();
});
h('opt_rtaTauFast', v => { CONFIG.RTA_IIR_TAU_FAST = clamp(+v, 0.01, 1.0); });
h('opt_rtaTauSlow', v => { CONFIG.RTA_IIR_TAU_SLOW = clamp(+v, 0.1, 5.0); });
h('opt_rtaTauFast', () => { CONFIG.RTA_IIR_TAU_FAST = 0.125; return 0.125; });
h('opt_rtaTauSlow', () => { CONFIG.RTA_IIR_TAU_SLOW = 1.0; return 1.0; });
h('opt_rtRenderMode', v => { CONFIG.REALTIME_RENDER_STYLE = (v === 'line') ? 'line' : 'bars'; });
h('opt_rtHold', v => CONFIG.REALTIME_BAR_HOLD_MS = clamp(+v, 100, 5000));
h('opt_rtDecay', v => CONFIG.REALTIME_BAR_DECAY_DB_PER_S = clamp(+v, 1, 60));
+8 -5
View File
@@ -31,10 +31,9 @@ const PEAK_HOLD_MAP = new Map(Object.entries({
}));
const INTEGRATION_TAU = {
impulse: 0.035,
fast: 0.125,
medium: 0.5,
slow: 1.0,
peak: 0.01,
};
@@ -283,6 +282,7 @@ function buildConfigSignature(CONFIG, nyq, binCount) {
CONFIG.RTA_BPO_MODE,
CONFIG.RTA_WEIGHTING,
CONFIG.RTA_BAR_LAYOUT,
CONFIG.RTA_DETECTOR,
CONFIG.RTA_INTEGRATION,
CONFIG.RTA_PEAK_HOLD_MODE,
CONFIG.RTA_PEAK_HOLD_SEC,
@@ -383,15 +383,14 @@ function computeBandLevels(state, utils, buf, CONFIG, range) {
}
function applyIntegration(state, levels, CONFIG, range) {
const mode = (CONFIG.RTA_INTEGRATION && INTEGRATION_TAU[CONFIG.RTA_INTEGRATION])
const mode = (CONFIG.RTA_INTEGRATION === 'impulse' || INTEGRATION_TAU[CONFIG.RTA_INTEGRATION])
? CONFIG.RTA_INTEGRATION
: 'fast';
const tau = INTEGRATION_TAU[mode] || 0.125;
const peakDetector = CONFIG.RTA_DETECTOR === 'peak';
const now = performance.now();
const last = state.lastIntegrationTs || now;
const dt = Math.max(1 / 240, (now - last) / 1000);
state.lastIntegrationTs = now;
const alpha = (tau > 0 && dt > 0) ? 1 - Math.exp(-dt / tau) : 1;
if (!state.ewma || state.ewma.length !== levels.length) {
state.ewma = new Float32Array(levels.length);
@@ -400,6 +399,10 @@ function applyIntegration(state, levels, CONFIG, range) {
const buffer = state.ewma;
for (let i = 0; i < levels.length; i++) {
const prev = buffer[i];
const tau = peakDetector
? 0.010
: (mode === 'impulse' ? (levels[i] >= prev ? 0.035 : 1.5) : (INTEGRATION_TAU[mode] || 0.125));
const alpha = (tau > 0 && dt > 0) ? 1 - Math.exp(-dt / tau) : 1;
buffer[i] = prev + (levels[i] - prev) * alpha;
}
return buffer;