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>
PiperTTSProvider ruft das piper-Binary (--output-raw) async auf, liest die native
Sample-Rate aus der .onnx.json und resampelt per ffmpeg auf 24000 Hz (Gateway-Norm).
Nicht passende Stimmen (z. B. Cloud-Stimme 'Zephyr' aus der Route) fallen auf die
konfigurierte Default-Stimme zurueck. Damit ist eine voll-lokale Konstellation
(faster-whisper + Ollama + piper) moeglich -> keine API-Kosten, max. Datenschutz.
- config: PIPER_BIN/PIPER_VOICES_DIR/PIPER_VOICE/TTS_SAMPLE_RATE (+ .env.example)
- dependencies: piper-Factory mit Settings verdrahtet
- tests: tests/test_piper_tts.py (offline, Fake-Binary; Resample-Test skippt ohne ffmpeg);
e2e/auth-Tests nutzen jetzt einen Stub-TTS statt 'piper' als Pseudo-Stub
- docs: README, BEDIENUNGSANLEITUNG (voll-lokal-Beispiel), Architektur-Roadmap
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>