| import numpy as np |
| import torch |
|
|
|
|
| class SileroVAD: |
| def __init__(self, path=None): |
| from silero_vad import load_silero_vad |
| self.vad = load_silero_vad(onnx=True) |
|
|
| def reset(self): |
| self.vad.reset_states() |
|
|
| def __call__(self, chunk, sr=16000): |
| if chunk.shape[-1] not in (256, 512): |
| return 0.0 |
| t = torch.from_numpy(chunk.reshape(1, -1).astype(np.float32)) |
| return float(self.vad(t, sr)) |
|
|
|
|
| class RealtimeSession: |
| def __init__(self, vad_path, sr=16000, threshold=0.5, min_speech_ms=128, min_silence_ms=800): |
| self.vad, self.sr, self.threshold = SileroVAD(vad_path), sr, threshold |
| self.min_speech, self.min_silence = int(sr * min_speech_ms / 1000), int(sr * min_silence_ms / 1000) |
| self.reset() |
|
|
| def reset(self): |
| self.vad.reset() |
| self.buffer, self.ring, self.speaking, self.generating, self.interrupt = [], [], False, False, False |
| self.speech_samples = self.silence_samples = self.tail_silence = 0 |
|
|
| def push_chunk(self, chunk, W=512): |
| for i in range(0, max(len(chunk), 1), W): |
| w = chunk[i:i + W] |
| if len(w) < W: |
| w = np.pad(w, (0, W - len(w))) |
| prob = self.vad(w, self.sr) |
| if prob > self.threshold: |
| self.silence_samples = self.tail_silence = 0 |
| self.speech_samples += len(w) |
| self.buffer.append(w) |
| if self.speech_samples >= self.min_speech and not self.speaking: |
| self.speaking = True |
| self.buffer = self.ring + self.buffer |
| self.ring = [] |
| if self.generating and self.speaking: |
| self.interrupt = True |
| return 'interrupt' |
| elif self.speaking: |
| self.silence_samples += len(w) |
| self.tail_silence += 1 |
| self.buffer.append(w) |
| if self.silence_samples >= self.min_silence: |
| if self.tail_silence > 1: |
| del self.buffer[-(self.tail_silence - 1):] |
| self.speaking, self.speech_samples, self.silence_samples, self.tail_silence = False, 0, 0, 0 |
| return 'speech_end' |
| else: |
| if self.speech_samples > 0: |
| self.buffer.clear() |
| self.speech_samples = 0 |
| self.ring = [w] |
| return 'listening' |
|
|
| def get_audio(self): |
| audio = np.concatenate(self.buffer) if self.buffer else np.array([], dtype=np.float32) |
| self.buffer.clear() |
| return audio |
|
|