File size: 3,640 Bytes
16f5171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/usr/bin/env python
"""Call a project Fish TTS API with a zero-shot voice or saved reference ID."""

from __future__ import annotations

import argparse
import base64
from pathlib import Path

import requests


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--url", default="http://127.0.0.1:8080/v1/tts")
    parser.add_argument("--text", required=True)
    voice = parser.add_mutually_exclusive_group()
    voice.add_argument("--reference-audio", type=Path)
    voice.add_argument("--reference-id")
    parser.add_argument("--reference-text")
    parser.add_argument("--output", type=Path, default=Path("output.wav"))
    parser.add_argument("--api-key")
    parser.add_argument("--seed", type=int)
    parser.add_argument("--max-new-tokens", type=int, default=1024)
    parser.add_argument("--chunk-length", type=int, default=200)
    parser.add_argument("--top-p", type=float, default=0.9)
    parser.add_argument("--temperature", type=float, default=0.9)
    parser.add_argument("--repetition-penalty", type=float, default=1.1)
    parser.add_argument(
        "--normalize",
        action=argparse.BooleanOptionalAction,
        default=True,
        help="Enable or disable API loudness normalization",
    )
    parser.add_argument(
        "--use-memory-cache",
        choices=("on", "off"),
        default="on",
        help="Reuse cached reference codes/text, or force a fresh reference encode",
    )
    return parser.parse_args()


def main() -> int:
    args = parse_args()
    references = []
    if args.reference_audio is not None:
        if not args.reference_audio.is_file():
            raise SystemExit(f"Reference audio is missing: {args.reference_audio}")
        if not args.reference_text or not args.reference_text.strip():
            raise SystemExit("--reference-text is required with --reference-audio")
        references.append(
            {
                "audio": base64.b64encode(args.reference_audio.read_bytes()).decode(
                    "ascii"
                ),
                "text": args.reference_text,
            }
        )
    elif args.reference_text:
        raise SystemExit("--reference-text requires --reference-audio")

    payload = {
        "text": args.text,
        "references": references,
        "reference_id": args.reference_id,
        "format": "wav",
        "streaming": False,
        "normalize": args.normalize,
        "max_new_tokens": args.max_new_tokens,
        "chunk_length": args.chunk_length,
        "top_p": args.top_p,
        "temperature": args.temperature,
        "repetition_penalty": args.repetition_penalty,
        "seed": args.seed,
        "use_memory_cache": args.use_memory_cache,
    }
    headers = {"Accept": "audio/wav"}
    if args.api_key:
        headers["Authorization"] = f"Bearer {args.api_key}"
    response = requests.post(
        args.url,
        json=payload,
        headers=headers,
        timeout=600,
    )
    if response.status_code != 200:
        raise SystemExit(
            f"TTS request failed ({response.status_code}): {response.text[:2000]}"
        )
    content_type = response.headers.get("content-type", "")
    if "audio/" not in content_type and not response.content.startswith(b"RIFF"):
        raise SystemExit(f"Unexpected response content type: {content_type}")
    args.output.parent.mkdir(parents=True, exist_ok=True)
    args.output.write_bytes(response.content)
    print(f"audio={args.output}")
    print(f"bytes={len(response.content)}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())