mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-29 18:23:59 +08:00
update:为 test 页面增加可配置的本地唤醒词运行时
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
@dataclass
|
||||
class WakewordSettings:
|
||||
enabled: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class AudioSettings:
|
||||
input_device: str | int | None = None
|
||||
sample_rate: int = 16000
|
||||
channels: int = 1
|
||||
|
||||
|
||||
@dataclass
|
||||
class DetectorSettings:
|
||||
num_threads: int = 4
|
||||
provider: str = "cpu"
|
||||
max_active_paths: int = 2
|
||||
keywords_score: float = 1.8
|
||||
keywords_threshold: float = 0.2
|
||||
num_trailing_blanks: int = 1
|
||||
cooldown_seconds: float = 1.5
|
||||
|
||||
|
||||
@dataclass
|
||||
class LoggingSettings:
|
||||
level: str = "INFO"
|
||||
directory: str = "logs"
|
||||
file_name: str = "wakeword-runtime.log"
|
||||
|
||||
|
||||
@dataclass
|
||||
class RuntimeConfig:
|
||||
runtime_root: Path
|
||||
wakeword: WakewordSettings
|
||||
wake_words: list[str]
|
||||
model_dir: Path
|
||||
audio: AudioSettings
|
||||
detector: DetectorSettings
|
||||
logging: LoggingSettings
|
||||
raw: dict[str, Any]
|
||||
|
||||
def validate(self) -> None:
|
||||
if self.wakeword.enabled and not self.wake_words:
|
||||
raise ValueError("wake_word or wake_words cannot be empty when wakeword is enabled")
|
||||
|
||||
if self.audio.sample_rate <= 0:
|
||||
raise ValueError("audio.sample_rate must be greater than 0")
|
||||
|
||||
if self.audio.channels <= 0:
|
||||
raise ValueError("audio.channels must be greater than 0")
|
||||
|
||||
if self.detector.num_threads <= 0:
|
||||
raise ValueError("detector.num_threads must be greater than 0")
|
||||
|
||||
if self.detector.cooldown_seconds < 0:
|
||||
raise ValueError("detector.cooldown_seconds cannot be negative")
|
||||
|
||||
if not self.logging.level:
|
||||
raise ValueError("logging.level cannot be empty")
|
||||
|
||||
@property
|
||||
def wakeword_enabled(self) -> bool:
|
||||
return self.wakeword.enabled
|
||||
|
||||
@property
|
||||
def log_dir(self) -> Path:
|
||||
return (self.runtime_root / self.logging.directory).resolve()
|
||||
|
||||
@property
|
||||
def log_file(self) -> Path:
|
||||
return self.log_dir / self.logging.file_name
|
||||
|
||||
@property
|
||||
def log_level(self) -> str:
|
||||
return self.logging.level.upper()
|
||||
|
||||
|
||||
def load_config(runtime_root: Path) -> RuntimeConfig:
|
||||
config_path = runtime_root / "config.json"
|
||||
raw = json.loads(config_path.read_text(encoding="utf-8"))
|
||||
wakeword_cfg = dict(raw.get("wakeword", {}))
|
||||
raw_words = raw.get("wake_words")
|
||||
wake_words: list[str] = []
|
||||
if isinstance(raw_words, list):
|
||||
wake_words = [str(item).strip() for item in raw_words if str(item).strip()]
|
||||
if not wake_words:
|
||||
wake_word = str(raw.get("wake_word", "")).strip()
|
||||
if wake_word:
|
||||
wake_words = [wake_word]
|
||||
|
||||
raw_model_dir = Path(str(raw.get("model_dir", "models")))
|
||||
model_dir = raw_model_dir.resolve() if raw_model_dir.is_absolute() else (runtime_root / raw_model_dir).resolve()
|
||||
|
||||
audio_cfg = dict(raw.get("audio", {}))
|
||||
detector_cfg = dict(raw.get("detector", {}))
|
||||
logging_cfg = dict(raw.get("logging", {}))
|
||||
|
||||
config = RuntimeConfig(
|
||||
runtime_root=runtime_root,
|
||||
wakeword=WakewordSettings(enabled=bool(wakeword_cfg.get("enabled", False))),
|
||||
wake_words=wake_words,
|
||||
model_dir=model_dir,
|
||||
audio=AudioSettings(
|
||||
input_device=audio_cfg.get("input_device"),
|
||||
sample_rate=int(audio_cfg.get("sample_rate", 16000)),
|
||||
channels=int(audio_cfg.get("channels", 1)),
|
||||
),
|
||||
detector=DetectorSettings(
|
||||
num_threads=int(detector_cfg.get("num_threads", 4)),
|
||||
provider=str(detector_cfg.get("provider", "cpu")),
|
||||
max_active_paths=int(detector_cfg.get("max_active_paths", 2)),
|
||||
keywords_score=float(detector_cfg.get("keywords_score", 1.8)),
|
||||
keywords_threshold=float(detector_cfg.get("keywords_threshold", 0.2)),
|
||||
num_trailing_blanks=int(detector_cfg.get("num_trailing_blanks", 1)),
|
||||
cooldown_seconds=float(detector_cfg.get("cooldown_seconds", 1.5)),
|
||||
),
|
||||
logging=LoggingSettings(
|
||||
level=str(logging_cfg.get("level", "INFO")).upper(),
|
||||
directory=str(logging_cfg.get("dir", "logs")),
|
||||
file_name=str(logging_cfg.get("file", "wakeword-runtime.log")),
|
||||
),
|
||||
raw=raw,
|
||||
)
|
||||
config.validate()
|
||||
return config
|
||||
Reference in New Issue
Block a user