ui(vu): narrower 44deg swing, peak-based level, faster response; mini progress bar fix
- VU needle swings -22..+22deg instead of -45..+45 for a more realistic VU look - Switch from RMS to peak frequency reading so the needle catches musical hits - Faster attack (0.7) and release (0.25) so it swings rather than pinning - Replace explicit grid lines with subtle repeating-conic-gradient ticks - Scope mini progress bar styles to .mini-player; taller (3px), clickable
This commit is contained in:
@@ -5377,12 +5377,17 @@ body.visualizer-active .vinyl-stage .spectrogram-canvas {
|
|||||||
}
|
}
|
||||||
#mini-current-time { color: var(--copper); font-weight: 500; }
|
#mini-current-time { color: var(--copper); font-weight: 500; }
|
||||||
|
|
||||||
.mini-progress-bar {
|
.mini-player .mini-progress-bar {
|
||||||
height: 2px;
|
flex: 0 0 auto;
|
||||||
|
height: 3px;
|
||||||
background: var(--rule-strong);
|
background: var(--rule-strong);
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.mini-progress-fill {
|
.mini-player .mini-progress-fill {
|
||||||
|
height: 100%;
|
||||||
background: var(--copper);
|
background: var(--copper);
|
||||||
box-shadow: 0 0 8px var(--copper-glow);
|
box-shadow: 0 0 8px var(--copper-glow);
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
@@ -7062,33 +7067,10 @@ body.audio-spectrum-live .now-playing .spectrum span {
|
|||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
/* 11 grid lines (every 9°) drawn ONLY in the needle's -45°..+45°
|
background: repeating-conic-gradient(from 195deg at 50% 100%,
|
||||||
swing range. No mask — the conic-gradient is explicitly transparent
|
transparent 0deg 4deg,
|
||||||
outside the 90° active wedge so the leftmost line aligns with the
|
rgba(242, 235, 220, 0.08) 4deg 5deg,
|
||||||
needle's rest position (proper zero). */
|
transparent 5deg 9deg);
|
||||||
background: conic-gradient(from 315deg at 50% 100%,
|
|
||||||
rgba(242, 235, 220, 0.18) 0deg 0.5deg,
|
|
||||||
transparent 0.5deg 9deg,
|
|
||||||
rgba(242, 235, 220, 0.18) 9deg 9.5deg,
|
|
||||||
transparent 9.5deg 18deg,
|
|
||||||
rgba(242, 235, 220, 0.18) 18deg 18.5deg,
|
|
||||||
transparent 18.5deg 27deg,
|
|
||||||
rgba(242, 235, 220, 0.18) 27deg 27.5deg,
|
|
||||||
transparent 27.5deg 36deg,
|
|
||||||
rgba(242, 235, 220, 0.18) 36deg 36.5deg,
|
|
||||||
transparent 36.5deg 45deg,
|
|
||||||
rgba(242, 235, 220, 0.25) 45deg 45.5deg, /* slightly brighter centre line at 0 */
|
|
||||||
transparent 45.5deg 54deg,
|
|
||||||
rgba(242, 235, 220, 0.18) 54deg 54.5deg,
|
|
||||||
transparent 54.5deg 63deg,
|
|
||||||
rgba(242, 235, 220, 0.18) 63deg 63.5deg,
|
|
||||||
transparent 63.5deg 72deg,
|
|
||||||
rgba(242, 235, 220, 0.18) 72deg 72.5deg,
|
|
||||||
transparent 72.5deg 81deg,
|
|
||||||
rgba(242, 235, 220, 0.18) 81deg 81.5deg,
|
|
||||||
transparent 81.5deg 90deg,
|
|
||||||
rgba(242, 235, 220, 0.18) 90deg 90.5deg,
|
|
||||||
transparent 90.5deg 360deg);
|
|
||||||
}
|
}
|
||||||
.now-playing .vu-meter::after {
|
.now-playing .vu-meter::after {
|
||||||
content: "VU";
|
content: "VU";
|
||||||
@@ -7109,8 +7091,7 @@ body.audio-spectrum-live .now-playing .spectrum span {
|
|||||||
height: 88%;
|
height: 88%;
|
||||||
background: linear-gradient(to top, var(--copper) 0%, var(--copper-hi) 70%, var(--ink) 100%);
|
background: linear-gradient(to top, var(--copper) 0%, var(--copper-hi) 70%, var(--ink) 100%);
|
||||||
transform-origin: bottom center;
|
transform-origin: bottom center;
|
||||||
/* Rest at full-left like a real VU meter at silence (-∞ dB) */
|
transform: rotate(-22deg);
|
||||||
transform: rotate(-45deg);
|
|
||||||
transition: transform 350ms var(--ease);
|
transition: transform 350ms var(--ease);
|
||||||
box-shadow: 0 0 8px var(--copper-glow);
|
box-shadow: 0 0 8px var(--copper-glow);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -763,10 +763,10 @@ export function updateUI(status) {
|
|||||||
dom.volumeDisplay.textContent = `${status.volume}%`;
|
dom.volumeDisplay.textContent = `${status.volume}%`;
|
||||||
dom.miniVolumeSlider.value = status.volume;
|
dom.miniVolumeSlider.value = status.volume;
|
||||||
dom.miniVolumeDisplay.textContent = `${status.volume}%`;
|
dom.miniVolumeDisplay.textContent = `${status.volume}%`;
|
||||||
// VU needle: map 0-100 volume to -45deg..+45deg rotation.
|
// VU needle: map 0-100 volume to -22deg..+22deg rotation.
|
||||||
const needle = document.getElementById('vuNeedle');
|
const needle = document.getElementById('vuNeedle');
|
||||||
if (needle) {
|
if (needle) {
|
||||||
const deg = -45 + (status.volume / 100) * 90;
|
const deg = -22 + (status.volume / 100) * 44;
|
||||||
needle.style.transform = `rotate(${deg}deg)`;
|
needle.style.transform = `rotate(${deg}deg)`;
|
||||||
}
|
}
|
||||||
// Editorial VU readout: VOL XX% / OUT (SYS or MUTED)
|
// Editorial VU readout: VOL XX% / OUT (SYS or MUTED)
|
||||||
@@ -802,22 +802,19 @@ export function updateUI(status) {
|
|||||||
// slider position so the needle still looks alive.
|
// slider position so the needle still looks alive.
|
||||||
let vuWobbleHandle = null;
|
let vuWobbleHandle = null;
|
||||||
let vuWobbleStart = 0;
|
let vuWobbleStart = 0;
|
||||||
let vuLevelSmoothed = 0; // Smoothed RMS of recent frequency frames
|
let vuLevelSmoothed = 0;
|
||||||
const VU_LEVEL_ATTACK = 0.55; // How fast needle climbs to a peak
|
const VU_LEVEL_ATTACK = 0.7; // Fast climb so the needle catches musical hits
|
||||||
const VU_LEVEL_RELEASE = 0.12; // How fast it falls back
|
const VU_LEVEL_RELEASE = 0.25; // Faster fall so it swings between hits, not pins
|
||||||
|
|
||||||
function readAudioLevel() {
|
function readAudioLevel() {
|
||||||
// frequencyData is the WS-driven FFT payload from player.js scope.
|
|
||||||
if (!frequencyData || !frequencyData.frequencies) return null;
|
if (!frequencyData || !frequencyData.frequencies) return null;
|
||||||
const bins = frequencyData.frequencies;
|
const bins = frequencyData.frequencies;
|
||||||
if (!bins.length) return null;
|
if (!bins.length) return null;
|
||||||
let sumSq = 0;
|
let peak = 0;
|
||||||
// Skip the very lowest bin (DC + sub-rumble) for cleaner level.
|
for (let i = 1; i < bins.length; i++) {
|
||||||
for (let i = 1; i < bins.length; i++) sumSq += bins[i] * bins[i];
|
if (bins[i] > peak) peak = bins[i];
|
||||||
const rms = Math.sqrt(sumSq / (bins.length - 1));
|
}
|
||||||
// The values are in 0..1 from the backend; gain a touch so quieter
|
return Math.min(1, peak * 1.4);
|
||||||
// tracks still swing the needle.
|
|
||||||
return Math.min(1, rms * 1.6);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function startVuWobble() {
|
function startVuWobble() {
|
||||||
@@ -839,11 +836,9 @@ function startVuWobble() {
|
|||||||
const wanted = audioLevel * (vol / 100);
|
const wanted = audioLevel * (vol / 100);
|
||||||
const k = wanted > vuLevelSmoothed ? VU_LEVEL_ATTACK : VU_LEVEL_RELEASE;
|
const k = wanted > vuLevelSmoothed ? VU_LEVEL_ATTACK : VU_LEVEL_RELEASE;
|
||||||
vuLevelSmoothed = vuLevelSmoothed * (1 - k) + wanted * k;
|
vuLevelSmoothed = vuLevelSmoothed * (1 - k) + wanted * k;
|
||||||
// Map 0..1 to -45deg..+45deg.
|
target = -22 + vuLevelSmoothed * 44;
|
||||||
target = -45 + vuLevelSmoothed * 90;
|
|
||||||
} else {
|
} else {
|
||||||
// Synthetic fallback: volume-mapped + sine wobble.
|
const base = -22 + (vol / 100) * 44;
|
||||||
const base = -45 + (vol / 100) * 90;
|
|
||||||
const mag = Math.max(2, Math.min(14, vol * 0.16));
|
const mag = Math.max(2, Math.min(14, vol * 0.16));
|
||||||
const t = (performance.now() - vuWobbleStart) / 1000;
|
const t = (performance.now() - vuWobbleStart) / 1000;
|
||||||
target = base
|
target = base
|
||||||
@@ -864,9 +859,8 @@ function stopVuWobble() {
|
|||||||
vuWobbleHandle = null;
|
vuWobbleHandle = null;
|
||||||
}
|
}
|
||||||
vuLevelSmoothed = 0;
|
vuLevelSmoothed = 0;
|
||||||
// Settle needle back to the bottom of the swing.
|
|
||||||
const needle = document.getElementById('vuNeedle');
|
const needle = document.getElementById('vuNeedle');
|
||||||
if (needle) needle.style.transform = 'rotate(-45deg)';
|
if (needle) needle.style.transform = 'rotate(-22deg)';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updatePlaybackState(state) {
|
export function updatePlaybackState(state) {
|
||||||
|
|||||||
Reference in New Issue
Block a user