fix(tts): Geräte-TTS-Regression für Sprachen ohne Geschlechtsstimme beheben
Commit 5c63f7b hatte deviceGenderReady als harte Bedingung eingeführt: das
Gerät musste eine Stimme des WAHLgeschlechts haben, sonst Server-Fallback.
Die konservative Geschlechts-Heuristik liefert für viele Geräte-Stimmen null
(z.B. NL am iPhone: nur eine Stimme ohne erkennbares Geschlecht) -> der
Server-Fallback griff und lieferte eine schlechte/fremdsprachige Aussprache.
Für NL kam (im noch nicht neu gestarteten Prozess) die deutsche Default-
Stimme -> "deutscher Akzent".
Lösung: deviceGenderReady -> deviceVoiceReady. Eine sprachrichtige Stimme
reicht (pickVoice wählt cands[0] als Best-Effort-Fallback). Die m/f-Wahl
wirkt am Gerät nur für Sprachen mit mehreren Stimmen (DE, EN), sonst
folgenlos — aber eine falsche Sprache ist schlechter als ein folgenloser
Schalter.
+ Test: NL in test_piper_single_voice_languages_ignore_gender aufgenommen
(serverseitige Komplementär-Logik; sichert voice_for_route NL-Auflösung).
Der client-seitige app.js-Fix ist mangels JS-Test-Framework manuell
verifiziert (Geräte-TTS für NL spricht niederländisch, DE/EN behalten
Geschlechtswahl).
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
71cb6bdd7f
commit
e814b5548d
2 changed files with 16 additions and 8 deletions
|
|
@ -147,12 +147,15 @@ function deviceVoicesReady() {
|
|||
return _voices.length > 0;
|
||||
}
|
||||
|
||||
// Hat das Gerät eine zum Wunschgeschlecht passende Stimme für diese Sprache? Wenn nicht,
|
||||
// soll auf Server-TTS ausgewichen werden (dort gibt es echte m/f-Stimmen), damit die
|
||||
// Geschlechtswahl hörbar wirkt, statt am Gerät folgenlos zu bleiben.
|
||||
function deviceGenderReady(lang) {
|
||||
// Hat das Gerät eine Stimme für diese Sprache? Geschlecht ist Best-Effort: pickVoice wählt
|
||||
// bei Treffer die geschlechtspassende, sonst die erste sprachrichtige Stimme (cands[0]).
|
||||
// Früher wurde bei fehlendem Geschlechtsmatch hart auf Server-TTS ausgewichen — das
|
||||
// verbannte Sprachen mit nur einer Geräte-Stimme (z.B. NL) auf eine schlechte Server-
|
||||
// Aussprache. Jetzt gilt: eine sprachrichtige Stimme reicht; die m/f-Wahl wirkt am Gerät
|
||||
// nur für Sprachen mit mehreren Stimmen (DE, EN …), sonst folgenlos.
|
||||
function deviceVoiceReady(lang) {
|
||||
const bcp = LANG_BCP47[lang] || lang || "de-DE";
|
||||
return pickVoice(bcp, _voiceGender).genderMatch;
|
||||
return pickVoice(bcp, _voiceGender).voice != null;
|
||||
}
|
||||
|
||||
// Server-Fallback: holt Audio via /api/speak (ohne tts_provider -> Server-Default,
|
||||
|
|
@ -176,18 +179,18 @@ async function serverSpeakFallback(text, lang, btn) {
|
|||
// Vorlesen ohne Button-Status (automatisches Vorlesen der Antwort).
|
||||
function deviceSpeak(text, lang) {
|
||||
if (!text) return;
|
||||
if (deviceVoicesReady() && deviceGenderReady(lang)) {
|
||||
if (deviceVoicesReady() && deviceVoiceReady(lang)) {
|
||||
speechSynthesis.cancel();
|
||||
speechSynthesis.speak(_makeUtterance(text, lang));
|
||||
} else {
|
||||
serverSpeakFallback(text, lang, null); // keine (passende) Stimme -> Server-Audio
|
||||
serverSpeakFallback(text, lang, null); // keine Stimme für diese Sprache -> Server-Audio
|
||||
}
|
||||
}
|
||||
|
||||
// Vorlesen mit Button-Status (Replay): Icon -> ⏹ während des Sprechens, danach zurück.
|
||||
function deviceSpeakBtn(text, lang, btn) {
|
||||
if (!text) { setReplayPlaying(btn, false); return; }
|
||||
if (!deviceVoicesReady() || !deviceGenderReady(lang)) { serverSpeakFallback(text, lang, btn); return; }
|
||||
if (!deviceVoicesReady() || !deviceVoiceReady(lang)) { serverSpeakFallback(text, lang, btn); return; }
|
||||
speechSynthesis.cancel();
|
||||
const u = _makeUtterance(text, lang);
|
||||
u.onend = u.onerror = () => setReplayPlaying(btn, false);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue