Skip to content

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

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages