1let voices = []; 2let utterance = null; 3const speechNoticeText = 'Your browser does not support the SpeechSynthesis API. Please use a recent version of Chrome, Edge, or Safari.'; 4 5function populateVoices() { 6 voices = speechSynthesis.getVoices().sort((a, b) => a.lang.localeCompare(b.lang)); 7} 8 9// Initial population of voices 10if ('speechSynthesis' in window) { 11 populateVoices(); 12 window.speechSynthesis.onvoiceschanged = populateVoices; 13} 14 15function pickVoiceForLang(lang) { 16 if (!lang || lang === 'auto') return null; 17 return ( 18 voices.find(v => v.lang.toLowerCase() === lang.toLowerCase()) || 19 voices.find(v => v.lang.toLowerCase().startsWith(lang.split('-')[0].toLowerCase())) 20 ); 21} 22 23/** ============= 24 * æ®éæè¯» 25 * ============= */ 26function speak(text, lang, voiceName, rate, pitch) { 27 if (!text) return; 28 speechSynthesis.cancel(); 29 utterance = new SpeechSynthesisUtterance(text); 30 31 if (lang !== 'auto') utterance.lang = lang; 32 33 let v = null; 34 if (voiceName) { 35 v = voices.find(x => x.name === voiceName); 36 } else if (lang !== 'auto') { 37 v = pickVoiceForLang(lang); 38 } 39 if (v) utterance.voice = v; 40 41 utterance.rate = parseFloat(rate) || 1; 42 utterance.pitch = parseFloat(pitch) || 1; 43 speechSynthesis.speak(utterance); 44} 45 46/** ======================================= 47 * ð èå¹´ç·å£°æ¨¡å¼ï¼é«çº§æè¯» + åé¡¿ + æ·±æ²å£°é³ï¼ 48 * ======================================= */ 49function speakElder(rawText) { 50 if (!rawText || rawText.trim() === '') return; // å¢å å¯¹ç©ºææ¬çæ£æ¥ 51 52 speechSynthesis.cancel(); 53 54 // å°ææ¬åå¥ï¼å¢å¼ºèªç¶åé¡¿æ 55 const parts = rawText 56 .replace(/\r?\n|\r/g, ' ') // ç»ä¸å¤çä¸åæä½ç³»ç»çæ¢è¡ç¬¦ï¼æ¿æ¢ä¸ºåä¸ªç©ºæ ¼ 57 .split(/[ãï¼ï¼.!?]/) // ä¿®æ¹åå¥é»è¾ï¼ä¸ä¿çæ ç¹ç¬¦å· 58 .filter(part => part.trim() !== ''); // è¿æ»¤æç©ºå符串ï¼ç¡®ä¿æ²¡æå¤ä½çç©ºæ ¼æç©ºå¥ 59 60 // ä¼åç·å£°å ³é®è¯ï¼å¢å æ´å¤å¹é 项 61 const maleKeywords = /(male|man|maged|tarik|daniel|mark|hassan|naayf|david|paul|steve|george|ryan|alex|tom|john|peter|robert|william|james|charles|thomas|joseph|christopher|daniel|paul|mark|donald|kenneth|anthony|kevin|jason|matthew|gary|timothy|larry|jeffrey|frank|scott|eric|stephen|andrew|raymond|gregory|joshua|dennis|walter|patrick|peter|harold|douglas|henry|carl|arthur|ralph|bruce|jeremy|terry|randy|louis|russell|eugene|bobby|fred|wayne|billy|howard|phillip|gordon|ronald|jose|franklin|harvey|earl|jerry|dennis|larry|walter|patrick|peter|harold|douglas|henry|carl|arthur|ralph|bruce|jeremy|terry|randy|louis|russell|eugene|bobby|fred|wayne|billy|howard|phillip|gordon|ronald|jose|franklin|harvey|earl|jerry)/i; 62 63 let maleVoice = voices.find(v => maleKeywords.test(v.name)) || null; 64 65 // å¦ææ²¡ææ¾å°å¹é çç·æ§å£°é³ï¼å°è¯ä½¿ç¨ç¬¬ä¸ä¸ªå¯ç¨ç声é³ä½ä¸ºå¤ç¨ 66 if (!maleVoice && voices.length > 0) { 67 maleVoice = voices[0]; 68 console.warn('æªæ¾å°å¹é çç·æ§å£°é³ï¼ä½¿ç¨ç¬¬ä¸ä¸ªå¯ç¨å£°é³ä½ä¸ºå¤ç¨ã'); 69 } 70 71 let index = 0; 72 73 function readNext() { 74 if (index >= parts.length) return; 75 76 const text = parts[index]; 77 const u = new SpeechSynthesisUtterance(text); 78 79 // 使ç¨ç·æ§å£°é³ 80 if (maleVoice) u.voice = maleVoice; 81 82 u.rate = 1; // èå¹´è¯éåæ ¢ 83 u.pitch = 0.6; // è年声é³å使² 84 u.volume = 1; 85 86 // åé¡¿å¤çï¼å¹é å¥å·ãéå·çï¼ 87 let pause = 300; 88 if (/[ã.!?]/.test(text)) pause = 600; 89 if (/,/.test(text)) pause = 450; 90 91 u.onend = function() { 92 index++; 93 setTimeout(readNext, pause); 94 }; 95 96 speechSynthesis.speak(u); 97 } 98 99 readNext(); 100} 101 102/** 103 * ä» HTML åç¬¦ä¸²ä¸æåçº¯ææ¬å 容 104 * @param {string} htmlString - å å« HTML æ ç¾çå符串 105 * @returns {string} æååºççº¯ææ¬å 容 106 */ 107function extractTextFromHtml(htmlString) { 108 const tempDiv = document.createElement('div'); 109 tempDiv.innerHTML = htmlString; 110 return tempDiv.innerText; 111} 112 113/** 114 * æ½ç¦»åºæ¥çå ¬å ±æè¯»æ¹æ³ 115 * @param {string} content - è¦æè¯»çææ¡æ HTML å符串 116 * @param {string} lang - è¯è¨ï¼ä¾å¦ 'zh-CN' (ç®ä½ä¸æ), 'zh-TW' (ç¹ä½ä¸æ), 'en-US' (ç¾å¼è±è¯), 'en-GB' (è±å¼è±è¯), 'de-DE' (å¾·è¯), 'es-ES' (西ççè¯), 'fr-FR' (æ³è¯), 'it-IT' (æå¤§å©è¯), 'ja-JP' (æ¥è¯), 'pt-PT' (è¡èçè¯), 'ru-RU' (ä¿è¯), 'ar-SA' (é¿æä¼¯è¯), 'ko-KR' (é©è¯) ç 117 * @param {string} voiceType - é³è²ç±»åï¼'elder' 为èå¹´ç·å£°æ¨¡å¼ï¼'default' 为æ®éæ¨¡å¼ 118 * @param {string} contentType - å 容类åï¼'plain' ä¸ºçº¯ææ¬ï¼'html' 为 HTML å符串 (é»è®¤ä¸º 'plain') 119 * @param {string} voiceName - å ·ä½çè¯é³åç§° (å¯é, ä» å¨ voiceType 为 'default' æ¶ææ) 120 * @param {number} rate - è¯é (å¯é, ä» å¨ voiceType 为 'default' æ¶ææ) 121 * @param {number} pitch - é³è° (å¯é, ä» å¨ voiceType 为 'default' æ¶ææ) 122 */ 123export function readTextWithConfig(content, lang, voiceType = 'elder', contentType = 'plain', voiceName = '', rate = 1, pitch = 1) { 124 if (!('speechSynthesis' in window)) { 125 alert(speechNoticeText); 126 return; 127 } 128 129 let textToSpeak = content; 130 if (contentType === 'html') { 131 textToSpeak = extractTextFromHtml(content); 132 } 133 134 if (!textToSpeak || textToSpeak.trim() === '') { 135 console.warn('没æå¯æè¯»çå 容'); 136 return; 137 } 138 139 if (voiceType === 'elder') { 140 speakElder(textToSpeak); 141 } else { 142 speak(textToSpeak, lang, voiceName, rate, pitch); 143 } 144} 145 146/** 147 * æåå½åæ£å¨è¿è¡çè¯é³æè¯»ã 148 */ 149export function pauseSpeech() { 150 speechSynthesis.pause(); 151} 152 153/** 154 * ç»§ç»ä¹åæåçè¯é³æè¯»ã 155 */ 156export function resumeSpeech() { 157 speechSynthesis.resume(); 158} 159 160/** 161 * 忢å½åæ£å¨è¿è¡çè¯é³æè¯»ï¼å¹¶åæ¶ææå¾ å¤ççæè¯»ä»»å¡ã 162 */ 163export function stopSpeech() { 164 speechSynthesis.cancel(); 165} 166 167/** 168 * è·åæµè§å¨æ¯æçææå¯ç¨è¯é³å表ã 169 * @returns {SpeechSynthesisVoice[]} å¯ç¨çè¯é³å表æ°ç»ã 170 */
171export function getAvailableVoices() { 172 return voices; 173}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.