2026-08-20 18:53:43 +00:00
|
|
|
function _buildBrowserUtterance(text, btn){
|
|
|
|
|
const utter=new SpeechSynthesisUtterance(text);
|
|
|
|
|
const savedVoice=localStorage.getItem('hermes-tts-voice');
|
|
|
|
|
const voices=speechSynthesis.getVoices();
|
|
|
|
|
if(savedVoice&&voices.length){
|
|
|
|
|
const match=voices.find(v=>v.name===savedVoice);
|
|
|
|
|
if(match) utter.voice=match;
|
|
|
|
|
}
|
|
|
|
|
return utter;
|
|
|
|
|
}
|
2026-08-20 23:13:51 +00:00
|
|
|
function _playEdgeTtsChunked(text, btn){
|
2026-08-20 18:53:43 +00:00
|
|
|
const voice=localStorage.getItem('hermes-tts-voice')||'zh-CN-XiaoxiaoNeural';
|
2026-08-20 23:13:51 +00:00
|
|
|
return fetch('/api/tts',{body:JSON.stringify({text:chunk, voice:voice, rate:rate, pitch:pitch})});
|
|
|
|
|
}
|
|
|
|
|
function speakSelected(clean, btn, engine){
|
|
|
|
|
if(engine==='edge'){
|
|
|
|
|
_playEdgeTtsChunked(clean, btn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
function speakAutomatically(clean, engine){
|
|
|
|
|
if(engine==='edge'){
|
|
|
|
|
_playEdgeTtsChunked(clean, null);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-08-20 18:53:43 +00:00
|
|
|
function registeredTts(engine, clean){
|
|
|
|
|
const _opts={
|
|
|
|
|
voice: localStorage.getItem('hermes-tts-voice')||'',
|
|
|
|
|
rate: parseFloat(localStorage.getItem('hermes-tts-rate')),
|
|
|
|
|
};
|
|
|
|
|
const autoOpts={
|
|
|
|
|
voice: localStorage.getItem('hermes-tts-voice')||'',
|
|
|
|
|
pitch: parseFloat(localStorage.getItem('hermes-tts-pitch')),
|
|
|
|
|
};
|
|
|
|
|
return [engine, clean, _opts, autoOpts];
|
|
|
|
|
}
|