chatterbox-tts-cli/README.md
dschlueter d1971049ce Add HTTP service, MCP adapter, systemd autostart; fix bugs and docs
- chatterbox_cli_v4.py: cooperative stop/interrupt via threading.Event;
  fix force_split_sentence (word boundary instead of mid-word cut);
  fix synthesize_streaming normalization order (split before preprocess)
- tts_service.py: FastAPI service with job queue, model cache, worker thread;
  LAN-accessible on 0.0.0.0:9999; audio_device default None (auto)
- mcp_adapter.py: MCP adapter (stdio + streamable-http) wrapping REST API;
  update docstring and default TTS_URL to port 9999
- requirements.txt: add fastapi, uvicorn, httpx, mcp
- README.md, BEDIENUNGSANLEITUNG.md: document service, MCP, AI integrations
  (Claude, Ollama, Open WebUI, llama.cpp, Home Assistant), systemd autostart
- CLAUDE.md: reflect current architecture (service + adapter now implemented)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 10:19:00 +02:00

344 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# chatterbox-tts-cli
Ein lokaler Text-to-Speech-Assistent auf Basis von
[Chatterbox TTS](https://github.com/resemble-ai/chatterbox) (Resemble AI).
Optimiert für deutsche Sprache; nutzbar als Kommandozeilen-Tool, als lokaler
HTTP-Service und als MCP-Server für KI-Assistenten.
## Features
- **Satz-für-Satz-Ausgabe** — gibt den ersten Satz aus, während die nächsten bereits generiert werden; minimale Latenz
- **Lückenlose Audiowiedergabe** — Callback-basierter OutputStream; keine Unterbrechungen zwischen Sätzen
- **Geschwindigkeitsanpassung** — pitch-erhaltende Zeitstreckung via pyrubberband (R3-Engine); `--speed 0.5``2.0`
- **Voice Cloning** — optionale WAV-Referenz für Akzent und Klang
- **Mehrsprachig** — Deutsch, Englisch und 20+ weitere Sprachen via `ChatterboxMultilingualTTS`
- **Deutsche Textnormalisierung** — Abkürzungen (ARD → „Ah Er De"), Uhrzeiten (14:58 → „vierzehn Uhr achtundfünfzig"), Jahreszahlen, Einheiten, Aussprache-Wörterbuch
- **HTTP-Service** — FastAPI-Service mit Job-Queue, Stop/Interrupt, Status-Endpunkt
- **MCP-Adapter** — direkte Integration in Claude Code, Claude Desktop und andere MCP-Hosts
- **Systemd-Autostart** — Service startet automatisch beim Login
---
## Systemvoraussetzungen
- Python 3.11+
- CUDA-GPU empfohlen (RTX 3070 oder besser; CPU möglich, aber langsam)
- Linux mit PipeWire oder PulseAudio
- `rubberband-cli` für Geschwindigkeitsanpassung:
```bash
sudo apt install rubberband-cli
```
---
## Installation
```bash
# 1. Conda-Umgebung
conda create -n chatterbox python=3.11
conda activate chatterbox
# 2. PyTorch mit CUDA (Beispiel CUDA 12.4)
pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu124
# 3. Alle Abhängigkeiten
pip install -r requirements.txt
```
Beim ersten Start mit `--lang de` werden Modelle automatisch heruntergeladen (~23 GB, `~/.cache/huggingface/`).
---
## Kommandozeilen-CLI
```bash
conda activate chatterbox
# Deutschen Text vorlesen
python chatterbox_cli_v4.py --lang de --input text.txt
# Mit Voice Cloning
python chatterbox_cli_v4.py --lang de --voice stimme.wav --input text.txt
# Text direkt übergeben
python chatterbox_cli_v4.py --lang en --text "Hello, how are you?"
# Langsamer sprechen (pitch bleibt gleich)
python chatterbox_cli_v4.py --lang de --speed 0.85 --input text.txt
# Nur speichern, nicht abspielen
python chatterbox_cli_v4.py --lang de --no-play --output ausgabe.wav --input text.txt
# Aussprache-Wörterbuch
python chatterbox_cli_v4.py --lang de --pronunciation-dict aussprache.json --input text.txt
```
### CLI-Optionen
| Option | Standard | Beschreibung |
|--------|----------|--------------|
| `--text TEXT` | — | Text direkt als Argument |
| `--input DATEI` | — | UTF-8-Textdatei |
| `--lang CODE` | `de` | Sprachcode (de, en, fr, es, …) |
| `--voice DATEI.wav` | — | Referenz-WAV für Voice Cloning (1030 s) |
| `--speed N` | `1.0` | Wiedergabegeschwindigkeit (0.52.0) |
| `--audio-device` | `pulse` | Ausgabegerät (z. B. `pulse`, `default`) |
| `--t3-model` | `v3` | Multilingual-Modell: `v3` oder `v2` |
| `--acronym-mode` | `german` | Akronym-Modus: `german`, `space`, `period_space` |
| `--pronunciation-dict` | — | JSON-Datei mit Aussprache-Substitutionen |
| `--save` | nein | WAV-Datei speichern |
| `--output DATEI.wav` | — | Ausgabepfad (impliziert `--save`) |
| `--no-play` | — | Nicht live abspielen |
| `--no-sentence-mode` | — | Größere Chunks statt satzweise |
| `--stream` | — | Streaming-Modus (experimentell) |
| `--no-progress` | — | Weniger Konsolenausgabe |
| `--debug-delay N` | `0` | Pause vor jedem Satz (zum Testen) |
| `--stop` | — | Laufende Ausgabe abbrechen |
---
## HTTP-Service (`tts_service.py`)
FastAPI-Service mit Job-Queue und Worker-Thread. Startet automatisch via systemd.
```bash
# Manueller Start
uvicorn tts_service:app --host 0.0.0.0 --port 9999
# Systemd (Autostart, läuft bereits)
systemctl --user status chatterbox-tts
systemctl --user restart chatterbox-tts
journalctl --user -u chatterbox-tts -f
```
### Endpunkte
| Methode | Pfad | Funktion |
|---------|------|----------|
| `POST` | `/speak` | Text in Queue einreihen |
| `POST` | `/stop` | Ausgabe abbrechen, Queue leeren |
| `GET` | `/health` | Service-Status und Gerät |
| `GET` | `/status` | Aktueller Job, Queue-Länge, letzte Jobs |
| `GET` | `/voices` | Unterstützte Sprachen |
### `/speak` Request-Body
```json
{
"text": "Hallo Welt",
"lang": "de",
"voice": null,
"interrupt": false,
"speed": 1.0,
"t3_model": "v3",
"audio_device": null,
"max_len": 400,
"save_wav": false,
"output_path": null,
"pronunciation_dict": null
}
```
```bash
# Beispiel
curl -X POST http://localhost:9999/speak \
-H "Content-Type: application/json" \
-d '{"text": "Hallo Welt", "lang": "de"}'
# Aus dem LAN
curl -X POST http://192.168.x.x:9999/speak \
-H "Content-Type: application/json" \
-d '{"text": "Text aus dem Netzwerk", "lang": "de"}'
# Laufende Ausgabe unterbrechen
curl -X POST http://localhost:9999/speak \
-d '{"text": "Wichtiger Text", "lang": "de", "interrupt": true}' \
-H "Content-Type: application/json"
# Stoppen
curl -X POST http://localhost:9999/stop
```
---
## MCP-Adapter (`mcp_adapter.py`)
Dünner Wrapper über die REST-API für MCP-fähige Hosts.
```bash
# stdio-Modus (Claude Code / Claude Desktop)
python mcp_adapter.py --stdio
# HTTP-Modus (andere MCP-Clients, Port 8001)
python mcp_adapter.py
# Anderen TTS-Service ansprechen
TTS_URL=http://192.168.1.10:9999 python mcp_adapter.py --stdio
```
### MCP-Tools
| Tool | Parameter | Funktion |
|------|-----------|----------|
| `speak` | text, lang, voice, interrupt, speed | Text ausgeben |
| `stop` | — | Ausgabe stoppen |
| `get_status` | — | Aktuellen Job abfragen |
| `list_voices` | — | Sprachen auflisten |
### Claude Code Konfiguration
Bereits eingerichtet via `claude mcp add --scope user`. Zur manuellen Einrichtung:
```bash
claude mcp add --scope user chatterbox-tts \
/home/dschlueter/miniforge3/envs/chatterbox/bin/python \
/home/dschlueter/chatterbox-tts-cli/mcp_adapter.py --stdio
```
### Claude Desktop (`~/.config/claude/claude_desktop_config.json`)
```json
{
"mcpServers": {
"chatterbox-tts": {
"command": "/home/dschlueter/miniforge3/envs/chatterbox/bin/python",
"args": ["/home/dschlueter/chatterbox-tts-cli/mcp_adapter.py", "--stdio"]
}
}
}
```
---
## Integration mit KI-Tools
### Claude Code / Claude Desktop — MCP (fertig eingerichtet)
Claude kann direkt die Tools `speak`, `stop`, `get_status` und `list_voices` aufrufen.
Kein weiterer Setup nötig.
### Ollama (llama3.2, qwen2.5, mistral-nemo u. a.)
Modelle mit Tool-Support können den REST-Service über Function Calling ansprechen:
```python
import ollama, httpx
tools = [{
"type": "function",
"function": {
"name": "speak",
"description": "Text als Sprache ausgeben",
"parameters": {
"type": "object",
"properties": {
"text": {"type": "string"},
"lang": {"type": "string", "default": "de"},
"speed": {"type": "number", "default": 1.0},
},
"required": ["text"],
},
},
}]
resp = ollama.chat(model="qwen2.5", messages=[{"role": "user", "content": "..."}], tools=tools)
for call in resp.message.tool_calls or []:
if call.function.name == "speak":
httpx.post("http://127.0.0.1:9999/speak", json=call.function.arguments)
```
### Open WebUI
Im Open-WebUI-Menü unter *Tools* eine neue Python-Klasse anlegen:
```python
import requests
class Tools:
def speak(self, text: str, lang: str = "de") -> str:
"""Text als Sprache ausgeben."""
r = requests.post("http://127.0.0.1:9999/speak",
json={"text": text, "lang": lang}, timeout=10)
return r.json().get("job_id", "error")
def stop(self) -> str:
"""Laufende Sprachausgabe stoppen."""
requests.post("http://127.0.0.1:9999/stop", timeout=5)
return "stopped"
```
### LM Studio
LM Studio bietet einen OpenAI-kompatiblen Endpunkt. Die Tool-Definition entspricht dem
Ollama-Beispiel oben; der Client wechselt lediglich auf die LM-Studio-URL.
### llama.cpp (Server-Modus)
llama.cpp mit `--jinja` unterstützt Function Calling, ruft aber nicht selbst HTTP-Endpoints
auf. Benötigt eine Middleware (z. B. das Ollama-Beispiel oben), die generierte Tool-Calls
abfängt und an `/speak` weiterleitet.
### Home Assistant
```yaml
# configuration.yaml
rest_command:
tts_speak:
url: "http://192.168.x.x:9999/speak"
method: POST
content_type: "application/json"
payload: '{"text": "{{ text }}", "lang": "de"}'
tts_stop:
url: "http://192.168.x.x:9999/stop"
method: POST
```
Aufruf in einer Automation:
```yaml
service: rest_command.tts_speak
data:
text: "Die Waschmaschine ist fertig."
```
### Node-RED / n8n
HTTP-Request-Node direkt auf `POST http://<host>:9999/speak` mit JSON-Body.
Kein weiterer Setup nötig.
### Pi (Inflection AI)
Keine Tool-API verfügbar — direkte Integration nicht möglich.
---
## Aussprache-Wörterbuch
```json
{
"Xi Jinping": "Schi Dschinping",
"Putin": "Pjutin",
"Seoul": "Söul"
}
```
```bash
python chatterbox_cli_v4.py --lang de \
--pronunciation-dict aussprache.json \
--input nachricht.txt
```
---
## Bekannte Einschränkungen
- **Wortbetonung** lässt sich nicht steuern — kein SSML. Abhilfe: Voice-Referenz mit gewünschter Betonung.
- **Laufendes `model.generate()`** kann nicht mid-call abgebrochen werden (Python-Thread-Grenzen); Stop greift am nächsten Chunk-Beginn.
- **Chinesische/japanische Namen** werden phonetisch angenähert.
---
## Lizenz
MIT — dieses Skript. Das Chatterbox-Modell: MIT-Lizenz (Resemble AI). Modellgewichte: CC BY-NC 4.0.