Initial commit: полный проект my_speaker

This commit is contained in:
2025-12-25 12:16:19 +03:00
commit 8279fbb35f
85 changed files with 6027 additions and 0 deletions
+181
View File
@@ -0,0 +1,181 @@
# app/core/config.py
import os
import logging
from typing import Dict, Any, Optional
from pydantic import BaseModel, Field
from ruamel.yaml import YAML
from functools import lru_cache
# Инициализация логгера
logger = logging.getLogger('VADApp')
# Путь к файлу конфигурации
CONFIG_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'config.yaml')
class AuthConfig(BaseModel):
username: str = "admin"
password: str = "admin"
class VADConfig(BaseModel):
mode: int = 1
sample_rate: int = 16000
frame_duration_ms: int = 30
ring_buffer_size: int = 20
speech_threshold: float = 0.5
energy_threshold: int = 1000
min_speech_duration_ms: int = 300
record_stop_delay_sec: float = 1.0
prebuffer_duration_sec: float = 1.0
max_record_duration_sec: float = 5.0
target_rms: float = 2000.0
max_gain: float = 5.0
noise_reduction_factor: float = 1.5
noise_update_alpha: float = 0.05
min_rms_threshold_factor: float = 2.0
gain_smoothing_alpha: float = 0.1
adaptive_energy_std_factor: float = 2.5
adaptive_energy: bool = True
noise_reduction_enabled: bool = True
auto_gain_enabled: bool = True
record_on_speech: bool = True
class SystemConfig(BaseModel):
history_maxlen: int = 100
polling_interval_ms: int = 500
logging_level: str = "INFO"
log_file: str = "logs/app.log"
log_save_file: bool = True
class STTConfig(BaseModel):
model_path: str = ""
class CommandsConfig(BaseModel):
commands_file: str = ""
class SessionConfig(BaseModel):
session_timeout_seconds: int = 30
class TTSConfig(BaseModel):
host: str = "192.168.200.81"
port: int = 10200
timeout: int = 30
pause_duration: float = 0.5
post_stop_wait: float = 1.0
voice: str = "ru_RU-ruslan-medium"
save_TTS_files: bool = False
class AudioSettingsConfig(BaseModel):
playback_device: int = 0
capture_device: int = 0
playback_volume: int = 80
capture_volume: int = 90
class Settings(BaseModel):
auth: AuthConfig = Field(default_factory=AuthConfig)
vad: VADConfig = Field(default_factory=VADConfig)
system: SystemConfig = Field(default_factory=SystemConfig)
stt: STTConfig = Field(default_factory=STTConfig)
commands: CommandsConfig = Field(default_factory=CommandsConfig)
session: SessionConfig = Field(default_factory=SessionConfig)
tts: TTSConfig = Field(default_factory=TTSConfig)
audio_settings: AudioSettingsConfig = Field(default_factory=AudioSettingsConfig)
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_yaml_config() -> Dict[str, Any]:
"""Загрузка конфигурации из YAML файла"""
logger.debug("Загрузка конфигурации из YAML")
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 {}
# Проверяем и адаптируем пути для текущей ОС
if 'commands' in config and 'commands_file' in config['commands']:
commands_file = config['commands']['commands_file']
# Преобразуем путь для текущей ОС, если необходимо
commands_file = os.path.normpath(commands_file)
config['commands']['commands_file'] = commands_file
logger.debug(f"Путь к файлу команд: {commands_file}")
if 'stt' in config and 'model_path' in config['stt']:
model_path = config['stt']['model_path']
# Преобразуем путь для текущей ОС, если необходимо
model_path = os.path.normpath(model_path)
config['stt']['model_path'] = model_path
logger.debug(f"Путь к модели STT: {model_path}")
return config
except Exception as e:
logger.error(f"Ошибка загрузки конфигурации: {e}", exc_info=True)
raise
@lru_cache()
def get_settings() -> Settings:
"""Получение настроек приложения (кэшируется)"""
config = load_yaml_config()
# Создаем объект настроек
settings = Settings(
auth=AuthConfig(**config.get('auth', {})),
vad=VADConfig(**config.get('vad', {})),
system=SystemConfig(**config.get('system', {})),
stt=STTConfig(**config.get('stt', {})),
commands=CommandsConfig(**config.get('commands', {})),
tts=TTSConfig(**config.get('tts', {})),
audio_settings=AudioSettingsConfig(**config.get('audio_settings', {}))
)
# Настраиваем логирование
setup_logging(settings.system.logging_level)
return settings
def save_settings(settings: Settings) -> bool:
"""Сохранение настроек в YAML файл"""
logger.debug("Сохранение конфигурации")
try:
# Загружаем текущую конфигурацию
yaml = YAML()
yaml.indent(mapping=2, sequence=4, offset=2)
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 {}
# Обновляем конфигурацию
config['auth'] = settings.auth.dict()
config['vad'] = settings.vad.dict()
config['system'] = settings.system.dict()
config['stt'] = settings.stt.dict()
config['commands'] = settings.commands.dict()
config['tts'] = settings.tts.dict()
config['audio_settings'] = settings.audio_settings.dict()
# Сохраняем обновленную конфигурацию
with open(CONFIG_PATH, 'w', encoding='utf-8') as f:
yaml.dump(config, f)
# Сбрасываем кэш настроек
get_settings.cache_clear()
logger.info("Конфигурация сохранена")
return True
except Exception as e:
logger.error(f"Ошибка сохранения конфигурации: {e}", exc_info=True)
return False