File size: 3,251 Bytes
a44ca9d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""OpenAI-compatible TTS Server for AX650 NPU3.

Start:  python -m inflect_tts_sdk.server --port 8000
API:    POST /v1/audio/speech   (OpenAI TTS API)
        GET  /health
        GET  /v1/models
"""
from __future__ import annotations
import io, sys, argparse, json, struct, wave
from pathlib import Path

try:
    from fastapi import FastAPI, Request
    from fastapi.responses import Response, JSONResponse
    import uvicorn
except ImportError:
    sys.exit("pip install fastapi uvicorn")


def create_app(model_dir: str | None = None):
    app = FastAPI(title="Inflect-Nano-v2 TTS", version="1.0.0")
    engine = None

    def get_engine():
        nonlocal engine
        if engine is None:
            sys.path.insert(0, str(Path(__file__).resolve().parent))
            from tts_engine import InflectTTSEngine
            engine = InflectTTSEngine(model_dir=model_dir)
        return engine

    @app.get("/health")
    async def health():
        return {"status": "ok", "model": "inflect-nano-v2", "hw": "AX650-NPU3"}

    @app.get("/v1/models")
    async def models():
        return {"object": "list", "data": [
            {"id": "inflect-nano-v2", "object": "model",
             "owned_by": "owensong", "created": 1753500000}
        ]}

    @app.post("/v1/audio/speech")
    async def speech(request: Request):
        try:
            body = await request.json()
        except Exception:
            return Response(content=b'{"error":"Invalid JSON"}',
                            status_code=400, media_type="application/json")

        text = body.get("input", "")
        if not text:
            return Response(content=b'{"error":"input is required"}',
                            status_code=400, media_type="application/json")

        eng = get_engine()
        sr, wav = eng.synthesize(
            text,
            speed=float(body.get("speed", 1.0)),
            variation=float(body.get("variation", 0.667)),
            seed=int(body.get("seed", 0)),
        )

        # Write WAV to buffer
        buf = io.BytesIO()
        import soundfile as sf
        sf.write(buf, wav, sr, format="WAV")
        buf.seek(0)

        return Response(
            content=buf.read(),
            media_type="audio/wav",
            headers={"X-Sample-Rate": str(sr),
                     "X-Audio-Duration": str(len(wav) / sr)}
        )

    return app


if __name__ == "__main__":
    ap = argparse.ArgumentParser(description="Inflect-Nano-v2 OpenAI TTS Server")
    ap.add_argument("--host", default="0.0.0.0")
    ap.add_argument("--port", type=int, default=8000)
    ap.add_argument("--model-dir", default=None)
    args = ap.parse_args()
    app = create_app(args.model_dir)
    print(f"🎤 Inflect-Nano-v2 TTS Server → http://{args.host}:{args.port}")
    print(f"   POST /v1/audio/speech  (OpenAI-compatible)")
    print(f"   GET  /health")
    print(f"   GET  /v1/models")
    print(f"")
    print(f"   Example:")
    print(f"   curl -X POST http://localhost:{args.port}/v1/audio/speech \\")
    print(f"     -H 'Content-Type: application/json' \\")
    print(f"     -d '{{\"model\":\"tts-1\",\"input\":\"Hello world\"}}' \\")
    print(f"     --output speech.wav")
    uvicorn.run(app, host=args.host, port=args.port)