Files
posefit-server/app/audio/generate.py
T
wsy182 b45a8e2e85 Add audio generation config, refactor rep_announcer
- AudioConfig now includes rep_max_count and rep_audio_dir
- app/audio/generate.py uses config instead of hardcoded constants
- RepAnnouncer rewrote with pre-generated audio cache
- Supports Windows winsound, macOS afplay, Linux paplay/aplay
- Pin requirements back to mediapipe==0.10.21 with numpy<2
2026-06-10 11:42:40 +08:00

125 lines
2.8 KiB
Python

# app/audio/generate.py
from __future__ import annotations
import platform
import shutil
import subprocess
from pathlib import Path
from loguru import logger
def generate_rep_audio_files(
*,
max_count: int,
rate: int,
output_dir: Path,
overwrite: bool = False,
) -> None:
"""
确保 0~max_count 的运动次数语音 wav 文件存在。
默认生成到:
app/audio/reps/0.wav
app/audio/reps/1.wav
...
app/audio/reps/200.wav
服务启动时调用一次即可。
"""
output_dir.mkdir(parents=True, exist_ok=True)
missing_counts = [
count
for count in range(0, max_count + 1)
if overwrite or not _audio_path(output_dir, count).exists()
]
if not missing_counts:
logger.info("Rep audio files already prepared: {}", output_dir)
return
system = platform.system().lower()
logger.info(
"Preparing rep audio files, system={}, count={}, output_dir={}",
system,
len(missing_counts),
output_dir,
)
if system == "darwin":
_generate_with_macos_say(
counts=missing_counts,
output_dir=output_dir,
rate=rate,
)
else:
_generate_with_pyttsx3(
counts=missing_counts,
output_dir=output_dir,
rate=rate,
)
logger.info("Rep audio files prepared: {}", output_dir)
def _generate_with_macos_say(
*,
counts: list[int],
output_dir: Path,
rate: int,
) -> None:
"""macOS 使用 say 命令生成 wav。"""
if platform.system().lower() != "darwin":
raise RuntimeError("say command is only available on macOS")
if shutil.which("say") is None:
raise RuntimeError("macOS say command not found")
for count in counts:
audio_file = _audio_path(output_dir, count)
subprocess.run(
[
"say",
"-r",
str(rate),
"--file-format=WAVE",
"-o",
str(audio_file),
str(count),
],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=True,
)
def _generate_with_pyttsx3(
*,
counts: list[int],
output_dir: Path,
rate: int,
) -> None:
"""Windows / Linux 使用 pyttsx3 生成 wav。"""
try:
import pyttsx3
except Exception as exc:
raise RuntimeError(f"pyttsx3 unavailable: {exc}") from exc
engine = pyttsx3.init()
engine.setProperty("rate", rate)
engine.setProperty("volume", 1.0)
for count in counts:
audio_file = _audio_path(output_dir, count)
engine.save_to_file(str(count), str(audio_file))
engine.runAndWait()
def _audio_path(output_dir: Path, count: int) -> Path:
return output_dir / f"{count}.wav"