Accuracy in LUFS/VU, configureable VU target

This commit is contained in:
Boof2015
2026-05-07 03:44:45 -04:00
parent d614952a15
commit 9dfd5732a4
8 changed files with 218 additions and 98 deletions
+3 -2
View File
@@ -1,6 +1,6 @@
import type { VectorscopeMode } from '../renderer/visualizers/Vectorscope'
import type { SpectrogramClarityMode, SpectrogramScaleMode } from './spectrogram'
import type { VUMeterMode, VUMeterNeedleChannels, VUMeterOrientation } from './vumeter'
import { DEFAULT_VU_REFERENCE_DBFS, type VUMeterMode, type VUMeterNeedleChannels, type VUMeterOrientation } from './vumeter'
import { DEFAULT_LUFS_METER_READOUT, type LUFSMeterMode, type LUFSMeterReadout } from './lufsmeter'
import { DEFAULT_WAVEFORM_MODE, type WaveformMode } from './waveform'
import { DEFAULT_SPECTRUM_PEAK_INFO_MODE, type SpectrumPeakInfoMode } from './spectrum'
@@ -42,6 +42,7 @@ export interface ScopeSettings {
mode: VUMeterMode
orientation: VUMeterOrientation
needleChannels: VUMeterNeedleChannels
referenceDb: number
}
lufsmeter: {
mode: LUFSMeterMode
@@ -67,7 +68,7 @@ export const DEFAULT_SCOPE_SETTINGS: ScopeSettings = {
oscilloscope: { pitchLock: true, underfillEnabled: false, showGrid: true, lineWidth: 2 },
vectorscope: { mode: 'lissajous', multiband: false, showGrid: true, persistence: 0.10, lineWidth: 1.5 },
spectrogram: { fftSize: 2048, scrollSpeed: 2, clarityMode: 'sharper', scaleMode: 'log', colorScheme: 'heat' },
vumeter: { mode: 'bar', orientation: 'horizontal', needleChannels: 'stereo' },
vumeter: { mode: 'bar', orientation: 'horizontal', needleChannels: 'stereo', referenceDb: DEFAULT_VU_REFERENCE_DBFS },
lufsmeter: { mode: 'bar', readout: DEFAULT_LUFS_METER_READOUT },
waveform: { mode: DEFAULT_WAVEFORM_MODE, scrollSpeed: 1, multiband: false },
nowPlaying: {
+34
View File
@@ -10,6 +10,27 @@ export const DEFAULT_VU_METER_MODE: VUMeterMode = 'bar'
export const DEFAULT_VU_METER_ORIENTATION: VUMeterOrientation = 'horizontal'
export const DEFAULT_VU_METER_NEEDLE_CHANNELS: VUMeterNeedleChannels = 'stereo'
// Reference-level calibration: 0 VU corresponds to this dBFS value.
export const DEFAULT_VU_REFERENCE_DBFS = -14
export const VU_REFERENCE_MIN_DBFS = -30
export const VU_REFERENCE_MAX_DBFS = 0
export interface VUReferencePreset {
readonly id: string
readonly label: string
readonly description: string
readonly dbfs: number
}
export const VU_REFERENCE_PRESETS: readonly VUReferencePreset[] = [
{ id: 'k20', label: 'K-20', description: 'SMPTE film', dbfs: -20 },
{ id: 'k18', label: 'K-18', description: 'EBU broadcast', dbfs: -18 },
{ id: 'k14', label: 'K-14', description: 'Streaming', dbfs: -14 },
{ id: 'k12', label: 'K-12', description: 'Modern music', dbfs: -12 },
{ id: 'k10', label: 'K-10', description: 'Loud masters', dbfs: -10 },
{ id: 'k6', label: 'K-6', description: 'Hot / legacy', dbfs: -6 },
]
export function isVUMeterMode(value: unknown): value is VUMeterMode {
return typeof value === 'string' && VU_METER_MODES.includes(value as VUMeterMode)
}
@@ -21,3 +42,16 @@ export function isVUMeterOrientation(value: unknown): value is VUMeterOrientatio
export function isVUMeterNeedleChannels(value: unknown): value is VUMeterNeedleChannels {
return typeof value === 'string' && VU_METER_NEEDLE_CHANNELS.includes(value as VUMeterNeedleChannels)
}
export function sanitizeVUReferenceDbfs(value: unknown): number {
if (typeof value !== 'number' || !Number.isFinite(value)) {
return DEFAULT_VU_REFERENCE_DBFS
}
if (value < VU_REFERENCE_MIN_DBFS) return VU_REFERENCE_MIN_DBFS
if (value > VU_REFERENCE_MAX_DBFS) return VU_REFERENCE_MAX_DBFS
return value
}
export function findVUReferencePreset(dbfs: number): VUReferencePreset | null {
return VU_REFERENCE_PRESETS.find((preset) => Math.abs(preset.dbfs - dbfs) < 1e-6) ?? null
}