Last active
November 24, 2021 18:55
-
-
Save Chengings/6e84c684b57129e118e3c43b8b434fc5 to your computer and use it in GitHub Desktop.
Web Speech API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> | |
<meta name="viewport" content="width=device-width"> | |
<title>Speech synthesiser</title> | |
<style> | |
body, html { | |
margin: 0; | |
} | |
html { | |
height: 100%; | |
} | |
body { | |
height: 90%; | |
max-width: 800px; | |
margin: 0 auto; | |
} | |
h1, p { | |
font-family: sans-serif; | |
text-align: center; | |
padding: 20px; | |
} | |
.txt, select, form > div { | |
display: block; | |
margin: 0 auto; | |
font-family: sans-serif; | |
font-size: 16px; | |
padding: 5px; | |
} | |
select { | |
width: 83%; | |
} | |
form > div { | |
width: 81%; | |
} | |
.txt, form > div { | |
margin-bottom: 10px; | |
font-size: 1.3rem; | |
overflow: auto; | |
} | |
.clearfix { | |
clear: both; | |
} | |
label { | |
float: left; | |
width: 10%; | |
line-height: 1.5; | |
} | |
.rate-value, .pitch-value { | |
float: right; | |
width: 5%; | |
line-height: 1.5; | |
} | |
#rate, #pitch { | |
float: right; | |
width: 81%; | |
} | |
.controls { | |
text-align: center; | |
margin-top: 10px; | |
} | |
.controls button { | |
padding: 15px; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Say it!</h1> | |
<form> | |
<textarea cols="50" rows="12" class="txt"></textarea> | |
<div class="controls"> | |
<button id="play" type="submit">Say π</button> | |
<button type="button" id="read-cb">Read Clipboard & Say ππ</button> | |
</div> | |
<div> | |
<label for="rate">Rate</label><input type="range" min="0.5" max="2" value="1" step="0.1" id="rate"> | |
<div class="rate-value">1</div> | |
<div class="clearfix"></div> | |
</div> | |
<div> | |
<label for="pitch">Pitch</label><input type="range" min="0" max="2" value="1" step="0.1" id="pitch"> | |
<div class="pitch-value">1</div> | |
<div class="clearfix"></div> | |
</div> | |
<select></select> | |
</form> | |
<script> | |
let synth = window.speechSynthesis; | |
let inputForm = document.querySelector('form') | |
let inputTxt = document.querySelector('.txt') | |
let voiceSelect = document.querySelector('select') | |
let pitch = document.querySelector('#pitch') | |
let pitchValue = document.querySelector('.pitch-value') | |
let rate = document.querySelector('#rate') | |
let rateValue = document.querySelector('.rate-value') | |
const readClipboard = document.querySelector('#read-cb') | |
let voices = []; | |
function populateVoiceList() { | |
voices = synth.getVoices().sort(function (a, b) { | |
const langA = a.lang, langB = b.lang; | |
if (langA < langB) { | |
return -1 | |
} else if (langA > langB) { | |
return 1 | |
} | |
return 0 | |
}) | |
let selectedIndex = voiceSelect.selectedIndex; | |
if (voiceSelect.selectedIndex < 0) { | |
let defaultNavLangIndex, customNavLangIndex; | |
voices.forEach(({ name, lang }, index) => { | |
if (lang.toLowerCase() === navigator.language.toLowerCase()) { | |
defaultNavLangIndex = index; | |
} | |
// For Edge only, prefer en-gb natural voice. | |
if (/natural.*english.*united.*kingdom/i.test(name)) { | |
customNavLangIndex = index; | |
} | |
}) | |
selectedIndex = customNavLangIndex || defaultNavLangIndex || 0; | |
} | |
voiceSelect.innerHTML = ''; | |
voices.forEach((voice, i) => { | |
let option = document.createElement('option') | |
option.textContent = `${voices[i].name} (${voices[i].lang})`; | |
option.setAttribute('data-lang', voices[i].lang) | |
option.setAttribute('data-name', voices[i].name) | |
voiceSelect.appendChild(option) | |
}) | |
voiceSelect.selectedIndex = selectedIndex | |
} | |
populateVoiceList(); | |
function speak(){ | |
if (synth.speaking) { | |
synth.cancel() | |
} | |
if (inputTxt.value !== '') { | |
let utterThis = new SpeechSynthesisUtterance(inputTxt.value) | |
utterThis.onend = function (event) { | |
console.log('SpeechSynthesisUtterance.onend') | |
} | |
utterThis.onerror = function (event) { | |
console.error('SpeechSynthesisUtterance.onerror') | |
} | |
let selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name') | |
utterThis.voice = voices.find((voice, index) => voice.name === selectedOption) | |
utterThis.pitch = pitch.value; | |
utterThis.rate = rate.value; | |
synth.speak(utterThis) | |
} | |
} | |
if (speechSynthesis.onvoiceschanged !== undefined) { | |
speechSynthesis.onvoiceschanged = populateVoiceList; | |
} | |
inputForm.onsubmit = function(event) { | |
event.preventDefault() | |
speak() | |
inputTxt.blur() | |
} | |
pitch.oninput = function() { | |
pitchValue.textContent = pitch.value; | |
} | |
rate.oninput = function() { | |
rateValue.textContent = rate.value; | |
} | |
voiceSelect.onchange = function(){ | |
speak() | |
} | |
readClipboard.onclick = e => { | |
navigator.clipboard.readText().then(txt => { | |
inputTxt.value = txt | |
speak() | |
}) | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment