# 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}")