Transcription live avec Twilio Media Streams, Azure cognitive Services et Java
Temps de lecture: 7 minutes
Twilio Media Streams peut être utilisé pour diffuser des données audio en temps réel d'un appel téléphonique vers votre serveur à l'aide de WebSockets. Associé à un système de synthèse vocale, il peut être utilisé pour générer une transcription en temps réel d'un appel téléphonique. Dans ce post, je vais vous montrer comment configurer un serveur Java WebSocket pour gérer les données audio de Twilio Media Streams et utiliser Azure cognitive Services Speech pour la transcription.
Prérequis
Pour suivre ce tutoriel, vous devez disposer des éléments suivants :
- Java 11 ou version ultérieure. Je recommande SDKMAN ! pour la gestion des versions Java
- Un compte Twilio et un compte Azure
- Ngrok ou la CLI Twilio
Si vous souhaitez passer à l'étape suivante, vous pouvez trouver le code complété dans mon répertoire sur GitHub.
Mise en route
Pour qu'un projet Web Java soit rapidement opérationnel, je vous recommande d'utiliser Spring Initializr. Ce lien vous permet de paramétrer toutes les configurations nécessaires pour ce projet. Cliquez sur « Generate » (Générer) pour télécharger le projet, puis décompressez-le et ouvrez le projet dans votre IDE.
Il y aura un seul fichier source Java dans src/main/Java/com/example/twilio/mediastreamsazuretranscription
intitulé MediaStreamsAzureTranscriptionApplication.java
. Vous n'aurez pas besoin de modifier ce fichier, mais il contient une méthode main
que vous pourrez utiliser ultérieurement pour exécuter le code.
Répondre à un appel téléphonique et lancer la diffusion multimédia en continu
Pour commencer, créons un code qui requêtera à Twilio de répondre à un appel téléphonique, de réciter un court message, puis de lancer un flux multimédia que nous utiliserons pour effectuer la transcription. Twilio va diffuser des données audio binaires sur une URL que nous fournissons, et nous les enverrons sur Azure pour transcription.
Pour commencer, nous devons créer un point de terminaison HTTP qui servira le TwiML suivant sur /twiml
:
Avant de commencer à coder, voyons ce TwiML en détail pour voir ce qui se passe :
- Le verbe
<Say>
(Dire) utilise la synthèse vocale pour lire le message « Hello ! » (Bonjour) à voix haute. - Ensuite, démarrez (
<Start>
) un flux multimédia (<Stream>
) vers une URL WebSocket (nous verrons comment créer cette URL ultérieurement). - Enfin, mettez en pause (
<Pause>
) pendant 30 secondes de temps de transcription, puis raccrochez pour mettre fin à l'appel et à la transcription.
Tout d'abord, ajoutez la bibliothèque Twilio Helper au projet. Il s'agit d'un projet Gradle. Il y a donc un fichier intitulé build.gradle
dans la racine du projet avec une section dependencies
à laquelle vous devez ajouter :
Nous vous recommandons toujours d'utiliser la dernière version de la bibliothèque Twilio Helper. La dernière version est la version 8.12.0, mais de nouvelles versions sont publiées fréquemment. Vous pouvez toujours vérifier la dernière version à l'adresse mvnreporistory.com.
Dans le même package que la classe MediaStreamsAzureTranscriptionApplication
, créez une classe intitulée TwimlRestController
avec le code suivant :
[ce code avec les instructions d'importation sur GitHub]
Vous pouvez voir que l'URL WebSocket est actuellement codée en dur. Cela ne nous convient pas.
Construire l'URL WebSocket
La même application que nous construisons pour gérer les requêtes HTTP traitera également les requêtes WebSocket de Twilio. Les URL WebSocket ressemblent beaucoup aux URL HTTP, mais au lieu de https://hostname/path
, nous avons besoin de wss://hostname/path
.
Pour créer l'URL WebSocket, nous avons besoin d'un nom d'hôte et d'un chemin. Pour le chemin, nous pouvons choisir tout ce que nous voulons (nous allons utiliser /messages
), mais le nom d'hôte nécessite un peu plus de travail. Nous pourrions le coder en dur, mais nous devons ensuite modifier le code à chaque fois que nous déployons quelque chose de nouveau. Une meilleure approche consiste à inspecter la requête HTTP vers /twiml
pour voir le nom d'hôte qui y a été utilisé. Nous le trouverons dans l'en-tête de l'hôte. La requête HTTP complète ressemble à ceci :
Il est possible que nous déployions cette application derrière un proxy ou une passerelle API, ce qui peut nécessiter de se faufiler et de modifier la valeur de l'en-tête de l'hôte. Ngrok (que je vais utiliser plus tard dans ce tutoriel) est un de ces proxys. Nous devons donc également vérifier l'en-tête X-Original-Host
qui sera défini si Host
a été modifié. Certains proxys l'appellent X-Forwarded-Host
ou même autrement, mais il s'agit de la même chose. Dans ce cas, une requête HTTP peut ressembler à ceci :
Pour une requête comme celle-ci, le nom d'hôte dans l'URL wss://
doit être be136ff2eaca.ngrok.io
. Maintenant que nous savons comment construire l'URL WebSocket, voyons-la dans le code. Remplacez le TwimlRestController
par ce qui suit :
[ce code avec les importations sur GitHub]
À vérifier rapidement...
Vérifiez que tout fonctionne comme prévu en exécutant la méthode main
dans MediaStreamsAzureTranscriptionApplication
via votre IDE ou en exécutant ./gradlew clean bootRun
sur la ligne de commande. L'application démarre et vous pouvez utiliser Curl ou tout autre outil HTTP pour envoyer une requête POST à http://localhost:8080/twiml
. Mon outil de choix pour ce genre de choses est HTTPie. Notez que l'URL WebSocket dans la réponse sera wss://localhost:8080/messages
, car votre client a placé Host: Localhost:8080
comme en-tête lorsqu'il a effectué la requête. C'est l'idéal.
Gérer les connexions WebSocket
Il est bien d'avoir une URL wss://
dans votre TwiML, mais nous devons vraiment y ajouter du code pour traiter les requêtes WebSocket de Twilio. Autrement, il ne s'agit que d'un lien 404. Dans le même package, créez à nouveau une classe intitulée TwilioMediaStreamsHandler
avec ce contenu :
[ce code avec les importations sur GitHub]
Nous avons également besoin d'une classe de configuration. Appelez-la WebSocketConfig
dans le même package :
[ce code avec les importations sur GitHub]
La prochaine chose à mettre en œuvre est de connecter ce système à Azure.
Avec une touche d'Azure...
Vous pouvez obtenir une bonne vue d'ensemble du service Azure Speech-to-Text à partir de sa documentation. Il s'agit d'un service de grande envergure et il existe de nombreuses façons de l'utiliser. Dans tous les cas, vous aurez besoin d'un compte Azure. Inscrivez-vous ici si vous n'en avez pas déjà un. Ce projet s'adapte confortablement au niveau gratuit et à ses 5 heures par mois. Suivez les instructions d'Azure pour configurer une ressource vocale. Les éléments importants dont vous aurez besoin pour votre code sont les suivants :
- La clé d'abonnement (une longue chaîne de lettres et de chiffres)
- L'emplacement ou la région (par exemple,
westus
)
Définissez-les comme variables d'environnement intitulées AZURE_SPEECH_SUBSCRIPTION_KEY
et AZURE_SERVICE_REGION
et voyons comment les utiliser dans du code.
Ajoutez une dépendance pour le SDK client Azure Speech à côté de l'emplacement où vous avez ajouté la dépendance Twilio dans build.gradle
. Nous aurons besoin d'un analyseur JSON ultérieurement. Par conséquent, ajoutez Jackson ici également :
Créez un package intitulé Azure
en regard de toutes vos classes, puis une classe intitulée AzureSpeechToTextService
pour encapsuler la connexion à Azure :
[ce code avec les importations sur GitHub]
Puisqu'il s'agit d'une quantité importante de code, nous allons la décomposer :
- Lignes 3 et 4 : lecture des variables d'environnement qui vous authentifient auprès d'Azure. Je les ai définis avec le plug-in EnvFile d'IntelliJ.
Dans le constructeur :
- Ligne 10 : créez un élément
PushAudioInputStream
que nous pouvons utiliser pour envoyer des données audio binaires à Azure. - Lignes 12 à 14 : créez et initialisez la classe de client Azure principale :
SpeechRecognizer
. - Lignes 16 à 19 : ajoutez un rappel pour les reconnaissances partielles. Cela permet d'obtenir des transcriptions mot à mot en temps réel.
- Lignes 21 à 24 : ajoutez un autre rappel, cette fois-ci pour obtenir des reconnaissances complètes. Il s'agit de phrases complètes avec une majuscule et une ponctuation correctes. Elles ont tendance à être plus précises que les reconnaissances partielles (nous verrons un exemple ci-dessous), mais elles sont fournies légèrement plus lentement.
- Ligne 26 :
speechRecognizer.startContinuousRecognitionAsync();
- Ouvrez la connexion à Azure.
La méthode accept
aux lignes 29-31 prend un byte[]
contenant des données audio binaires de Twilio. Le codage utilisé par Twilio est appelé μ-law et est conçu pour une compression efficace de la voix enregistrée. Malheureusement, Azure n'accepte pas les données codées au format μ-law. Nous devons donc les transcoder dans un format accepté, à savoir PCM. Ces détails ne sont pas inclus dans ce tutoriel, mais vous pouvez télécharger la classe MulawToPcm utilisée à la ligne 30 ci-dessus depuis GitHub dans le package
azure
et l'utiliser directement.
Connecter le gestionnaire WebSocket à Azure
Le dernier morceau de code à ajouter est l'élément TwilioMediaStreamsHandler
que vous avez créé précédemment. Les méthodes ne comportent que des espaces réservés pour le moment, mais elles doivent utiliser AzureSpeechToTextService
. Remplacez le contenu de cette classe par :
[ce code avec les importations sur GitHub]
Ce code conserve un élément Map<WebSocketSession, AzureSpeechToTextService>
afin que plusieurs appels puissent être transcrits en même temps sans que le son n'interfère d'un appel à un autre. Chaque AzureSpeechToTextService
est initialisé sur la ligne 11. Le constructeur prend un élément Consumer<String>
qui est utilisé pour gérer les transcriptions lorsqu'elles reviennent d'Azure. Ici, j'ai transmis System.out::println
en tant que référence de méthode.
La méthode handleTextMessage
sera appelée environ 50 fois par seconde, car de nouvelles données audio arrivent de Twilio en petits morceaux. Les messages sont au format JSON avec les données audio μ-law codées en base64 dans le JSON. Nous utilisons donc Jackson et java.util.Base64
pour extraire les données audio et les transmettre.
Le code est enfin complet
Utiliser ce code avec un vrai numéro de téléphone
Connectez-vous à votre compte Twilio (utilisez ce lien si vous devez en créer un maintenant. Vous recevrez un crédit supplémentaire de 10 $ lorsque vous mettez à niveau votre compte). Achetez un numéro de téléphone et rendez-vous sur la page de configuration pour obtenir votre nouveau numéro. Vous souhaitez insérer une URL lorsqu'un appel entre (« a call comes in ») sous « Voice & Fax » (Synthèse vocal et télécopie). Mais quelle URL ? Actuellement, l'application n'est disponible que sur une URL localhost
que Twilio ne peut atteindre. Vous disposez de deux options pour exposer votre application à l'Internet public :
- Exécutez ngrok directement
- Utilisez la CLI Twilio
Redémarrez l'application, soit en exécutant la méthode main
de MediaStreamsAzureTranscriptionApplication
, soit avec ./gradlew clean bootRun
sur la ligne de commande. Dans tous les cas, le serveur écoute sur le port 8080.
Créer une URL publique à l'aide de ngrok
La commande suivante crée une URL publique pour votre serveur localhost:8080
:
La sortie Ngrok contiendra une URL https de transfert comme https://<RANDOM LETTERS>
. Il s'agit de l'URL que vous devez saisir lorsqu'un appel entre (« a call comes in ») dans votre console Twilio. N'oubliez pas d'ajouter le chemin d'accès de /twiml
et d'enregistrer la configuration du numéro de téléphone.
Se connecter à l'aide de la CLI Twilio
La CLI Twilio peut être utilisé pour configurer ngrok et votre numéro de téléphone en une seule étape :
La CLI Twilio détecte l'URL localhost
et configure ngrok pour vous. Génial, non ?
Appelez-moi ?
Une fois que l'application est en cours d'exécution et que ngrok ou la CLI Twilio vous a donné une URL publique configurée dans la console Twilio, il est temps de passer un appel téléphonique et de regarder la sortie sur votre console :
[voir la session complète sur asciinema]
Conclusion
Twilio Media Streams et Azure Cognitive Services Speech collaborent pour produire des transcriptions en temps réel de haute qualité. Vous pouvez étendre cette fonctionnalité pour transcrire chaque intervenant d'une conférence téléphonique séparément, l'associer à d'autres services cloud pour créer des résumés, rechercher des mots-clés lors d'un appel, construire des outils pour aider les agents d'appel ou aller là où votre imagination vous mène. Si vous avez des questions à ce sujet ou sur tout autre élément que vous construisez avec Twilio, je serais heureux d'en savoir plus.
- @MaximumGilliard
- mgilliard@twilio.com
J'ai hâte de voir ce que vous allez construire !
Articles associés
Ressources connexes
Twilio Docs
Des API aux SDK en passant par les exemples d'applications
Documentation de référence sur l'API, SDK, bibliothèques d'assistance, démarrages rapides et didacticiels pour votre langage et votre plateforme.
Centre de ressources
Les derniers ebooks, rapports de l'industrie et webinaires
Apprenez des experts en engagement client pour améliorer votre propre communication.
Ahoy
Le hub de la communauté des développeurs de Twilio
Meilleures pratiques, exemples de code et inspiration pour créer des expériences de communication et d'engagement numérique.