"""Thử giọng đọc chạy tại chỗ: tổng hợp một câu, in tốc độ và mốc từng từ. SPEECH_PROVIDER=vieneu python scripts/smoke_tts.py SPEECH_PROVIDER=vieneu python scripts/smoke_tts.py "Câu muốn nghe thử." Lần chạy đầu tiên sẽ tải model từ HuggingFace về ~/.cache/huggingface, mất vài phút. Những lần sau chỉ còn thời gian nạp model. """ import base64 import logging import os import sys import time from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "backend")) os.environ.setdefault("SPEECH_PROVIDER", "vieneu") logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s") from app.config import settings # noqa: E402 from app.services.speech import SpeechUnavailable, synthesize, warm_up # noqa: E402 DEFAULT = ( "Bình phương cạnh huyền bằng tổng bình phương hai cạnh góc vuông. " "Em nhìn lên bảng nhé, thầy viết công thức ra đây." ) def main() -> int: text = sys.argv[1] if len(sys.argv) > 1 else DEFAULT print(f"Provider : {settings.speech_provider}") print(f"Giọng : {settings.vieneu_voice}") print(f"Câu : {text}\n") # Nạp model trước rồi mới bấm giờ — máy chủ cũng nạp sẵn lúc khởi động, nên # gộp chung vào sẽ ra một con số không giống thứ học sinh thực sự phải chờ. started = time.perf_counter() warm_up() print(f"Nạp model: {time.perf_counter() - started:.1f}s\n") started = time.perf_counter() try: clip = synthesize(text, speed=1.0) except SpeechUnavailable as exc: print(f"Không đọc được: {exc}") return 1 elapsed = time.perf_counter() - started audio = base64.b64decode(clip.audio_base64) spoken = clip.timings[-1].end_ms / 1000 if clip.timings else 0.0 out = Path("smoke_tts.mp3" if clip.mime_type == "audio/mpeg" else "smoke_tts.wav") out.write_bytes(audio) print(f"Mất : {elapsed:.1f}s cho ~{spoken:.1f}s tiếng (RTF {elapsed / max(spoken, 1e-6):.2f})") print(f"File : {out} — {len(audio) // 1024} KB, {clip.mime_type}") print(f"Mốc : {len(clip.timings)} từ") for timing in clip.timings[:8]: print(f" {timing.start_ms:>6} → {timing.end_ms:>6} ms {timing.word}") if len(clip.timings) > 8: print(" ...") return 0 if __name__ == "__main__": raise SystemExit(main())