feat: optional noisereduce preprocessing before vosk

This commit is contained in:
Bossiara13 2026-04-23 10:54:20 +03:00
parent 6a710c55b8
commit 13370da5b7
2 changed files with 34 additions and 1 deletions

View file

@ -47,3 +47,10 @@ TTS_PITCH_SEMITONES = 0
# фраза (без алиасов и vа_tbr); 0.45 эмпирически отделяет валидные команды # фраза (без алиасов и vа_tbr); 0.45 эмпирически отделяет валидные команды
# от шума, оставляя запас на разговорные перефразировки. # от шума, оставляя запас на разговорные перефразировки.
INTENT_SIMILARITY_THRESHOLD = 0.45 INTENT_SIMILARITY_THRESHOLD = 0.45
# Шумоподавление аудио перед Vosk на этапе захвата команды (после wake-word).
# Спектральное гейтирование через noisereduce; даёт небольшую латентность,
# поэтому выключено по умолчанию и применяется только к буферу команды.
DENOISE_ENABLED = False
DENOISE_PROP = 0.85
DENOISE_STATIONARY = False

26
main.py
View file

@ -10,6 +10,7 @@ import time
import webbrowser import webbrowser
from ctypes import POINTER, cast from ctypes import POINTER, cast
import numpy as np
import openai import openai
from openai import OpenAI from openai import OpenAI
import simpleaudio as sa import simpleaudio as sa
@ -64,6 +65,25 @@ vad = webrtcvad.Vad(config.VAD_AGGRESSIVENESS)
intent_classifier = IntentClassifier() intent_classifier = IntentClassifier()
intent_classifier.prime({c: v['phrases'] for c, v in VA_CMD_LIST.items()}) intent_classifier.prime({c: v['phrases'] for c, v in VA_CMD_LIST.items()})
if config.DENOISE_ENABLED:
import noisereduce as nr
else:
nr = None
def denoise_pcm(raw: bytes) -> bytes:
if not raw or nr is None:
return raw
samples = np.frombuffer(raw, dtype=np.int16).astype(np.float32) / 32768.0
reduced = nr.reduce_noise(
y=samples,
sr=samplerate,
prop_decrease=config.DENOISE_PROP,
stationary=config.DENOISE_STATIONARY,
)
clipped = np.clip(reduced * 32768.0, -32768, 32767).astype(np.int16)
return clipped.tobytes()
def gpt_answer(): def gpt_answer():
global message_log global message_log
@ -294,6 +314,7 @@ while True:
silence_ms = 0 silence_ms = 0
vad_buf = b"" vad_buf = b""
finalize = False finalize = False
cmd_buf = b"" if config.DENOISE_ENABLED else None
while True: while True:
elapsed_ms = (time.time() - listen_started) * 1000 elapsed_ms = (time.time() - listen_started) * 1000
@ -302,7 +323,10 @@ while True:
pcm = recorder.read() pcm = recorder.read()
sp = struct.pack("h" * len(pcm), *pcm) sp = struct.pack("h" * len(pcm), *pcm)
if cmd_buf is None:
kaldi_rec.AcceptWaveform(sp) kaldi_rec.AcceptWaveform(sp)
else:
cmd_buf += sp
vad_buf += sp vad_buf += sp
while len(vad_buf) >= VAD_FRAME_BYTES: while len(vad_buf) >= VAD_FRAME_BYTES:
@ -321,6 +345,8 @@ while True:
break break
if finalize or speech_ms > 0: if finalize or speech_ms > 0:
if cmd_buf is not None:
kaldi_rec.AcceptWaveform(denoise_pcm(cmd_buf))
text = json.loads(kaldi_rec.FinalResult()).get("text", "") text = json.loads(kaldi_rec.FinalResult()).get("text", "")
if text: if text:
va_respond(text) va_respond(text)