uncloseai-speech/README.md

396 lines
11 KiB
Markdown

# uncloseai-speech
OpenAI-compatible text-to-speech API server with state-of-the-art voice cloning.
**Default Engines:**
- [Qwen3-TTS](https://huggingface.co/Qwen/Qwen3-TTS-12Hz-1.7B-Base) - 1.7B parameters, 10 languages, 97ms latency (`tts-1-qwen`)
- [F5-TTS](https://huggingface.co/SWivid/F5-TTS) - 336M parameters, flow-matching zero-shot voice cloning, lower VRAM (`tts-1-f5`)
## Quick Start
```bash
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:
```bash
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](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html):
```bash
# 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:
```bash
docker run --rm --gpus all nvidia/cuda:12.0-base nvidia-smi
```
## Installation
### Docker with GPU
```bash
cp sample.env speech.env
make local
# Or: docker compose up -d --build
```
### Docker CPU Only
```bash
cp sample.env speech.env
make local-cpu
# Or: docker compose -f docker-compose.cpu.yml up -d --build
```
### Python Virtual Environment
```bash
# 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)
```bash
docker compose -f docker-compose.rocm.yml up -d --build
```
## API Reference
### Generate Speech
```bash
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:**
```bash
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
```bash
GET /v1/models
```
### List Voices
```bash
GET /v1/voices
```
Returns all voices with metadata including engine, sample rate, and language support.
## Python SDK Usage
```python
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`:
```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
```bash
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`:
```bash
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
```bash
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](docs/MODELS.md) for details.
## Troubleshooting
### Model Download Fails
```bash
# 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:
1. Add to `speech.env`: `EXTRA_ARGS=--xtts_device cpu`
2. Reduce workers: `EXTRA_ARGS=--workers 1`
3. 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](https://www.gnu.org/licenses/agpl-3.0.html).
### Key AGPL v3 Obligations
1. **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.
2. **What you must provide:**
- Complete source code of the running version
- Any modifications you've made
- Build instructions
3. **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:
```python
# 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
## Links
- [Documentation](docs/MODELS.md)
- [Contributing](CLAUDE.md)
- [Qwen3-TTS Model](https://huggingface.co/Qwen/Qwen3-TTS-12Hz-1.7B-Base)
- [OpenAI TTS API Reference](https://platform.openai.com/docs/api-reference/audio/createSpeech)