feat: optional noisereduce preprocessing before vosk
This commit is contained in:
parent
6a710c55b8
commit
13370da5b7
2 changed files with 34 additions and 1 deletions
|
|
@ -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
26
main.py
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue