File size: 2,302 Bytes
f90aaa2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""Small GPT-SoVITS-compatible test server.

This implements the GET `/tts` endpoint used by `gpt_sovits_tts.py`.
It is only a connectivity/mock server; run the real GPT-SoVITS API for
actual voice cloning.
"""

from __future__ import annotations

import math
import tempfile
from pathlib import Path

import numpy as np
import soundfile as sf
import uvicorn
from fastapi import FastAPI, HTTPException, Query
from fastapi.responses import FileResponse


app = FastAPI(title="GPT-SoVITS Mock TTS")


def synthesize_tone(text: str, sample_rate: int = 24000) -> Path:
    duration = max(0.4, min(5.0, 0.075 * len(text)))
    samples = int(sample_rate * duration)
    timeline = np.linspace(0.0, duration, samples, endpoint=False)

    # Vary the tone a little so different input is audibly different.
    frequency = 220.0 + (sum(text.encode("utf-8")) % 280)
    carrier = np.sin(2.0 * math.pi * frequency * timeline)
    overtone = 0.25 * np.sin(2.0 * math.pi * frequency * 2.0 * timeline)
    fade = min(sample_rate // 20, samples // 2)
    envelope = np.ones(samples, dtype=np.float32)
    if fade > 0:
        envelope[:fade] = np.linspace(0.0, 1.0, fade)
        envelope[-fade:] = np.linspace(1.0, 0.0, fade)

    audio = (0.18 * (carrier + overtone) * envelope).astype(np.float32)
    output = Path(tempfile.NamedTemporaryFile(prefix="gpt_sovits_mock_", suffix=".wav", delete=False).name)
    sf.write(output, audio, sample_rate)
    return output


@app.get("/tts")
def tts(
    text: str = Query(...),
    text_lang: str = "en",
    ref_audio_path: str = "",
    prompt_lang: str = "en",
    prompt_text: str = "",
    text_split_method: str = "cut5",
    batch_size: str = "1",
    media_type: str = "wav",
    streaming_mode: str = "false",
) -> FileResponse:
    if media_type != "wav":
        raise HTTPException(status_code=400, detail="Mock server only supports media_type=wav")

    if ref_audio_path and not Path(ref_audio_path).exists():
        raise HTTPException(status_code=400, detail=f"ref_audio_path not found: {ref_audio_path}")

    output = synthesize_tone(text)
    return FileResponse(output, media_type="audio/wav", filename=output.name)


def main() -> None:
    uvicorn.run(app, host="127.0.0.1", port=9880)


if __name__ == "__main__":
    main()