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