Commit ·
1afc0fb
1
Parent(s): 03cbad9
Fix crossfade loudness bump: apply db_boost to overlap as a whole
Browse filesApplying gain to each side independently (old code) caused a ~+3 dB
loudness spike at every segment seam. Equal-power crossfade (cos/sin)
already sums to 1.0 at the midpoint; the boost should be applied to
the blended overlap as a unit, not to each fade arm separately.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
app.py
CHANGED
|
@@ -260,7 +260,12 @@ def _build_segments(total_dur_s: float, window_s: float, crossfade_s: float) ->
|
|
| 260 |
def _cf_join(a: np.ndarray, b: np.ndarray,
|
| 261 |
crossfade_s: float, db_boost: float, sr: int) -> np.ndarray:
|
| 262 |
"""Equal-power crossfade join. Works for both mono (T,) and stereo (C, T) arrays.
|
| 263 |
-
Stereo arrays are expected in (channels, samples) layout.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
stereo = a.ndim == 2
|
| 265 |
n_a = a.shape[1] if stereo else len(a)
|
| 266 |
n_b = b.shape[1] if stereo else len(b)
|
|
@@ -272,10 +277,11 @@ def _cf_join(a: np.ndarray, b: np.ndarray,
|
|
| 272 |
fade_out = np.cos(t * np.pi / 2) # 1 → 0
|
| 273 |
fade_in = np.sin(t * np.pi / 2) # 0 → 1
|
| 274 |
if stereo:
|
| 275 |
-
|
|
|
|
| 276 |
return np.concatenate([a[:, :-cf], overlap, b[:, cf:]], axis=1)
|
| 277 |
else:
|
| 278 |
-
overlap = a[-cf:] * fade_out
|
| 279 |
return np.concatenate([a[:-cf], overlap, b[cf:]])
|
| 280 |
|
| 281 |
|
|
|
|
| 260 |
def _cf_join(a: np.ndarray, b: np.ndarray,
|
| 261 |
crossfade_s: float, db_boost: float, sr: int) -> np.ndarray:
|
| 262 |
"""Equal-power crossfade join. Works for both mono (T,) and stereo (C, T) arrays.
|
| 263 |
+
Stereo arrays are expected in (channels, samples) layout.
|
| 264 |
+
|
| 265 |
+
db_boost is applied to the overlap region as a whole (after blending), so
|
| 266 |
+
it compensates for the -3 dB equal-power dip without doubling amplitude.
|
| 267 |
+
Applying gain to each side independently (the common mistake) causes a
|
| 268 |
+
+3 dB loudness bump at the seam — this version avoids that."""
|
| 269 |
stereo = a.ndim == 2
|
| 270 |
n_a = a.shape[1] if stereo else len(a)
|
| 271 |
n_b = b.shape[1] if stereo else len(b)
|
|
|
|
| 277 |
fade_out = np.cos(t * np.pi / 2) # 1 → 0
|
| 278 |
fade_in = np.sin(t * np.pi / 2) # 0 → 1
|
| 279 |
if stereo:
|
| 280 |
+
# Blend first, then apply boost to the overlap region as a unit
|
| 281 |
+
overlap = (a[:, -cf:] * fade_out + b[:, :cf] * fade_in) * gain
|
| 282 |
return np.concatenate([a[:, :-cf], overlap, b[:, cf:]], axis=1)
|
| 283 |
else:
|
| 284 |
+
overlap = (a[-cf:] * fade_out + b[:cf] * fade_in) * gain
|
| 285 |
return np.concatenate([a[:-cf], overlap, b[cf:]])
|
| 286 |
|
| 287 |
|