"""
Détection d'activité vocale (VAD) neuronale via Silero VAD ONNX.
Module encapsule le modèle Silero VAD v4 pour segmenter en temps réel
les trames de parole et les silences sans solliciter la mémoire GPU.
"""
import os
import urllib.request
import numpy as np
import onnxruntime
[docs]
class SileroVadDetector:
"""
Detection d'activite vocale par Silero VAD, execute sur CPU en moins d'une
milliseconde par tranche de 32 ms et sans occupation de VRAM.
Attributes
----------
modelPath : str
Chemin du fichier ONNX, telecharge au premier besoin.
sampleRate : int
Frequence d'echantillonnage attendue.
speechThreshold : float
Probabilite au-dessus de laquelle la tranche compte comme de la parole.
"""
[docs]
def __init__(
self,
modelPath: str = "/tmp/silero_vad.onnx",
sampleRate: int = 16000,
speechThreshold: float = 0.45,
):
"""
Initialise le modèle Silero VAD ONNX et prépare la session d'inférence.
Parameters
----------
modelPath : str, optional
Chemin local de stockage du fichier ONNX, default is "/tmp/silero_vad.onnx".
sampleRate : int, optional
Fréquence d'échantillonnage de l'audio en Hz, default is 16000.
speechThreshold : float, optional
Seuil de probabilité pour valider la parole, default is 0.45.
"""
self.modelPath = modelPath
self.sampleRate = sampleRate
self.speechThreshold = speechThreshold
self.session = None
self._ensureModel()
self.reset()
def _ensureModel(self) -> None:
"""
Telecharge le fichier ONNX si absent, puis ouvre la session.
Returns
-------
None
"""
if not os.path.exists(self.modelPath):
url = "https://github.com/snakers4/silero-vad/raw/master/src/silero_vad/data/silero_vad.onnx"
urllib.request.urlretrieve(url, self.modelPath)
opts = onnxruntime.SessionOptions()
opts.inter_op_num_threads = 1
opts.intra_op_num_threads = 1
self.session = onnxruntime.InferenceSession(
self.modelPath,
sess_options=opts,
providers=["CPUExecutionProvider"],
)
[docs]
def reset(self) -> None:
"""
Remet a zero l'etat recurrent du reseau.
Returns
-------
None
"""
self._state = np.zeros((2, 1, 128), dtype=np.float32)
[docs]
def predict(self, audioChunk512: np.ndarray) -> tuple[bool, float]:
"""
Évalue une tranche audio de 512 échantillons (32 ms à 16 kHz).
Parameters
----------
audioChunk512 : np.ndarray
Tranche audio mono float32 normalisée entre -1.0 et 1.0.
Returns
-------
tuple of (bool, float)
Tuple (isSpeech, confidenceProbability) où `isSpeech` est True
si la probabilité dépasse `speechThreshold`.
"""
if len(audioChunk512) != 512:
if len(audioChunk512) < 512:
audioChunk512 = np.pad(audioChunk512, (0, 512 - len(audioChunk512)))
else:
audioChunk512 = audioChunk512[:512]
ortInputs = {
"input": np.expand_dims(audioChunk512, axis=0).astype(np.float32),
"state": self._state,
"sr": np.array(self.sampleRate, dtype=np.int64),
}
out, self._state = self.session.run(None, ortInputs)
prob = float(out[0][0])
return prob > self.speechThreshold, prob