From a4b919c11eea3316ef2e15d5a09649831b6156a6 Mon Sep 17 00:00:00 2001 From: Bossiara13 <236771060+DmitryBykov-ISPO@users.noreply.github.com> Date: Wed, 22 Apr 2026 17:46:44 +0300 Subject: [PATCH] refactor: replace porcupine wake-word with vosk grammar-constrained recognizer uses a second KaldiRecognizer with a JSON grammar of WAKE_WORDS + [unk] for the wake phase, then hands off to the full-vocab recognizer for the 10s command window. drops the pvporcupine dependency entirely. --- config.py | 10 ++++++---- main.py | 52 +++++++++++++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/config.py b/config.py index 28a9710..01f9f2c 100644 --- a/config.py +++ b/config.py @@ -6,10 +6,15 @@ load_dotenv("dev.env") # Конфигурация VA_NAME = 'Jarvis' -VA_VER = "0.1.0" +VA_VER = "0.2.0" VA_ALIAS = ('джарвис',) VA_TBR = ('скажи', 'покажи', 'ответь', 'произнеси', 'расскажи', 'сколько', 'слушай') +# Wake-words распознаются Vosk'ом с grammar-constraint. Добавляйте сюда +# варианты написания — Vosk с русской моделью может расшифровать "jarvis" +# как "джарвис", "жарвис" и т.п. +WAKE_WORDS = ('jarvis', 'джарвис') + # ID микрофона (можете просто менять ID пока при запуске не отобразится нужный) # -1 это стандартное записывающее устройство MICROPHONE_INDEX = -1 @@ -17,9 +22,6 @@ MICROPHONE_INDEX = -1 # Путь к браузеру Google Chrome CHROME_PATH = 'C:/Program Files (x86)/Google/Chrome/Application/chrome.exe %s' -# Токен Picovoice -PICOVOICE_TOKEN = os.getenv('PICOVOICE_TOKEN') - # Токен Groq GROQ_TOKEN = os.getenv('GROQ_TOKEN') GROQ_BASE_URL = "https://api.groq.com/openai/v1" diff --git a/main.py b/main.py index dc7208c..52aa60a 100644 --- a/main.py +++ b/main.py @@ -11,7 +11,6 @@ from ctypes import POINTER, cast import openai from openai import OpenAI -import pvporcupine import simpleaudio as sa import vosk import yaml @@ -39,18 +38,14 @@ message_log = [system_message] client = OpenAI(api_key=config.GROQ_TOKEN, base_url=config.GROQ_BASE_URL) -# PORCUPINE -porcupine = pvporcupine.create( - access_key=config.PICOVOICE_TOKEN, - keywords=['jarvis'], - sensitivities=[1] -) -# print(pvporcupine.KEYWORDS) - -# VOSK model = vosk.Model("model_small") samplerate = 16000 device = config.MICROPHONE_INDEX + +# wake-word phase uses a grammar-constrained recognizer so only WAKE_WORDS +# (plus [unk] filler) can match — command phase needs full vocab, hence two. +wake_grammar = json.dumps(list(config.WAKE_WORDS) + ["[unk]"], ensure_ascii=False) +wake_rec = vosk.KaldiRecognizer(model, samplerate, wake_grammar) kaldi_rec = vosk.KaldiRecognizer(model, samplerate) q = queue.Queue() @@ -268,33 +263,42 @@ def execute_cmd(cmd: str, voice: str): elif cmd == 'off': play("off", True) - - porcupine.delete() exit(0) -# `-1` is the default input audio device. -recorder = PvRecorder(device_index=config.MICROPHONE_INDEX, frame_length=porcupine.frame_length) +recorder = PvRecorder(device_index=config.MICROPHONE_INDEX, frame_length=512) recorder.start() print('Using device: %s' % recorder.selected_device) -print(f"Jarvis (v0.1.0) начал свою работу ...") +print(f"Jarvis (v{config.VA_VER}) начал свою работу ...") play("run") time.sleep(0.5) -ltc = time.time() - 1000 + +def heard_wake_word(text: str) -> bool: + if not text: + return False + lowered = text.lower() + return any(w in lowered for w in config.WAKE_WORDS) + while True: try: pcm = recorder.read() - keyword_index = porcupine.process(pcm) + sp = struct.pack("h" * len(pcm), *pcm) - if keyword_index >= 0: - recorder.stop() - play("greet", True) - print("Yes, sir.") - recorder.start() # prevent self recording - ltc = time.time() + if not wake_rec.AcceptWaveform(sp): + continue + + result_text = json.loads(wake_rec.Result()).get("text", "") + if not heard_wake_word(result_text): + continue + + play("greet", True) + print("Yes, sir.") + # reset the command recognizer so stale audio doesn't bleed into it + kaldi_rec.Reset() + ltc = time.time() while time.time() - ltc <= 10: pcm = recorder.read() @@ -306,6 +310,8 @@ while True: break + wake_rec.Reset() + except Exception as err: print(f"Unexpected {err=}, {type(err)=}") raise