fix(audio): raise trim threshold, cap pad at net-neutral

Review feedback on the preprocessor:

1. ``trim_top_db=30`` was labelled "conservative" in the docstring but is
   actually *more* aggressive than librosa's default of 60. Normal
   speech dynamic range sits around 30 dB, so 30 dB would eat quiet
   trailing syllables and soft consonants. Raise the default to 40 dB —
   below normal speech dynamic range but still catching obvious edge
   silence — and fix the docstring.

2. Unconditional 100 ms edge padding ran even when ``librosa.effects.trim``
   removed nothing. For a well-recorded 29.9 s upload that path would
   push the waveform past the 30 s ceiling and trigger a spurious "too
   long" rejection. Only pad when trimming actually shortened the
   audio, and cap the pad so the output never exceeds the input length.

Adds a regression test for the net-neutral length behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
James Pine
2026-04-19 18:25:52 -07:00
co-authored by Claude Opus 4.7
parent a49cc6afbb
commit 4ffbc03d15
2 changed files with 25 additions and 6 deletions
+9
View File
@@ -58,6 +58,15 @@ def test_silence_is_trimmed_with_padding_kept():
assert len(out) >= int(3.0 * SR), "speech body should be preserved"
def test_clean_audio_is_not_padded_past_original_length():
# Well-recorded audio with no edge silence shouldn't get longer after
# preprocessing — otherwise a 29.9 s upload could be pushed past the
# 30 s max_duration ceiling downstream.
audio = _tone(3.0, amp=0.3)
out = preprocess_reference_audio(audio, SR)
assert len(out) <= len(audio)
def test_empty_input_returns_empty():
out = preprocess_reference_audio(np.zeros(0, dtype=np.float32), SR)
assert out.size == 0
+16 -6
View File
@@ -203,7 +203,7 @@ def preprocess_reference_audio(
audio: np.ndarray,
sample_rate: int,
peak_target: float = 0.95,
trim_top_db: float = 30.0,
trim_top_db: float = 40.0,
edge_padding_ms: int = 100,
) -> np.ndarray:
"""
@@ -221,9 +221,14 @@ def preprocess_reference_audio(
peak_target: Peak amplitude cap in [0, 1]. Applied only if the input
peak exceeds this value.
trim_top_db: Silence threshold for edge trimming, in dB below peak.
Conservative (30 dB) so soft speech at the edges isn't clipped off.
edge_padding_ms: Milliseconds of padding retained at each edge after
trimming, so TTS engines have a brief silence to anchor on.
40 dB sits below normal speech dynamic range (≈30 dB) so soft
trailing syllables are preserved, while still catching obvious
leading/trailing silence. Lower values are more aggressive;
librosa's own default is 60.
edge_padding_ms: Milliseconds of padding to add back at each edge
*only if* trimming shortened the waveform, so TTS engines have a
brief silence to anchor on without ever making the output longer
than the input.
Returns:
Preprocessed audio array (float32).
@@ -236,8 +241,13 @@ def preprocess_reference_audio(
audio = audio - float(np.mean(audio))
trimmed, _ = librosa.effects.trim(audio, top_db=trim_top_db)
if trimmed.size > 0:
pad = int(sample_rate * edge_padding_ms / 1000)
if 0 < trimmed.size < audio.size:
pad_each = int(sample_rate * edge_padding_ms / 1000)
# Never pad past the original length — for near-max-duration uploads
# an unconditional pad would push them over the 30 s ceiling and
# trigger a spurious "too long" rejection.
headroom = (audio.size - trimmed.size) // 2
pad = min(pad_each, max(headroom, 0))
if pad > 0:
trimmed = np.pad(trimmed, (pad, pad), mode="constant")
audio = trimmed