Spaces:
Sleeping
Sleeping
| title: OmniVoice TTS | |
| emoji: 🎙️ | |
| colorFrom: indigo | |
| colorTo: green | |
| sdk: docker | |
| pinned: false | |
| license: apache-2.0 | |
| short_description: In-process OmniVoice TTS — Urdu / Punjabi / 600+ languages | |
| suggested_hardware: t4-small | |
| suggested_storage: small | |
| # OmniVoice TTS — Self-hosted | |
| Runs `k2-fsa/OmniVoice` directly on this Space's GPU (T4 / dedicated). No | |
| upstream proxying — the model is loaded in-process at startup. | |
| ## Endpoints | |
| | Method | Path | Purpose | | |
| |--------|----------------|---------| | |
| | GET | `/` | Service descriptor (JSON, lists languages / attributes) | | |
| | GET | `/health` | Liveness probe | | |
| | POST | `/tts` | One-shot WAV (audio/wav, 24 kHz mono int16) | | |
| | POST | `/tts/stream` | Chunked WAV (header + raw PCM tail) — same JSON body | | |
| | POST | `/tts/clone` | Ad-hoc voice cloning (multipart: `text`, `ref_audio`, `ref_text`?) | | |
| | POST | `/voices` | Register a voice **once** (multipart: `name`, `ref_audio`, `ref_text`?) | | |
| | GET | `/voices` | List cached voice ids | | |
| | DELETE | `/voices/{name}` | Free a registered voice | | |
| | WS | `/ws/tts` | Real-time PCM frames + JSON status messages | | |
| ## Request body (`/tts` and `/tts/stream`) | |
| ```json | |
| { | |
| "text": "آپ کا شکریہ", | |
| "language": "Urdu", | |
| "gender": "Female", | |
| "age": "Young Adult", | |
| "pitch": "Moderate", | |
| "style": "Auto", | |
| "accent": "Auto", | |
| "dialect": "Auto", | |
| "speed": 1.0, | |
| "nfe_steps": 32, | |
| "guidance": 2.0, | |
| "denoise": true | |
| } | |
| ``` | |
| - `language` accepts the English label (`"Urdu"`, `"Panjabi"`, `"Western Panjabi"`, …) or `"Auto"`. | |
| - Voice-design attributes accept friendly aliases (`"female"`, `"young"`, `"low"`) **or** the canonical bilingual labels from the upstream Gradio demo (`"Female / 女"`, `"Young Adult / 青年"`). | |
| - `instruct` (string) optionally overrides the auto-built attribute string with a free-form description. | |
| ## WebSocket protocol (`/ws/tts`) | |
| ``` | |
| client → server (text) : <TTSRequest JSON> | |
| server → client (text) : {"type":"started","sample_rate":24000} | |
| server → client (binary) : raw int16 PCM frames (~0.5 s each) | |
| server → client (text) : {"type":"complete","duration_s":1.23} | |
| ``` | |
| On error: `{"type":"error","message":"..."}` then close. | |
| ## Voice cloning | |
| Two modes: | |
| ### Ad-hoc (re-clones every request) | |
| ```bash | |
| curl -X POST https://<space>.hf.space/tts/clone \ | |
| -F text="کلوننگ کا تجربہ" \ | |
| -F language=Urdu \ | |
| -F ref_audio=@reference.wav \ | |
| -F ref_text="ریفرنس آڈیو کا متن" \ | |
| --output cloned.wav | |
| ``` | |
| ### Register once, reuse via `voice_id` (recommended) | |
| Compute the clone prompt a single time, then synthesize unlimited utterances in | |
| that voice without re-uploading or re-encoding the reference: | |
| ```bash | |
| # 1. Register the voice ONCE (name it whatever you like) | |
| curl -X POST https://<space>.hf.space/voices \ | |
| -F name=narrator_urdu \ | |
| -F ref_audio=@reference.wav \ | |
| -F ref_text="ریفرنس آڈیو کا متن" | |
| # → {"voice_id":"narrator_urdu","voices_cached":1,"max_voices":50} | |
| # 2. Reuse it on /tts, /tts/stream, or /ws/tts — just add "voice_id" | |
| curl -X POST https://<space>.hf.space/tts \ | |
| -H 'Content-Type: application/json' \ | |
| -d '{"text":"آپ کا شکریہ","language":"Urdu","voice_id":"narrator_urdu"}' \ | |
| --output out.wav | |
| curl https://<space>.hf.space/voices # list cached voices | |
| curl -X DELETE https://<space>.hf.space/voices/narrator_urdu # free it | |
| ``` | |
| Notes: | |
| - Re-registering the same `name` overwrites it (idempotent). | |
| - An unknown `voice_id` returns **404** — register first. | |
| - The cache is **in-process and in-memory**: voices are lost on restart/redeploy, and the least-recently-used voice is evicted once `OMNIVOICE_MAX_VOICES` (default 50) is exceeded. | |
| - Omitting `ref_text` at registration triggers Whisper ASR, so it requires `OMNIVOICE_LOAD_ASR=1`. | |
| ## Configuration | |
| Environment variables (set in the Space settings): | |
| | Variable | Default | Purpose | | |
| |---|---|---| | |
| | `OMNIVOICE_MODEL` | `k2-fsa/OmniVoice` | HF repo id or local path | | |
| | `OMNIVOICE_DEVICE` | `cuda` (auto-falls-back CPU) | torch device for the model | | |
| | `OMNIVOICE_LOAD_ASR` | `1` | Set `0` to skip whisper download (saves ~1 GB) | | |
| | `OMNIVOICE_MAX_VOICES`| `50` | Max registered voices kept in memory (LRU-evicted) | | |
| | `LOG_LEVEL` | `INFO` | Python logging level | | |
| | `PORT` | `7860` | HF Spaces standard port | | |
| ## Cold start | |
| First request after a cold boot triggers the model download (~2 GB to `/data/.cache/huggingface`) and weight load (~30 s on T4). Subsequent requests are ~1–3 s/utterance for short Urdu / Punjabi sentences. The voice-agent client already retries 502/503/504 with backoff, so cold starts are absorbed silently. | |
| ## Pushing this Space | |
| ```bash | |
| # From the repo root | |
| huggingface-cli login | |
| git -C deployments/omnivoice_space init | |
| git -C deployments/omnivoice_space remote add origin https://huggingface.co/spaces/ebitlogix/omnivoice-tts | |
| git -C deployments/omnivoice_space add . | |
| git -C deployments/omnivoice_space commit -m "OmniVoice TTS — self-hosted FastAPI" | |
| git -C deployments/omnivoice_space push -u origin main | |
| ``` | |
| After push, set hardware to `t4-small` (or higher) in the Space's "Settings → Hardware" tab. | |