JsonDONSON's picture
Upload 3 files
2b668de verified
Raw
History Blame Contribute Delete
3.03 kB
"""
===============================================================================
features/padding.py — Fixed-Size Spectrogram Padding / Trimming
===============================================================================
"""
import numpy as np
# we will use (top_db=40), trimmed clips average ~8.5 to 9.0 seconds.
# Calculation: 9.0 seconds * 16000 Hz / 512 hop_length = ~281 frames.
DEFAULT_FIXED_FRAMES = 281
# Padding strategy. Options: "constant" (fill with a number) or "edge" (repeat last frame)
PAD_MODE = "constant"
# Log-mel spectrograms use negative numbers for silence (e.g., -80.0).
# Padding with absolute 0.0 might accidentally represent maximum volume.
# Setting this to True forces the pad to use the quietest part of the current file.
USE_MIN_VALUE = True
CONSTANT_PAD_VALUE = 0.0
def pad_or_trim_spectrogram(mel_spec, target_length=None):
"""
Pad or trim a mel spectrogram to a fixed number of time frames.
Notes
-----
- Zero-padding doesn't add energy — padded regions are "silence" in the
spectrogram and the CNN will learn to ignore them.
- Trimming from the right assumes the important machine sound is at the
beginning. After silence removal, this is guaranteed.
"""
if target_length is None:
target_length = DEFAULT_FIXED_FRAMES
n_mels, current_length = mel_spec.shape
if current_length < target_length:
# --- PAD: Add values on the right ---
pad_width = target_length - current_length
if PAD_MODE == "constant":
# Decide what "silence" means mathematically
# so here I try to use the lowest possible sound in the current spectrogram as the padding value, which is more realistic than using a fixed constant like 0.0 that might not represent silence in log-mel space.
pad_val = mel_spec.min() if USE_MIN_VALUE else CONSTANT_PAD_VALUE
mel_spec = np.pad(
mel_spec,
pad_width=((0, 0), (0, pad_width)), # Only pad the time axis, not the mel axis
mode="constant",
constant_values=pad_val
)
else:
# Fallback for other numpy pad modes "edge"
# so in this method we just repeat the last frame of the spectrogram, which is a common padding strategy that doesn't introduce new values but extends the existing pattern.
# This can be useful if the end of the spectrogram contains relevant information that we want to preserve.
# also depending on the CNN we need to try and error for this as in the CNN it might see the sudden
# silience as a FEATURE
mel_spec = np.pad(
mel_spec,
pad_width=((0, 0), (0, pad_width)),
mode=PAD_MODE
)
elif current_length > target_length:
# --- TRIM: Cut from the right ---
mel_spec = mel_spec[:, :target_length]
return mel_spec