73 lines
2.3 KiB
Python
73 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
from pathlib import Path
|
|
import sys
|
|
import tempfile
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from services.ai_voice_runtime_service.audiosocket import pcm16le_to_wav_bytes, resample_pcm16le
|
|
from services.ai_voice_runtime_service.providers.tts import build_tts_provider
|
|
|
|
|
|
def _parse_args() -> argparse.Namespace:
|
|
default_provider = os.getenv("AI_VOICE_TTS_PROVIDER", "openai").strip() or "openai"
|
|
parser = argparse.ArgumentParser(
|
|
description="Generate TTS once, cache it on the server, and materialize a WAV prompt file.",
|
|
)
|
|
parser.add_argument("--text", required=True, help="Text to synthesize.")
|
|
parser.add_argument("--output", required=True, help="Target WAV path.")
|
|
parser.add_argument("--language", default=None, help="Optional language hint.")
|
|
parser.add_argument(
|
|
"--provider",
|
|
default=default_provider,
|
|
help="TTS provider name. Defaults to AI_VOICE_TTS_PROVIDER or openai.",
|
|
)
|
|
parser.add_argument("--sample-rate", type=int, default=8000, help="Target WAV sample rate. Defaults to 8000.")
|
|
return parser.parse_args()
|
|
|
|
|
|
def _write_atomic(target: Path, payload: bytes) -> None:
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
temp_path: str | None = None
|
|
try:
|
|
with tempfile.NamedTemporaryFile(dir=target.parent, delete=False, suffix=".tmp") as handle:
|
|
handle.write(payload)
|
|
temp_path = handle.name
|
|
Path(temp_path).replace(target)
|
|
target.chmod(0o644)
|
|
finally:
|
|
if temp_path:
|
|
try:
|
|
Path(temp_path).unlink(missing_ok=True)
|
|
except OSError:
|
|
pass
|
|
|
|
|
|
def main() -> int:
|
|
args = _parse_args()
|
|
provider = build_tts_provider(args.provider)
|
|
synthesis = provider.synthesize(args.text, language=args.language)
|
|
pcm_bytes = resample_pcm16le(
|
|
synthesis.audio_bytes,
|
|
input_rate_hz=synthesis.sample_rate_hz,
|
|
output_rate_hz=max(args.sample_rate, 1),
|
|
)
|
|
wav_bytes = pcm16le_to_wav_bytes(
|
|
pcm_bytes,
|
|
sample_rate_hz=max(args.sample_rate, 1),
|
|
)
|
|
output_path = Path(args.output).expanduser()
|
|
_write_atomic(output_path, wav_bytes)
|
|
print(output_path)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|