44 lines
1.3 KiB
Python
44 lines
1.3 KiB
Python
"""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
|