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>
This commit is contained in:
parent
bcf6374c29
commit
d1971049ce
7 changed files with 494 additions and 146 deletions
|
|
@ -13,13 +13,32 @@ Es wandelt geschriebenen Text in natürlich klingende Sprache um.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Das Programm starten
|
## Automatischer Start im Hintergrund
|
||||||
|
|
||||||
Öffne ein Terminal und gib folgende Befehle ein:
|
Der Sprach-Service startet automatisch, sobald du dich am Computer anmeldest.
|
||||||
|
Du musst nichts weiter tun — er läuft im Hintergrund und wartet auf Anfragen.
|
||||||
|
|
||||||
|
Ob der Service läuft, prüfst du so:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user status chatterbox-tts
|
||||||
|
```
|
||||||
|
|
||||||
|
Bei Problemen neu starten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user restart chatterbox-tts
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Das Kommandozeilen-Programm starten
|
||||||
|
|
||||||
|
Für die direkte Nutzung über das Terminal:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
conda activate chatterbox
|
conda activate chatterbox
|
||||||
cd ~/Python_Programs/chatterbox
|
cd ~/chatterbox-tts-cli
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -85,7 +104,7 @@ python chatterbox_cli_v4.py --lang en --text "Good morning, how are you?"
|
||||||
Mit `--speed` kann man einstellen, wie schnell der Text gesprochen wird.
|
Mit `--speed` kann man einstellen, wie schnell der Text gesprochen wird.
|
||||||
|
|
||||||
- `1.0` = normale Geschwindigkeit (Standard)
|
- `1.0` = normale Geschwindigkeit (Standard)
|
||||||
- `0.85` = etwas langsamer — gut für ältere Hörer
|
- `0.85` = etwas langsamer — gut für entspanntes Zuhören
|
||||||
- `0.75` = deutlich langsamer
|
- `0.75` = deutlich langsamer
|
||||||
- `1.2` = etwas schneller
|
- `1.2` = etwas schneller
|
||||||
|
|
||||||
|
|
@ -149,6 +168,73 @@ python chatterbox_cli_v4.py --lang de \
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Den Service aus dem Netzwerk nutzen
|
||||||
|
|
||||||
|
Der Service ist im gesamten Heimnetzwerk erreichbar — zum Beispiel vom Handy,
|
||||||
|
Tablet oder einem anderen Computer.
|
||||||
|
|
||||||
|
**Text vorlesen lassen** (aus jedem Gerät im Netzwerk):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://COMPUTER-IP:9999/speak \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"text": "Hallo aus dem Netzwerk", "lang": "de"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
`COMPUTER-IP` ersetzen durch die IP-Adresse dieses Computers (z. B. `192.168.1.42`).
|
||||||
|
|
||||||
|
**Aktuelle IP-Adresse herausfinden:**
|
||||||
|
```bash
|
||||||
|
hostname -I
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ausgabe stoppen:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://COMPUTER-IP:9999/stop
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## KI-Assistenten lassen vorlesen
|
||||||
|
|
||||||
|
Wenn du einen KI-Assistenten auf diesem oder einem anderen Gerät nutzt,
|
||||||
|
kann er den TTS-Service direkt ansprechen:
|
||||||
|
|
||||||
|
### Claude (Claude Code / Claude Desktop)
|
||||||
|
|
||||||
|
Claude ist bereits mit dem TTS-Service verbunden. Du kannst Claude einfach bitten,
|
||||||
|
etwas vorzulesen — er ruft den Service automatisch auf.
|
||||||
|
|
||||||
|
Beispiel-Anfrage an Claude:
|
||||||
|
> „Lies mir bitte diesen Text vor: ..."
|
||||||
|
|
||||||
|
### Home Assistant
|
||||||
|
|
||||||
|
In der `configuration.yaml` folgendes eintragen:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
rest_command:
|
||||||
|
tts_sprechen:
|
||||||
|
url: "http://COMPUTER-IP:9999/speak"
|
||||||
|
method: POST
|
||||||
|
content_type: "application/json"
|
||||||
|
payload: '{"text": "{{ text }}", "lang": "de"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach in einer Automation verwendbar:
|
||||||
|
```yaml
|
||||||
|
service: rest_command.tts_sprechen
|
||||||
|
data:
|
||||||
|
text: "Die Waschmaschine ist fertig."
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ollama / LM Studio / Open WebUI
|
||||||
|
|
||||||
|
Lokale KI-Modelle (z. B. llama, qwen) können über eine kleine Hilfsklasse mit dem
|
||||||
|
Service verbunden werden — Details in der `README.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Typischer Arbeitsablauf
|
## Typischer Arbeitsablauf
|
||||||
|
|
||||||
1. Text in einem Editor schreiben und als `.txt`-Datei speichern
|
1. Text in einem Editor schreiben und als `.txt`-Datei speichern
|
||||||
|
|
@ -175,11 +261,17 @@ python chatterbox_cli_v4.py --lang de \
|
||||||
|
|
||||||
**Kein Ton zu hören:**
|
**Kein Ton zu hören:**
|
||||||
```bash
|
```bash
|
||||||
# Ausgabegerät prüfen
|
|
||||||
python -c "import sounddevice; print(sounddevice.query_devices())"
|
python -c "import sounddevice; print(sounddevice.query_devices())"
|
||||||
```
|
```
|
||||||
Dann `--audio-device pulse` oder das passende Gerät angeben.
|
Dann `--audio-device pulse` oder das passende Gerät angeben.
|
||||||
|
|
||||||
|
**Service antwortet nicht:**
|
||||||
|
```bash
|
||||||
|
systemctl --user restart chatterbox-tts
|
||||||
|
# Warte 5 Sekunden, dann:
|
||||||
|
curl http://localhost:9999/health
|
||||||
|
```
|
||||||
|
|
||||||
**„Modell nicht gefunden":**
|
**„Modell nicht gefunden":**
|
||||||
Beim ersten Start wird das Modell heruntergeladen (~2 GB).
|
Beim ersten Start wird das Modell heruntergeladen (~2 GB).
|
||||||
Sicherstellen, dass eine Internetverbindung besteht.
|
Sicherstellen, dass eine Internetverbindung besteht.
|
||||||
|
|
@ -198,3 +290,5 @@ kann 30–60 Sekunden brauchen. Mit GPU (CUDA) dauert es ca. 5–10 Sekunden.
|
||||||
nicht immer perfekt — mit der Aussprache-Datei lässt sich das korrigieren.
|
nicht immer perfekt — mit der Aussprache-Datei lässt sich das korrigieren.
|
||||||
- Das Programm liest alles vor, was in der Datei steht — also auch
|
- Das Programm liest alles vor, was in der Datei steht — also auch
|
||||||
Überschriften und Metadaten wie „Schlagzeile:" oder „Stand:".
|
Überschriften und Metadaten wie „Schlagzeile:" oder „Stand:".
|
||||||
|
- Eine laufende Ausgabe kann erst am Ende des aktuellen Satzes unterbrochen
|
||||||
|
werden, nicht sofort mitten im Wort.
|
||||||
|
|
|
||||||
113
CLAUDE.md
113
CLAUDE.md
|
|
@ -31,56 +31,105 @@ python chatterbox_cli_v4.py --lang de --pronunciation-dict aussprache.json --inp
|
||||||
|
|
||||||
No build step, no test suite, no linter configuration — this is a single-file script.
|
No build step, no test suite, no linter configuration — this is a single-file script.
|
||||||
|
|
||||||
|
## Running the HTTP Service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Läuft als systemd-User-Service (Autostart beim Login):
|
||||||
|
systemctl --user status chatterbox-tts
|
||||||
|
systemctl --user restart chatterbox-tts
|
||||||
|
journalctl --user -u chatterbox-tts -f
|
||||||
|
|
||||||
|
# Manuell starten (Port 9999, LAN-weit erreichbar):
|
||||||
|
uvicorn tts_service:app --host 0.0.0.0 --port 9999
|
||||||
|
|
||||||
|
# Health-Check:
|
||||||
|
curl http://127.0.0.1:9999/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Endpunkte: `POST /speak`, `POST /stop`, `GET /health`, `GET /status`, `GET /voices`
|
||||||
|
|
||||||
|
## Running the MCP Adapter
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# stdio (Claude Code / Claude Desktop) — bereits in ~/.claude.json konfiguriert:
|
||||||
|
python mcp_adapter.py --stdio
|
||||||
|
|
||||||
|
# HTTP-Transport (Port 8001):
|
||||||
|
python mcp_adapter.py
|
||||||
|
|
||||||
|
# Anderen TTS-Service ansprechen:
|
||||||
|
TTS_URL=http://192.168.1.10:9999 python mcp_adapter.py --stdio
|
||||||
|
```
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
Everything lives in `chatterbox_cli_v4.py`. The processing pipeline is:
|
### Files
|
||||||
|
|
||||||
**Text input → normalization → chunking → TTS generation → audio output**
|
| Datei | Funktion |
|
||||||
|
|-------|----------|
|
||||||
|
| `chatterbox_cli_v4.py` | Kern-CLI und alle Hilfsfunktionen; wird von `tts_service.py` importiert |
|
||||||
|
| `tts_service.py` | FastAPI-Service mit Job-Queue und Worker-Thread |
|
||||||
|
| `mcp_adapter.py` | MCP-Wrapper über die REST-API |
|
||||||
|
|
||||||
|
### CLI pipeline (`chatterbox_cli_v4.py`)
|
||||||
|
|
||||||
|
**Text input → `clean_raw_text` → chunking → `preprocess_tts_text` per chunk → TTS generation → audio output**
|
||||||
|
|
||||||
|
Reihenfolge ist kritisch: erst splitten (Satzgrenzen auf Rohtext erkennen), dann normalisieren (Akronym-Punkte würden sonst falsche Satzgrenzen erzeugen).
|
||||||
|
|
||||||
|
### Stop/Interrupt
|
||||||
|
|
||||||
|
Modul-globales `threading.Event`:
|
||||||
|
```python
|
||||||
|
STOP_REQUESTED = threading.Event()
|
||||||
|
request_stop() # setzt das Event
|
||||||
|
clear_stop() # löscht es vor jedem neuen Job
|
||||||
|
stop_requested() # abfragen
|
||||||
|
```
|
||||||
|
`PlaybackWorker` und beide Synthesize-Funktionen prüfen das Event an Chunk-Grenzen. Ein laufendes `model.generate()` kann nicht mid-call abgebrochen werden (Python-Thread-Grenzen) — der Abbruch greift am nächsten Chunk.
|
||||||
|
|
||||||
### Text normalization (`preprocess_tts_text`)
|
### Text normalization (`preprocess_tts_text`)
|
||||||
Applied per chunk before synthesis. Order matters:
|
|
||||||
1. Pronunciation dict substitutions (before acronym expansion, so proper names are caught first)
|
1. Pronunciation dict (vor Akronym-Expansion, damit Eigennamen zuerst greifen)
|
||||||
2. Unit normalization (120 km/h → "120 Kilometer pro Stunde")
|
2. Unit normalization (120 km/h → "120 Kilometer pro Stunde")
|
||||||
3. Time normalization (14:58 → "vierzehn Uhr achtundfünfzig")
|
3. Time normalization (14:58 → "vierzehn Uhr achtundfünfzig")
|
||||||
4. Year normalization (2026 → "zweitausendsechsundzwanzig")
|
4. Year normalization (2026 → "zweitausendsechsundzwanzig")
|
||||||
5. Acronym spelling (ARD → "Ah Er De"; skips entries in `NON_SPELLED_ACRONYMS`)
|
5. Acronym spelling (ARD → "Ah Er De"; `NON_SPELLED_ACRONYMS` ausgenommen)
|
||||||
|
|
||||||
`DEFAULT_PRONUNCIATION_DE` contains built-in German phonetic approximations (e.g. Xi → "Schi").
|
`DEFAULT_PRONUNCIATION_DE` enthält eingebaute deutsche Lautschrift-Näherungen (z. B. Xi → "Schi").
|
||||||
|
|
||||||
### Text chunking
|
### Text chunking
|
||||||
Three modes (chosen by CLI flags):
|
|
||||||
- **sentence_mode** (default): `split_into_sentences()` — one sentence per TTS call, lowest latency to first audio
|
|
||||||
- **conversation_mode**: `split_for_conversation()` — first chunk is small (`--first-chunk-len`, default 80 chars), rest up to `--len` (400)
|
|
||||||
- **plain**: `split_long_text()` — paragraph-aware chunking up to `--len`
|
|
||||||
|
|
||||||
`SENTENCE_END_RE` handles edge cases like ordinal numbers, ellipses, and CJK punctuation. `SEPARATOR_LINE_RE` silently drops lines like `--- Ende ---`.
|
Drei Modi (CLI-Flags):
|
||||||
|
- **sentence_mode** (default): `split_into_sentences()` — ein Satz pro TTS-Call, geringste Latenz
|
||||||
|
- **conversation_mode**: `split_for_conversation()` — erster Chunk klein (`--first-chunk-len`, default 80), Rest bis `--len` (400)
|
||||||
|
- **plain**: `split_long_text()` — absatzbasiertes Chunking bis `--len`
|
||||||
|
|
||||||
|
`force_split_sentence` sucht bei Überlänge erst vorwärts zum nächsten Wortende — kein Schneiden mitten im Wort.
|
||||||
|
|
||||||
### Model loading (`load_model`)
|
### Model loading (`load_model`)
|
||||||
- `--lang en` → `ChatterboxTTS` (mono, always available)
|
|
||||||
- Other languages → `ChatterboxMultilingualTTS` (requires multilingual package; `HAS_MULTILINGUAL` flag guards import)
|
- `--lang en` → `ChatterboxTTS` (mono, immer verfügbar)
|
||||||
- `--t3-model v3` (default) or `v2` selects the multilingual T3 checkpoint
|
- Andere Sprachen → `ChatterboxMultilingualTTS` (`HAS_MULTILINGUAL`-Flag bewacht Import)
|
||||||
- Models are downloaded to `~/.cache/huggingface/` on first use (~2–3 GB)
|
- `--t3-model v3` (default) oder `v2` wählt den multilingualen T3-Checkpoint
|
||||||
- **Critical**: `attn_implementation = "eager"` is forced at import time because SDPA returns `None` attention weights, breaking the `AlignmentStreamAnalyzer` hook
|
- Modelle werden in `~/.cache/huggingface/` gecacht (~2–3 GB)
|
||||||
|
- **Kritisch**: `attn_implementation = "eager"` wird beim Import erzwungen — SDPA gibt `None`-Attention-Weights zurück und bricht den `AlignmentStreamAnalyzer`-Hook
|
||||||
|
|
||||||
### Audio output (`PlaybackWorker`)
|
### Audio output (`PlaybackWorker`)
|
||||||
- Uses `sounddevice.OutputStream` with a callback at 48 kHz (PipeWire/PulseAudio standard)
|
|
||||||
- Internal producer thread converts Torch tensors → `CALLBACK_BLOCK`-sized (2048 samples) numpy arrays
|
- `sounddevice.OutputStream` mit Callback bei 48 kHz (PipeWire/PulseAudio-Standard)
|
||||||
- If `--speed != 1.0`: pyrubberband R3-Engine (`--fine` flag) stretches time without pitch change before resampling
|
- Interner Producer-Thread: Torch-Tensoren → `CALLBACK_BLOCK`-große (2048 Samples) numpy-Arrays
|
||||||
- Resampling: `torchaudio.functional.resample(chunk, model_sr, 48000)`
|
- `--speed != 1.0`: pyrubberband R3-Engine (`--fine`) streckt Zeit ohne Pitch-Änderung, dann Resampling via `torchaudio.functional.resample(chunk, model_sr, 48000)`
|
||||||
- `PlaybackWorker.stop()` sends `None` sentinel into the queue and joins the thread
|
- `PlaybackWorker.stop()` schickt `None`-Sentinel in die Queue und jointed den Thread
|
||||||
|
|
||||||
### Two synthesis paths
|
### Two synthesis paths
|
||||||
- **`synthesize_non_streaming`**: generates each chunk fully, feeds finished tensors to `PlaybackWorker`, concatenates all wavs for `--save`
|
|
||||||
- **`synthesize_streaming`**: calls `model.generate_stream()` with `chunk_size`; each yielded audio sub-chunk goes directly to `PlaybackWorker`; marked experimental in docs
|
|
||||||
|
|
||||||
## Planned extensions (Ideen/)
|
- **`synthesize_non_streaming`**: generiert jeden Chunk vollständig, füttert fertige Tensoren in `PlaybackWorker`, concateniert alle WAVs für `--save`
|
||||||
|
- **`synthesize_streaming`**: ruft `model.generate_stream()` mit `chunk_size` auf; jeder Audio-Sub-Chunk geht direkt in `PlaybackWorker`; experimentell
|
||||||
|
|
||||||
The `Ideen/` folder documents a planned **REST/MCP bridge**:
|
### HTTP Service (`tts_service.py`)
|
||||||
- `tts_service.py` (FastAPI): `POST /speak`, `POST /stop`, `GET /health`, `GET /voices`
|
|
||||||
- `mcp_adapter.py`: thin MCP wrapper calling the REST API
|
|
||||||
- `chatterbox_backend.py`: imports `chatterbox_cli_v4.py` via `importlib` and calls `synthesize_non_streaming()` directly
|
|
||||||
|
|
||||||
Key gaps to address before building the service:
|
- **Modell-Cache**: `_model_cache: dict[(lang, t3_model), (model, kind, sr)]` — einmal laden, halten; Thread-sicher via `_model_lock`
|
||||||
1. **Stop/interrupt**: `PlaybackWorker.stop()` drains the audio queue, but a blocking `model.generate()` call cannot be interrupted mid-run. A `threading.Event`-based cancel token threaded through `synthesize_non_streaming` is the planned approach.
|
- **Job-Queue**: `queue.Queue[SpeakJob]` mit einzelnem Worker-Thread; verhindert parallelen GPU/Audio-Zugriff
|
||||||
2. **Model caching**: `load_model()` reloads from disk on every call; a service needs a per-language singleton.
|
- **`SpeakRequest.interrupt`**: ruft `request_stop()` + `_drain_queue()` vor dem Einreihen auf
|
||||||
3. **Status object**: progress is `print()`-based; a service needs structured state.
|
- **Status**: `_current_job`, `_recent_jobs` (max. 20) via `_state_lock` thread-safe lesbar
|
||||||
|
|
|
||||||
357
README.md
357
README.md
|
|
@ -1,115 +1,320 @@
|
||||||
# chatterbox-tts-cli
|
# chatterbox-tts-cli
|
||||||
|
|
||||||
Ein kommandozeilenbasierter TTS-Assistent (Text-to-Speech) auf Basis von
|
Ein lokaler Text-to-Speech-Assistent auf Basis von
|
||||||
[Chatterbox TTS](https://github.com/resemble-ai/chatterbox) (Resemble AI).
|
[Chatterbox TTS](https://github.com/resemble-ai/chatterbox) (Resemble AI).
|
||||||
Optimiert für deutsche Sprache und den Einsatz als Audio-Vorlesehilfe, z. B.
|
Optimiert für deutsche Sprache; nutzbar als Kommandozeilen-Tool, als lokaler
|
||||||
für Senioren oder Accessibility-Anwendungen.
|
HTTP-Service und als MCP-Server für KI-Assistenten.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Satz-für-Satz-Streaming** — gibt den ersten Satz aus, während die nächsten
|
- **Satz-für-Satz-Ausgabe** — gibt den ersten Satz aus, während die nächsten bereits generiert werden; minimale Latenz
|
||||||
bereits generiert werden; minimale Latenz
|
- **Lückenlose Audiowiedergabe** — Callback-basierter OutputStream; keine Unterbrechungen zwischen Sätzen
|
||||||
- **Lückenlose Audiowiedergabe** — Callback-basierter OutputStream mit
|
- **Geschwindigkeitsanpassung** — pitch-erhaltende Zeitstreckung via pyrubberband (R3-Engine); `--speed 0.5`–`2.0`
|
||||||
vorgefertigten Blöcken; keine Unterbrechungen zwischen Sätzen
|
|
||||||
- **Geschwindigkeitsanpassung** — pitch-erhaltende Zeitstreckung via
|
|
||||||
pyrubberband (R3-Engine); konfigurierbar per `--speed`
|
|
||||||
- **Voice Cloning** — optionale WAV-Referenz für Akzent und Klang
|
- **Voice Cloning** — optionale WAV-Referenz für Akzent und Klang
|
||||||
- **Mehrsprachig** — Deutsch, Englisch und 20+ weitere Sprachen via
|
- **Mehrsprachig** — Deutsch, Englisch und 20+ weitere Sprachen via `ChatterboxMultilingualTTS`
|
||||||
`ChatterboxMultilingualTTS` (erfordert Multilingual-Setup, s. u.)
|
- **Deutsche Textnormalisierung** — Abkürzungen (ARD → „Ah Er De"), Uhrzeiten (14:58 → „vierzehn Uhr achtundfünfzig"), Jahreszahlen, Einheiten, Aussprache-Wörterbuch
|
||||||
- **Deutsche Textnormalisierung**
|
- **HTTP-Service** — FastAPI-Service mit Job-Queue, Stop/Interrupt, Status-Endpunkt
|
||||||
- Abkürzungen: ARD → "Ah Er De", YMCA → "Ypsilon Em Tse Ah"
|
- **MCP-Adapter** — direkte Integration in Claude Code, Claude Desktop und andere MCP-Hosts
|
||||||
- Komposita: US-Präsident → "U Es Präsident"
|
- **Systemd-Autostart** — Service startet automatisch beim Login
|
||||||
- Uhrzeiten: 14:58 → "vierzehn Uhr achtundfünfzig"
|
|
||||||
- Jahreszahlen: 2026 → "zweitausendsechsundzwanzig"
|
---
|
||||||
- Einheiten: 120 km/h → "120 Kilometer pro Stunde"
|
|
||||||
- **Konfigurierbares Aussprache-Wörterbuch** — Eigennamen und Fremdwörter
|
|
||||||
per JSON-Datei phonetisch überschreiben
|
|
||||||
- **Automatische Satz-Erkennung** — intelligentes Splitting inkl.
|
|
||||||
Ordinalzahlen, Paragraphen und Trennzeilen
|
|
||||||
|
|
||||||
## Systemvoraussetzungen
|
## Systemvoraussetzungen
|
||||||
|
|
||||||
- Python 3.11+
|
- Python 3.11+
|
||||||
- CUDA-GPU empfohlen (RTX 3070 oder besser; CPU möglich aber langsam)
|
- CUDA-GPU empfohlen (RTX 3070 oder besser; CPU möglich, aber langsam)
|
||||||
- Linux mit PipeWire oder PulseAudio (für `--audio-device pulse`)
|
- Linux mit PipeWire oder PulseAudio
|
||||||
- `rubberband-cli` (nur wenn `--speed` != 1.0 genutzt wird):
|
- `rubberband-cli` für Geschwindigkeitsanpassung:
|
||||||
```bash
|
```bash
|
||||||
sudo apt install rubberband-cli
|
sudo apt install rubberband-cli
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Conda-Umgebung erstellen (empfohlen)
|
# 1. Conda-Umgebung
|
||||||
conda create -n chatterbox python=3.11
|
conda create -n chatterbox python=3.11
|
||||||
conda activate chatterbox
|
conda activate chatterbox
|
||||||
|
|
||||||
# 2. PyTorch mit CUDA installieren (Beispiel für CUDA 12.4)
|
# 2. PyTorch mit CUDA (Beispiel CUDA 12.4)
|
||||||
pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu124
|
pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu124
|
||||||
|
|
||||||
# 3. Chatterbox und weitere Abhängigkeiten
|
# 3. Alle Abhängigkeiten
|
||||||
pip install chatterbox-tts sounddevice pyrubberband
|
pip install -r requirements.txt
|
||||||
|
|
||||||
# 4. Skript herunterladen
|
|
||||||
# chatterbox_cli_v4.py in das Arbeitsverzeichnis legen
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multilingual-Setup (für Deutsch und andere Nicht-Englisch-Sprachen)
|
Beim ersten Start mit `--lang de` werden Modelle automatisch heruntergeladen (~2–3 GB, `~/.cache/huggingface/`).
|
||||||
|
|
||||||
Das Standard-Paket `chatterbox-tts` enthält die Multilingual-Unterstützung
|
---
|
||||||
noch nicht vollständig. Notwendige Schritte:
|
|
||||||
|
## Kommandozeilen-CLI
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Multilingual-Modell herunterladen (beim ersten Start automatisch)
|
conda activate chatterbox
|
||||||
# Modell-Auswahl: v3 (Standard, besser) oder v2
|
|
||||||
# Wird in ~/.cache/huggingface/ gespeichert
|
# 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
|
||||||
```
|
```
|
||||||
|
|
||||||
Beim ersten Start mit `--lang de` werden die Modelle automatisch heruntergeladen
|
### CLI-Optionen
|
||||||
(ca. 2–3 GB).
|
|
||||||
|
|
||||||
## Schnellstart
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Deutschen Text vorlesen (aus Datei)
|
|
||||||
python chatterbox_cli_v4.py --lang de --input mein_text.txt
|
|
||||||
|
|
||||||
# Mit eigener Stimme (Voice Cloning)
|
|
||||||
python chatterbox_cli_v4.py --lang de \
|
|
||||||
--voice meine_stimme.wav \
|
|
||||||
--input mein_text.txt
|
|
||||||
|
|
||||||
# Etwas langsamer sprechen
|
|
||||||
python chatterbox_cli_v4.py --lang de --speed 0.85 --input mein_text.txt
|
|
||||||
|
|
||||||
# Englisch
|
|
||||||
python chatterbox_cli_v4.py --lang en --text "Hello, how are you today?"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Optionen
|
|
||||||
|
|
||||||
| Option | Standard | Beschreibung |
|
| Option | Standard | Beschreibung |
|
||||||
|--------|----------|--------------|
|
|--------|----------|--------------|
|
||||||
| `--text TEXT` | — | Text direkt als Argument |
|
| `--text TEXT` | — | Text direkt als Argument |
|
||||||
| `--input DATEI` | — | UTF-8-Textdatei als Eingabe |
|
| `--input DATEI` | — | UTF-8-Textdatei |
|
||||||
| `--lang CODE` | `de` | Sprachcode (de, en, fr, es, …) |
|
| `--lang CODE` | `de` | Sprachcode (de, en, fr, es, …) |
|
||||||
| `--voice DATEI.wav` | — | Referenz-WAV für Voice Cloning |
|
| `--voice DATEI.wav` | — | Referenz-WAV für Voice Cloning (10–30 s) |
|
||||||
| `--speed 0.85` | `1.0` | Geschwindigkeit (0.7–1.3); pitch bleibt gleich |
|
| `--speed N` | `1.0` | Wiedergabegeschwindigkeit (0.5–2.0) |
|
||||||
| `--audio-device` | `pulse` | Ausgabegerät (z. B. `pulse`, `default`) |
|
| `--audio-device` | `pulse` | Ausgabegerät (z. B. `pulse`, `default`) |
|
||||||
| `--t3-model` | `v3` | Multilingual-Modell: `v3` oder `v2` |
|
| `--t3-model` | `v3` | Multilingual-Modell: `v3` oder `v2` |
|
||||||
| `--acronym-mode` | `german` | Akronym-Modus: `german`, `space`, `period_space` |
|
| `--acronym-mode` | `german` | Akronym-Modus: `german`, `space`, `period_space` |
|
||||||
| `--pronunciation-dict` | — | JSON-Datei mit Aussprache-Substitutionen |
|
| `--pronunciation-dict` | — | JSON-Datei mit Aussprache-Substitutionen |
|
||||||
| `--save` | nein | WAV-Datei speichern |
|
| `--save` | nein | WAV-Datei speichern |
|
||||||
| `--output DATEI.wav` | — | Ausgabepfad (impliziert `--save`) |
|
| `--output DATEI.wav` | — | Ausgabepfad (impliziert `--save`) |
|
||||||
| `--no-play` | — | Nur speichern, nicht abspielen |
|
| `--no-play` | — | Nicht live abspielen |
|
||||||
| `--no-sentence-mode` | — | Text als Ganzes statt satzweise verarbeiten |
|
| `--no-sentence-mode` | — | Größere Chunks statt satzweise |
|
||||||
| `--debug-delay N` | `0` | Pause in Sekunden vor jedem Satz (zum Testen) |
|
| `--stream` | — | Streaming-Modus (experimentell) |
|
||||||
| `--speed` | `1.0` | Sprechgeschwindigkeit |
|
| `--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
|
## Aussprache-Wörterbuch
|
||||||
|
|
||||||
Für Eigennamen und Fremdwörter, die das Modell falsch ausspricht:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"Xi Jinping": "Schi Dschinping",
|
"Xi Jinping": "Schi Dschinping",
|
||||||
|
|
@ -124,16 +329,16 @@ python chatterbox_cli_v4.py --lang de \
|
||||||
--input nachricht.txt
|
--input nachricht.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Bekannte Einschränkungen
|
## Bekannte Einschränkungen
|
||||||
|
|
||||||
- **Wortbetonung** lässt sich nicht steuern — das Modell kennt kein SSML.
|
- **Wortbetonung** lässt sich nicht steuern — kein SSML. Abhilfe: Voice-Referenz mit gewünschter Betonung.
|
||||||
Abhilfe: Voice-Referenz mit gewünschter Betonung aufnehmen.
|
- **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; das Modell
|
- **Chinesische/japanische Namen** werden phonetisch angenähert.
|
||||||
ist nicht für asiatische Phonetik optimiert.
|
|
||||||
- **Sehr lange Texte** werden satzweise verarbeitet; zwischen Absätzen können
|
---
|
||||||
kurze Pausen entstehen (Generierungszeit für den nächsten Satz).
|
|
||||||
|
|
||||||
## Lizenz
|
## Lizenz
|
||||||
|
|
||||||
MIT — dieses Skript. Das Chatterbox-Modell unterliegt der MIT-Lizenz von
|
MIT — dieses Skript. Das Chatterbox-Modell: MIT-Lizenz (Resemble AI). Modellgewichte: CC BY-NC 4.0.
|
||||||
Resemble AI. Die Modellgewichte sind nicht-kommerziell (CC BY-NC 4.0).
|
|
||||||
|
|
|
||||||
|
|
@ -463,7 +463,9 @@ def force_split_sentence(text: str, max_len: int) -> List[str]:
|
||||||
while len(remaining) > max_len:
|
while len(remaining) > max_len:
|
||||||
split_pos = remaining.rfind(" ", 0, max_len + 1)
|
split_pos = remaining.rfind(" ", 0, max_len + 1)
|
||||||
if split_pos <= 0:
|
if split_pos <= 0:
|
||||||
split_pos = max_len
|
# Kein Leerzeichen vor max_len — vorwärts zum nächsten Wortende suchen
|
||||||
|
next_space = remaining.find(" ", max_len)
|
||||||
|
split_pos = next_space if next_space != -1 else len(remaining)
|
||||||
parts.append(remaining[:split_pos].strip())
|
parts.append(remaining[:split_pos].strip())
|
||||||
remaining = remaining[split_pos:].strip()
|
remaining = remaining[split_pos:].strip()
|
||||||
|
|
||||||
|
|
@ -830,15 +832,9 @@ def synthesize_streaming(
|
||||||
if voice_path and not Path(voice_path).exists():
|
if voice_path and not Path(voice_path).exists():
|
||||||
raise FileNotFoundError(f"Voice-Referenz nicht gefunden: {voice_path}")
|
raise FileNotFoundError(f"Voice-Referenz nicht gefunden: {voice_path}")
|
||||||
|
|
||||||
text = preprocess_tts_text(
|
# Erst bereinigen und splitten, dann pro Chunk normalisieren —
|
||||||
text=text,
|
# sonst erzeugen Akronym-Punkte ("ARD" → "Ah Er De.") falsche Satzgrenzen.
|
||||||
lang=lang,
|
text = clean_raw_text(text)
|
||||||
spell_uppercase_acronyms=spell_uppercase_acronyms,
|
|
||||||
acronym_mode=acronym_mode,
|
|
||||||
normalize_time_values=normalize_time_values,
|
|
||||||
normalize_year_values=normalize_year_values,
|
|
||||||
normalize_units_values=normalize_units_values,
|
|
||||||
)
|
|
||||||
|
|
||||||
model, model_kind, sr = load_model(lang, device)
|
model, model_kind, sr = load_model(lang, device)
|
||||||
|
|
||||||
|
|
@ -849,9 +845,20 @@ def synthesize_streaming(
|
||||||
)
|
)
|
||||||
|
|
||||||
if conversation_mode:
|
if conversation_mode:
|
||||||
text_chunks = split_for_conversation(text, first_chunk_len=first_chunk_len, max_len=max_len)
|
raw_chunks = split_for_conversation(text, first_chunk_len=first_chunk_len, max_len=max_len)
|
||||||
else:
|
else:
|
||||||
text_chunks = split_long_text(text, max_len=max_len)
|
raw_chunks = split_long_text(text, max_len=max_len)
|
||||||
|
|
||||||
|
preprocess_kw = dict(
|
||||||
|
lang=lang,
|
||||||
|
spell_uppercase_acronyms=spell_uppercase_acronyms,
|
||||||
|
acronym_mode=acronym_mode,
|
||||||
|
normalize_time_values=normalize_time_values,
|
||||||
|
normalize_year_values=normalize_year_values,
|
||||||
|
normalize_units_values=normalize_units_values,
|
||||||
|
)
|
||||||
|
text_chunks = [preprocess_tts_text(c, **preprocess_kw) for c in raw_chunks]
|
||||||
|
text_chunks = [c for c in text_chunks if c.strip()]
|
||||||
|
|
||||||
if not text_chunks:
|
if not text_chunks:
|
||||||
raise ValueError("Kein verwertbarer Text nach dem Einlesen gefunden.")
|
raise ValueError("Kein verwertbarer Text nach dem Einlesen gefunden.")
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"""
|
"""
|
||||||
Chatterbox TTS – MCP-Adapter
|
Chatterbox TTS – MCP-Adapter
|
||||||
|
|
||||||
Setzt einen laufenden tts_service.py voraus (Standard: http://127.0.0.1:8000).
|
Setzt einen laufenden tts_service.py voraus (Standard: http://127.0.0.1:9999).
|
||||||
|
|
||||||
Start (streamable-http, Port 8001 – für beliebige MCP-Clients):
|
Start (streamable-http, Port 8001 – für beliebige MCP-Clients):
|
||||||
python mcp_adapter.py
|
python mcp_adapter.py
|
||||||
|
|
@ -10,18 +10,13 @@ Start (streamable-http, Port 8001 – für beliebige MCP-Clients):
|
||||||
Start (stdio – für Claude Code / Claude Desktop):
|
Start (stdio – für Claude Code / Claude Desktop):
|
||||||
python mcp_adapter.py --stdio
|
python mcp_adapter.py --stdio
|
||||||
|
|
||||||
Claude Code Konfiguration (.claude/settings.json):
|
Claude Code (bereits konfiguriert via `claude mcp add --scope user`):
|
||||||
{
|
claude mcp add --scope user chatterbox-tts \
|
||||||
"mcpServers": {
|
/home/dschlueter/miniforge3/envs/chatterbox/bin/python \
|
||||||
"chatterbox-tts": {
|
/home/dschlueter/chatterbox-tts-cli/mcp_adapter.py --stdio
|
||||||
"command": "python",
|
|
||||||
"args": ["/home/dschlueter/chatterbox-tts-cli/mcp_adapter.py", "--stdio"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Umgebungsvariable TTS_URL überschreibt die Service-Adresse:
|
Umgebungsvariable TTS_URL überschreibt die Service-Adresse:
|
||||||
TTS_URL=http://192.168.1.10:8000 python mcp_adapter.py --stdio
|
TTS_URL=http://192.168.1.10:9999 python mcp_adapter.py --stdio
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
@ -31,7 +26,7 @@ import os
|
||||||
import httpx
|
import httpx
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
TTS_URL = os.environ.get("TTS_URL", "http://127.0.0.1:8000").rstrip("/")
|
TTS_URL = os.environ.get("TTS_URL", "http://127.0.0.1:9999").rstrip("/")
|
||||||
|
|
||||||
mcp = FastMCP(
|
mcp = FastMCP(
|
||||||
"Chatterbox TTS",
|
"Chatterbox TTS",
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,25 @@
|
||||||
# Chatterbox TTS CLI — Abhängigkeiten
|
# Chatterbox TTS CLI — Abhängigkeiten
|
||||||
# Getestet mit Python 3.11, CUDA 12.x, Ubuntu 22.04/24.04
|
# Getestet mit Python 3.11, CUDA 12.x, Ubuntu 22.04/24.04
|
||||||
|
#
|
||||||
|
# PyTorch separat installieren (passende CUDA-Version via pytorch.org):
|
||||||
|
# pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu124
|
||||||
|
|
||||||
# TTS-Kern
|
# --- TTS-Kern ---
|
||||||
chatterbox-tts>=0.1.7
|
chatterbox-tts>=0.1.7
|
||||||
|
|
||||||
# PyTorch (passende CUDA-Version separat installieren, z. B. via pytorch.org)
|
# --- Audio-Ausgabe (Linux/PipeWire/PulseAudio) ---
|
||||||
torch>=2.6.0
|
|
||||||
torchaudio>=2.6.0
|
|
||||||
|
|
||||||
# Audio-Ausgabe (Linux/PipeWire/PulseAudio)
|
|
||||||
sounddevice>=0.4.0
|
sounddevice>=0.4.0
|
||||||
|
|
||||||
# Pitch-erhaltende Zeitstreckung (Geschwindigkeitsanpassung)
|
# --- Pitch-erhaltende Zeitstreckung (--speed != 1.0) ---
|
||||||
|
# Systempaket zusätzlich erforderlich: sudo apt install rubberband-cli
|
||||||
pyrubberband>=0.4.0
|
pyrubberband>=0.4.0
|
||||||
# rubberband-cli muss zusätzlich als Systempakete installiert sein:
|
|
||||||
# sudo apt install rubberband-cli
|
|
||||||
|
|
||||||
# HTTP-Service (Phase 2)
|
# --- HTTP-Service (tts_service.py) ---
|
||||||
fastapi>=0.115.0
|
fastapi>=0.115.0
|
||||||
uvicorn[standard]>=0.32.0
|
uvicorn[standard]>=0.32.0
|
||||||
|
|
||||||
# HTTP-Client für MCP-Adapter (Phase 3)
|
# --- HTTP-Client (mcp_adapter.py → tts_service.py) ---
|
||||||
httpx>=0.28.0
|
httpx>=0.28.0
|
||||||
|
|
||||||
# MCP-Adapter (Phase 3)
|
# --- MCP-Adapter (mcp_adapter.py) ---
|
||||||
mcp>=1.0.0
|
mcp>=1.0.0
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Chatterbox TTS – lokaler HTTP-Service
|
Chatterbox TTS – lokaler HTTP-Service
|
||||||
|
|
||||||
Start:
|
Start:
|
||||||
uvicorn tts_service:app --host 127.0.0.1 --port 8000
|
uvicorn tts_service:app --host 0.0.0.0 --port 9999
|
||||||
|
|
||||||
Endpunkte:
|
Endpunkte:
|
||||||
POST /speak – Text in Warteschlange einreihen
|
POST /speak – Text in Warteschlange einreihen
|
||||||
|
|
@ -178,8 +178,8 @@ class SpeakRequest(BaseModel):
|
||||||
interrupt: bool = False
|
interrupt: bool = False
|
||||||
speed: float = Field(default=1.0, ge=0.5, le=2.0)
|
speed: float = Field(default=1.0, ge=0.5, le=2.0)
|
||||||
t3_model: str = "v3"
|
t3_model: str = "v3"
|
||||||
audio_device: str = "pulse"
|
audio_device: Optional[str] = None
|
||||||
max_len: int = Field(default=400, ge=50, le=1000)
|
max_len: int = Field(default=400, ge=100, le=1000)
|
||||||
save_wav: bool = False
|
save_wav: bool = False
|
||||||
output_path: Optional[str] = None
|
output_path: Optional[str] = None
|
||||||
session_id: Optional[str] = None
|
session_id: Optional[str] = None
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue