# 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)
