你說:
I run the following HTML code to do text to speech offline on my Android phone. Can you help me improve synthesized speech quality by using the modern Android TTS engine?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Interactive Multi‑Lang TTS with Controls</title>
<style>
body { font-family: sans-serif; padding: 1em; }
ul { list-style: none; padding: 0; }
li { padding: 0.5em; cursor: pointer; }
li:hover { background: #eef; }
.active { background: #cce; }
label, select, input { display: block; margin: 0.5em 0; }
</style>
</head>
<body>
<h1>Upload a .txt file & speak selected sentences</h1>
<input type="file" id="fileInput" accept=".txt">
<label for="voiceSelect">Voice:</label>
<select id="voiceSelect"></select>
<label for="rate">Rate: <span id="rateVal">1</span></label>
<input type="range" id="rate" min="0.5" max="2" value="1" step="0.1">
<label for="pitch">Pitch: <span id="pitchVal">1</span></label>
<input type="range" id="pitch" min="0" max="2" value="1" step="0.1">
<ul id="sentenceList"></ul>
<script>
const fileInput = document.getElementById('fileInput');
const sentenceList = document.getElementById('sentenceList');
const voiceSelect = document.getElementById('voiceSelect');
const rate = document.getElementById('rate'), rateVal = document.getElementById('rateVal');
const pitch = document.getElementById('pitch'), pitchVal = document.getElementById('pitchVal');
const synth = window.speechSynthesis;
function populateVoices() {
const voices = synth.getVoices();
if (!voices.length) return;
// Preferred languages in order
const pref = ['ja-JP', 'en', 'zh-TW'];
const ordered = [];
const others = [];
voices.forEach(v => {
(pref.some(l => v.lang.startsWith(l)) ? ordered : others).push(v);
});
voiceSelect.innerHTML = '';
ordered.concat(others).forEach(v => {
const opt = document.createElement('option');
opt.value = v.name;
opt.textContent =
${v.name} (${v.lang})
;
voiceSelect.appendChild(opt);
});
}
populateVoices();
if (synth.onvoiceschanged !== undefined) {
synth.onvoiceschanged = populateVoices;
}
fileInput.addEventListener('change', e => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.readAsText(file);
reader.onload = () => {
const lines = reader.result.split(/\r?\n/).filter(l => l.trim());
sentenceList.innerHTML = '';
lines.forEach(line => {
const li = document.createElement('li');
li.textContent = line;
li.addEventListener('click', () => speakFiltered(line, li));
sentenceList.appendChild(li);
});
};
reader.onerror = () => alert('Error reading file.');
});
rate.addEventListener('input', () => rateVal.textContent = rate.value);
pitch.addEventListener('input', () => pitchVal.textContent = pitch.value);
function speakFiltered(text, li) {
sentenceList.querySelectorAll('li').forEach(el => el.classList.remove('active'));
li.classList.add('active');
const filtered = text.replace(/\s*\(.*?\)/g, '').trim();
if (!filtered) return;
const utter = new SpeechSynthesisUtterance(filtered);
const voice = synth.getVoices().find(v => v.name === voiceSelect.value);
if (voice) utter.voice = voice;
utter.rate = parseFloat(rate.value);
utter.pitch = parseFloat(pitch.value);
synth.cancel();
synth.speak(utter);
}
</script>
</body>
</html>