File size: 3,026 Bytes
2b668de | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | """
===============================================================================
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
|