Spaces:
Running
Running
| #!/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 | |
| 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() | |