D1: LUFS EBU R128 + true peak sau render (app/core/loudness.py), API /plugins/render tra them lufs/true_peak_db, test 4 cases

This commit is contained in:
2026-08-22 21:50:29 +07:00
parent 2a5e2af04a
commit fe82b19557
3 changed files with 149 additions and 1 deletions
+43
View File
@@ -0,0 +1,43 @@
"""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