feat: implement professional logarithmic dB scale for level meters
Converted level meters from linear to logarithmic (dB) scale to match professional audio software behavior and human hearing. The Problem: - Linear scale (0-100%) doesn't match perceived loudness - Doesn't match professional DAW meter behavior - Half-volume audio appears at 50% but sounds much quieter - No industry-standard dB reference The Solution: - Convert linear amplitude to dB: 20 * log10(linear) - Normalize -60dB to 0dB range to 0-100% display - Matches professional audio metering standards dB Scale Mapping: 0 dB (linear 1.0) = 100% (full scale, clipping) -6 dB (linear ~0.5) = 90% (loud) -12 dB (linear ~0.25) = 80% (normal) -20 dB (linear ~0.1) = 67% (moderate) -40 dB (linear ~0.01) = 33% (quiet) -60 dB (linear ~0.001) = 0% (silence threshold) Implementation: - Added linearToDbScale() function to both hooks - useMultiTrackPlayer: playback level monitoring - useRecording: input level monitoring - Formula: (dB - minDb) / (maxDb - minDb) - Range: -60dB (min) to 0dB (max) Benefits: ✅ Professional audio metering standards ✅ Matches human perception of loudness ✅ Consistent with DAWs (Pro Tools, Logic, Ableton) ✅ Better visual feedback for mixing/mastering ✅ More responsive in useful range (-20dB to 0dB) Now properly mastered tracks will show levels in the 90-100% range, matching what you'd see in professional software. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,23 @@ export function useMultiTrackPlayer(tracks: Track[], masterVolume: number = 1) {
|
||||
setDuration(maxDuration);
|
||||
}, [tracks]);
|
||||
|
||||
// Convert linear amplitude to dB scale normalized to 0-1 range
|
||||
const linearToDbScale = (linear: number): number => {
|
||||
if (linear === 0) return 0;
|
||||
|
||||
// Convert to dB (20 * log10(linear))
|
||||
const db = 20 * Math.log10(linear);
|
||||
|
||||
// Normalize -60dB to 0dB range to 0-1
|
||||
// -60dB or lower = 0%, 0dB = 100%
|
||||
const minDb = -60;
|
||||
const maxDb = 0;
|
||||
const normalized = (db - minDb) / (maxDb - minDb);
|
||||
|
||||
// Clamp to 0-1 range
|
||||
return Math.max(0, Math.min(1, normalized));
|
||||
};
|
||||
|
||||
// Monitor playback levels for all tracks
|
||||
const monitorPlaybackLevels = useCallback(() => {
|
||||
if (!isMonitoringLevelsRef.current || analyserNodesRef.current.length === 0) return;
|
||||
@@ -73,7 +90,8 @@ export function useMultiTrackPlayer(tracks: Track[], masterVolume: number = 1) {
|
||||
}
|
||||
}
|
||||
|
||||
levels[track.id] = peak;
|
||||
// Convert linear peak to logarithmic dB scale
|
||||
levels[track.id] = linearToDbScale(peak);
|
||||
});
|
||||
|
||||
setTrackLevels(levels);
|
||||
|
||||
@@ -66,6 +66,23 @@ export function useRecording(): UseRecordingReturn {
|
||||
selectedDeviceIdRef.current = deviceId;
|
||||
}, []);
|
||||
|
||||
// Convert linear amplitude to dB scale normalized to 0-1 range
|
||||
const linearToDbScale = React.useCallback((linear: number): number => {
|
||||
if (linear === 0) return 0;
|
||||
|
||||
// Convert to dB (20 * log10(linear))
|
||||
const db = 20 * Math.log10(linear);
|
||||
|
||||
// Normalize -60dB to 0dB range to 0-1
|
||||
// -60dB or lower = 0%, 0dB = 100%
|
||||
const minDb = -60;
|
||||
const maxDb = 0;
|
||||
const normalized = (db - minDb) / (maxDb - minDb);
|
||||
|
||||
// Clamp to 0-1 range
|
||||
return Math.max(0, Math.min(1, normalized));
|
||||
}, []);
|
||||
|
||||
// Monitor input level
|
||||
const monitorInputLevel = React.useCallback(() => {
|
||||
if (!analyserRef.current) return;
|
||||
@@ -87,13 +104,16 @@ export function useRecording(): UseRecordingReturn {
|
||||
}
|
||||
}
|
||||
|
||||
setState((prev) => ({ ...prev, inputLevel: peak }));
|
||||
// Convert linear peak to logarithmic dB scale
|
||||
const dbLevel = linearToDbScale(peak);
|
||||
|
||||
setState((prev) => ({ ...prev, inputLevel: dbLevel }));
|
||||
|
||||
animationFrameRef.current = requestAnimationFrame(updateLevel);
|
||||
};
|
||||
|
||||
updateLevel();
|
||||
}, []);
|
||||
}, [linearToDbScale]);
|
||||
|
||||
// Start recording
|
||||
const startRecording = React.useCallback(async (): Promise<void> => {
|
||||
|
||||
Reference in New Issue
Block a user