Skip to content

Commit a2ee788

Browse files
fix: prefer browser speech synthesis over Google TTS
Use local zh-CN speech synthesis first for stable offline playback; fall back to Google TTS only when synthesis is unavailable or fails. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent a949fe2 commit a2ee788

1 file changed

Lines changed: 28 additions & 23 deletions

File tree

js/app.js

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -60,30 +60,39 @@
6060
throw new Error('Google TTS failed');
6161
}
6262

63-
async function speakGoogle(text, rate = DEFAULT_SPEECH_RATE) {
64-
try {
65-
await playGoogleTts(text, rate);
66-
} catch {
67-
speakSynthFallback(text, rate);
68-
}
63+
function speakWithSynth(text, rate = DEFAULT_SPEECH_RATE) {
64+
return new Promise((resolve, reject) => {
65+
if (!('speechSynthesis' in window)) {
66+
reject(new Error('No speech synthesis'));
67+
return;
68+
}
69+
70+
stopSpeechAudio();
71+
speechSynthesis.cancel();
72+
73+
const utterance = new SpeechSynthesisUtterance(text);
74+
utterance.lang = 'zh-CN';
75+
utterance.rate = rate;
76+
const voice = getChineseVoice();
77+
if (voice) utterance.voice = voice;
78+
utterance.onend = () => resolve();
79+
utterance.onerror = () => reject(new Error('Speech synthesis failed'));
80+
speechSynthesis.speak(utterance);
81+
});
6982
}
7083

71-
function speakSynthFallback(text, rate) {
72-
if (!('speechSynthesis' in window)) {
73-
alert('Không phát được âm thanh. Kiểm tra mạng và thử lại.');
74-
return;
84+
async function speak(text, rate = DEFAULT_SPEECH_RATE) {
85+
try {
86+
await speakWithSynth(text, rate);
87+
} catch {
88+
try {
89+
await playGoogleTts(text, rate);
90+
} catch {
91+
alert('Không phát được âm thanh. Kiểm tra mạng và thử lại.');
92+
}
7593
}
76-
77-
speechSynthesis.cancel();
78-
const utterance = new SpeechSynthesisUtterance(text);
79-
utterance.lang = 'zh-CN';
80-
utterance.rate = rate;
81-
chineseVoice = getChineseVoice();
82-
if (chineseVoice) utterance.voice = chineseVoice;
83-
speechSynthesis.speak(utterance);
8494
}
8595

86-
// --- Speech ---
8796
function initSpeech() {
8897
if (!('speechSynthesis' in window)) return;
8998

@@ -108,10 +117,6 @@
108117
);
109118
}
110119

111-
function speak(text, rate = DEFAULT_SPEECH_RATE) {
112-
speakGoogle(text, rate);
113-
}
114-
115120
function flashAudioBtn(btn) {
116121
if (!btn) return;
117122
btn.classList.add('playing');

0 commit comments

Comments
 (0)