Reputation: 11285
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:
我是一个兵。来自老百姓。 打败了日本狗强盗。 消灭了蒋匪军。
Upvotes: 5
Views: 943
Reputation: 11285
The answer actually turned out to be related to the value of the rate property.
While the official documentation for the Web Speech API says that it supports a value of .1 to 10 for rate, it appears that Chrome (at least Chrome 68, the version I am running) does not fully support a rate outside of the range of .5 to 2.
Anything outside this range causes sound dispersion and breaks after one use of the API and the sound will not work anymore until page refresh.
Upvotes: 1
Reputation: 3526
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>
Upvotes: 4
Reputation: 23863
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)
Upvotes: 1