Spaces:
Sleeping
Sleeping
| """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()) | |