"""D1: EBU R128 loudness + true peak metrics (app/core/loudness.py).""" import numpy as np from app.core import loudness def _sine(dbfs, sr=44100, sec=3.0, freq=1000.0): t = np.arange(int(sr * sec)) / sr x = (10 ** (dbfs / 20.0)) * np.sin(2 * np.pi * freq * t) return np.stack([x, x]) def test_sine_minus20_lufs(): # 1 kHz sine @ -20 dBFS: RMS -23.01 dB + K-weight +0.44 dB - 0.691 offset # => ~ -23.3 LUFS (0 dBFS sine ~ -3.7 LUFS per EBU R128). audio = _sine(-20.0) lufs = loudness.integrated_lufs(audio, 44100) assert -24.5 < lufs < -22.0, lufs tp = loudness.true_peak_db(audio, 44100) assert abs(tp - (-20.0)) < 0.2, tp def test_true_peak_inter_sample_overshoot(): # Square-ish: sample peak 0 dBFS, oversampled true peak > 0 dBTP. sr = 44100 x = np.repeat([1.0, 1.0, -1.0, -1.0], int(sr * 0.125)) audio = np.stack([x, x]) assert loudness.true_peak_db(audio, sr) > 0.05 def test_silence_inf(): audio = np.zeros((2, 44100)) assert loudness.integrated_lufs(audio, 44100) == -np.inf assert loudness.true_peak_db(audio, 44100) == -np.inf def test_mono_and_stereo_same(): sr = 44100 t = np.arange(sr * 2) / sr x = 0.2 * np.sin(2 * np.pi * 440 * t) mono = loudness.integrated_lufs(x, sr) stereo = loudness.integrated_lufs(np.stack([x, x]), sr) assert abs(mono - stereo) < 1e-6