Files

153 lines
7.1 KiB
Python

# app/utils.py
import os
import sys
import logging
from ruamel.yaml import YAML
from vad_core import VADPolling
from vosk_local_STT import VoskSTT
from commands import handle_command
from volume import set_volume
# Инициализация логгера
logger = logging.getLogger('VADApp')
logger.setLevel(logging.DEBUG)
logger.handlers.clear()
log_format = logging.Formatter('[%(asctime)s] %(levelname)s] %(message)s', datefmt='%H:%M:%S')
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(log_format)
logger.addHandler(console_handler)
logger.debug("Логгер VADApp инициализирован")
CONFIG_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'config.yaml')
def setup_logging(logging_level='INFO'):
logger.debug(f"Установка уровня логирования: {logging_level}")
try:
logger.setLevel(getattr(logging, logging_level.upper(), logging.INFO))
logger.info(f"Уровень логирования установлен: {logging_level}")
except Exception as e:
logger.error(f"Ошибка настройки логирования: {e}", exc_info=True)
raise
def load_config():
logger.debug("Загрузка конфигурации")
try:
yaml = YAML()
yaml.preserve_quotes = True
if not os.path.exists(CONFIG_PATH):
logger.error(f"Файл конфигурации не найден: {CONFIG_PATH}")
raise FileNotFoundError(f"Файл конфигурации не найден: {CONFIG_PATH}")
with open(CONFIG_PATH, 'r', encoding='utf-8') as f:
config = yaml.load(f) or {}
commands_file = config.get('commands', {}).get('commands_file', os.path.join(os.path.dirname(os.path.dirname(__file__)), 'commands', 'commands.yaml'))
if commands_file:
if not os.path.exists(commands_file):
logger.error(f"Файл команд не найден: {commands_file}")
raise FileNotFoundError(f"Файл команд не найден: {commands_file}")
with open(commands_file, 'r', encoding='utf-8') as cmd_file:
config['commands'] = yaml.load(cmd_file) or {}
config['commands_file'] = commands_file
logger.debug(f"Команды загружены: {commands_file}")
config.setdefault('system', {})
config['system'].setdefault('history_maxlen', 100)
config['system'].setdefault('polling_interval_ms', 500)
config['system'].setdefault('logging_level', 'INFO')
config.setdefault('vad', {})
config['vad'].setdefault('mode', 1)
config['vad'].setdefault('sample_rate', 16000)
config['vad'].setdefault('frame_duration_ms', 30)
config['vad'].setdefault('ring_buffer_size', 20)
config['vad'].setdefault('speech_threshold', 0.5)
config['vad'].setdefault('energy_threshold', 1000)
config['vad'].setdefault('min_speech_duration_ms', 300)
config['vad'].setdefault('record_stop_delay_sec', 1.0)
config['vad'].setdefault('prebuffer_duration_sec', 1.0)
config['vad'].setdefault('max_record_duration_sec', 5.0)
config['vad'].setdefault('target_rms', 2000.0)
config['vad'].setdefault('max_gain', 5.0)
config['vad'].setdefault('noise_reduction_factor', 1.5)
config['vad'].setdefault('noise_update_alpha', 0.05)
config['vad'].setdefault('min_rms_threshold_factor', 2.0)
config['vad'].setdefault('gain_smoothing_alpha', 0.1)
config['vad'].setdefault('adaptive_energy_std_factor', 2.5)
config['vad'].setdefault('adaptive_energy', True)
config['vad'].setdefault('noise_reduction_enabled', True)
config['vad'].setdefault('auto_gain_enabled', True)
config['vad'].setdefault('record_on_speech', True)
config.setdefault('auth', {})
config['auth'].setdefault('username', 'admin')
config['auth'].setdefault('password', 'admin')
setup_logging(config['system']['logging_level'])
logger.info("Конфигурация загружена")
return config
except Exception as e:
logger.error(f"Ошибка загрузки конфигурации: {e}", exc_info=True)
raise
def save_config(config):
logger.debug("Сохранение конфигурации")
try:
yaml = YAML()
yaml.indent(mapping=2, sequence=4, offset=2)
yaml.preserve_quotes = True
config_to_save = config.copy()
if 'commands_file' in config_to_save:
del config_to_save['commands_file']
with open(CONFIG_PATH, 'w', encoding='utf-8') as f:
yaml.dump(config_to_save, f)
logger.info("Конфигурация сохранена")
return True
except Exception as e:
logger.error(f"Ошибка сохранения конфигурации: {e}", exc_info=True)
return False
def init_vad_and_stt():
logger.debug("Инициализация VAD и STT")
try:
config = load_config()
commands = config.get('commands', {})
stt_instance = VoskSTT(
model_path=config.get('stt', {}).get('model_path', os.path.join(os.path.dirname(os.path.dirname(__file__)), 'models', 'vosk-model-small-ru-0.22')),
commands_file=config.get('commands_file', os.path.join(os.path.dirname(os.path.dirname(__file__)), 'commands', 'commands.yaml')),
sample_rate=config.get('vad', {}).get('sample_rate', 16000)
)
def process_speech(audio_data, sample_rate):
try:
recognized = stt_instance.recognize(audio_data)
if recognized:
logger.info(f"Команда распознана: {recognized}")
handle_command(recognized, commands)
except Exception as e:
logger.error(f"Ошибка обработки речи: {e}")
vad_config = config.get('vad', {})
vad_config['polling_interval_ms'] = config['system']['polling_interval_ms']
vad_config['history_maxlen'] = config['system']['history_maxlen']
vad_instance = VADPolling(vad_config, on_speech_end_callback=process_speech)
logger.info("VAD и STT инициализированы")
return vad_instance, stt_instance, commands
except Exception as e:
logger.error(f"Ошибка инициализации VAD/STT: {e}", exc_info=True)
raise
def cleanup_vad_and_stt(vad_instance, stt_instance):
logger.debug("Завершение работы VAD и STT")
try:
if vad_instance:
logger.info("Завершаем VAD...")
vad_instance.close()
if stt_instance:
logger.info("Завершаем STT...")
stt_instance = None
logger.info("VAD и STT завершены")
except Exception as e:
logger.error(f"Ошибка завершения VAD/STT: {e}")