mirror of
https://codeberg.org/armin/noiz.git
synced 2026-09-01 05:50:52 +02:00
Add configurable spectrum analyzer peak hold, fade, and snap behavior
This commit is contained in:
parent
45bf10105b
commit
dd657c6ab3
1 changed files with 40 additions and 2 deletions
42
index.html
42
index.html
|
|
@ -228,6 +228,23 @@ footer .footer-track { flex: 1; min-width: 0; white-space: nowrap; overflow: hid
|
||||||
<option value="60">1s</option>
|
<option value="60">1s</option>
|
||||||
<option value="120">2s</option>
|
<option value="120">2s</option>
|
||||||
</select>
|
</select>
|
||||||
|
<span style="font-size:11px;color:#6b7089;margin-left:8px">Spec Peak Hold:</span>
|
||||||
|
<select id="peakHoldSelect">
|
||||||
|
<option value="0">Off</option>
|
||||||
|
<option value="60">1s</option>
|
||||||
|
<option value="120" selected>2s</option>
|
||||||
|
<option value="180">3s</option>
|
||||||
|
<option value="240">4s</option>
|
||||||
|
</select>
|
||||||
|
<span style="font-size:11px;color:#6b7089;margin-left:8px">Peak Fade:</span>
|
||||||
|
<select id="peakFadeSelect">
|
||||||
|
<option value="0" selected>0ms</option>
|
||||||
|
<option value="12">200ms</option>
|
||||||
|
<option value="24">400ms</option>
|
||||||
|
<option value="36">600ms</option>
|
||||||
|
<option value="48">800ms</option>
|
||||||
|
<option value="60">1s</option>
|
||||||
|
</select>
|
||||||
<span style="font-size:11px;color:#6b7089;margin-left:8px">Crossfade:</span>
|
<span style="font-size:11px;color:#6b7089;margin-left:8px">Crossfade:</span>
|
||||||
<select id="crossfadeSelect">
|
<select id="crossfadeSelect">
|
||||||
<option value="0" selected>Off</option>
|
<option value="0" selected>Off</option>
|
||||||
|
|
@ -361,11 +378,15 @@ const canvas = document.getElementById('spectrum');
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
const bigTime = document.getElementById('bigTime');
|
const bigTime = document.getElementById('bigTime');
|
||||||
let peakData = [];
|
let peakData = [];
|
||||||
|
let peakHoldCounters = [];
|
||||||
|
let peakFadeCounters = [];
|
||||||
let smoothedData = [];
|
let smoothedData = [];
|
||||||
let vuPeakL = 0, vuPeakR = 0;
|
let vuPeakL = 0, vuPeakR = 0;
|
||||||
let vuSmoothL = 0, vuSmoothR = 0;
|
let vuSmoothL = 0, vuSmoothR = 0;
|
||||||
let vuPeakHoldL = 0, vuPeakHoldR = 0;
|
let vuPeakHoldL = 0, vuPeakHoldR = 0;
|
||||||
let vuPeakGrace = 0;
|
let vuPeakGrace = 0;
|
||||||
|
let peakHoldGrace = 0;
|
||||||
|
let peakFadeGrace = 0;
|
||||||
function bessi0(x) {
|
function bessi0(x) {
|
||||||
let s = 1, t = 1;
|
let s = 1, t = 1;
|
||||||
for (let k = 1; k <= 20; k++) {
|
for (let k = 1; k <= 20; k++) {
|
||||||
|
|
@ -495,6 +516,8 @@ function drawSpectrum() {
|
||||||
}
|
}
|
||||||
if (peakData.length !== NUM_BARS) {
|
if (peakData.length !== NUM_BARS) {
|
||||||
peakData = new Float32Array(NUM_BARS);
|
peakData = new Float32Array(NUM_BARS);
|
||||||
|
peakHoldCounters = new Uint32Array(NUM_BARS);
|
||||||
|
peakFadeCounters = new Uint32Array(NUM_BARS);
|
||||||
smoothedData = new Float32Array(NUM_BARS);
|
smoothedData = new Float32Array(NUM_BARS);
|
||||||
}
|
}
|
||||||
const rect = canvas.getBoundingClientRect();
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
|
@ -532,15 +555,23 @@ function drawSpectrum() {
|
||||||
ctx.fillRect(x, h - barH, barW, barH);
|
ctx.fillRect(x, h - barH, barW, barH);
|
||||||
if (barH > peakData[i]) {
|
if (barH > peakData[i]) {
|
||||||
peakData[i] = barH;
|
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 {
|
} else {
|
||||||
peakData[i] *= peakDecay;
|
peakData[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.fillStyle = '#e27878';
|
ctx.fillStyle = '#e27878';
|
||||||
for (let i = 0; i < NUM_BARS; i++) {
|
for (let i = 0; i < NUM_BARS; i++) {
|
||||||
const x = offset + i * totalBar;
|
const x = offset + i * totalBar;
|
||||||
if (peakData[i] > 1) {
|
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');
|
const vuCanvas = document.getElementById('vuMeter');
|
||||||
|
|
@ -743,6 +774,8 @@ const windowSelect = document.getElementById('windowSelect');
|
||||||
const decaySelect = document.getElementById('decaySelect');
|
const decaySelect = document.getElementById('decaySelect');
|
||||||
const barDecaySelect = document.getElementById('barDecaySelect');
|
const barDecaySelect = document.getElementById('barDecaySelect');
|
||||||
const vuPeakHoldSelect = document.getElementById('vuPeakHoldSelect');
|
const vuPeakHoldSelect = document.getElementById('vuPeakHoldSelect');
|
||||||
|
const peakHoldSelect = document.getElementById('peakHoldSelect');
|
||||||
|
const peakFadeSelect = document.getElementById('peakFadeSelect');
|
||||||
const crossfadeSelect = document.getElementById('crossfadeSelect');
|
const crossfadeSelect = document.getElementById('crossfadeSelect');
|
||||||
const infoBtn = document.getElementById('infoBtn');
|
const infoBtn = document.getElementById('infoBtn');
|
||||||
const tagEditorOverlay = document.getElementById('tagEditorOverlay');
|
const tagEditorOverlay = document.getElementById('tagEditorOverlay');
|
||||||
|
|
@ -1706,7 +1739,12 @@ windowSelect.addEventListener('change', () => { windowType = windowSelect.value;
|
||||||
decaySelect.addEventListener('change', () => { peakDecay = parseFloat(decaySelect.value); });
|
decaySelect.addEventListener('change', () => { peakDecay = parseFloat(decaySelect.value); });
|
||||||
barDecaySelect.addEventListener('change', () => { barDecay = parseFloat(barDecaySelect.value); });
|
barDecaySelect.addEventListener('change', () => { barDecay = parseFloat(barDecaySelect.value); });
|
||||||
vuPeakHoldSelect.addEventListener('change', () => { vuPeakGrace = parseInt(vuPeakHoldSelect.value, 10); });
|
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); });
|
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);
|
crossfadeDuration = parseFloat(crossfadeSelect.value);
|
||||||
repeatSelect.addEventListener('change', () => { repeatMode = repeatSelect.value; });
|
repeatSelect.addEventListener('change', () => { repeatMode = repeatSelect.value; });
|
||||||
shuffleSelect.addEventListener('change', () => { shuffleOn = shuffleSelect.value === 'on'; });
|
shuffleSelect.addEventListener('change', () => { shuffleOn = shuffleSelect.value === 'on'; });
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue