File size: 1,841 Bytes
c3eb7ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import numpy as np

from . import features, fusion, models
from .config import SR
from .schema import Result


def analyze_one(path: str) -> Result:
    y = features.load_audio(path)
    feat = features.extract(y)

    speech_segs = models.speech_segments(y)
    snr_gap_db, longest_gap_s = _gap_stats(y, speech_segs, feat.duration_s)

    ser_categorical = models.categorical_emotion(y)
    dim_emotion = models.dimensional_emotion(y)

    tags = models.noise_tags(y)
    noise_type = models.noise_type_from_tags(tags) if tags else ""
    if not noise_type and snr_gap_db is not None and snr_gap_db < 18:
        noise_type = "background noise (unspecified)"

    overlap_s = models.overlap_seconds(path)

    return fusion.fuse(
        feat=feat,
        snr_gap_db=snr_gap_db,
        ser_categorical=ser_categorical,
        dimensional_emotion=dim_emotion,
        noise_type=noise_type,
        overlap_s=overlap_s,
        longest_gap_s=longest_gap_s,
    )


def _gap_stats(
    y: np.ndarray, speech_segs: list[tuple[float, float]] | None, duration_s: float
) -> tuple[float | None, float]:
    if not speech_segs:
        return None, 0.0

    speech_mask = np.zeros(len(y), dtype=bool)
    for start_s, end_s in speech_segs:
        speech_mask[int(start_s * SR) : int(end_s * SR)] = True

    gap = y[~speech_mask]
    speech = y[speech_mask]
    gap_rms = float(np.sqrt(np.mean(gap**2))) if len(gap) else 0.0
    speech_rms = float(np.sqrt(np.mean(speech**2))) if len(speech) else 0.0
    snr_gap_db = 20 * np.log10((speech_rms + 1e-8) / (gap_rms + 1e-8))

    longest_gap_s = 0.0
    prev_end = 0.0
    for start_s, end_s in speech_segs:
        longest_gap_s = max(longest_gap_s, start_s - prev_end)
        prev_end = end_s
    longest_gap_s = max(longest_gap_s, duration_s - prev_end)

    return snr_gap_db, longest_gap_s