Correct RTA detector timing and peak integration
This commit is contained in:
+4
-1
@@ -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
@@ -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
@@ -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
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user