Correct audio metering and realtime displays

This commit is contained in:
Mikei386
2026-07-22 10:50:15 +02:00
parent 97352ecfce
commit 2e868bfa3a
26 changed files with 999 additions and 903 deletions
+11 -57
View File
@@ -1,6 +1,7 @@
// views/peak_history.js - Meter-Verlauf (rechter Slot) mit RTA-Layout
import { FRAME_COLOR, GRID_MAJOR_COLOR, GRID_MINOR_COLOR, LABEL_COLOR, MID_COLOR, PANEL_BG, WARN_COLOR } from '../core/theme.js';
import { TRUE_PEAK_SCALE } from '../meters/true_peak_scale.js';
const PLOT = { left: 43, top: 70, right: 0, bottom: 18 };
const METER_WIDTH = 140;
@@ -47,24 +48,10 @@ const DIN_SCALE = [
{ db: +5, pos: 1.0000 },
];
const TP_PERCENT_SCALE = [
{ db: -60, frac: 0.0000 },
{ db: -50, frac: 0.0373 },
{ db: -40, frac: 0.1429 },
{ db: -35, frac: 0.2112 },
{ db: -30, frac: 0.2857 },
{ db: -25, frac: 0.3851 },
{ db: -20, frac: 0.4783 },
{ db: -15, frac: 0.6087 },
{ db: -10, frac: 0.7391 },
{ db: -5, frac: 0.8696 },
{ db: 0, frac: 1.0000 },
];
const VU_TICKS = [-20, -10, -7, -5, -3, 0, 1, 2, 3];
const PPM_DIN_TICKS = [-50, -40, -30, -20, -10, -5, 0, 5];
const PPM_EBU_TICKS = [-12, -8, -4, 0, 4, 8, 12];
const TP_TICKS = [-60, -50, -40, -30, -20, -10, 0];
const TP_TICKS = [-60, -50, -40, -30, -20, -10, 0, 3, 6];
const RMS_DBFS_TICKS = [0, -6, -12, -18, -24, -30, -40, -60];
const RMS_DBU_TICKS = [24, 20, 10, 0, -10, -20, -30, -40, -50, -60];
const LUFS_TICKS = [-50, -40, -30, -23, -18, -10, -5];
@@ -91,7 +78,6 @@ export function init() {
meterLabel: '',
neutralNorm: 0,
neutralValue: 0,
rmsSmooth: null,
staticLayer: null,
};
}
@@ -605,7 +591,6 @@ async function drawMeter(env, plotX, plotY, plotW, plotH, meterRects = null) {
function resolveMeterInfo(env, state, meterId) {
const cfg = env.config || {};
const shared = env.meters?.getState?.(meterId) || null;
if (meterId !== 'rms') state.rmsSmooth = null;
switch (meterId) {
case 'ppm-din':
@@ -615,7 +600,7 @@ function resolveMeterInfo(env, state, meterId) {
case 'tp':
return sampleTp(cfg, shared);
case 'rms':
return sampleRms(cfg, shared, state);
return sampleRms(cfg, shared);
case 'lufs':
return sampleLufs(cfg, shared);
case 'vu':
@@ -715,8 +700,8 @@ function samplePpmEbu(cfg, shared) {
}
function sampleTp(cfg, shared) {
const bottom = TP_PERCENT_SCALE[0].db;
const top = TP_PERCENT_SCALE[TP_PERCENT_SCALE.length - 1].db;
const bottom = TRUE_PEAK_SCALE[0].db;
const top = TRUE_PEAK_SCALE[TRUE_PEAK_SCALE.length - 1].db;
const effOff = Number(cfg?.TP_OFFSET_DB) || 0;
const offCorr = effOff - (shared?.offset || 0);
const warnVal = Number.isFinite(cfg?.TP_RED_START) ? cfg.TP_RED_START : -1;
@@ -740,21 +725,21 @@ function sampleTp(cfg, shared) {
});
}
function sampleRms(cfg, shared, state) {
function sampleRms(cfg, shared) {
const mode = String(cfg?.RMS_MODE || 'dbfs').toLowerCase();
const isDBU = mode === 'dbu';
const bottom = -60;
const top = isDBU ? 24 : 0;
const refDbfs = Number.isFinite(cfg?.RMS_REF_DBFS_FOR_REF_DBU) ? cfg.RMS_REF_DBFS_FOR_REF_DBU : -18;
const refDbu = Number.isFinite(cfg?.RMS_REF_DBU) ? cfg.RMS_REF_DBU : 0;
const offset = Number.isFinite(cfg?.RMS_OFFSET_DB) ? cfg.RMS_OFFSET_DB : 0;
const warnVal = Number.isFinite(cfg?.RMS_RED_START)
? cfg.RMS_RED_START
: (isDBU ? 20 : 0);
const vL = Number.isFinite(shared?.values?.L) ? shared.values.L : bottom;
const vR = Number.isFinite(shared?.values?.R) ? shared.values.R : bottom;
const base = Math.max(vL, vR);
const smoothed = applyRmsSmoothing(state, base, cfg);
const display = isDBU ? (smoothed - refDbfs + refDbu) : smoothed;
const base = Math.max(vL, vR) + offset;
const display = isDBU ? (base - refDbfs + refDbu) : base;
const val = clamp(display, bottom, top);
const toNorm = (db) => {
const c = clamp(db, bottom, top);
@@ -778,43 +763,12 @@ function sampleRms(cfg, shared, state) {
toNorm,
range: { bottom, top },
ticks: isDBU ? RMS_DBU_TICKS : RMS_DBFS_TICKS,
signature: ['rms', mode, refDbfs, refDbu, cfg?.RMS_TC_MODE, cfg?.RMS_TC_MS].join('|'),
signature: ['rms', mode, refDbfs, refDbu, offset, cfg?.RMS_TC_MODE].join('|'),
hasValue: !!shared,
warnValue: warnVal,
});
}
function applyRmsSmoothing(state, value, cfg) {
const tauMs = resolveRmsTau(cfg);
if (!Number.isFinite(tauMs) || tauMs <= 0) {
state.rmsSmooth = null;
return value;
}
const now = performance.now();
if (!state.rmsSmooth || !Number.isFinite(state.rmsSmooth.value)) {
state.rmsSmooth = { value, lastTs: now };
return value;
}
const dt = Math.max(1 / 240, (now - (state.rmsSmooth.lastTs || now)) / 1000);
state.rmsSmooth.lastTs = now;
const alpha = 1 - Math.exp(-dt / (tauMs / 1000));
state.rmsSmooth.value += alpha * (value - state.rmsSmooth.value);
return state.rmsSmooth.value;
}
function resolveRmsTau(cfg = {}) {
const mode = String(cfg.RMS_TC_MODE || 'fast').toLowerCase();
const custom = Number(cfg.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 sampleLufs(cfg, shared) {
const bottom = -50;
const top = -5;
@@ -888,7 +842,7 @@ function interpolateScale(table, db) {
}
function interpolateTp(db) {
return interpolateScale(TP_PERCENT_SCALE, db);
return interpolateScale(TRUE_PEAK_SCALE, db);
}
function deflectionFrac(db) {