| 172 | 172 | //#################// |
| 173 | 173 | fetchTtsVoiceObjects() { |
| 174 | 174 | if (!('speechSynthesis' in window)) { |
| 175 | | - // Browser doesn't support speech synthesis |
| 176 | 175 | return Promise.resolve([]); |
| 177 | 176 | } |
| 178 | 177 | |
| 179 | 178 | return new Promise((resolve) => { |
| 180 | | - // Use a minimal timeout to allow the voice list to potentially populate |
| 181 | 179 | setTimeout(() => { |
| 182 | 180 | let voices = speechSynthesis.getVoices(); |
| 183 | 181 | |
| 184 | 182 | if (voices.length === 0) { |
| 185 | | - // If no voices returned (e.g., Edge on first load), provide a default option |
| 183 | + // Edge compat: Provide default when voices empty |
| 186 | 184 | console.warn('SystemTTS: getVoices() returned empty list. Providing browser default option.'); |
| 187 | 185 | const defaultVoice = { |
| 188 | 186 | name: SystemTtsProvider.BROWSER_DEFAULT_VOICE_NAME, |
| 189 | 187 | voice_id: SystemTtsProvider.BROWSER_DEFAULT_VOICE_ID, |
| 190 | 188 | preview_url: false, |
| 191 | | - // Try to guess the browser's default language |
| 192 | 189 | lang: navigator.language || 'en-US', |
| 193 | 190 | }; |
| 194 | 191 | resolve([defaultVoice]); |
| 195 | 192 | } else { |
| 196 | | - // If voices are available, map them as before |
| 197 | 193 | const mappedVoices = voices |
| 198 | 194 | .sort((a, b) => a.lang.localeCompare(b.lang) || a.name.localeCompare(b.name)) |
| 199 | 195 | .map(x => ({ name: x.name, voice_id: x.voiceURI, preview_url: false, lang: x.lang })); |
| 200 | 196 | resolve(mappedVoices); |
| 201 | 197 | } |
| 202 | | - }, 50); // Increased timeout slightly just in case it helps voice population on some browsers |
| 198 | + }, 50); |
| 203 | 199 | }); |
| 204 | 200 | } |
| 205 | 201 | |
| 206 | | - |
| 207 | 202 | previewTtsVoice(voiceId) { |
| 208 | 203 | if (!('speechSynthesis' in window)) { |
| 209 | 204 | throw new Error('Speech synthesis API is not supported'); // Keep Error type for consistency |
| 210 | 205 | } |
| 211 | 206 | |
| 212 | 207 | let voice = null; |
| 213 | | - // Check if the requested voice is NOT the browser default |
| 214 | 208 | if (voiceId !== SystemTtsProvider.BROWSER_DEFAULT_VOICE_ID) { |
| 215 | 209 | const voices = speechSynthesis.getVoices(); |
| 216 | | - // Try to find the actual voice |
| 217 | 210 | voice = voices.find(x => x.voiceURI === voiceId); |
| 218 | 211 | |
| 219 | 212 | if (!voice && voices.length > 0) { |
| 220 | | - // If voices are loaded but the specific ID wasn't found, log a warning |
| 221 | 213 | console.warn(`SystemTTS Preview: Voice ID "${voiceId}" not found among available voices. Using browser default.`); |
| 222 | | - // Fallback to default (voice remains null) |
| 223 | 214 | } else if (!voice && voices.length === 0) { |
| 224 | | - // If no voices are loaded at all, we expect to use default |
| 225 | 215 | console.warn('SystemTTS Preview: Voice list is empty. Using browser default.'); |
| 226 | | - // Fallback to default (voice remains null) |
| 227 | 216 | } |
| 228 | 217 | } else { |
| 229 | 218 | console.log('SystemTTS Preview: Using browser default voice as requested.'); |
| 230 | | - // Use default (voice remains null) |
| 231 | 219 | } |
| 232 | 220 | |
| 233 | 221 | speechSynthesis.cancel(); // Stop any previous speech |
| 234 | | - // Use the language from the found voice if available, otherwise default to 'en-US' or browser lang for the preview text |
| 235 | 222 | const langForPreview = voice ? voice.lang : (navigator.language || 'en-US'); |
| 236 | 223 | const text = getPreviewString(langForPreview); |
| 237 | 224 | const utterance = new SpeechSynthesisUtterance(text); |
| 238 | 225 | |
| 239 | | - // Only set the voice if we found a specific one and it wasn't the default request |
| 240 | 226 | if (voice) { |
| 241 | 227 | utterance.voice = voice; |
| 242 | 228 | } |
| 243 | | - // Otherwise, utterance.voice remains null/undefined, causing the browser to use its default |
| 244 | 229 | |
| 245 | 230 | utterance.rate = this.settings.rate || 1; |
| 246 | 231 | utterance.pitch = this.settings.pitch || 1; |
| 247 | 232 | |
| 248 | | - // Add error handling for the speech itself |
| 249 | 233 | utterance.onerror = (event) => { |
| 250 | 234 | console.error(`SystemTTS Preview Error: ${event.error}`, event); |
| 251 | | - // Potentially notify the user here |
| 252 | 235 | }; |
| 253 | 236 | |
| 254 | 237 | speechSynthesis.speak(utterance); |