diff --git a/index.html b/index.html index 4495187..2284955 100644 --- a/index.html +++ b/index.html @@ -228,6 +228,23 @@ footer .footer-track { flex: 1; min-width: 0; white-space: nowrap; overflow: hid 1s 2s + Spec Peak Hold: + + Off + 1s + 2s + 3s + 4s + + Peak Fade: + + 0ms + 200ms + 400ms + 600ms + 800ms + 1s + Crossfade: Off @@ -361,11 +378,15 @@ const canvas = document.getElementById('spectrum'); const ctx = canvas.getContext('2d'); const bigTime = document.getElementById('bigTime'); let peakData = []; +let peakHoldCounters = []; +let peakFadeCounters = []; let smoothedData = []; let vuPeakL = 0, vuPeakR = 0; let vuSmoothL = 0, vuSmoothR = 0; let vuPeakHoldL = 0, vuPeakHoldR = 0; let vuPeakGrace = 0; +let peakHoldGrace = 0; +let peakFadeGrace = 0; function bessi0(x) { let s = 1, t = 1; for (let k = 1; k <= 20; k++) { @@ -495,6 +516,8 @@ function drawSpectrum() { } if (peakData.length !== NUM_BARS) { peakData = new Float32Array(NUM_BARS); + peakHoldCounters = new Uint32Array(NUM_BARS); + peakFadeCounters = new Uint32Array(NUM_BARS); smoothedData = new Float32Array(NUM_BARS); } const rect = canvas.getBoundingClientRect(); @@ -532,15 +555,23 @@ function drawSpectrum() { ctx.fillRect(x, h - barH, barW, barH); if (barH > peakData[i]) { peakData[i] = barH; + peakHoldCounters[i] = 0; + peakFadeCounters[i] = 0; + } else if (peakHoldCounters[i] < peakHoldGrace) { + peakHoldCounters[i]++; + } else if (peakFadeCounters[i] < peakFadeGrace) { + const remaining = peakFadeGrace - peakFadeCounters[i]; + peakData[i] *= (remaining - 1) / remaining; + peakFadeCounters[i]++; } else { - peakData[i] *= peakDecay; + peakData[i] = 0; } } ctx.fillStyle = '#e27878'; for (let i = 0; i < NUM_BARS; i++) { const x = offset + i * totalBar; if (peakData[i] > 1) { - ctx.fillRect(x, h - Math.round(peakData[i]), barW, 4); + ctx.fillRect(x, Math.min(h - 4, h - Math.round(peakData[i])), barW, 4); } } const vuCanvas = document.getElementById('vuMeter'); @@ -743,6 +774,8 @@ const windowSelect = document.getElementById('windowSelect'); const decaySelect = document.getElementById('decaySelect'); const barDecaySelect = document.getElementById('barDecaySelect'); const vuPeakHoldSelect = document.getElementById('vuPeakHoldSelect'); +const peakHoldSelect = document.getElementById('peakHoldSelect'); +const peakFadeSelect = document.getElementById('peakFadeSelect'); const crossfadeSelect = document.getElementById('crossfadeSelect'); const infoBtn = document.getElementById('infoBtn'); const tagEditorOverlay = document.getElementById('tagEditorOverlay'); @@ -1706,7 +1739,12 @@ 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); }); +peakHoldSelect.addEventListener('change', () => { peakHoldGrace = parseInt(peakHoldSelect.value, 10); }); +peakFadeSelect.addEventListener('change', () => { peakFadeGrace = parseInt(peakFadeSelect.value, 10); }); crossfadeSelect.addEventListener('change', () => { crossfadeDuration = parseFloat(crossfadeSelect.value); }); +vuPeakGrace = parseInt(vuPeakHoldSelect.value, 10); +peakHoldGrace = parseInt(peakHoldSelect.value, 10); +peakFadeGrace = parseInt(peakFadeSelect.value, 10); crossfadeDuration = parseFloat(crossfadeSelect.value); repeatSelect.addEventListener('change', () => { repeatMode = repeatSelect.value; }); shuffleSelect.addEventListener('change', () => { shuffleOn = shuffleSelect.value === 'on'; });