Englische Fachbegriffe und fremde Eigennamen wurden auf Deutsch falsch vorgelesen
("Repository", "Donald Trump"). Chatterbox hat keinen Lautschrift-Eingang, also bleibt
nur die Umschrift in deutscher Rechtschreibung — die gab es zwar schon, aber nur als
kleines Dict im CLI-Skript, das keine andere Anwendung sinnvoll erweitern konnte.
Empirisch geprüft, warum es keinen besseren Weg gibt:
- Der multilinguale Tokenizer heißt "grapheme_mtl_merged_expanded_v1" und enthält zwar
IPA-Zeichen und ein [ipa]-Sprachtoken. generate() lehnt language_id="ipa" ab; umgeht
man nur die Whitelist, kommt Unsinn heraus: "ɹɪˈpɑzɪˌtɔɹi" klingt wie "Ripacitari",
"dɪˈplɔɪmənt" wie "Deep Romant" (per Whisper zurücktranskribiert). IPA ist tot.
- Der vorhandene Sprachwechsel im Satz ([en]...[/en]) synthetisiert die Teile korrekt,
klebt sie aber ohne Überblendung zusammen: gemessen 4 Nahtpausen von 0,5-0,8 s, der
Satz wächst von 3,9 s auf 7,1 s. Für Fließtext unbrauchbar.
Neu:
- pronunciation.py — eigenständiges Modul, reine Standardbibliothek (kein torch), damit
CLI, HTTP-Service, MCP-Adapter und fremde Projekte (Open Notebook) dasselbe Wörterbuch
nutzen können, ohne das Modell zu laden. Namen werden aus chatterbox_cli_v4
re-exportiert, bestehende Importe bleiben gültig.
- Ladereihenfolge mit Vorrang: eingebaut -> pronunciation/<lang>.json (mitgeliefert) ->
~/.config/chatterbox/pronunciation/<lang>.json -> $CHATTERBOX_PRONUNCIATION_DICT ->
explizit übergebenes Dict. Kaputte JSON-Dateien werden gemeldet, nicht fatal.
- pronunciation/de.json — kuratierte Einträge (Tech, KI, Firmen, Personen, Medien).
- pronunciation_candidates.py — findet in einem Text alles, was hunspell (de) nicht kennt
und noch nicht im Wörterbuch steht. An einem echten Podcast-Transkript getestet: 61
Kandidaten, darunter Voicebox, Scrapling, Scraping.
Fehlerbehebung: Ersetzt wird jetzt an Wortgrenzen und in einem einzigen Durchgang. Das
alte str.replace() machte aus "UNESCO" ein "Uh EnnESCO" und aus "UNIX" ein "Uh EnnIX",
weil der Eintrag "UN" auch mitten im Wort traf.
Verifiziert: 199 Einträge laden in 44 ms ohne torch; UNESCO/UNIX/Xiaomi bleiben intakt;
Open Notebooks TTS-Server spricht die Umschrift ohne eine einzige Codeänderung dort; der
MCP-Service auf :9999 läuft unverändert weiter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
545 lines
17 KiB
Markdown
545 lines
17 KiB
Markdown
# 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
|
||
- **Pause/Resume** — Ausgabe pausieren und fortsetzen ohne Datenverlust (`POST /pause`, `POST /resume`)
|
||
- **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`
|
||
- **Gemischtsprachige Texte** — `[en]...[/en]`-Markierungen für englische Passagen in deutschen Texten
|
||
- **Deutsche Textnormalisierung** — Datumsangaben (03.06.2026 → „Dritter Sechster Zwanzigsechsundzwanzig"), Uhrzeiten (14:58 → „vierzehn Uhr achtundfünfzig"), Jahreszahlen bis Milliarden, Einheiten (°C, °F, kWh, m², …), Abkürzungen, Aussprache-Wörterbuch
|
||
- **Markdown-Bereinigung** — entfernt `**fett**`, `# Überschrift`, Links, Code-Blöcke automatisch vor der Synthese
|
||
- **HTTP-Service** — FastAPI-Service mit Job-Queue, Stop/Pause/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 (~2–3 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
|
||
|
||
# Gemischtsprachiger Text
|
||
python chatterbox_cli_v4.py --lang de --text \
|
||
"Das [en]Machine Learning[/en] Modell kostet ca. 50 €."
|
||
|
||
# Markdown-Bereinigung deaktivieren
|
||
python chatterbox_cli_v4.py --lang de --no-strip-markdown --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 (10–30 s) |
|
||
| `--speed N` | `1.0` | Wiedergabegeschwindigkeit (0.5–2.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 |
|
||
| `--no-strip-markdown` | — | Markdown-Formatierung nicht entfernen |
|
||
| `--no-normalize-dates` | — | Datumsangaben nicht in Ordinalform umwandeln |
|
||
| `--no-normalize-times` | — | Uhrzeiten nicht umwandeln |
|
||
| `--no-normalize-years` | — | Jahreszahlen nicht umwandeln |
|
||
| `--no-normalize-units` | — | Einheiten nicht umwandeln |
|
||
| `--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 |
|
||
|
||
---
|
||
|
||
## Voice Cloning — Stimme aufnehmen
|
||
|
||
Für Voice Cloning braucht Chatterbox eine WAV-Aufnahme von 10–60 Sekunden in ruhiger Umgebung.
|
||
|
||
### Aufnahme-Workflow
|
||
|
||
```bash
|
||
# 1. Verfügbare Mikrofon-Eingänge anzeigen
|
||
pactl list sources short | grep -v monitor
|
||
|
||
# 2. Trainingstext in der Datei anzeigen / lesen
|
||
cat ~/chatterbox-tts-cli/Trainings_Text.txt
|
||
|
||
# 3. Aufnahme starten — 60 Sekunden, stoppt automatisch
|
||
# (nutzt das Standard-Eingabegerät von PipeWire/PulseAudio)
|
||
arecord -D pulse -f S16_LE -r 48000 -c 1 --duration=60 \
|
||
~/chatterbox-tts-cli/my_voice_deutsch_60s.wav
|
||
|
||
# 4. Aufnahme abhören und prüfen
|
||
aplay ~/chatterbox-tts-cli/my_voice_deutsch_60s.wav
|
||
|
||
# 5. Als Voice-Profil nutzen
|
||
conda activate chatterbox
|
||
python chatterbox_cli_v4.py --lang de \
|
||
--voice my_voice_deutsch_60s.wav \
|
||
--text "Testtext mit meiner Stimme."
|
||
```
|
||
|
||
### Bestimmtes Mikrofon auswählen (z. B. MOTU M2)
|
||
|
||
```bash
|
||
# Quellname des M2 herausfinden
|
||
pactl list sources short | grep -v monitor
|
||
|
||
# Aufnahme explizit vom MOTU M2:
|
||
arecord -D pulse \
|
||
--default-device=alsa_input.usb-MOTU_M2_M20000046918-00.analog-stereo \
|
||
-f S16_LE -r 48000 -c 1 --duration=60 \
|
||
~/chatterbox-tts-cli/my_voice_deutsch_60s.wav
|
||
```
|
||
|
||
### Tipps für gute Aufnahmen
|
||
|
||
- Ruhige Umgebung, kein Hintergrundlärm
|
||
- Normaler Gesprächsabstand zum Mikrofon (20–40 cm)
|
||
- Gleichmäßiges, natürliches Sprechtempo
|
||
- 30–60 Sekunden sind ideal; 10 Sekunden sind das Minimum
|
||
|
||
---
|
||
|
||
## Gemischtsprachige Texte
|
||
|
||
Deutsche Texte enthalten oft englische Fachbegriffe, Markennamen oder Zitate.
|
||
Mit `[xx]...[/xx]`-Markierungen werden diese Passagen mit der richtigen `language_id`
|
||
an das Multilingual-Modell übergeben:
|
||
|
||
```
|
||
Das [en]Machine Learning[/en] Framework kostet ca. 50 €.
|
||
Der [en]CEO[/en] sagte: [en]"We are committed to innovation."[/en]
|
||
```
|
||
|
||
Ohne Markierungen verhält sich das System identisch wie bisher.
|
||
|
||
Häufige englische Tech-Begriffe werden bereits automatisch korrekt ausgesprochen
|
||
(eingebaut in `DEFAULT_PRONUNCIATION_DE`):
|
||
|
||
| Begriff | Aussprache |
|
||
|---------|-----------|
|
||
| GitHub | „Git Hab" |
|
||
| YouTube | „Jutjub" |
|
||
| LinkedIn | „Linked In" |
|
||
| Wi-Fi | „Wai Fai" |
|
||
| iPhone | „Aiphone" |
|
||
| ChatGPT | „Tschet Dschie Pie Tie" |
|
||
|
||
---
|
||
|
||
## 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
|
||
|
||
# Mit Modell-Warmup (kein Cold-Start beim ersten Request)
|
||
TTS_PRELOAD_LANG=de 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 |
|
||
| `POST` | `/pause` | Ausgabe pausieren (ohne Datenverlust) |
|
||
| `POST` | `/resume` | Pausierte Ausgabe fortsetzen |
|
||
| `GET` | `/audio/{job_id}` | Fertige WAV herunterladen (nur mit `keep_audio=true`) |
|
||
| `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,
|
||
"session_id": null,
|
||
"keep_audio": false
|
||
}
|
||
```
|
||
|
||
```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 \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"text": "Wichtiger Text", "lang": "de", "interrupt": true}'
|
||
|
||
# WAV-Datei erzeugen und herunterladen
|
||
JOB=$(curl -s -X POST http://localhost:9999/speak \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"text": "Hallo Welt", "lang": "de", "keep_audio": true}' \
|
||
| python3 -c "import sys,json; print(json.load(sys.stdin)['job_id'])")
|
||
sleep 40 # warten bis Synthese fertig
|
||
curl -o ausgabe.wav http://localhost:9999/audio/$JOB
|
||
# → WAV-Datei; wird nach dem Download automatisch gelöscht
|
||
|
||
# Pausieren und fortsetzen
|
||
curl -X POST http://localhost:9999/pause
|
||
curl -X POST http://localhost:9999/resume
|
||
|
||
# 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, session_id, keep_audio | Text ausgeben |
|
||
| `stop` | — | Ausgabe stoppen und Queue leeren |
|
||
| `pause` | — | Ausgabe pausieren (ohne Datenverlust) |
|
||
| `resume` | — | Pausierte Ausgabe fortsetzen |
|
||
| `get_status` | — | Aktuellen Job und Queue abfragen |
|
||
| `list_voices` | — | Unterstützte 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`, `pause`, `resume`, `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)
|
||
```
|
||
|
||
### TTS Agent (`tts_agent.py`)
|
||
|
||
Eigenständiger Konversationsagent mit eingebautem Function Calling:
|
||
|
||
```bash
|
||
# Mit Ollama
|
||
python tts_agent.py --model qwen2.5
|
||
|
||
# Mit LM Studio
|
||
python tts_agent.py --base-url http://localhost:1234/v1 --model local-model
|
||
|
||
# Mit OpenAI
|
||
OPENAI_API_KEY=sk-... python tts_agent.py --model gpt-4o
|
||
|
||
# Mit Voice Cloning
|
||
python tts_agent.py --model qwen2.5 --voice my_voice.wav
|
||
```
|
||
|
||
### 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"
|
||
```
|
||
|
||
### 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
|
||
|
||
tts_pause:
|
||
url: "http://192.168.x.x:9999/pause"
|
||
method: POST
|
||
|
||
tts_resume:
|
||
url: "http://192.168.x.x:9999/resume"
|
||
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.
|
||
|
||
---
|
||
|
||
## Aussprache-Wörterbuch
|
||
|
||
Für Namen oder Begriffe, die das Modell falsch ausspricht:
|
||
|
||
```json
|
||
{
|
||
"Xi Jinping": "Schi Dschinping",
|
||
"Putin": "Pjutin",
|
||
"Seoul": "Söul",
|
||
"Kubernetes": "Kubernetis"
|
||
}
|
||
```
|
||
|
||
```bash
|
||
python chatterbox_cli_v4.py --lang de \
|
||
--pronunciation-dict aussprache.json \
|
||
--input nachricht.txt
|
||
```
|
||
|
||
### Ladereihenfolge
|
||
|
||
Das Wörterbuch liegt in **`pronunciation.py`** — einem eigenständigen Modul **ohne
|
||
torch-Abhängigkeit**, damit jede Chatterbox-Anwendung (CLI, HTTP-Service, MCP-Adapter,
|
||
fremde Projekte) es nutzen kann, ohne ein 4-GB-Modell zu laden:
|
||
|
||
```python
|
||
from pronunciation import load_pronunciation_dict, apply_pronunciation_dict
|
||
text = apply_pronunciation_dict(text, load_pronunciation_dict("de"))
|
||
```
|
||
|
||
Quellen werden zusammengeführt, spätere überschreiben frühere:
|
||
|
||
| # | Quelle | Zweck |
|
||
|---|---|---|
|
||
| 1 | `DEFAULT_PRONUNCIATION_DE` (in `pronunciation.py`) | Notnagel, falls keine Datei da ist |
|
||
| 2 | `pronunciation/<lang>.json` | **die gepflegte Liste** (~200 Einträge), versioniert |
|
||
| 3 | `~/.config/chatterbox/pronunciation/<lang>.json` | persönliche Ergänzungen |
|
||
| 4 | `$CHATTERBOX_PRONUNCIATION_DICT` (Pfad, `:`-getrennt) | pro Projekt/Deployment |
|
||
| 5 | `--pronunciation-dict` bzw. API-Parameter | pro Aufruf |
|
||
|
||
Inhalt anzeigen: `python pronunciation.py de`
|
||
|
||
### Kandidaten finden
|
||
|
||
`pronunciation_candidates.py` sucht in einem Text alles, was **hunspell** (deutsches
|
||
Wörterbuch) nicht kennt und noch nicht im Wörterbuch steht — praktisch immer genau die
|
||
englischen Fachbegriffe und fremden Eigennamen:
|
||
|
||
```bash
|
||
python pronunciation_candidates.py transkript.txt # nach Häufigkeit sortiert
|
||
python pronunciation_candidates.py *.txt --min-count 2 --json # Gerüst zum Ausfüllen
|
||
```
|
||
|
||
Braucht `hunspell` + `hunspell-de-de`. Die Umschrift schreibt ein Mensch.
|
||
|
||
### Ersetzt wird an Wortgrenzen
|
||
|
||
Früher ein nacktes `str.replace()`: aus „UNESCO" wurde „Uh EnnESCO", aus „UNIX" ein
|
||
„Uh EnnIX". Jetzt wird nur an Wortgrenzen ersetzt, alles in einem Durchgang (längste
|
||
Einträge zuerst), sodass Ersetzungen sich nicht gegenseitig treffen.
|
||
|
||
**Umschrift nie komplett großschreiben:** die Akronym-Expansion läuft danach und würde
|
||
„EIGEN" zu „E I Ge E En" buchstabieren.
|
||
|
||
> **Warum keine Lautschrift (IPA)?** Chatterbox ist graphem-basiert; der multilinguale
|
||
> Tokenizer heißt wörtlich `grapheme_mtl_merged_expanded_v1`. Im Vokabular stehen zwar
|
||
> IPA-Zeichen und ein `[ipa]`-Sprachtoken, aber empirisch spricht das Modell IPA **nicht**
|
||
> als Lautschrift: `ɹɪˈpɑzɪˌtɔɹi` klingt wie „Ripacitari", `dɪˈplɔɪmənt` wie „Deep Romant".
|
||
> Auch der Weg über Sprachwechsel im Satz (`[en]…[/en]`) hilft hier nicht: die Segmente
|
||
> werden ohne Überblendung aneinandergehängt, jede Naht kostet 0,5–0,8 s Pause. Deshalb
|
||
> Umschrift.
|
||
|
||
---
|
||
|
||
## Textnormalisierung im Überblick
|
||
|
||
Die Normalisierungspipeline läuft automatisch vor der TTS-Synthese (nur `--lang de`):
|
||
|
||
| Schritt | Beispiel Eingabe | Beispiel Ausgabe |
|
||
|---------|-----------------|-----------------|
|
||
| Aussprache-Dict | „Xi Jinping" | „Schi Jinping" |
|
||
| Einheiten | „25 °C", „100 kWh", „10 m²" | „25 Grad Celsius", „100 Kilowattstunde", „10 Quadratmeter" |
|
||
| **Datum** | „03.06.2026" | „Dritter Sechster Zwanzigsechsundzwanzig" |
|
||
| Uhrzeit | „14:58 Uhr" | „vierzehn Uhr achtundfünfzig" |
|
||
| Jahreszahl | „2026" | „zweitausendsechsundzwanzig" |
|
||
| Akronyme | „ARD", „CPU" | „Ah Er De", „C P U" |
|
||
|
||
Jeder Schritt kann einzeln deaktiviert werden:
|
||
`--no-normalize-dates`, `--no-normalize-times`, `--no-normalize-years`, `--no-normalize-units`, `--no-spell-acronyms`
|
||
|
||
---
|
||
|
||
## 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/Pause greift am nächsten Chunk-Beginn.
|
||
- **Sprachmarkierungen `[en]...[/en]`** funktionieren nur mit `ChatterboxMultilingualTTS`; bei `--lang en` (mono) werden sie ignoriert.
|
||
- **Streaming-Modus** unterstützt keine Sprachmarkierungen.
|
||
|
||
---
|
||
|
||
## Lizenz
|
||
|
||
MIT — dieses Skript. Das Chatterbox-Modell: MIT-Lizenz (Resemble AI). Modellgewichte: CC BY-NC 4.0.
|