Eigentliche Ursache für 'immer weibliche Stimme, egal welcher Provider': UserPrefs kannte kein Feld voice_gender, daher verwarf pydantic in PUT /api/me/prefs die Geschlechtswahl still. resolve_route sah immer 'any' -> Piper/Cartesia lieferten die weibliche Stimme (kerstin bzw. Alina). - voice_gender zum Schema hinzugefügt; Frontend sendet es bereits, resolve_route liest es bereits (ROUTE_KEYS), voice_for_route mappt m/f bereits. - Regressions-Test: voice_gender persistiert über /api/me/prefs. - Docs/stimmen-inventar.md: zentrale Übersicht aller Stimmen je Provider/ Sprache/Geschlecht (Name, Datei, UUID) inkl. Fundstellen im Code. Live verifiziert (Profil jamulix, de): m -> de_DE-thorsten-high, f -> de_DE-kerstin-low. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.5 KiB
Stimmen-Inventar (TTS) — eine Quelle der Wahrheit
Diese Datei listet alle TTS-Stimmen je Provider, Sprache und Geschlecht: Stimmenname, Datei bzw. UUID und die Stelle im Code/Config, an der sie definiert ist. Ziel: Schluss mit dem Suchen über mehrere Dateien.
Wer wählt die Stimme?
app/dependencies.py::voice_for_route(tts_provider, language, voice_gender). Es liestvoice_genderaus der aufgelösten Route (resolve_route), die es wiederum aus den Nutzer-Prefs zieht. Die Geschlechtswahl im Web-UI wird überPUT /api/me/prefsgespeichert — dafür mussvoice_genderim Schemaapp/schemas.py::UserPrefsstehen (sonst verwirft pydantic das Feld still → Nutzer hört immer „any" = weiblich).
Legende: ♀ = weiblich, ♂ = männlich · „—" = nicht vorhanden/keine Geschlechtswahl.
1. Piper (lokal, Standard im Profil jamulix)
- Definition Geschlecht→Stimme:
app/dependencies.py→PIPER_VOICE_GENDERED(mit Geschlecht) undLANG_TO_PIPER_VOICE(geschlechtsloser Fallback). - Modelldateien:
config/voices/<name>.onnx(+.onnx.json); Verzeichnis ausconfig/voice-assistant.toml::piper_voices_dir.
| Sprache | ♀ Stimme | Datei (.onnx) | ♂ Stimme | Datei (.onnx) |
|---|---|---|---|---|
| de | de_DE-kerstin-low | de_DE-kerstin-low |
de_DE-thorsten-high | de_DE-thorsten-high |
| en | en_US-amy-medium | en_US-amy-medium |
en_US-lessac-high | en_US-lessac-high |
| fr | fr_FR-siwis-medium | fr_FR-siwis-medium |
fr_FR-tom-medium | fr_FR-tom-medium |
| es | es_ES-sharvard-medium #1 | es_ES-sharvard-medium (Speaker 1=F) |
es_ES-sharvard-medium #0 | es_ES-sharvard-medium (Speaker 0=M) |
| it | it_IT-paola-medium | it_IT-paola-medium |
it_IT-riccardo-x_low | it_IT-riccardo-x_low |
| ru | ru_RU-irina-medium | ru_RU-irina-medium |
ru_RU-ruslan-medium | ru_RU-ruslan-medium |
| pl | pl_PL-gosia-medium | pl_PL-gosia-medium |
pl_PL-darkman-medium | pl_PL-darkman-medium |
| pt | — (keine ♀ in Piper) | — | pt_BR-faber-medium | pt_BR-faber-medium |
| ar | — (keine ♀ in Piper) | — | ar_JO-kareem-medium | ar_JO-kareem-medium |
| zh / cmn | zh_CN-huayan-medium | zh_CN-huayan-medium |
— (kein ♂ nutzbar) | — |
Hinweise:
esist ein Multi-Speaker-Modell: Speaker-Index#0=männlich,#1=weiblich (speaker_id_map).pt/ar/zhhaben in Piper keine Geschlechts-Varianten → beide Schalterstellungen liefern dieselbe Stimme (Fallback ausLANG_TO_PIPER_VOICE).- zh ♂ wäre nur über
zh_CN-chaowen-mediummöglich, das aberphoneme_type=pinyin(g2pW) braucht — in dieser Installation nicht lauffähig (erzeugt kein Audio).
2. Cartesia (Cloud, Modell sonic-turbo)
- Definition:
config/voice-assistant.toml::cartesia_voices, Formatlang:gender:uuid:name. - Auflösung UUID:
app/providers/tts/cartesia.py::_resolve_voice_id(Fallback-Kette f→m→any→cartesia_voice_id).
| Sprache | Geschlecht | Name | UUID |
|---|---|---|---|
| de | ♂ | Sebastian | b7187e84-fe22-4344-ba4a-bc013fcb533e |
| de | ♀ | Alina | 38aabb6a-f52b-4fb0-a3d1-988518f4dc06 |
| en | ♂ | Caspar | 4f7f1324-1853-48a6-b294-4e78e8036a83 |
| en | ♀ | Gemma | 62ae83ad-4f6a-430b-af41-a9bede9286ca |
| fr | ♂ | Antoine | 0418348a-0ca2-4e90-9986-800fb8b3bbc0 |
| fr | ♀ | Amélie | 7c58f4a4-a72c-42fa-a503-41b9408820f3 |
| es | ♂ | Augustin (mx) | 2695b6b5-5543-4be1-96d9-3967fb5e7fec |
| es | ♀ | Daniela (mx) | 5c5ad5e7-1020-476b-8b91-fdcbe9cc313c |
| pt | ♂ | Bruno | b603811e-54c2-4a0a-8854-09eab9ffa63f |
| pt | ♀ | Isabella | c9611be8-aae9-4a93-bb1c-98dd6b7d52a4 |
| it | — | sonic-turbo unterstützt IT nicht (auskommentiert) | — |
| pl | — | sonic-turbo unterstützt PL nicht (auskommentiert) | — |
| ru | — | sonic-turbo unterstützt RU nicht (auskommentiert) | — |
| ar | — | Cartesia unterstützt Arabic-TTS nicht | — |
| zh | — | nicht konfiguriert | — |
Für it/pl/ru/ar/zh greift bei Cartesia die globale Fallback-Kette → letztlich
tts_fallback(config/voice-assistant.toml, aktuellpiper).
3. OpenRouter (Cloud, Modell openai/gpt-4o-mini-tts)
- Definition:
app/config.py::openrouter_tts_voice(Defaultalloy),openrouter_tts_model. - Keine Geschlechts- oder Sprachauswahl: eine feste Stimme für alle Sprachen.
voice_for_routegibt für OpenRouterNonezurück → Default-Stimme.
| Sprache | ♀ | ♂ |
|---|---|---|
| alle | — (eine feste Stimme alloy) |
— |
4. Chatterbox (lokaler GPU-Dienst, Voice-Cloning)
- Definition:
app/config.py::chatterbox_*; Referenz-WAV je Sprache unterchatterbox_voices_diralsconfig/voices/<lang>.wav. - Keine Geschlechtswahl: pro Sprache eine geklonte Stimme (Geschlecht = Aufnahme).
| vorhandene Referenz-WAVs | en, es, fr, it, nl, ru, zh |
|---|---|
ohne <lang>.wav → globale Default-Stimme |
de, pt, pl, ar, … |
Lücken-Überblick (welche Kombination fehlt)
| Sprache | Piper | Cartesia | OpenRouter | Chatterbox |
|---|---|---|---|---|
| de, en, fr, es | ♀+♂ | ♀+♂ | nur 1 fix | 1 (Klon) |
| pt | nur ♂ | ♀+♂ | nur 1 fix | Default |
| it, pl, ru | ♀+♂ | — | nur 1 fix | teils 1 |
| ar | nur ♂ | — | nur 1 fix | Default |
| zh | nur ♀ | — | nur 1 fix | 1 (Klon) |
Für die echten Lücken (pt ♀ / ar ♀ / zh ♂) braucht es sprach-/geschlechtsabhängiges
Provider-Routing (z. B. pt ♀ → Cartesia Isabella) plus UI-Ausgrauen nicht verfügbarer
Kombinationen. Siehe MULTITOOL_AUSBAU_PLAN.md / offene Aufgaben.