Correct audio metering and realtime displays
This commit is contained in:
+12
-67
@@ -15,32 +15,25 @@ import { drawCachedStaticLayer } from './static_layer.js';
|
||||
import { METER_HEADER_FONT } from './scale_helpers.js';
|
||||
import { HEADER_BG, LABEL_COLOR, OK_COLOR, WARN_COLOR } from '../core/theme.js';
|
||||
|
||||
const RMS_FRAME_MS_NOMINAL = 16; // ~60 Hz UI-Refresh
|
||||
const RMS_FRAME_MS_MAX = 40; // Obergrenze für dt in der Glättung
|
||||
const RMS_FRAME_MS_SKIP = 250; // Heuristischer Schutz: riesige Gaps komplett skippen
|
||||
|
||||
export const id = 'rms';
|
||||
|
||||
export function initShared(CONFIG = {}) {
|
||||
const offset = CONFIG.RMS_OFFSET_DB || 0;
|
||||
const initVal = -60 + offset;
|
||||
const initVal = -60;
|
||||
return {
|
||||
values: { L: initVal, R: initVal }, // intern immer dBFS RMS aus der Messkette
|
||||
offset,
|
||||
values: { L: initVal, R: initVal }, // unveränderte dBFS-RMS-Werte aus dem Backend
|
||||
offset: 0,
|
||||
lastValidL: initVal,
|
||||
lastValidR: initVal,
|
||||
_smooth: null,
|
||||
};
|
||||
}
|
||||
|
||||
export function update(packet, shared) {
|
||||
const offset = shared.offset || 0;
|
||||
const floor = -60 + offset;
|
||||
const floor = -60;
|
||||
if (!Number.isFinite(shared.lastValidL)) shared.lastValidL = floor;
|
||||
if (!Number.isFinite(shared.lastValidR)) shared.lastValidR = floor;
|
||||
|
||||
if (Number.isFinite(packet?.rmsL)) {
|
||||
const vL = packet.rmsL + offset;
|
||||
const vL = packet.rmsL;
|
||||
shared.values.L = vL;
|
||||
shared.lastValidL = vL;
|
||||
} else {
|
||||
@@ -48,7 +41,7 @@ export function update(packet, shared) {
|
||||
}
|
||||
|
||||
if (Number.isFinite(packet?.rmsR)) {
|
||||
const vR = packet.rmsR + offset;
|
||||
const vR = packet.rmsR;
|
||||
shared.values.R = vR;
|
||||
shared.lastValidR = vR;
|
||||
} else {
|
||||
@@ -65,6 +58,7 @@ export function draw(g, rect, CONFIG = {}, shared) {
|
||||
|
||||
// Anzeige-Umschaltung & Skala
|
||||
const isDBU = (MODE === 'dbu');
|
||||
const offset = Number.isFinite(CONFIG.RMS_OFFSET_DB) ? CONFIG.RMS_OFFSET_DB : 0;
|
||||
const LOG_MIN = -60;
|
||||
const LOG_TOP = isDBU ? +24 : 0;
|
||||
|
||||
@@ -120,35 +114,9 @@ export function draw(g, rect, CONFIG = {}, shared) {
|
||||
g.fillRect(rect.x, rect.y - 24, rect.w, 24);
|
||||
g.restore();
|
||||
|
||||
// IEC-ähnliche Zeitkonstanten (Impulse/Fast/Slow) für die Anzeige
|
||||
const tauMs = resolveRmsTau(CONFIG);
|
||||
if (tauMs > 0) {
|
||||
const now = performance.now();
|
||||
if (!shared._smooth) {
|
||||
shared._smooth = {
|
||||
L: shared.values.L,
|
||||
R: shared.values.R,
|
||||
lastTs: now - RMS_FRAME_MS_NOMINAL,
|
||||
};
|
||||
}
|
||||
const lastTs = shared._smooth.lastTs ?? (now - RMS_FRAME_MS_NOMINAL);
|
||||
const dtRawMs = Math.max(0, now - lastTs);
|
||||
shared._smooth.lastTs = now;
|
||||
|
||||
if (dtRawMs <= RMS_FRAME_MS_SKIP) {
|
||||
const dtUsedMs = Math.min(dtRawMs, RMS_FRAME_MS_MAX); // clamp dt to ignore occasional large gaps
|
||||
const alpha = 1 - Math.exp(-dtUsedMs / tauMs);
|
||||
shared._smooth.L += alpha * (shared.values.L - shared._smooth.L);
|
||||
shared._smooth.R += alpha * (shared.values.R - shared._smooth.R);
|
||||
}
|
||||
} else {
|
||||
shared._smooth = null;
|
||||
}
|
||||
|
||||
// Werte in Anzeigeeinheit clampen
|
||||
const rawDispL = toDisplay(shared._smooth?.L ?? shared.values.L);
|
||||
const rawDispR = toDisplay(shared._smooth?.R ?? shared.values.R);
|
||||
const smooth = smoothHeader(shared, rawDispL, rawDispR);
|
||||
const rawDispL = toDisplay(shared.values.L + offset);
|
||||
const rawDispR = toDisplay(shared.values.R + offset);
|
||||
const vL = clamp(rawDispL, LOG_MIN, LOG_TOP);
|
||||
const vR = clamp(rawDispR, LOG_MIN, LOG_TOP);
|
||||
g.save();
|
||||
@@ -167,11 +135,11 @@ export function draw(g, rect, CONFIG = {}, shared) {
|
||||
const isRedL = rawDispL > RED_START;
|
||||
const isRedR = rawDispR > RED_START;
|
||||
g.fillStyle = isRedL ? WARN_COLOR : (CONFIG.HEADER_TEXT_COLOR || MID_COLOR);
|
||||
g.fillText(fmt(smooth.L), centerLeft, yText);
|
||||
g.fillText(fmt(rawDispL), centerLeft, yText);
|
||||
g.fillStyle = CONFIG.HEADER_TEXT_COLOR || MID_COLOR;
|
||||
g.fillText('|', centerX, yText);
|
||||
g.fillStyle = isRedR ? WARN_COLOR : (CONFIG.HEADER_TEXT_COLOR || MID_COLOR);
|
||||
g.fillText(fmt(smooth.R), centerRight, yText);
|
||||
g.fillText(fmt(rawDispR), centerRight, yText);
|
||||
} else {
|
||||
const title = isDBU ? 'RMS (dBu)' : 'RMS (dBFS RMS)';
|
||||
g.fillText(title, centerX, rect.y - 12);
|
||||
@@ -284,7 +252,7 @@ function drawRmsStaticOverlay(g, shared, rect, CONFIG, geom) {
|
||||
bottomValue: LOG_MIN,
|
||||
highlightTick: null,
|
||||
});
|
||||
const unitLabel = 'dBFS (RMS)';
|
||||
const unitLabel = isDBU ? 'dBu (RMS)' : 'dBFS (RMS)';
|
||||
cg.fillStyle = LABEL_COLOR;
|
||||
cg.textAlign = 'center';
|
||||
const baseY = rect.y + rect.h + 16;
|
||||
@@ -303,19 +271,6 @@ function drawRmsStaticOverlay(g, shared, rect, CONFIG, geom) {
|
||||
|
||||
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
|
||||
|
||||
function resolveRmsTau(CONFIG = {}) {
|
||||
const mode = String(CONFIG.RMS_TC_MODE || 'fast').toLowerCase();
|
||||
const custom = Number(CONFIG.RMS_TC_MS);
|
||||
if (Number.isFinite(custom) && custom > 0) return custom;
|
||||
switch (mode) {
|
||||
case 'impulse': return 35;
|
||||
case 'slow': return 1000;
|
||||
case 'none': return 0;
|
||||
case 'fast':
|
||||
default: return 125;
|
||||
}
|
||||
}
|
||||
|
||||
function drawRedStripeLR(g, leftX, barW, rightX, centerX, mapY, yWarn, yTop) {
|
||||
const innerRight = leftX + barW;
|
||||
const gapL = Math.abs(centerX - innerRight);
|
||||
@@ -491,13 +446,3 @@ function getRmsAlignmentHighlight(CONFIG) {
|
||||
color: WARN_COLOR,
|
||||
};
|
||||
}
|
||||
|
||||
function smoothHeader(shared, rawL, rawR, alpha = 0.2) {
|
||||
if (!shared._header) {
|
||||
shared._header = { L: rawL, R: rawR };
|
||||
return shared._header;
|
||||
}
|
||||
shared._header.L += alpha * (rawL - shared._header.L);
|
||||
shared._header.R += alpha * (rawR - shared._header.R);
|
||||
return shared._header;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user