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:
@@ -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
|
||||
Reference in New Issue
Block a user