diff --git a/index.html b/index.html index 271fe55..3687fdb 100644 --- a/index.html +++ b/index.html @@ -219,6 +219,15 @@ footer .footer-track { flex: 1; min-width: 0; white-space: nowrap; overflow: hid Fast Very Fast + Peak Hold: + + Off + 100ms + 200ms + 500ms + 1s + 2s + Crossfade: Off @@ -355,6 +364,8 @@ let peakData = []; let smoothedData = []; let vuPeakL = 0, vuPeakR = 0; let vuSmoothL = 0, vuSmoothR = 0; +let vuPeakHoldL = 0, vuPeakHoldR = 0; +let vuPeakGrace = 0; function bessi0(x) { let s = 1, t = 1; for (let k = 1; k <= 20; k++) { @@ -572,10 +583,11 @@ function drawSpectrum() { if (levelR > vuSmoothR) vuSmoothR = levelR; else vuSmoothR = vuSmoothR * vuSmooth + levelR * (1 - vuSmooth); levelL = vuSmoothL; levelR = vuSmoothR; - function drawVuRow(y, level, peak) { + function drawVuRow(y, level, peak, hold) { const lit = Math.round(level * segCount); - if (level > peak) peak = level; - else peak *= 0.990; + if (level > peak) { peak = level; hold = 0; } + else if (hold < vuPeakGrace) { hold++; } + else { peak *= 0.990; } for (let i = 0; i < segCount; i++) { const sx = segLeft + i * (segW + segG); const on = i < lit; @@ -594,10 +606,12 @@ function drawSpectrum() { vuctx.fillRect(sx, y, segW, rowH); } } - return peak; + return [peak, hold]; } - vuPeakL = drawVuRow(0, levelL, vuPeakL); - vuPeakR = drawVuRow(gap + rowH, levelR, vuPeakR); + const rL = drawVuRow(0, levelL, vuPeakL, vuPeakHoldL); + vuPeakL = rL[0]; vuPeakHoldL = rL[1]; + const rR = drawVuRow(gap + rowH, levelR, vuPeakR, vuPeakHoldR); + vuPeakR = rR[0]; vuPeakHoldR = rR[1]; } animId = requestAnimationFrame(drawSpectrum); } @@ -723,6 +737,7 @@ const errorEl = document.getElementById('error'); const windowSelect = document.getElementById('windowSelect'); const decaySelect = document.getElementById('decaySelect'); const barDecaySelect = document.getElementById('barDecaySelect'); +const vuPeakHoldSelect = document.getElementById('vuPeakHoldSelect'); const crossfadeSelect = document.getElementById('crossfadeSelect'); const infoBtn = document.getElementById('infoBtn'); const tagEditorOverlay = document.getElementById('tagEditorOverlay'); @@ -1685,6 +1700,7 @@ sortSelect.addEventListener('change', () => { windowSelect.addEventListener('change', () => { windowType = windowSelect.value; }); decaySelect.addEventListener('change', () => { peakDecay = parseFloat(decaySelect.value); }); barDecaySelect.addEventListener('change', () => { barDecay = parseFloat(barDecaySelect.value); }); +vuPeakHoldSelect.addEventListener('change', () => { vuPeakGrace = parseInt(vuPeakHoldSelect.value, 10); }); crossfadeSelect.addEventListener('change', () => { crossfadeDuration = parseFloat(crossfadeSelect.value); }); crossfadeDuration = parseFloat(crossfadeSelect.value); repeatSelect.addEventListener('change', () => { repeatMode = repeatSelect.value; });