From 8367cbf6e737d39c9be709d32d4c5cefb09f221d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Tue, 18 Nov 2025 15:15:16 +0100 Subject: [PATCH] fix: switch from RMS to peak detection for more accurate level meters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed level calculation from RMS to peak detection to show more realistic and responsive meter values. The Problem: - RMS calculation produced values typically in 0-30% range - Audio signals have low average RMS (0.1-0.3 for music) - Meters appeared broken, never reaching higher levels The Solution: - Switched to peak detection (max absolute value) - Peaks now properly show 0-100% range - More responsive to transients and dynamics - Matches typical DAW meter behavior Algorithm Change: Before (RMS): rms = sqrt(sum(normalized²) / length) After (Peak): peak = max(abs(normalized)) Applied to Both: - Recording input level monitoring (useRecording) - Playback output level monitoring (useMultiTrackPlayer) Benefits: ✅ Full 0-100% range utilization ✅ More responsive visual feedback ✅ Accurate representation of audio peaks ✅ Consistent with professional audio software 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- lib/hooks/useMultiTrackPlayer.ts | 14 ++++++++------ lib/hooks/useRecording.ts | 13 +++++++------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/hooks/useMultiTrackPlayer.ts b/lib/hooks/useMultiTrackPlayer.ts index 1226691..3399a80 100644 --- a/lib/hooks/useMultiTrackPlayer.ts +++ b/lib/hooks/useMultiTrackPlayer.ts @@ -64,14 +64,16 @@ export function useMultiTrackPlayer(tracks: Track[], masterVolume: number = 1) { const dataArray = new Uint8Array(analyser.frequencyBinCount); analyser.getByteTimeDomainData(dataArray); - // Calculate RMS level - let sum = 0; + // Calculate peak level (more responsive than RMS for visual meters) + let peak = 0; for (let i = 0; i < dataArray.length; i++) { - const normalized = (dataArray[i] - 128) / 128; - sum += normalized * normalized; + const normalized = Math.abs((dataArray[i] - 128) / 128); + if (normalized > peak) { + peak = normalized; + } } - const rms = Math.sqrt(sum / dataArray.length); - levels[track.id] = rms; + + levels[track.id] = peak; }); setTrackLevels(levels); diff --git a/lib/hooks/useRecording.ts b/lib/hooks/useRecording.ts index b94f11d..606a98d 100644 --- a/lib/hooks/useRecording.ts +++ b/lib/hooks/useRecording.ts @@ -78,15 +78,16 @@ export function useRecording(): UseRecordingReturn { analyser.getByteTimeDomainData(dataArray); - // Calculate RMS level - let sum = 0; + // Calculate peak level (more responsive than RMS for visual meters) + let peak = 0; for (let i = 0; i < dataArray.length; i++) { - const normalized = (dataArray[i] - 128) / 128; - sum += normalized * normalized; + const normalized = Math.abs((dataArray[i] - 128) / 128); + if (normalized > peak) { + peak = normalized; + } } - const rms = Math.sqrt(sum / dataArray.length); - setState((prev) => ({ ...prev, inputLevel: rms })); + setState((prev) => ({ ...prev, inputLevel: peak })); animationFrameRef.current = requestAnimationFrame(updateLevel); };