Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding pause and play functionality to Speech Synthesis API

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;
});
like image 975
wawaloo_17 Avatar asked Jan 03 '19 00:01

wawaloo_17


People also ask

How does the speechsynthesisutterance API work?

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.

What does the pause() method of the speechsynthesis interface do?

The pause () method of the SpeechSynthesis interface puts the SpeechSynthesis object into a paused state.

Why is speech synthesis not working on my browser?

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.

What is the speechsynthesisvoice component?

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.


1 Answers

[..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/

like image 197
traktor Avatar answered Sep 25 '22 04:09

traktor