Related to this text to speech question, I have the following code:
First of all, I am not sure if this is the best way to write the code, because it is initializing, and then setting a listener to "call itself". (seems a little bit hacky).
Second, although the listeners got "notified", the voices.length
is still 0
. Nothing really happens with an array of voices
. I had to uncomment this line:
// voices = synth.getVoices(); // this line should not be needed
so that it can come back with an array of voices
, but even so, the first word was not pronounced. Why does synth.getVoices()
need to be called twice, and why did the first word not get pronounced? It would appear you only have to call synth.getVoices()
once.
Note that if you try it as a snippet, no voice will be given out (due to iframe or security reasons? To hear something, the code needs to run in developer's console).
(one note when I was debugging: if all the voices were obtained, and the last 3 lines were run again, the second and third line became the same. It looks like some kind of "speech end" event needs to be listened to, to serialize them one by one -- maybe using a promise or async function. But then further debugging showed that it seemed a new instance of SpeechSynthesisUtterance
is needed each time, so I moved the let msg = new SpeechSynthesisUtterance();
to inside of the last else { }
and running those 3 lines have no problem).
So let me hide the original snippet:
let msg, synth, voices;
function foo(phrase) {
if (!voices) {
msg = new SpeechSynthesisUtterance();
synth = window.speechSynthesis;
voices = synth.getVoices();
console.log("Waiting 01", voices);
synth.addEventListener('voiceschanged', function() {
foo(phrase);
});
} else if (voices.length === 0) {
// this section is needed if foo() is called twice or multiple times in a row initially
console.log("Waiting 02", voices);
// voices = synth.getVoices(); // this line should not be needed
synth.addEventListener('voiceschanged', function() {
foo(phrase);
});
} else {
console.log("How many voices", voices.length);
// the voices are ready and could be changed here,
// but since each system is different, so it won't be
// changed here:
// msg.voice = voices[0];
msg.text = phrase;
synth.speak(msg);
}
}
foo("Hello");
foo("World");
foo("a third line");
and show the improved version (which still has the same problem):
let msg, synth, voices;
function foo(phrase) {
if (!voices) {
synth = window.speechSynthesis;
voices = synth.getVoices();
console.log("Waiting 01", voices);
synth.addEventListener('voiceschanged', function() {
foo(phrase);
});
} else if (voices.length === 0) {
// this section is needed if foo() is called twice or multiple times in a row initially.
// synth.getVoices() has been called and we shouldn't need to call it again.
// but if voices.length is still 0 we just again listen on the voiceschanged event and when ready, call foo(phrase)
console.log("Waiting 02", voices);
// voices = synth.getVoices(); // this line should not be needed
synth.addEventListener('voiceschanged', function() {
foo(phrase);
});
} else {
let msg = new SpeechSynthesisUtterance();
console.log("How many voices", voices.length);
// the voices are ready and could be changed here,
// but since each system is different, so it won't be
// changed here:
// msg.voice = voices[0];
msg.text = phrase;
synth.speak(msg);
}
}
foo("Hello");
foo("World");
foo("a third line");