|
60 | 60 | throw new Error('Google TTS failed'); |
61 | 61 | } |
62 | 62 |
|
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 | + }); |
69 | 82 | } |
70 | 83 |
|
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 | + } |
75 | 93 | } |
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); |
84 | 94 | } |
85 | 95 |
|
86 | | - // --- Speech --- |
87 | 96 | function initSpeech() { |
88 | 97 | if (!('speechSynthesis' in window)) return; |
89 | 98 |
|
|
108 | 117 | ); |
109 | 118 | } |
110 | 119 |
|
111 | | - function speak(text, rate = DEFAULT_SPEECH_RATE) { |
112 | | - speakGoogle(text, rate); |
113 | | - } |
114 | | - |
115 | 120 | function flashAudioBtn(btn) { |
116 | 121 | if (!btn) return; |
117 | 122 | btn.classList.add('playing'); |
|
0 commit comments