Restore smooth realtime fall ballistics

This commit is contained in:
Mikei386
2026-08-05 20:39:19 +02:00
parent c6a1037611
commit 66110be76d
5 changed files with 55 additions and 30 deletions
+10 -29
View File
@@ -200,10 +200,13 @@ export async function render(env, state) {
} else {
const integrated = baseLevels;
const displayBase = applyDisplayHold(state, integrated, CONFIG, range);
// Native IIR values already contain the selected power-domain
// integration. A second browser attack/hold stage would falsify it.
const display = displayBase;
syncNativePeakHold(state, displayRtaPacket, CONFIG, range, state.mapping.length, 'iir');
// Detector integration remains authoritative in the native DSP. The
// display-only hold/release stages advance on every VSync so repeated
// transport anchors cannot freeze a falling bar or peak hairline.
const display = (CONFIG.REALTIME_RENDER_STYLE || 'bars') === 'bars'
? applyRealtimeBarBallistics(state, displayBase, CONFIG, range)
: displayBase;
applyPeakHold(state, integrated, CONFIG, range);
state.displayLevels = display;
state.currentRange = range;
if (typeof window !== 'undefined') window.__RTA_STATE__ = state;
@@ -237,7 +240,7 @@ export async function render(env, state) {
const display = (CONFIG.REALTIME_RENDER_STYLE || 'bars') === 'bars'
? applyRealtimeBarBallistics(state, displayBase, CONFIG, range)
: displayBase;
syncNativePeakHold(state, displayRtaPacket, CONFIG, range, state.mapping.length, 'fft');
applyPeakHold(state, integrated, CONFIG, range);
state.displayLevels = display;
state.currentRange = range;
if (typeof window !== 'undefined') window.__RTA_STATE__ = state;
@@ -439,7 +442,7 @@ function applyIntegration(state, levels, CONFIG, range) {
return buffer;
}
function applyPeakHold(state, levels, CONFIG, range) {
export function applyPeakHold(state, levels, CONFIG, range) {
const mode = CONFIG.RTA_PEAK_HOLD_MODE || 'fall';
const len = levels.length;
const mapped = PEAK_HOLD_MAP.get(mode);
@@ -482,28 +485,6 @@ function applyPeakHold(state, levels, CONFIG, range) {
return buffer;
}
function syncNativePeakHold(state, packet, CONFIG, range, expectedLen, engine) {
const src = isVectorLike(packet?.bands_peak) && packet.bands_peak.length === expectedLen
? packet.bands_peak
: null;
if (!src) return applyPeakHold(state, state.displayLevels, CONFIG, range);
const gain = engine === 'iir'
? (Number(CONFIG.RTA_DISPLAY_GAIN_IIR_DB ?? 0) || 0)
: (Number(CONFIG.RTA_DISPLAY_GAIN_FFT_DB ?? 0) || 0);
if (!state.peakHold || state.peakHold.length !== expectedLen) {
state.peakHold = new Float32Array(expectedLen);
}
for (let i = 0; i < expectedLen; i++) {
const value = Number(src[i]);
state.peakHold[i] = clamp(
(Number.isFinite(value) ? value : range.bottom) + gain,
range.bottom,
range.top,
);
}
return state.peakHold;
}
function applyDisplayHold(state, levels, CONFIG, range) {
const holdSec = Math.max(0, Number(CONFIG.RTA_DISPLAY_HOLD_SEC) || 0);
const len = levels.length;
@@ -538,7 +519,7 @@ function applyDisplayHold(state, levels, CONFIG, range) {
return buffer;
}
function applyRealtimeBarBallistics(state, levels, CONFIG, range) {
export function applyRealtimeBarBallistics(state, levels, CONFIG, range) {
const holdMs = Math.max(0, Number(CONFIG.REALTIME_BAR_HOLD_MS) || 0);
const decayRate = Math.max(0, Number(CONFIG.REALTIME_BAR_DECAY_DB_PER_S) || 0);
const len = levels.length;