diff --git a/docs/plugins/text-to-speech-announcer.md b/docs/plugins/text-to-speech-announcer.md index 1ad2da19..c3c907db 100644 --- a/docs/plugins/text-to-speech-announcer.md +++ b/docs/plugins/text-to-speech-announcer.md @@ -79,7 +79,7 @@ In some cases you may not want to clear the entire queue, but instead cancel out Imagine an App with a row of tiles, it's possible that before the title of the role is being spoken out, the user already navigates through the tiles within the row. Traditionally you'd use the focus event to speak out info about each tile (i.e. adding tot the queue). You don't want all previously focused tiles to still be announced, but would still want the category of the row to be announced, making clearing the queue not required. -The `speak()`-method return a Promise that also contains a `cancel()` function. When called, it will cancel that specific message and remove it from the queue before it can be spoken out. +The `speak()`-method return a Promise that also contains a `remove()` function. When called, it will remove it from the queue before it can be spoken out. Additionally if you want to _interrupt_ a specific messages as it's being spoken out as well and go straight to the next message in the queue (i.e. the newly focused item, for example). You can use the `stop()` message that is returned on the Promise returned by the `speak()`-method. @@ -100,8 +100,8 @@ Blits.Component('MyTile', { unfocus() { // when unfocused interrupt the message if it's already being spoken out this.message.stop() - // and cancel the message to remove it from the queue - this.message.cancel() + // and remove the message from the queue + this.message.remove() } } }) @@ -117,4 +117,4 @@ Alternatively the announcer can be enabled or disabled run time by using one of - `this.$announcer.enable()` - activates the announcer - `this.$announcer.disable()` - deactivates the announcer -- `this.$announcer.disable(true/false)` - turns the announcer or on off \ No newline at end of file +- `this.$announcer.disable(true/false)` - turns the announcer on or off diff --git a/src/announcer/announcer.js b/src/announcer/announcer.js index ddecd7b7..01ac6df6 100644 --- a/src/announcer/announcer.js +++ b/src/announcer/announcer.js @@ -15,6 +15,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { Log } from '../lib/log.js' import speechSynthesis from './speechSynthesis.js' let active = false @@ -28,6 +29,7 @@ const noopAnnouncement = { then() {}, done() {}, cancel() {}, + remove() {}, stop() {}, } @@ -66,11 +68,11 @@ const addToQueue = (message, politeness, delay = false) => { resolveFn = resolve }) - // augment the promise with a cancel function - done.cancel = () => { + // augment the promise with a cancel / remove function + done.remove = done.cancel = () => { const index = queue.findIndex((item) => item.id === id) if (index !== -1) queue.splice(index, 1) - isProcessing = false + Log.debug(`Announcer - removed from queue: "${message}" (id: ${id})`) resolveFn('canceled') } @@ -92,6 +94,8 @@ const addToQueue = (message, politeness, delay = false) => { queue.push({ delay, resolveFn, id }) } + Log.debug(`Announcer - added to queue: "${message}" (id: ${id})`) + setTimeout(() => { processQueue() }, 100) @@ -118,17 +122,22 @@ const processQueue = async () => { if (debounce !== null) clearTimeout(debounce) // add some easing when speaking the messages to reduce stuttering debounce = setTimeout(() => { + Log.debug(`Announcer - speaking: "${message}" (id: ${id})`) + speechSynthesis - .speak({ message }) + .speak({ message, id }) .then(() => { - isProcessing = false + Log.debug(`Announcer - finished speaking: "${message}" (id: ${id})`) + currentId = null + isProcessing = false resolveFn('finished') processQueue() }) .catch((e) => { - isProcessing = false currentId = null + isProcessing = false + Log.debug(`Announcer - error ("${e.error}") while speaking: "${message}" (id: ${id})`) resolveFn(e.error) processQueue() }) diff --git a/src/announcer/speechSynthesis.js b/src/announcer/speechSynthesis.js index 697a8356..5c454ba4 100644 --- a/src/announcer/speechSynthesis.js +++ b/src/announcer/speechSynthesis.js @@ -64,7 +64,7 @@ const initialize = () => { const speak = (options) => { const utterance = new SpeechSynthesisUtterance(options.message) - const id = Date.now() + Math.random() // Unique ID for tracking + const id = options.id utterance.lang = options.lang || defaultUtteranceProps.lang utterance.pitch = options.pitch || defaultUtteranceProps.pitch utterance.rate = options.rate || defaultUtteranceProps.rate