Skip to content

REYIL/VoiceTrigger

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Python Versions (3.8, 3.9, 3.10, 3.11, 3.12) Vosk License

VoiceTrigger

Realtime-распознаватель речи на базе Vosk: управление микрофоном, детекция уровня голоса (whisper / normal / shout), опциональное шумоподавление, декораторный API для обработки текста, ключевых слов и быстрых команд.


Содержание

  1. Установка
  2. Пример: простой голосовой помощник
  3. Управление (методы и рекомендации)
  4. Декораторы и Filter (API событий)
  5. Калибровка голоса (calibrate_voice.py)
  6. Конфиг для mode — как создать и применить
  7. Выбор микрофона вручную
  8. Режим шумоподавления (опционально)
  9. Отладка, советы и частые ошибки
  10. Структура проекта
  11. Лицензии

Установка

Установка через репозиторий

  1. Склонируйте или скопируйте проект в папку:

    git clone https://github.com/REYIL/VoiceTrigger.git
    cd VoiceTrigger
  2. Установите зависимости:

    pip install -r requirements.txt

Установка через pip

pip install VoiceTrigger

Дополнительно

  • Скачайте Vosk-модель (например, model_small) с официального сайта Vosk.
  • Укажите путь к модели в параметре model_path при использовании пакета.

Пример: простой голосовой помощник

Пример использования VoiceTrigger. В этом примере помощник «просыпается» по ключевому слову Алиса, слушает фразы, реагирует на быстрые команды (quick_words), и по длительной тишине возвращается в режим прослушивания ключевых слов.

import asyncio
import time
from pathlib import Path
from VoiceTrigger import (
    VoiceTrigger,
    Filter, TextContext,
    ColorLogger, Mode
)

rms_thresholds = {
    "whisper": -43.0,
    "normal": -15.0,
    "shout": 0.0
}

bot = VoiceTrigger(
    model_path="model_small",  # Путь к модели
    keywords=["Алиса"],  # Не обязательно, может брать автоматически с Filter
    quick_words=["стоп", "назад", "вперед"],  # Не обязательно, может брать автоматически с Filter
    # calibration_path=Path("voice_calibration.json"),
        # Путь указывать не обязательно
        # Если не указывать будет пытаться брать из "voice_calibration.json", а если файла не будет то определение будет работать по системным параметрам
    # rms_thresholds=rms_thresholds,
        # Если калибровка работает плохо, можно сделать ручную настройку
    device=None,  # Устройство ввода, если не указывать выберет сам
    logger=ColorLogger(level="debug")  # Логгер
)

state = {"active_until": 0.0}


@bot.keyword(Filter("Алиса"))
async def on_alisa(ctx: TextContext):
    bot.log.info(f"[KW] {ctx.match} mode={ctx.mode}")
    bot.start_recognition_main()
    bot.stop_recognition_keywords()
    state["active_until"] = time.time() + 10.0


@bot.quick(Filter(["стоп", "пауза"]))
async def on_quick(ctx: TextContext):
    bot.log.info(f"[QUICK] {ctx.match} mode={ctx.mode}")
    if ctx.match and ctx.match.lower() == "стоп":
        bot.stop_recognition_main()
        bot.start_recognition_keywords()
        state["active_until"] = 0.0


@bot.text()
async def on_all_text(ctx: TextContext):
    if ctx.match is None and ctx.text:
        bot.log.info(f"[TEXT] mode={ctx.mode} text='{ctx.text}'")


@bot.text(Filter(["привет", "здарова"], lv=10, mode=Mode.normal))
async def on_greeting(ctx: TextContext):
    bot.log.info(f"[GREETING] {ctx.match} text='{ctx.text}' mode={ctx.mode}")


@bot.on_silence()  # Возвращает время с последнего quick_words
async def handle_silence_main(sec: float):
    now = time.time()
    if 0 < state["active_until"] <= now and bot.active_main and sec >= 10.0:
        bot.log.info(f"[Silence] {sec:.1f}s -> back to keywords")
        bot.stop_recognition_main()
        bot.start_recognition_keywords()
        state["active_until"] = 0.0


# @bot.on_kw_silence()  # Возвращает время с последнего keywords
# async def handle_kw_silence(sec: float):
#     if sec >= 5.0:
#         bot.log.debug(f"[KW Silence] {sec:.1f}s with no keywords")


if __name__ == "__main__":
    devices = bot.list_input_devices()  # Вывод всех аудио устройств
    bot.log.debug(f"Available input devices: {devices}")
    try:
        asyncio.run(bot.run(initial_keywords_mode=True))  # Запуск с вначале включенным keywords_mode
    except KeyboardInterrupt:
        bot.log.info("Interrupted by user.")

Управление (методы и рекомендации)

Основные методы:

  • start_recognition_main() — включить основной режим распознавания (continuous).
  • stop_recognition_main() — выключить основной режим.
  • start_recognition_keywords() — включить режим прослушивания ключевых слов.
  • stop_recognition_keywords() — выключить режим ключевых слов.
  • reload_model(new_model_path=None) — перезагрузить Vosk-модель (опционально указать новый путь).
  • list_input_devices() — вернуть список доступных входных устройств (index, name, max_input_channels).
  • set_input_device(device, restart_stream=False) — установить устройство ввода (индекс или имя). restart_stream=True попытается перезапустить поток.

Рекомендации:

  • Не рекомендуется включать одновременно main и keywords. Эти режимы имеют разные цели — keywords оптимизирован для обнаружения wake-word, main — для непрерывной речи.
  • Если нужна быстрая реакция на короткие команды, используйте quick_words совместно с main — quick-обработчики работают параллельно с основным распознаванием и оптимизированы под короткие команды.
  • keyword-режим хорош для «пробуждения» (wake word). Обычно вы запускаете keywords по умолчанию, а при обнаружении wake-word временно переключаетесь в main.

Декораторы и Filter (API событий)

Декораторы:

  • @bot.text(FILTER?) — обработчики общего текста (по умолчанию wildcard). Аргумент — TextContext.
  • @bot.keyword(FILTER?) — обработчики ключевых слов; указанные фразы добавляются в список keywords.
  • @bot.quick(FILTER?) — быстрые команды (короткие слова/фразы).
  • @bot.on_silence() — обработчики тишины для main (параметр — количество секунд молчания).
  • @bot.on_kw_silence() — обработчики тишины для keywords.

Filter:

Filter(phrases=None | "слово" | ["а","б"], lv=10, mode=Mode.normal|whisper|shout)
  • phrases — список фраз; пустой список / None → wildcard (обработчик принимает все тексты).
  • lv — процент допуска ошибок для Levenshtein (число 0..100). Чем больше — тем сильнее допускаются отличия при сравнении.
  • mode — (Mode.whisper, Mode.normal, Mode.shout) — если указан, обработчик вызовется только при совпадении голосового режима.

Контекст обработчика (TextContext):

  • text — распознанный текст (final/partial).
  • mode — строка: "whisper" | "normal" | "shout".
  • match — совпавшая фраза из Filter или None (для wildcard).
  • timestamp — время события (epoch).

Калибровка голоса

В проекте есть модуль VoiceCalibrator, она собирает статистику по трём уровням речи (quiet, normal, loud) и сохраняет voice_calibration.json. Этот файл используется VoiceTrigger для адаптивной настройки порогов RMS/HF и порога тишины.

Запуск калибровки:

from pathlib import Path

from VoiceTrigger import VoiceCalibrator

CALIBRATION_PATH = Path(__file__).parent / "voice_calibration.json"

VoiceCalibrator.calibrate(calibration_path=CALIBRATION_PATH)  # Путь указывать не обязательно

Скрипт попросит записать несколько фрагментов для каждого уровня и сохранит средние значения в voice_calibration.json.

Почему калибровка полезна:

  • Подстраивает пороги под конкретный микрофон, акустику комнаты и расстояние до источника.
  • Повышает корректность определения whisper/normal/shout.

Минусы калибровки:

  • Требует аккуратного прохождения процедуры пользователем — если человек говорит слишком громко или тихо, результаты могут быть некорректными.
  • Чувствительна к положению микрофона: смена расстояния или угла может сильно изменить RMS и HF, что исказит пороги.
  • Автопороги могут оказаться слишком близко друг к другу, особенно если различие между whisper, normal и shout маленькое — классификация становится менее надёжной.
  • Плохие условия записи (шум, эхо, фоновые источники) могут «засорить» калибровку.
  • При многократной смене среды/оборудования нужна новая калибровка, иначе точность падает.

Конфиг для mode — как создать и применить

Можно задать пороги вручную через JSON-конфиг, либо использовать voice_calibration.json, полученный через calibrate_voice.py.

Пример mode_config.json:

{
  "rms_thresholds": {
    "whisper": -45.0,
    "normal": -18.0,
    "shout": -1.0
  },
  "hf_ratio_threshold": 1.5,
  "silence_db": -46.0
}

Применение конфигурации в коде:

import json
from pathlib import Path
from VoiceTrigger import VoiceTrigger

cfg = json.loads(Path("mode_config.json").read_text(encoding="utf-8"))
bot = VoiceTrigger(...)

bot.voice_detector.rms_thresholds = cfg.get("rms_thresholds", bot.voice_detector.rms_thresholds)
bot.voice_detector.hf_ratio_threshold = cfg.get("hf_ratio_threshold", bot.voice_detector.hf_ratio_threshold)
bot.voice_detector.silence_db = cfg.get("silence_db", bot.voice_detector.silence_db)

Или положите результаты калибровки в voice_calibration.jsonVoiceTrigger автоматически прочитает его при создании VoiceTrigger (если файл доступен).

А также можно использовать rms_thresholds

from VoiceTrigger import VoiceTrigger, ColorLogger

rms_thresholds = {
    "whisper": -43.0,
    "normal": -15.0,
    "shout": 0.0
}

bot = VoiceTrigger(
    model_path="model_small",
    quick_words=["стоп", "назад", "вперед", "какая погода"],
    logger=ColorLogger(level="debug"),
    rms_thresholds=rms_thresholds
)

Выбор микрофона вручную

Список устройств:

devices = VoiceTrigger.list_input_devices()
# или через экземпляр:
devices = bot.list_input_devices()

Установка устройства:

  • При инициализации:
bot = VoiceTrigger(..., device=2)  # индекс
# или
bot = VoiceTrigger(..., device="USB Microphone")  # имя устройства
  • Во время работы:
bot.set_input_device(2, restart_stream=True)

restart_stream=True попытается перезапустить поток (может потребоваться освобождение устройства системой).


Режим шумоподавления (опционально)

Можно включить встроенное шумоподавление для микрофона. Для этого необходимо:

  1. Установить зависимости:

    pip install noisereduce scipy
  2. Включить режим в коде:

    bot = VoiceTrigger(..., noise_reduction=True)

Когда использовать:

  • если в помещении много фонового шума (ПК-вентиляторы, улица, кондиционер),
  • при записи на встроенные микрофоны ноутбука,
  • если нужно повысить точность распознавания.

Отладка, советы и частые ошибки

  • Включите подробный логгер:
logger = ColorLogger(level="debug")
bot = VoiceTrigger(..., logger=logger)
  • Если модель не загружается — проверьте model_path и файлы модели.

  • Если нет звука или пустые результаты — проверьте sounddevice.query_devices() и системные права доступа к микрофону.

  • Если плохое распознавание:

    • проверьте sample rate (обычно 16000),
    • расположение микрофона,
    • при необходимости запустите calibrate_voice.py,
    • попробуйте включить noise_reduction (если установлен noisereduce).
  • Для коротких команд используйте quick_words (работают быстрее и подходят для single-word commands).


Структура проекта

.
├── model_small # vosk
├── voicetrigger
│   ├── core
│   │   ├── asmanager.py
│   │   ├── decorators.py
│   │   ├── speechr.py
│   │   └── vldetector.py
│   ├── services
│   │   └── calibration.py
│   ├── utils
│   │   ├── filter.py
│   │   ├── levenshtein.py
│   │   └── logger.py
│   └── __init__.py
├── main.py
└── requirements.txt

Лицензии

Основной код проекта распространяется под лицензией MIT — вы можете свободно использовать, изменять и распространять его.

Проект также использует сторонние библиотеки:

  • colorlog — лицензия MIT
  • noisereduce — лицензия MIT
  • numpy — лицензия BSD 3-Clause
  • scipy — лицензия BSD 3-Clause
  • sounddevice — лицензия MIT
  • vosk — лицензия Apache License 2.0

Все перечисленные библиотеки используются в соответствии с условиями их лицензий.

About

Realtime-распознаватель речи на базе Vosk: управление микрофоном, детекция уровня голоса (whisper / normal / shout), опциональное шумоподавление, декораторный API для обработки текста, ключевых слов и быстрых команд.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages