amanetize's picture
Upload folder using huggingface_hub
dc98a34 verified
Raw
History Blame Contribute Delete
3.1 kB
from __future__ import annotations
import time
import numpy as np
import webrtcvad
import config
from backend.types import StageResult
_silero_model = None
_silero_utils = None
def _get_silero():
global _silero_model, _silero_utils
if _silero_model is None:
import torch
_silero_model, _silero_utils = torch.hub.load(repo_or_dir='snakers4/silero-vad', model='silero_vad', trust_repo=True)
return (_silero_model, _silero_utils)
def _float_audio(audio: np.ndarray) -> np.ndarray:
audio = np.asarray(audio)
if audio.dtype.kind == 'i':
return audio.astype(np.float32) / 32768.0
return audio.astype(np.float32)
def _pcm16_bytes(audio_f32: np.ndarray) -> bytes:
clipped = np.clip(audio_f32, -1.0, 1.0)
return (clipped * 32767.0).astype(np.int16).tobytes()
def run_webrtcvad(audio: np.ndarray, sample_rate: int=config.SAMPLE_RATE, aggressiveness: int=2) -> StageResult:
start = time.perf_counter()
audio_f32 = _float_audio(audio)
pcm = _pcm16_bytes(audio_f32)
frame_ms = 30
frame_bytes = int(sample_rate * (frame_ms / 1000.0)) * 2
vad = webrtcvad.Vad(aggressiveness)
speech_frames = 0
total_frames = 0
for offset in range(0, len(pcm) - frame_bytes + 1, frame_bytes):
frame = pcm[offset:offset + frame_bytes]
total_frames += 1
if vad.is_speech(frame, sample_rate):
speech_frames += 1
speech_detected = total_frames > 0 and speech_frames / total_frames > 0.1
timing_ms = (time.perf_counter() - start) * 1000
return StageResult(stage='gate.webrtcvad', timing_ms=timing_ms, output={'speech_detected': speech_detected, 'speech_frame_ratio': speech_frames / total_frames if total_frames else 0.0}, available=True, provenance='real_checkpoint')
def run_silero(audio: np.ndarray, sample_rate: int=config.SAMPLE_RATE, threshold: float=0.5) -> StageResult:
import torch
start = time.perf_counter()
model, utils = _get_silero()
get_speech_timestamps = utils[0]
audio_f32 = _float_audio(audio)
tensor = torch.from_numpy(audio_f32)
timestamps = get_speech_timestamps(tensor, model, sampling_rate=sample_rate, threshold=threshold)
speech_detected = len(timestamps) > 0
timing_ms = (time.perf_counter() - start) * 1000
return StageResult(stage='gate.silero_vad', timing_ms=timing_ms, output={'speech_detected': speech_detected, 'speech_segments': timestamps}, available=True, provenance='real_checkpoint')
def run_none(audio: np.ndarray, sample_rate: int=config.SAMPLE_RATE) -> StageResult:
return StageResult(stage='gate.none', timing_ms=0.0, output={'speech_detected': True}, available=True, provenance='rule')
def run(gate: str, audio: np.ndarray, sample_rate: int=config.SAMPLE_RATE, **kwargs) -> StageResult:
if gate == 'webrtcvad':
return run_webrtcvad(audio, sample_rate, aggressiveness=kwargs.get('vad_aggressiveness', 2))
if gate == 'silero_vad':
return run_silero(audio, sample_rate)
if gate == 'none':
return run_none(audio, sample_rate)
raise ValueError(f'unknown gate: {gate!r}')