autoace / src /pipeline.py
edwixx's picture
Deploy AutoAce voice tone dashboard
c3eb7ea verified
Raw
History Blame Contribute Delete
1.84 kB
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