Release v0.2.0 — VAD + YAML plugin commands
This commit is contained in:
commit
f45ac77a2e
5 changed files with 374 additions and 244 deletions
43
README.md
43
README.md
|
|
@ -49,10 +49,51 @@ python main.py
|
|||
## Использование
|
||||
|
||||
1. Скажите **«Джарвис»** (или **«jarvis»**) — ассистент даст звуковой сигнал готовности.
|
||||
2. В течение 10 секунд произнесите команду:
|
||||
2. Произнесите команду — окно записи закрывается само, как только вы замолчите (см. ниже про VAD):
|
||||
- либо одну из фраз из `commands.yaml` (открыть браузер, выключить звук и т.д.);
|
||||
- либо начните со слова **«скажи …»** — остаток фразы уйдёт в Groq, ответ будет зачитан вслух.
|
||||
|
||||
## VAD (определение конца команды)
|
||||
|
||||
Окно записи команды закрывается по голосовой активности (webrtcvad), а не по таймеру. Параметры в `config.py`:
|
||||
|
||||
- `VAD_AGGRESSIVENESS` (0..3) — насколько агрессивно VAD режет шум; по умолчанию 2.
|
||||
- `COMMAND_END_SILENCE_MS` — длительность тишины, после которой окно закрывается (по умолчанию 1200 мс).
|
||||
- `COMMAND_MIN_SPEECH_MS` — минимальная длительность речи, иначе тишина не считается концом (500 мс).
|
||||
- `COMMAND_MIN_LISTEN_MS` — минимальное время записи, чтобы не закрыться мгновенно (1000 мс).
|
||||
- `COMMAND_MAX_LISTEN_MS` — жёсткий потолок (15000 мс).
|
||||
|
||||
## Схема commands.yaml
|
||||
|
||||
Каждая команда — это пара `phrases` + `action`:
|
||||
|
||||
```yaml
|
||||
open_browser:
|
||||
phrases:
|
||||
- открой браузер
|
||||
- запусти браузер
|
||||
action: {type: exe, file: "Run browser.exe"}
|
||||
```
|
||||
|
||||
Типы действий:
|
||||
|
||||
- `exe` — запустить `.exe` из `custom-commands/`. Поля: `file` (имя файла), необязательные `wait: true` (ждать завершения), `delay_ms` (пауза после запуска). По умолчанию после `exe` проигрывается звук подтверждения `ok` — отключается через `confirm_sound: null`.
|
||||
- `play_sound` — проиграть WAV из `sound/`. Поле `name` (без расширения). Имена `greet` и `ok` проигрываются с рандомным выбором из вариантов `greet1/2/3.wav`, `ok1/2/3.wav`.
|
||||
- `system` — встроенные операции. `op: volume_mute`, `volume_unmute`, `exit`.
|
||||
- `multi` — последовательность шагов: `steps: [...]`.
|
||||
|
||||
Шаги внутри `multi` — тех же типов. Пример:
|
||||
|
||||
```yaml
|
||||
sound_off:
|
||||
phrases: [выключи звук, отключи звук]
|
||||
action:
|
||||
type: multi
|
||||
steps:
|
||||
- {type: play_sound, name: ok}
|
||||
- {type: system, op: volume_mute}
|
||||
```
|
||||
|
||||
## Лицензия
|
||||
|
||||
CC BY-NC-SA 4.0 — см. [LICENSE.txt](LICENSE.txt). Авторство оригинала — Abraham Tugalov / Priler. Изменения в этом форке — Bossiara13 (Dmitry Bykov), 2026.
|
||||
|
|
|
|||
386
commands.yaml
386
commands.yaml
|
|
@ -1,157 +1,239 @@
|
|||
gaming_mode_off:
|
||||
- рабочий режим
|
||||
- вернись в рабочий режим
|
||||
- переключись на компьютер
|
||||
- обратно на компьютер
|
||||
- отключи игровой режим
|
||||
- выйди из игрового режима
|
||||
- вернись на компьютер
|
||||
- выход с игрового режима
|
||||
- рабочее пространство
|
||||
- вернись в игровой режим
|
||||
- выключи игровой режим
|
||||
gaming_mode_on:
|
||||
- включи игровой режим
|
||||
- перейди в игровой режим
|
||||
- я хочу поиграть
|
||||
- переключись на телевизор
|
||||
music:
|
||||
- давай музыку
|
||||
- музыка
|
||||
- яндекс музыка
|
||||
- включи музыку
|
||||
- открой музыку
|
||||
- послушаем музыку
|
||||
- хочу музыку
|
||||
- врубай музло
|
||||
- включи что-то послушать
|
||||
- давай что-то послушаем
|
||||
music_next:
|
||||
- следующий трек
|
||||
- следующая мелодия
|
||||
- следующая песня
|
||||
- переключи трек
|
||||
- переключи музыку
|
||||
- переключи мелодию
|
||||
- переключи песню
|
||||
- следующая музыка
|
||||
music_off:
|
||||
- выключи музыку
|
||||
- отруби музыку
|
||||
- убери музыку
|
||||
- отключи музыку
|
||||
- закрой музыку
|
||||
- останови музыку
|
||||
- хватит музыки
|
||||
music_prev:
|
||||
- предыдущая музыка
|
||||
- предыдущий трек
|
||||
- предыдущая мелодия
|
||||
- предыдущая песня
|
||||
- верни трек
|
||||
- верни мелодию
|
||||
- верни песню
|
||||
- верни прошлый трек
|
||||
- верни прошлую мелодию
|
||||
- верни прошлую песню
|
||||
- верни прошлую музыку
|
||||
- верни то что играло
|
||||
- давай предыдущий трек
|
||||
- переключи музыку обратно
|
||||
- переключи на прошлый трек
|
||||
- переключи на прошлую мелодию
|
||||
- переключи на прошлую песню
|
||||
music_save:
|
||||
- сохрани трек
|
||||
- мне нравится трек
|
||||
- сохрани песню
|
||||
- мне нравится песня
|
||||
- добавь трек в избранное
|
||||
- добавь песню в избранное
|
||||
- запомни мелодию
|
||||
- запомни трек
|
||||
- запомни песню
|
||||
- добавь мелодию в избранное
|
||||
- сохрани то что сейчас играет
|
||||
- добавь то что сейчас играет в избранное
|
||||
- лайкни трек
|
||||
- лайкни мелодию
|
||||
- лайкни песню
|
||||
'off':
|
||||
- выключись
|
||||
- вырубись
|
||||
- завершить работу
|
||||
- закройся
|
||||
- отключись
|
||||
- заверши свою работу
|
||||
- на сегодня хватит
|
||||
- выгрузи себя из памяти
|
||||
- ты мне надоел
|
||||
- пора спать
|
||||
open_browser:
|
||||
- открой браузер
|
||||
- запусти браузер
|
||||
- открой гугл хром
|
||||
- гугл хром
|
||||
open_google:
|
||||
- открой гугл
|
||||
- гугл
|
||||
- запусти гугл
|
||||
phrases:
|
||||
- открой браузер
|
||||
- запусти браузер
|
||||
- открой гугл хром
|
||||
- гугл хром
|
||||
action: {type: exe, file: "Run browser.exe"}
|
||||
|
||||
open_youtube:
|
||||
- открой ютуб
|
||||
- ютуб
|
||||
- запусти ютуб
|
||||
phrases:
|
||||
- открой ютуб
|
||||
- ютуб
|
||||
- запусти ютуб
|
||||
action: {type: exe, file: "Run youtube.exe"}
|
||||
|
||||
open_google:
|
||||
phrases:
|
||||
- открой гугл
|
||||
- гугл
|
||||
- запусти гугл
|
||||
action: {type: exe, file: "Run google.exe"}
|
||||
|
||||
music:
|
||||
phrases:
|
||||
- давай музыку
|
||||
- музыка
|
||||
- яндекс музыка
|
||||
- включи музыку
|
||||
- открой музыку
|
||||
- послушаем музыку
|
||||
- хочу музыку
|
||||
- врубай музло
|
||||
- включи что-то послушать
|
||||
- давай что-то послушаем
|
||||
action: {type: exe, file: "Run music.exe"}
|
||||
|
||||
music_off:
|
||||
phrases:
|
||||
- выключи музыку
|
||||
- отруби музыку
|
||||
- убери музыку
|
||||
- отключи музыку
|
||||
- закрой музыку
|
||||
- останови музыку
|
||||
- хватит музыки
|
||||
action: {type: exe, file: "Stop music.exe", delay_ms: 200}
|
||||
|
||||
music_save:
|
||||
phrases:
|
||||
- сохрани трек
|
||||
- мне нравится трек
|
||||
- сохрани песню
|
||||
- мне нравится песня
|
||||
- добавь трек в избранное
|
||||
- добавь песню в избранное
|
||||
- запомни мелодию
|
||||
- запомни трек
|
||||
- запомни песню
|
||||
- добавь мелодию в избранное
|
||||
- сохрани то что сейчас играет
|
||||
- добавь то что сейчас играет в избранное
|
||||
- лайкни трек
|
||||
- лайкни мелодию
|
||||
- лайкни песню
|
||||
action: {type: exe, file: "Save music.exe", delay_ms: 200}
|
||||
|
||||
music_next:
|
||||
phrases:
|
||||
- следующий трек
|
||||
- следующая мелодия
|
||||
- следующая песня
|
||||
- переключи трек
|
||||
- переключи музыку
|
||||
- переключи мелодию
|
||||
- переключи песню
|
||||
- следующая музыка
|
||||
action: {type: exe, file: "Next music.exe", delay_ms: 200}
|
||||
|
||||
music_prev:
|
||||
phrases:
|
||||
- предыдущая музыка
|
||||
- предыдущий трек
|
||||
- предыдущая мелодия
|
||||
- предыдущая песня
|
||||
- верни трек
|
||||
- верни мелодию
|
||||
- верни песню
|
||||
- верни прошлый трек
|
||||
- верни прошлую мелодию
|
||||
- верни прошлую песню
|
||||
- верни прошлую музыку
|
||||
- верни то что играло
|
||||
- давай предыдущий трек
|
||||
- переключи музыку обратно
|
||||
- переключи на прошлый трек
|
||||
- переключи на прошлую мелодию
|
||||
- переключи на прошлую песню
|
||||
action: {type: exe, file: "Prev music.exe", delay_ms: 200}
|
||||
|
||||
sound_off:
|
||||
- выключи звук
|
||||
- беззвучный режим
|
||||
- режим без звука
|
||||
- отключи звук
|
||||
phrases:
|
||||
- выключи звук
|
||||
- беззвучный режим
|
||||
- режим без звука
|
||||
- отключи звук
|
||||
action:
|
||||
type: multi
|
||||
steps:
|
||||
- {type: play_sound, name: ok}
|
||||
- {type: system, op: volume_mute}
|
||||
|
||||
sound_on:
|
||||
- включи звук
|
||||
- режим со звуком
|
||||
- верни звук
|
||||
stupid:
|
||||
- ты дурак
|
||||
- ты дебил
|
||||
- ты глупый
|
||||
- ты тупой
|
||||
switch_to_dynamics:
|
||||
- переключи на динамики
|
||||
- включи динамики
|
||||
- перейди на динамики
|
||||
- давай звук в динамики
|
||||
- давай звук на динамики
|
||||
- переключи звук в динамики
|
||||
- переключи звук на динамики
|
||||
- переключи на колонки
|
||||
- включи колонки
|
||||
- перейди на колонки
|
||||
- давай звук в колонки
|
||||
- давай звук на колонки
|
||||
- переключи звук в колонки
|
||||
- переключи звук на колонки
|
||||
switch_to_headphones:
|
||||
- переключи на наушники
|
||||
- включи наушники
|
||||
- перейди на наушники
|
||||
- давай звук в наушники
|
||||
- давай звук на наушники
|
||||
- переключи звук в наушники
|
||||
- переключи звук на наушники
|
||||
- переключи на кракены
|
||||
- включи кракены
|
||||
- перейди на кракены
|
||||
- давай звук в кракены
|
||||
- давай звук на кракены
|
||||
- переключи звук в кракены
|
||||
- переключи звук на кракены
|
||||
phrases:
|
||||
- включи звук
|
||||
- режим со звуком
|
||||
- верни звук
|
||||
action:
|
||||
type: multi
|
||||
steps:
|
||||
- {type: system, op: volume_unmute}
|
||||
- {type: play_sound, name: ok}
|
||||
|
||||
thanks:
|
||||
- спасибо
|
||||
- молодец
|
||||
- респект
|
||||
- ты супер
|
||||
- отличная работа
|
||||
- ты крут
|
||||
- ты большой молодец
|
||||
- ты реально крут
|
||||
- ты афигенный
|
||||
phrases:
|
||||
- спасибо
|
||||
- молодец
|
||||
- респект
|
||||
- ты супер
|
||||
- отличная работа
|
||||
- ты крут
|
||||
- ты большой молодец
|
||||
- ты реально крут
|
||||
- ты афигенный
|
||||
action: {type: play_sound, name: thanks}
|
||||
|
||||
stupid:
|
||||
phrases:
|
||||
- ты дурак
|
||||
- ты дебил
|
||||
- ты глупый
|
||||
- ты тупой
|
||||
action: {type: play_sound, name: stupid}
|
||||
|
||||
gaming_mode_on:
|
||||
phrases:
|
||||
- включи игровой режим
|
||||
- перейди в игровой режим
|
||||
- я хочу поиграть
|
||||
- переключись на телевизор
|
||||
action:
|
||||
type: multi
|
||||
steps:
|
||||
- {type: play_sound, name: ok}
|
||||
- {type: exe, file: "Switch to gaming mode.exe", wait: true}
|
||||
- {type: play_sound, name: ready}
|
||||
|
||||
gaming_mode_off:
|
||||
phrases:
|
||||
- рабочий режим
|
||||
- вернись в рабочий режим
|
||||
- переключись на компьютер
|
||||
- обратно на компьютер
|
||||
- отключи игровой режим
|
||||
- выйди из игрового режима
|
||||
- вернись на компьютер
|
||||
- выход с игрового режима
|
||||
- рабочее пространство
|
||||
- вернись в игровой режим
|
||||
- выключи игровой режим
|
||||
action:
|
||||
type: multi
|
||||
steps:
|
||||
- {type: play_sound, name: ok}
|
||||
- {type: exe, file: "Switch back to workspace.exe", wait: true}
|
||||
- {type: play_sound, name: ready}
|
||||
|
||||
switch_to_headphones:
|
||||
phrases:
|
||||
- переключи на наушники
|
||||
- включи наушники
|
||||
- перейди на наушники
|
||||
- давай звук в наушники
|
||||
- давай звук на наушники
|
||||
- переключи звук в наушники
|
||||
- переключи звук на наушники
|
||||
- переключи на кракены
|
||||
- включи кракены
|
||||
- перейди на кракены
|
||||
- давай звук в кракены
|
||||
- давай звук на кракены
|
||||
- переключи звук в кракены
|
||||
- переключи звук на кракены
|
||||
action:
|
||||
type: multi
|
||||
steps:
|
||||
- {type: play_sound, name: ok}
|
||||
- {type: exe, file: "Switch to headphones.exe", wait: true, delay_ms: 500}
|
||||
- {type: play_sound, name: ready}
|
||||
|
||||
switch_to_dynamics:
|
||||
phrases:
|
||||
- переключи на динамики
|
||||
- включи динамики
|
||||
- перейди на динамики
|
||||
- давай звук в динамики
|
||||
- давай звук на динамики
|
||||
- переключи звук в динамики
|
||||
- переключи звук на динамики
|
||||
- переключи на колонки
|
||||
- включи колонки
|
||||
- перейди на колонки
|
||||
- давай звук в колонки
|
||||
- давай звук на колонки
|
||||
- переключи звук в колонки
|
||||
- переключи звук на колонки
|
||||
action:
|
||||
type: multi
|
||||
steps:
|
||||
- {type: play_sound, name: ok}
|
||||
- {type: exe, file: "Switch to dynamics.exe", wait: true, delay_ms: 500}
|
||||
- {type: play_sound, name: ready}
|
||||
|
||||
'off':
|
||||
phrases:
|
||||
- выключись
|
||||
- вырубись
|
||||
- завершить работу
|
||||
- закройся
|
||||
- отключись
|
||||
- заверши свою работу
|
||||
- на сегодня хватит
|
||||
- выгрузи себя из памяти
|
||||
- ты мне надоел
|
||||
- пора спать
|
||||
action:
|
||||
type: multi
|
||||
steps:
|
||||
- {type: play_sound, name: "off"}
|
||||
- {type: system, op: exit}
|
||||
|
|
|
|||
|
|
@ -26,3 +26,11 @@ CHROME_PATH = 'C:/Program Files (x86)/Google/Chrome/Application/chrome.exe %s'
|
|||
GROQ_TOKEN = os.getenv('GROQ_TOKEN')
|
||||
GROQ_BASE_URL = "https://api.groq.com/openai/v1"
|
||||
GROQ_MODEL = "llama-3.3-70b-versatile"
|
||||
|
||||
# VAD (webrtcvad) — определяет конец команды по тишине вместо фиксированных 10 сек.
|
||||
# 0..3, выше — агрессивнее режет шум (но и обрезает речь).
|
||||
VAD_AGGRESSIVENESS = 2
|
||||
COMMAND_END_SILENCE_MS = 1200
|
||||
COMMAND_MIN_SPEECH_MS = 500
|
||||
COMMAND_MIN_LISTEN_MS = 1000
|
||||
COMMAND_MAX_LISTEN_MS = 15000
|
||||
|
|
|
|||
178
main.py
178
main.py
|
|
@ -13,6 +13,7 @@ import openai
|
|||
from openai import OpenAI
|
||||
import simpleaudio as sa
|
||||
import vosk
|
||||
import webrtcvad
|
||||
import yaml
|
||||
from comtypes import CLSCTX_ALL
|
||||
from fuzzywuzzy import fuzz
|
||||
|
|
@ -54,6 +55,10 @@ wake_rec = vosk.KaldiRecognizer(model, samplerate, wake_grammar)
|
|||
kaldi_rec = vosk.KaldiRecognizer(model, samplerate)
|
||||
q = queue.Queue()
|
||||
|
||||
VAD_FRAME_MS = 30
|
||||
VAD_FRAME_BYTES = int(samplerate * VAD_FRAME_MS / 1000) * 2
|
||||
vad = webrtcvad.Vad(config.VAD_AGGRESSIVENESS)
|
||||
|
||||
|
||||
def gpt_answer():
|
||||
global message_log
|
||||
|
|
@ -175,8 +180,7 @@ def filter_cmd(raw_voice: str):
|
|||
def recognize_cmd(cmd: str):
|
||||
rc = {'cmd': '', 'percent': 0}
|
||||
for c, v in VA_CMD_LIST.items():
|
||||
|
||||
for x in v:
|
||||
for x in v['phrases']:
|
||||
vrt = fuzz.ratio(cmd, x)
|
||||
if vrt > rc['percent']:
|
||||
rc['cmd'] = c
|
||||
|
|
@ -185,90 +189,59 @@ def recognize_cmd(cmd: str):
|
|||
return rc
|
||||
|
||||
|
||||
def _set_mute(state: int):
|
||||
devices = AudioUtilities.GetSpeakers()
|
||||
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
|
||||
volume = cast(interface, POINTER(IAudioEndpointVolume))
|
||||
volume.SetMute(state, None)
|
||||
|
||||
|
||||
def _shutdown():
|
||||
try:
|
||||
recorder.stop()
|
||||
recorder.delete()
|
||||
finally:
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def run_action(action):
|
||||
t = action['type']
|
||||
if t == 'exe':
|
||||
path = f"{CDIR}\\custom-commands\\{action['file']}"
|
||||
if action.get('wait'):
|
||||
subprocess.check_call([path])
|
||||
else:
|
||||
subprocess.Popen([path])
|
||||
if 'delay_ms' in action:
|
||||
time.sleep(action['delay_ms'] / 1000)
|
||||
elif t == 'play_sound':
|
||||
play(action['name'])
|
||||
elif t == 'system':
|
||||
op = action['op']
|
||||
if op == 'volume_mute':
|
||||
_set_mute(1)
|
||||
elif op == 'volume_unmute':
|
||||
_set_mute(0)
|
||||
elif op == 'exit':
|
||||
_shutdown()
|
||||
elif t == 'sleep':
|
||||
time.sleep(action['ms'] / 1000)
|
||||
elif t == 'multi':
|
||||
for step in action['steps']:
|
||||
run_action(step)
|
||||
|
||||
|
||||
def execute_cmd(cmd: str, voice: str):
|
||||
if cmd == 'open_browser':
|
||||
subprocess.Popen([f'{CDIR}\\custom-commands\\Run browser.exe'])
|
||||
play("ok")
|
||||
|
||||
elif cmd == 'open_youtube':
|
||||
subprocess.Popen([f'{CDIR}\\custom-commands\\Run youtube.exe'])
|
||||
play("ok")
|
||||
|
||||
elif cmd == 'open_google':
|
||||
subprocess.Popen([f'{CDIR}\\custom-commands\\Run google.exe'])
|
||||
play("ok")
|
||||
|
||||
elif cmd == 'music':
|
||||
subprocess.Popen([f'{CDIR}\\custom-commands\\Run music.exe'])
|
||||
play("ok")
|
||||
|
||||
elif cmd == 'music_off':
|
||||
subprocess.Popen([f'{CDIR}\\custom-commands\\Stop music.exe'])
|
||||
time.sleep(0.2)
|
||||
play("ok")
|
||||
|
||||
elif cmd == 'music_save':
|
||||
subprocess.Popen([f'{CDIR}\\custom-commands\\Save music.exe'])
|
||||
time.sleep(0.2)
|
||||
play("ok")
|
||||
|
||||
elif cmd == 'music_next':
|
||||
subprocess.Popen([f'{CDIR}\\custom-commands\\Next music.exe'])
|
||||
time.sleep(0.2)
|
||||
play("ok")
|
||||
|
||||
elif cmd == 'music_prev':
|
||||
subprocess.Popen([f'{CDIR}\\custom-commands\\Prev music.exe'])
|
||||
time.sleep(0.2)
|
||||
play("ok")
|
||||
|
||||
elif cmd == 'sound_off':
|
||||
play("ok", True)
|
||||
|
||||
devices = AudioUtilities.GetSpeakers()
|
||||
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
|
||||
volume = cast(interface, POINTER(IAudioEndpointVolume))
|
||||
volume.SetMute(1, None)
|
||||
|
||||
elif cmd == 'sound_on':
|
||||
devices = AudioUtilities.GetSpeakers()
|
||||
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
|
||||
volume = cast(interface, POINTER(IAudioEndpointVolume))
|
||||
volume.SetMute(0, None)
|
||||
|
||||
play("ok")
|
||||
|
||||
elif cmd == 'thanks':
|
||||
play("thanks")
|
||||
|
||||
elif cmd == 'stupid':
|
||||
play("stupid")
|
||||
|
||||
elif cmd == 'gaming_mode_on':
|
||||
play("ok")
|
||||
subprocess.check_call([f'{CDIR}\\custom-commands\\Switch to gaming mode.exe'])
|
||||
play("ready")
|
||||
|
||||
elif cmd == 'gaming_mode_off':
|
||||
play("ok")
|
||||
subprocess.check_call([f'{CDIR}\\custom-commands\\Switch back to workspace.exe'])
|
||||
play("ready")
|
||||
|
||||
elif cmd == 'switch_to_headphones':
|
||||
play("ok")
|
||||
subprocess.check_call([f'{CDIR}\\custom-commands\\Switch to headphones.exe'])
|
||||
time.sleep(0.5)
|
||||
play("ready")
|
||||
|
||||
elif cmd == 'switch_to_dynamics':
|
||||
play("ok")
|
||||
subprocess.check_call([f'{CDIR}\\custom-commands\\Switch to dynamics.exe'])
|
||||
time.sleep(0.5)
|
||||
play("ready")
|
||||
|
||||
elif cmd == 'off':
|
||||
play("off", True)
|
||||
exit(0)
|
||||
spec = VA_CMD_LIST.get(cmd)
|
||||
if not spec:
|
||||
return
|
||||
action = spec['action']
|
||||
run_action(action)
|
||||
confirm = spec.get('confirm_sound')
|
||||
if confirm is None and action['type'] == 'exe':
|
||||
confirm = 'ok'
|
||||
if confirm:
|
||||
play(confirm)
|
||||
|
||||
|
||||
recorder = PvRecorder(device_index=config.MICROPHONE_INDEX, frame_length=512)
|
||||
|
|
@ -301,20 +274,43 @@ while True:
|
|||
|
||||
play("greet", True)
|
||||
print("Yes, sir.")
|
||||
# reset the command recognizer so stale audio doesn't bleed into it
|
||||
kaldi_rec.Reset()
|
||||
ltc = time.time()
|
||||
listen_started = time.time()
|
||||
speech_ms = 0
|
||||
silence_ms = 0
|
||||
vad_buf = b""
|
||||
finalize = False
|
||||
|
||||
while True:
|
||||
elapsed_ms = (time.time() - listen_started) * 1000
|
||||
if elapsed_ms >= config.COMMAND_MAX_LISTEN_MS:
|
||||
break
|
||||
|
||||
while time.time() - ltc <= 10:
|
||||
pcm = recorder.read()
|
||||
sp = struct.pack("h" * len(pcm), *pcm)
|
||||
kaldi_rec.AcceptWaveform(sp)
|
||||
|
||||
if kaldi_rec.AcceptWaveform(sp):
|
||||
if va_respond(json.loads(kaldi_rec.Result())["text"]):
|
||||
ltc = time.time()
|
||||
vad_buf += sp
|
||||
while len(vad_buf) >= VAD_FRAME_BYTES:
|
||||
frame = vad_buf[:VAD_FRAME_BYTES]
|
||||
vad_buf = vad_buf[VAD_FRAME_BYTES:]
|
||||
if vad.is_speech(frame, samplerate):
|
||||
speech_ms += VAD_FRAME_MS
|
||||
silence_ms = 0
|
||||
else:
|
||||
silence_ms += VAD_FRAME_MS
|
||||
|
||||
if elapsed_ms < config.COMMAND_MIN_LISTEN_MS:
|
||||
continue
|
||||
if speech_ms >= config.COMMAND_MIN_SPEECH_MS and silence_ms >= config.COMMAND_END_SILENCE_MS:
|
||||
finalize = True
|
||||
break
|
||||
|
||||
if finalize or speech_ms > 0:
|
||||
text = json.loads(kaldi_rec.FinalResult()).get("text", "")
|
||||
if text:
|
||||
va_respond(text)
|
||||
|
||||
wake_rec.Reset()
|
||||
|
||||
except Exception as err:
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ pvrecorder
|
|||
# Vosk
|
||||
vosk
|
||||
|
||||
# Voice activity detection (smart command-end)
|
||||
webrtcvad-wheels
|
||||
|
||||
# Groq / OpenAI-compatible LLM
|
||||
openai>=1.0
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue