I'm new to JavaScript and I am trying to add a pause button by linking a button to synth.pause(speakText);
where const synth = window.speechSynthesis;
and const speakText = new SpeechSynthesisUtterance(textInput.value);
.
I can't make speakText accessible to the pause function since I construct my speakText object in my speak() function. I tried making speakText a global variable by calling its constructor outside of the function but that causes speak() to throw an error.
Any idea on how I can achieve this?
JS Code:
//Speak
const speak = () => {
//Check to see if already speaking
if (synth.speaking && state === "play") {
console.error("Already speaking");
state = "paused";
console.log("state: " + state);
return;
}
//Make sure there is some input
if (textInput.value !== "" && state === "stop") {
const speakText = new SpeechSynthesisUtterance(textInput.value);
state = "play";
//Speak error
speakText.onerror = e => {
console.log("Something went wrong!");
};
//Selected voice
const selectedVoice = voiceSelect.selectedOptions[0].getAttribute(
"data-name"
);
//Loop through voices to set the correct voice
voices.forEach(voice => {
if (selectedVoice === voice.name) {
speakText.voice = voice;
}
});
//Set the rate and pitch
speakText.rate = rate.value;
speakText.pitch = pitch.value;
//Speak end
speakText.onend = e => {
console.log("Done Speaking");
state = "stop";
};
speakController(speakText);
}
};
const speakController = speakText => {
console.log("state: " + state);
if (state === "play") {
synth.speak(speakText);
} else if (state === "pause") {
synth.pause(speakText);
} else if (state === "stop") {
synth.cancel(speakText);
}
};
//----------EVENT LISTENERS----------
var state = "stop";
// Text form submit
textForm.addEventListener("submit", e => {
e.preventDefault();
speak();
textInput.blur();
});
//Pause button
pauseButton.addEventListener("onClick", e => {
e.preventDefault();
speakController;
});
When the user submits it, the API must read the input's content and speak it with the selected voice. Check it out: First, we create a SpeechSynthesisUtterance object with the typed text as the constructor's argument.
The pause () method of the SpeechSynthesis interface puts the SpeechSynthesis object into a paused state.
Speech synthesis isn't handles the same by all browsers; that code won't always work on Chrome or Firefox for example. The flag the code uses to determine if there is speech running is superfluous as speech will queue. I suggest using separate pause and resume buttons.
This component will basically hold a state to the voice list and a select element to choose between them. The state is typed with SpeechSynthesisVoice, which is an object that has some properties regarding the voices offered by the browser, as: name, language and a default flag, that will be true to the default voice of your browser.
[..How..] to add a pause button by linking a button to synth.pause(speakText);
The cheap answer would be to have the button call speechSynthesis.pause()
(which doesn't take arguments) - because synth
is just a copy of the global window.speechSynthesis
property.
A better solution is to create a controller that exposes an interface of methods and properties to outside callers, and encapsulates its own internal workings.
You touched on the problem here:
I can't make speakText accessible to the pause function since I construct my speakText object in my speak() function.
which means there's a code structure design issue. But there is another problem: the speech synthesizer doesn't have states of "play", "pause" and "stop". It has two mutually exclusive states of "play" and "pause", and a totally independent condition of "queue empty".
I don't propose to fix the posted code - although I certainly tried. Here's what I ended up with to determine what was going on - it is experimental code but hopefully the podcast will help!
"use strict";
const tr = {
queue: null,
pause: null,
play: null,
cancel: null,
defaultRate: 1.1,
defaultPitch: 1,
// voice selection to do;
};
function createTextReader( tr) {
let synth = window.speechSynthesis; // abbreviation
let Utter = SpeechSynthesisUtterance; // abbreviation
// queue
tr.queue = (text, rate, pitch, voiceIndex) => {
let utter = new Utter();
utter.text = text;
utter.rate = rate || tr.defaultRate || 1;
utter.pitch = pitch || tr.defaultPitch || 1;
// voice selection to do
// if( voiceParam) ....
synth.speak( utter);
};
tr.pause = () => synth.pause();
tr.play = () => synth.resume();
tr.cancel = () => synth.cancel();
}
window.addEventListener( "DOMContentLoaded", function (e) {
createTextReader( tr)}, false);
window.addEventListener("unload", e=>tr.cancel(), false);
<textarea cols=40 rows=4 id="queueText">
Press "queue text" to add text area content to the text reader. Press it multiple times to add text more than once.
Press "pause" to pause reading.
Press "play" to start or resume reading queued text from the speech synthesizer's fifo queue. Play mode is in effect at startup - but you could pause the reader before queuing text.
Press "cancel" to stop reading and empty the queue. It does not change pause versus play mode. If the reader is paused when cancel is clicked, it remains so afterwards.
This voice is the default voice in this browser, and may be different in another. More code is needed for voice selection. If you visit MDN's speech synthesis example on git hub, view page source and click on the link to "script.js" you can see how they do it.
Oh, and don't forget to cancel speech synthesis on window unload.
Thanks for listening!
</textarea><br>
<button type="button" onclick="tr.queue(queueText.value)">queue text</button>
<p>
<button type="button" onclick="tr.pause()">pause</button>
<button type="button" onclick="tr.play()">play</button>
<button type="button" onclick="tr.cancel()">cancel</button>
<p>
The link for the MDN page referred to is https://mdn.github.io/web-speech-api/speak-easy-synthesis/
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