Créer un plugin de lecteur d’écran avec le Robot /text/speak

Note historique sur les tarifs : les prix et quotas ci-dessous décrivent l’offre au moment de la publication de cet article. Consultez les tarifs actuels pour les forfaits d’aujourd’hui.
Nous avons récemment présenté une mise à jour de l’un de nos Robots d’IA préférés, et aujourd’hui, nous prolongeons cet élan en ajoutant un nouveau membre à la famille des Robots d’IA. Dites bonjour à notre nouveau Robot /text/speak ! Comme le dernier Robot d’IA que nous avons présenté, le Robot /speech/transcribe, la fonctionnalité principale de ce Robot tourne autour de la parole. Mais au lieu de transcrire la parole en texte, il vous permet de convertir du texte en parole (TTS).
Cet ajout étant particulièrement enthousiasmant, mon collègue Joseph et moi avons décidé de créer chacun un projet construit autour de ce Robot, puis de rédiger un article de blog sur notre démarche de conception pour montrer la polyvalence de ce Robot. Si cet article vous plaît, surveillez donc celui de Joseph, qui arrive bientôt !
Pour mon projet, je vais créer un lecteur d’écran facile à intégrer à votre site web, qui convertit en parole tout le texte d’une page en un clic.
Il s’agit d’une expérience de synthèse vocale, et non d’un substitut à un lecteur d’écran d’assistance prenant en charge la navigation et l’interaction. Lorsque vous appuyez sur le bouton, la démo envoie à Transloadit le texte du tutoriel ci-dessous, sans les exemples de code. L’audio suit la langue affichée dans le menu des langues du site ; utilisez les commandes audio pour lire le résultat. L’exemple autonome ci-dessous choisit plutôt sa voix à partir de l’attribut de langue de son premier bloc de texte balisé.

Prérequis
Avant de commencer, vous devez créer un dossier de site web standard. Créez les fichiers
suivants : index.html, index.js et, éventuellement, style.css. Pour ne pas alourdir cet
article, nous aborderons uniquement le contenu du fichier js et la manière de le relier à notre
fichier HTML. Toutefois, si vous souhaitez copier l’intégralité du contenu de nos autres fichiers,
ainsi que le GIF d’animation de chargement utilisé plus loin dans cette démonstration, vous pouvez
consulter le dépôt suivant.
HTML
Commençons par mettre en place la partie HTML de notre application. Comme indiqué ci-dessus, nous n’aborderons pas la création d’une page web, seulement les éléments nécessaires pour communiquer avec notre fichier JavaScript. Créez donc un fichier HTML de base contenant les éléments suivants dans la balise body :
<button id="button_event" onclick="runScript()">Generate</button>
<p id="status" role="status"></p>
<div data-screenreaderlanguage="en-US">
<p>Sample Text</p>
</div>
<script src="index.js"></script>
Cela met en place l’interface utilisateur de notre application. Le bouton que nous avons créé déclenche notre requête d’Assembly /text/speak à partir du texte contenu dans notre balise div. Pour référencer cette balise div, nous lui attribuons une valeur de dataset que le développeur back-end utilise également pour sélectionner la langue cible du Robot, d’après les options décrites dans notre documentation. Enfin, nous lions notre fichier JavaScript à la fin du body.
Pour communiquer avec l’API Transloadit, nous utiliserons Robodog, une version allégée de notre outil gratuit d’envoi de fichiers Uppy.
Remarque : Robodog est désormais déprécié. Pour les nouvelles intégrations, utilisez le plugin Transloadit d’Uppy (interface Dashboard ou interface personnalisée).
Dans l’en-tête de votre page HTML, ajoutez la ligne suivante :
<script src="https://releases.transloadit.com/uppy/robodog/v1.10.7/robodog.min.js"></script>
JavaScript
Combinez dans l’ordre les extraits JavaScript suivants dans index.js. Ces extraits répartissent un
seul programme entre plusieurs explications ; il ne s’agit pas de modules distincts. Le bundle
historique de Robodog est épinglé ci-dessous ; la démo interactive ci-dessus utilise le plugin
Transloadit d’Uppy, toujours maintenu.
N’utilisez que du texte public que les utilisateurs ont accepté d’envoyer. En production, créez sur votre serveur des paramètres signés de courte durée à l’aide de la fonctionnalité Signature Authentication, limitez les Instructions autorisées et la taille des entrées, et n’exposez jamais d’Auth Secret dans le code exécuté par le navigateur.
Récupérer notre texte
Passons à notre fichier JavaScript : nous pouvons maintenant commencer à écrire notre programme. La première chose à faire est d’analyser le texte lisible de notre site web et de l’affecter à une variable pour l’utiliser plus tard.
// …
const result = document.querySelectorAll('[data-screenreaderlanguage]')
const textArray = []
for (const element of result) textArray.push(element.innerText)
const language = document.querySelector('[data-screenreaderlanguage]').dataset.screenreaderlanguage
// …
Dans ce code, nous utilisons la méthode Document.querySelectorAll() pour créer un tableau contenant tous les
éléments d’information associés au dataset screenreaderlanguage. Nous utilisons un dataset afin que,
si nous voulons exclure du texte lisible de la sortie de notre lecteur d’écran (comme le texte
d’une balise <code>), nous puissions fermer notre balise div avant ce texte indésirable, puis
créer une nouvelle balise div avec le même dataset à l’endroit où notre lecteur d’écran doit
reprendre le traitement du texte.
Une fois ces informations de base rassemblées, nous devons encore extraire le texte lisible des
données collectées. Nous initialisons donc d’abord une variable de type tableau pour stocker notre
texte lisible, puis nous utilisons la boucle for...of pour parcourir chaque élément sélectionné et
enregistrer sa valeur innerText.
Pour terminer cette section, nous définissons une variable de langue afin que notre
Robot /text/speak connaisse la langue ciblée plus loin dans notre
programme. Pour cela, nous déclarons notre variable de langue en lui affectant la valeur screenreaderlanguage
de notre dataset.
Convertir pour Uppy
Nos données étant prêtes, nous devons les rendre exploitables par notre instance Robodog.
// …
const mytextfile = new File([textArray.join('\n\n')], 'mytextfile.txt', { type: 'text/plain' })
// …
Cela crée un fichier texte avec des sauts de paragraphe entre les blocs de texte collectés. Ne balisez que du contenu dans une même langue ; l’exemple lit la langue du premier bloc balisé.
Animation de chargement
Pour signaler aux utilisateurs qu’un traitement est en cours, nous voulons afficher une animation de chargement pendant que notre Robot /text/speak effectue son traitement. Pour cela, nous avons besoin de quelques variables temporaires pour remplacer notre élément bouton par un GIF. Ensuite, si le traitement du texte réussit, nous remplaçons cet élément GIF par un lecteur audio.
// …
const buttonEl = document.getElementById('button_event')
const statusEl = document.getElementById('status')
const tmpGif = document.createElement('img')
const audioPlayer = document.createElement('AUDIO')
tmpGif.src = 'loader.gif'
tmpGif.width = 100
tmpGif.height = 100
tmpGif.id = 'tmpGif'
tmpGif.alt = 'Generating speech'
// …
Ici, nous avons déclaré trois variables pour stocker des éléments de notre document HTML ; la
première variable, buttonEl, référence un élément existant, tandis que les deux autres créent de
nouveaux éléments. En dessous, nous attribuons plusieurs attributs à notre élément GIF de
remplacement. Enfin, nous définissons un fichier GIF source issu de notre répertoire de travail,
ses dimensions et un ID auquel nous ferons référence plus loin dans notre programme.
Déclencher le script
Ces étapes préliminaires étant en place, nous pouvons maintenant intégrer la fonctionnalité principale de notre programme. Définissons le gestionnaire de clic du bouton Generate.
Pour commencer, déclarez une fonction portant le même nom que celle que nous avons référencée plus tôt dans notre page HTML.
// …
function runScript() {
if (!buttonEl.isConnected) return
statusEl.textContent = 'Generating speech.'
buttonEl.parentNode.replaceChild(tmpGif, buttonEl)
// …
Notre fonction étant en place, nous pouvons utiliser notre première méthode de manipulation du DOM,
parentNode.replaceChild(). Elle remplace le bouton de notre page par le nouvel élément GIF lorsque notre fonction
est exécutée.
Maintenant que le GIF est en place pour indiquer que les données sont en cours de traitement, nous pouvons utiliser l’API Transloadit pour synthétiser la parole de notre programme. Pour cela, nous utilisons le script Robodog que nous avons importé dans notre fichier HTML.
// …
window.Robodog.upload([mytextfile], {
waitForEncoding: true,
params: {
auth: { key: 'TRANSLOADIT_AUTH_KEY' },
steps: {
':original': {
robot: '/upload/handle',
},
speech: {
use: ':original',
robot: '/text/speak',
provider: 'aws',
target_language: language,
},
},
},
})
// …
Dans notre nouvelle instance Robodog, nous devons configurer deux paramètres. Le premier correspond au fichier à envoyer, pour lequel nous passons la variable de fichier texte créée plus tôt dans notre programme. L’autre paramètre est un objet contenant toutes les autres options supplémentaires.
Dans notre paramètre objet, nous devons configurer quelques étapes. Tout
d’abord, waitForEncoding doit être défini sur true afin que nous puissions récupérer les résultats de notre
Assembly plus loin dans notre programme. Ensuite, nous devons également insérer
notre clé d’authentification dans le paramètre auth. Cette clé se trouve dans l’onglet
Informations d’identification de votre console Transloadit. Enfin, nous devons insérer les
Steps de notre Template : l’un pour l’envoi
et l’autre pour la synthèse vocale.
Plusieurs paramètres composent le Step de synthèse vocale. D’abord, nous devons
indiquer à ce Step que nous voulons utiliser le fichier texte envoyé ; c’est le
rôle du paramètre "use". Ensuite, nous devons indiquer avec le paramètre "robot" que nous
utilisons notre Robot /text/speak. Le paramètre suivant,
"provider", permet de choisir l’API backend de synthèse vocale. Les valeurs disponibles sont
« gcp » et « aws ». Chaque fournisseur propose une grande variété de voix, mais nous nous en
tiendrons à la voix par défaut. Le dernier paramètre que nous utiliserons, "target_language", indique à notre
API dans quelle langue notre texte doit être prononcé. Cette valeur provient de la variable language
que nous avons définie au début de notre programme.
Une fois cette fonctionnalité en place, nous utilisons la méthode JavaScript then() pour renvoyer
une promesse contenant nos données vocales afin de pouvoir y faire référence plus tard.
Utiliser le résultat
Robodog étant entièrement configuré, nous pouvons maintenant stocker la parole générée pour la lire ensuite depuis le navigateur.
// …
.then((bundle) => {
const audio_url = bundle.results?.find((result) => result.stepName === 'speech')?.ssl_url
if (!audio_url) throw new Error('No speech result returned')
audioPlayer.setAttribute('src', audio_url)
audioPlayer.setAttribute('controls', 'controls')
audioPlayer.setAttribute('aria-label', 'Generated speech')
audioPlayer.setAttribute('lang', language)
audioPlayer.setAttribute('style', 'width: 100%;')
tmpGif.parentNode.replaceChild(audioPlayer, tmpGif)
statusEl.textContent = 'Speech is ready. Use the audio controls to play it.'
})
// …
Dans notre méthode then(), grâce à la promesse fournie par Robodog, nous pouvons analyser les
résultats JSON de l’Assembly et enregistrer l’URL du résultat dans une nouvelle
variable. Avec cette nouvelle variable, audio_url, nous pouvons utiliser la même méthode de
remplacement d’éléments que celle employée plus tôt dans cette démonstration. Ensuite, pour rendre
le lecteur audio opérationnel, nous définissons quelques attributs qui fournissent la source audio
et affichent les commandes de lecture. La lecture est déclenchée par l’utilisateur ; elle ne
dépend donc pas de l’autorisation de lecture automatique. Une fois tout cela en place, un lecteur
de synthèse vocale est généré et intégré à votre site web !
Il ne faut toutefois pas oublier de prévoir une solution de secours. C’est là que nous utilisons la
méthode JavaScript catch() pour gérer les éventuelles erreurs.
// …
.catch(() => {
statusEl.textContent = 'Could not generate speech. Please try again.'
tmpGif.parentNode.replaceChild(buttonEl, tmpGif)
})
}
// …
Les erreurs produisent un statut lisible, et notre GIF de traitement est de nouveau remplacé par l’élément bouton d’origine. Ainsi, si vous perdiez momentanément votre connexion réseau avant de la retrouver, vous pourriez cliquer sur le bouton Generate pour relancer la génération de la parole.
Et voilà, cette démonstration touche à sa fin ! Nous espérons que vous conviendrez qu’elle a une fois de plus démontré toute la polyvalence de notre API. Bien sûr, vous pouvez simplement ajouter le programme que nous avons créé à n’importe quel site web pour obtenir un lecteur d’écran simple et rapide – mais ne vous arrêtez pas là ! Utilisez plutôt cet article comme un ensemble de briques de base pour enrichir vos projets, et n’oubliez pas de nous faire part de vos résultats :) Notre Robot /text/speak est disponible pour tous nos clients payants ; si cet article a suscité votre intérêt, vous pourriez donc envisager de mettre à niveau votre compte. Notre premier forfait payant est à 49 $/mois avec 10 GB de données d’encodage !