File size: 2,850 Bytes
b55fb22
 
 
 
 
 
 
 
 
 
 
 
 
 
b7102cf
b55fb22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/usr/bin/env python3
"""Small standard-library client for the bundled llama-compatible TTS API."""

from __future__ import annotations

import argparse
import base64
import json
import urllib.error
import urllib.request
from pathlib import Path


def _parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Generate speech with VoxCPM2 FP8 W8A8 V1")
    parser.add_argument("--url", default="http://127.0.0.1:8080/v1/audio/speech")
    parser.add_argument("--text", required=True)
    parser.add_argument("--output", type=Path, default=Path("output.wav"))
    parser.add_argument("--reference-audio", type=Path)
    parser.add_argument("--reference-text", default="")
    parser.add_argument("--seed", type=int, default=42)
    parser.add_argument("--cfg-value", type=float, default=2.0)
    parser.add_argument("--inference-timesteps", type=int, default=10)
    parser.add_argument("--max-steps", type=int, default=200)
    parser.add_argument("--temperature", type=float, default=1.0)
    parser.add_argument("--timeout", type=float, default=600.0)
    parser.add_argument("--force", action="store_true")
    return parser


def main() -> None:
    args = _parser().parse_args()
    output = args.output.expanduser().resolve()
    if output.exists() and not args.force:
        raise SystemExit(f"Refusing to overwrite {output}; pass --force to replace it")

    payload = {
        "model": "voxcpm",
        "input": args.text,
        "voice": "default",
        "response_format": "wav",
        "seed": args.seed,
        "cfg_value": args.cfg_value,
        "inference_timesteps": args.inference_timesteps,
        "max_steps": args.max_steps,
        "temperature": args.temperature,
    }
    if args.reference_audio is not None:
        reference = args.reference_audio.expanduser().resolve()
        payload["reference_audio"] = base64.b64encode(reference.read_bytes()).decode()
        if args.reference_text:
            payload["reference_text"] = args.reference_text

    request = urllib.request.Request(
        args.url,
        data=json.dumps(payload).encode(),
        headers={"Content-Type": "application/json"},
    )
    try:
        with urllib.request.urlopen(request, timeout=args.timeout) as response:
            content_type = response.headers.get_content_type()
            if content_type != "audio/wav":
                raise RuntimeError(f"Expected audio/wav, received {content_type}")
            audio = response.read()
    except urllib.error.HTTPError as exc:
        detail = exc.read().decode(errors="replace")
        raise SystemExit(f"TTS request failed with HTTP {exc.code}: {detail}") from exc

    output.parent.mkdir(parents=True, exist_ok=True)
    output.write_bytes(audio)
    print(f"Wrote {len(audio)} bytes to {output}")


if __name__ == "__main__":
    main()