So I am using a modified script to try to play some text from the Web Speech API.
The code was originally here:
Chrome Speech Synthesis with longer texts
Here's my modified variant:
function googleSpeech(text, rate) {
if (!reading) {
speechSynthesis.cancel();
if (timer) {
clearInterval(timer);
}
let msg = new SpeechSynthesisUtterance();
let voices = window.speechSynthesis.getVoices();
msg.voice = voices[63];
msg.voiceURI = 'native';
msg.volume = 1; // 0 to 1
msg.rate = rate; // 0.1 to 10
msg.pitch = 1; //0 to 2
msg.text = text;
msg.lang = 'zh-CN';
msg.onerror = function (e) {
speechSynthesis.cancel();
reading = false;
clearInterval(timer);
};
msg.onpause = function (e) {
};
msg.onboundary = function (event) {
};
msg.onend = function (e) {
speechSynthesis.cancel();
reading = false;
clearInterval(timer);
};
speechSynthesis.onerror = function (e) {
speechSynthesis.cancel();
reading = false;
clearInterval(timer);
};
console.log(msg);
speechSynthesis.speak(msg);
timer = setInterval(function () {
if (speechSynthesis.paused) {
speechSynthesis.resume();
}
}, 100);
reading = true;
}
}
I am able to get this to play ONCE. Whenever I try to get it to play again, it doesn't work - this is despite running speechSynthesis.cancel();
. When I reload the page, everything works fine again - for one playback.
Is there any consistent way to play the text again? It seems like this might be related to the many bugs in the Web Speech API. This is on Chrome 68.
Here's a sample of the text I am playing:
我是一个兵。来自老百姓。 打败了日本狗强盗。 消灭了蒋匪军。
The Web Speech API is powerful and somewhat underused. However, there are a few annoying bugs and the SpeechRecognition interface is poorly supported. speechSynthesis works surprisingly well once you iron out all of its quirks and issues.
The SpeechSynthesisUtterance interface of the Web Speech API represents a speech request. It contains the content the speech service should read and information about how to read it (e.g. language, pitch and volume.)
The Text-to-Speech API enables developers to generate human-like speech. The API converts text into audio formats such as WAV, MP3, or Ogg Opus. It also supports Speech Synthesis Markup Language (SSML) inputs to specify pauses, numbers, date and time formatting, and other pronunciation instructions.
Your code worked as it is, except I had to define reading = false
and timer = false
before the function.
My observation is when you pass rate value not between 0.1 to 1o
, your function is called only once. You may have to check your rate
values.
Additionally, if rate values not between specified, your onend
event doesn't get a call.
My system is Mac
and chrome is Version 67.0.3396.99 (Official Build) (64-bit)
<script type="text/javascript">
reading = false;
timer = false;
function googleSpeech(text, rate) {
if (!reading) {
speechSynthesis.cancel();
if (timer) {
clearInterval(timer);
}
let msg = new SpeechSynthesisUtterance();
let voices = window.speechSynthesis.getVoices();
msg.voice = voices[63];
msg.voiceURI = 'native';
msg.volume = 1; // 0 to 1
msg.rate = rate; // 0.1 to 10
msg.pitch = 1; //0 to 2
msg.text = text;
msg.lang = 'zh-CN';
msg.onerror = function (e) {
speechSynthesis.cancel();
reading = false;
clearInterval(timer);
};
msg.onpause = function (e) {
};
msg.onboundary = function (event) {
};
msg.onend = function (e) {
speechSynthesis.cancel();
reading = false;
clearInterval(timer);
};
speechSynthesis.onerror = function (e) {
speechSynthesis.cancel();
reading = false;
clearInterval(timer);
};
console.log(msg);
speechSynthesis.speak(msg);
timer = setInterval(function () {
if (speechSynthesis.paused) {
speechSynthesis.resume();
}
}, 100);
reading = true;
}
}
</script>
<button onclick="googleSpeech('我是一个兵。来自老百姓。 打败了日本狗强盗。 消灭了蒋匪军。',1)"> Play </button>
let reading = false;
let timer;
// let VV="ja-JP";
let VV = 'en-US';
function handleSaying(msg, text, rate) {
let voices = window.speechSynthesis.getVoices();
// console.log(voices);
msg.voice = voices.filter(v => v.lang === VV)[0];
// console.log("voice: ", msg.voice);
msg.voiceURI = 'native';
msg.volume = 1; // 0 to 1
msg.rate = rate; // 0.1 to 10
msg.pitch = 1; //0 to 2
msg.text = text;
msg.lang = VV;
msg.onerror = function(e) {
speechSynthesis.cancel();
reading = false;
clearInterval(timer);
};
msg.onpause = function(e) {};
msg.onboundary = function(event) {};
msg.onend = function(e) {
console.log("On end...");
// speechSynthesis.cancel();
reading = false;
clearInterval(timer);
};
speechSynthesis.onerror = function(e) {
speechSynthesis.cancel();
reading = false;
clearInterval(timer);
};
speechSynthesis.speak(msg);
timer = setInterval(function() {
if (speechSynthesis.paused) {
speechSynthesis.resume();
}
}, 100);
reading = true;
}
function googleSpeech(text, rate) {
if (!reading) {
speechSynthesis.cancel();
if (timer) {
clearInterval(timer);
}
let msg = new SpeechSynthesisUtterance();
// Here is the problem -- if the voices are ALREADY loaded from an earlier attempt
// onvoiceschanged does not fire a second time
if (window.speechSynthesis.getVoices().length > 0) {
handleSaying(msg, text, rate);
}
// wait on voices to be loaded before fetching list
window.speechSynthesis.onvoiceschanged = function() {
handleSaying(msg, text, rate);
}
};
}
<button type="button" onclick="googleSpeech('The quick brown fox jumped over the lazy dogs', 5)">English</button>
I've modified your code to play the voice twice and I commented in the code what the issue was. I ran into this problem before -- its a puzzler.
I've also had the issue of the onvoicechanged
firing MORE than once as it loaded voices then loaded a few more. I didn't put in the code to verify the voice that we want exists.
My system wouldn't play whatever language you were trying to use, so I changed to English text and voice, but that is easy enough to change back.
I also removed the hard-coded reference to a specific voice number, since those can change. Instead I look for the first voice matching the language ID.
You may want to reference this answer.
Getting the list of voices in speechSynthesis of Chrome (Web Speech API)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With