Upload dsp.py with huggingface_hub
Browse files
dsp.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
import tempfile
|
| 4 |
import numpy as np
|
| 5 |
from pedalboard import (
|
| 6 |
-
Pedalboard, Compressor,
|
| 7 |
LowShelfFilter, HighShelfFilter, PeakFilter,
|
| 8 |
HighpassFilter,
|
| 9 |
)
|
|
@@ -14,63 +14,6 @@ from stereo import apply_stereo_width, linkwitz_riley_crossover
|
|
| 14 |
from loudness import measure_loudness, measure_true_peak, normalize_to_lufs
|
| 15 |
|
| 16 |
|
| 17 |
-
# ---------------------------------------------------------------------------
|
| 18 |
-
# Maximizer — fill headroom between LUFS target and true-peak ceiling
|
| 19 |
-
# ---------------------------------------------------------------------------
|
| 20 |
-
|
| 21 |
-
def _maximize(processed, sample_rate, target_lufs, ceiling_dbtp=-0.1,
|
| 22 |
-
target_tp_min=-1.0, max_iters=4):
|
| 23 |
-
"""Iterative gain + limit loop to push true peak into the target range.
|
| 24 |
-
|
| 25 |
-
If true peak is well below the ceiling after LUFS normalization (i.e. the
|
| 26 |
-
track has a high crest factor), this loop progressively reduces the crest
|
| 27 |
-
factor by:
|
| 28 |
-
1. Applying gain to bring peaks near the ceiling
|
| 29 |
-
2. Brick-wall limiting at the ceiling to shave the loudest peaks
|
| 30 |
-
3. Re-normalizing to the target LUFS
|
| 31 |
-
|
| 32 |
-
Each iteration reduces the peak-to-loudness ratio so that after
|
| 33 |
-
re-normalization the true peak lands closer to the ceiling.
|
| 34 |
-
|
| 35 |
-
Args:
|
| 36 |
-
processed: numpy array (samples, channels)
|
| 37 |
-
sample_rate: int
|
| 38 |
-
target_lufs: float — target integrated loudness
|
| 39 |
-
ceiling_dbtp: float — true-peak ceiling (default -0.1)
|
| 40 |
-
target_tp_min: float — minimum acceptable true peak (default -1.0)
|
| 41 |
-
max_iters: int — maximum iterations (default 4)
|
| 42 |
-
|
| 43 |
-
Returns:
|
| 44 |
-
processed (modified in-place where possible)
|
| 45 |
-
"""
|
| 46 |
-
for _ in range(max_iters):
|
| 47 |
-
tp = measure_true_peak(processed, sample_rate)
|
| 48 |
-
headroom = ceiling_dbtp - tp # positive = wasted headroom
|
| 49 |
-
|
| 50 |
-
if tp >= target_tp_min or headroom < 0.5:
|
| 51 |
-
break # already in range or close enough
|
| 52 |
-
|
| 53 |
-
# Gain up so peaks approach the ceiling (leave 0.1 dB for limiter)
|
| 54 |
-
boost_db = headroom - 0.1
|
| 55 |
-
processed = (processed * 10.0 ** (boost_db / 20.0)).astype(np.float32)
|
| 56 |
-
|
| 57 |
-
# Brick-wall limit at ceiling — fast release for transparency
|
| 58 |
-
lim_board = Pedalboard([Limiter(
|
| 59 |
-
threshold_db=ceiling_dbtp,
|
| 60 |
-
release_ms=80.0,
|
| 61 |
-
)])
|
| 62 |
-
processed = lim_board(processed.T, sample_rate).T
|
| 63 |
-
|
| 64 |
-
# Re-normalize to target LUFS (limiting raised the loudness)
|
| 65 |
-
cur_lufs = measure_loudness(processed, sample_rate)
|
| 66 |
-
if not np.isinf(cur_lufs):
|
| 67 |
-
processed = normalize_to_lufs(
|
| 68 |
-
processed, sample_rate, cur_lufs, target_lufs
|
| 69 |
-
).astype(np.float32)
|
| 70 |
-
|
| 71 |
-
return processed
|
| 72 |
-
|
| 73 |
-
|
| 74 |
# ---------------------------------------------------------------------------
|
| 75 |
# Slider-to-parameter mappings
|
| 76 |
# ---------------------------------------------------------------------------
|
|
@@ -298,12 +241,6 @@ def master_audio(input_path, lows_db, mid_boost_db, highs_db, bass_boost_db,
|
|
| 298 |
processed, sample_rate, pre_norm_lufs, target_lufs
|
| 299 |
).astype(np.float32)
|
| 300 |
|
| 301 |
-
# --- Stage 5.5: Maximizer — fill wasted headroom ---
|
| 302 |
-
# If true peak is well below -1.0 dBTP after LUFS normalization,
|
| 303 |
-
# iteratively gain up + limit to reduce crest factor so peaks land
|
| 304 |
-
# between -1.0 and -0.1 dBTP at the target LUFS.
|
| 305 |
-
processed = _maximize(processed, sample_rate, target_lufs)
|
| 306 |
-
|
| 307 |
# --- Stage 6: Soft clipper (piecewise tanh saturation) ---
|
| 308 |
# Perfectly linear below the knee; only the tips of peaks above it are
|
| 309 |
# shaped with a tanh curve that asymptotes to the ceiling. The knee
|
|
@@ -496,9 +433,6 @@ def master_audio_full(input_path, params, target_lufs):
|
|
| 496 |
processed, sample_rate, pre_norm_lufs, target_lufs
|
| 497 |
).astype(np.float32)
|
| 498 |
|
| 499 |
-
# --- Stage 5.5: Maximizer — fill wasted headroom ---
|
| 500 |
-
processed = _maximize(processed, sample_rate, target_lufs)
|
| 501 |
-
|
| 502 |
# --- Stage 6: Soft clipper (piecewise tanh saturation) ---
|
| 503 |
ceiling_lin = 10.0 ** (-0.1 / 20.0)
|
| 504 |
peak_lin = np.max(np.abs(processed))
|
|
|
|
| 3 |
import tempfile
|
| 4 |
import numpy as np
|
| 5 |
from pedalboard import (
|
| 6 |
+
Pedalboard, Compressor,
|
| 7 |
LowShelfFilter, HighShelfFilter, PeakFilter,
|
| 8 |
HighpassFilter,
|
| 9 |
)
|
|
|
|
| 14 |
from loudness import measure_loudness, measure_true_peak, normalize_to_lufs
|
| 15 |
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
# ---------------------------------------------------------------------------
|
| 18 |
# Slider-to-parameter mappings
|
| 19 |
# ---------------------------------------------------------------------------
|
|
|
|
| 241 |
processed, sample_rate, pre_norm_lufs, target_lufs
|
| 242 |
).astype(np.float32)
|
| 243 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
# --- Stage 6: Soft clipper (piecewise tanh saturation) ---
|
| 245 |
# Perfectly linear below the knee; only the tips of peaks above it are
|
| 246 |
# shaped with a tanh curve that asymptotes to the ceiling. The knee
|
|
|
|
| 433 |
processed, sample_rate, pre_norm_lufs, target_lufs
|
| 434 |
).astype(np.float32)
|
| 435 |
|
|
|
|
|
|
|
|
|
|
| 436 |
# --- Stage 6: Soft clipper (piecewise tanh saturation) ---
|
| 437 |
ceiling_lin = 10.0 ** (-0.1 / 20.0)
|
| 438 |
peak_lin = np.max(np.abs(processed))
|