Previous 'Prov-uh-dence' kept the wrong open vowel; native RI pronunciation is 'prav-uh-dence' with short-a in the first syllable. |
||
|---|---|---|
| .github/workflows | ||
| cloned-voices | ||
| config | ||
| docs | ||
| scripts | ||
| voices | ||
| .gitignore | ||
| audio_reader.py | ||
| boot_docker_compose_ubuntu.sh | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| docker-compose.cpu.yml | ||
| docker-compose.min.yml | ||
| docker-compose.rocm.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| Dockerfile.min | ||
| download_voices_tts-1-hd.sh | ||
| download_voices_tts-1.sh | ||
| LICENSE | ||
| Makefile | ||
| openedai.py | ||
| pre_process_map.default.yaml | ||
| README.md | ||
| requirements-min.txt | ||
| requirements-rocm.txt | ||
| requirements.txt | ||
| sample.env | ||
| speech.py | ||
| startup.min.sh | ||
| startup.sh | ||
| TESTING.md | ||
| vars.sh.example | ||
| voice_registry.json | ||
| voice_to_speaker.default.yaml | ||
uncloseai-speech
OpenAI-compatible text-to-speech API server with state-of-the-art voice cloning.
Default Engines:
- Qwen3-TTS - 1.7B parameters, 10 languages, 97ms latency (
tts-1-qwen) - F5-TTS - 336M parameters, flow-matching zero-shot voice cloning, lower VRAM (
tts-1-f5)
Quick Start
git clone https://github.com/uncloseai/uncloseai-speech.git
cd uncloseai-speech
# Option 1: Docker with GPU (recommended)
make local
# Option 2: Docker CPU only
make local-cpu
# Option 3: Python venv (no Docker)
make venv && make venv-run
Test the API:
curl http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"input":"Hello from Qwen TTS!","voice":"alloy"}' \
-o test.mp3
Requirements
| Setup | GPU | RAM | Disk | Notes |
|---|---|---|---|---|
| Docker + GPU | NVIDIA 8GB+ VRAM | 8GB | 5GB | Recommended, fastest |
| Docker + CPU | None | 16GB | 5GB | ~10x slower |
| Python venv | Optional | 16GB | 5GB | Direct install |
GPU Setup (NVIDIA)
Install nvidia-container-toolkit:
# Ubuntu/Debian
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
Verify GPU access:
docker run --rm --gpus all nvidia/cuda:12.0-base nvidia-smi
Installation
Docker with GPU
cp sample.env speech.env
make local
# Or: docker compose up -d --build
Docker CPU Only
cp sample.env speech.env
make local-cpu
# Or: docker compose -f docker-compose.cpu.yml up -d --build
Python Virtual Environment
# Create and activate venv
make venv
# Run the server
make venv-run
# Or manually:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python speech.py
AMD GPU (ROCm)
docker compose -f docker-compose.rocm.yml up -d --build
API Reference
Generate Speech
POST /v1/audio/speech
| Parameter | Type | Default | Description |
|---|---|---|---|
input |
string | required | Text to synthesize |
voice |
string | alloy |
Voice name |
model |
string | tts-1-qwen |
Model ID |
response_format |
string | mp3 |
mp3, opus, aac, flac, wav, pcm |
speed |
float | 1.0 |
Speed multiplier (0.25-4.0) |
Example:
curl http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "tts-1-qwen",
"voice": "alloy",
"input": "The quick brown fox jumped over the lazy dog.",
"response_format": "mp3",
"speed": 1.0
}' -o speech.mp3
List Models
GET /v1/models
List Voices
GET /v1/voices
Returns all voices with metadata including engine, sample rate, and language support.
Python SDK Usage
import openai
client = openai.OpenAI(
api_key="not-needed",
base_url="http://localhost:8000/v1",
)
# Basic usage
with client.audio.speech.with_streaming_response.create(
model="tts-1-qwen",
voice="alloy",
input="Hello world!"
) as response:
response.stream_to_file("speech.mp3")
# With options
with client.audio.speech.with_streaming_response.create(
model="tts-1-qwen",
voice="nova",
input="This is faster speech.",
response_format="opus",
speed=1.2
) as response:
response.stream_to_file("speech.opus")
Voice Cloning
Qwen3-TTS clones any voice from a 3+ second audio sample.
1. Prepare Reference Audio
- Length: 3-30 seconds (6-10 optimal)
- Quality: Clear speech, minimal noise
- Format: WAV, MP3, or URL
2. Configure Voice
Edit config/voice_to_speaker.yaml:
tts-1-qwen:
my_voice:
ref_audio: voices/my_sample.wav # Local file or URL
ref_text: "Exact transcript of the audio."
language: English
3. Use the Voice
curl http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"voice":"my_voice","input":"Hello in my cloned voice!"}' \
-o output.mp3
Supported Languages
Chinese, English, Japanese, Korean, German, French, Russian, Portuguese, Spanish, Italian
Default Voices
| Voice | Description |
|---|---|
alloy |
Neutral, balanced |
echo |
Warm, conversational |
fable |
Expressive, storytelling |
onyx |
Deep, authoritative |
nova |
Friendly, upbeat |
shimmer |
Soft, gentle |
All voices use Qwen3-TTS voice cloning with pre-configured reference audio.
Configuration
Environment Variables
Edit speech.env:
TTS_HOME=voices # Model cache directory
HF_HOME=voices # HuggingFace cache
EXTRA_ARGS=--log-level INFO # Additional server args
Server Arguments
--xtts_device DEVICE Device: cuda, cpu, none (default: auto-detect)
--workers N Worker processes (default: 4)
--port PORT Listen port (default: 8000)
--host HOST Bind address (default: 0.0.0.0)
--log-level LEVEL DEBUG, INFO, WARNING, ERROR, CRITICAL
Makefile Commands
make help # Show all commands
# Local Development
make local # Docker with GPU
make local-cpu # Docker CPU only
make venv # Create Python venv
make venv-run # Run in venv
# Testing
make test # Test API
make logs # View logs
# Remote Deployment
make deploy # Sync + restart remote
make sync # Sync files only
make restart # Restart container
# Container
make start # Start container
make stop # Stop container
make clean # Remove container
Engines
Enabled by default:
| Model | Engine | Voices | Speed | Notes |
|---|---|---|---|---|
tts-1-qwen |
Qwen3-TTS | 40 | Fast | Voice cloning, 10 languages, 1.7B params |
tts-1-f5 |
F5-TTS | 40 | Faster | Voice cloning, flow-matching, 336M params, lower VRAM |
Disabled by default — enable by uncommenting in config/voice_to_speaker.yaml and requirements.txt:
| Model | Engine | Voices | Speed | Notes |
|---|---|---|---|---|
tts-1 |
Piper | 55 | Fast | CPU optimized |
tts-1-hd |
XTTS v2 | 8 | Medium | Voice cloning |
tts-1-silero |
Silero | 148 | Fast | 5 languages |
tts-1-kokoro |
Kokoro | 34 | Fast | 82M params |
See docs/MODELS.md for details.
Troubleshooting
Model Download Fails
# Check logs
docker logs uncloseai-speech-server-1
# Manual download
docker exec -it uncloseai-speech-server-1 \
huggingface-cli download Qwen/Qwen3-TTS-12Hz-1.7B-Base
Out of GPU Memory
Qwen3-TTS needs ~6GB VRAM. Options:
- Add to
speech.env:EXTRA_ARGS=--xtts_device cpu - Reduce workers:
EXTRA_ARGS=--workers 1 - Use CPU-only:
make local-cpu
Slow Generation
- GPU: ~1-2 seconds per sentence
- CPU: ~10-20 seconds per sentence
For faster CPU inference, enable Piper or Silero engines.
Voice Quality Issues
- Use 6-10 seconds of clear reference audio
- Ensure transcript exactly matches audio
- Avoid background noise
- Match language setting to audio language
Architecture
┌─────────────────────────────────────────────┐
│ Client │
│ (OpenAI SDK / curl) │
└─────────────────┬───────────────────────────┘
│ HTTP POST /v1/audio/speech
▼
┌─────────────────────────────────────────────┐
│ FastAPI Server │
│ (speech.py, port 8000) │
├─────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ Voice Config │
│ │ Qwen3-TTS │◄─────────────────────────┐ │
│ │ (default) │ config/voice_to_speaker │ │
│ └──────┬──────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────┐ │ │
│ │ FFmpeg │ Audio encoding │ │
│ │ (mp3/opus) │ │ │
│ └──────┬──────┘ │ │
│ │ │ │
└─────────┼──────────────────────────────────┘
│
▼ Audio stream
Client
License
AGPL v3 - This software is licensed under the GNU Affero General Public License v3.
Key AGPL v3 Obligations
-
Network use triggers copyleft - Unlike regular GPL, AGPL closes the "SaaS loophole". If you run uncloseai-speech as a service (even without distributing binaries), users have the right to request source code.
-
What you must provide:
- Complete source code of the running version
- Any modifications you've made
- Build instructions
-
How to comply:
- Link to your source repository in API responses or docs
- Offer source code download from the same server
- Keep your modifications in a public git repo
Practical Implementation
For a service at ai.foxhop.net, you could:
# Add to API response headers or /source endpoint
"source_code": "https://github.com/uncloseai/uncloseai-speech"
Or include it in your API's /models or root endpoint response.
Why AGPL for TTS?
From the Raccoon Mission values:
- Liberation - Keeps TTS libre
- Resilience - Ensures forks remain open
- Unification - Community improvements flow back