Friday, 25 June, 2021 UTC


Summary


Hallo und Danke fürs Lesen! Dieser Blogpost ist eine Übersetzung von Live Transcribing Phone Calls using Twilio Media Streams and Google Speech-to-Text. Während wir unsere Übersetzungsprozesse verbessern, würden wir uns über Dein Feedback an [email protected] freuen, solltest Du etwas bemerken, was falsch übersetzt wurde. Wir bedanken uns für hilfreiche Beiträge mit Twilio Swag :)
Mit Twilio-Mediendatenströmen können wir jetzt die Funktionen unserer auf Twilio basierenden Sprachanwendung erweitern: durch Zugriff auf den Stream der Audiorohdaten von Anrufen in Echtzeit. So können wir beispielsweise Tools erstellen, die die Sprache eines Telefonanrufs live in ein Browserfenster transkribieren, eine Sentimentanalyse der Sprache bei einem Telefonanruf durchführen oder sogar Stimmbiometrie nutzen, um Personen zu erkennen.
In diesem Blog zeige ich Schritt für Schritt, wie wir Sprache von einem Telefonanruf mithilfe von Twilio und Google Speech-to-Text mit Node.js live im Browser in Text transkribieren.
Wenn du die Schritt-für-Schritt-Anleitungen überspringen möchtest, kannst du mein GitHub-Repository klonen und anhand der Infodatei die Einrichtung durchführen. Oder wenn du dir lieber ein Video ansiehst, dann findest du eine beispielhafte Vorgehensweise hier.
Voraussetzungen
Bevor wir beginnen, benötigen wir noch Folgendes:
  • Ein kostenloses Twilio-Konto
  • Ein Google Cloud-Konto
  • Installiertes ngrok
  • Installierte Twilio-CLI
Einrichten des lokalen Servers
Twilio-Mediendatenströme nutzen die WebSocket-API, um die Audiodaten des Telefonanrufs live in unsere Anwendung zu streamen. Beginnen wir mit dem Einrichten eines Servers, der WebSocket-Verbindungen verarbeiten kann.
Wir öffnen das Terminal und erstellen einen neuen Projektordner sowie eine index.js-Datei.
$ mkdir twilio-streams $ cd twilio-streams $ touch index.js 
Zur Verarbeitung von HTTP-Anfragen verwenden wir das integrierte http-Modul von Nodes und Express. Für die WebSocket-Verbindungen verwenden wir ws, einen einfachen WebSocket-Client für Node.
Zur Installation von ws und Express führen wir im Terminal die folgenden Befehle aus:
$ npm install ws express 
Wir öffnen die index.js-Datei und fügen ihr den folgenden Code hinzu, um den Server einzurichten.
const WebSocket = require("ws"); const express = require("express"); const app = express(); const server = require("http").createServer(app); const wss = new WebSocket.Server({ server }); // Handle Web Socket Connection wss.on("connection", function connection(ws) { console.log("New Connection Initiated"); }); //Handle HTTP Request app.get("/", (req, res) => res.send("Hello World")); console.log("Listening at Port 8080"); server.listen(8080); 
Wir speichern die index.js-Datei und führen sie mit node index.js aus. Wir öffnen den Browser und navigieren zu http://localhost:8080. Im Browser sollten wir Hello World sehen.
Nachdem wir jetzt wissen, dass HTTP-Anfragen funktionieren, können wir unsere WebSocket-Verbindung testen. Wir öffnen die Konsole des Browsers und führen den folgenden Befehl aus:
var connection = new WebSocket('ws://localhost:8080') 
Wenn wir zum Terminal zurückkehren, sollten wir ein Protokoll mit folgendem Text sehen: New Connection Initiated.
Einrichten von Telefonanrufen
Wir richten nun unsere Twilio-Nummer ein, um eine Verbindung zum WebSocket-Server herzustellen.
Zuerst müssen wir den Server so ändern, dass er die WebSocket-Nachrichten verarbeiten kann, die von Twilio gesendet werden, sobald der Telefonanruf mit dem Streaming beginnt. Es gibt vier wichtige Nachrichtenereignisse, die wir überwachen möchten: „connected“, „start“, „media“ und „stop“.
  • Connected: Wenn Twilio erfolgreich eine WebSocket-Verbindung zu einem Server herstellt
  • Start: Wenn Twilio mit dem Streaming von Medienpaketen beginnt
  • Media: Kodierte Medienpakete (Audiorohdaten)
  • Stop: Wenn das Streaming endet, wird das Stoppereignis gesendet.
Wir ändern die index.js-Datei so, dass Nachrichten protokolliert werden, wenn jede einzelne dieser Nachrichten auf dem Server eingeht.
const WebSocket = require("ws"); const express = require("express"); const app = express(); const server = require("http").createServer(app); const wss = new WebSocket.Server({ server }); // Handle Web Socket Connection wss.on("connection", function connection(ws) { console.log("New Connection Initiated"); ws.on("message", function incoming(message) { const msg = JSON.parse(message); switch (msg.event) { case "connected": console.log(`A new call has connected.`); break; case "start": console.log(`Starting Media Stream ${msg.streamSid}`); break; case "media": console.log(`Receiving Audio...`) break; case "stop": console.log(`Call Has Ended`); break; } }); }); //Handle HTTP Request app.get("/", (req, res) => res.send("Hello World"); console.log("Listening at Port 8080"); server.listen(8080); 
Jetzt müssen wir unsere Twilio-Nummer einrichten, um mit dem Streaming von Audiodaten auf den Server zu beginnen. Mit TwiML können wir steuern, was passiert, wenn wir unsere Twilio-Nummer anrufen. Wir erstellen eine HTTP-Route, die TwiML zurückgibt und Twilio anweist, Audiodaten vom Anruf auf den Server zu streamen.
Wir fügen der index.js-Datei die folgende POST-Route hinzu.
const WebSocket = require("ws"); const express = require("express"); const app = express(); const server = require("http").createServer(app); const wss = new WebSocket.Server({ server }); // Handle Web Socket Connection wss.on("connection", function connection(ws) { console.log("New Connection Initiated"); ws.on("message", function incoming(message) { const msg = JSON.parse(message); switch (msg.event) { case "connected": console.log(`A new call has connected.`); break; case "start": console.log(`Starting Media Stream ${msg.streamSid}`); break; case "media": console.log(`Receiving Audio...`) break; case "stop": console.log(`Call Has Ended`); break; } }); }; //Handle HTTP Request app.get("/", (req, res) => res.send("Hello World"); app.post("/", (req, res) => { res.set("Content-Type", "text/xml"); res.send(` <Response> <Start> <Stream url="wss://${req.headers.host}/"/> </Start> <Say>I will stream the next 60 seconds of audio through your websocket</Say> <Pause length="60" /> </Response> `); }); console.log("Listening at Port 8080"); server.listen(8080); 
Damit Twilio eine Verbindung zum lokalen Server herstellen kann, müssen wir den Port für das Internet verfügbar machen. Das gelingt am einfachsten mit der Twilio CLI. Dazu öffnen wir ein neues Terminal.
Wir kaufen zuerst eine Telefonnummer. Im Terminal führen wir den folgenden Befehl aus. Ich habe den Ländercode GB verwendet, um eine Mobiltelefonnummer zu kaufen, aber das kann ganz einfach in eine lokale Nummer geändert werden. Wir sollten den Friendly Name der Nummer aufbewahren, sobald die Antwort zurückgegeben wurde.
$ twilio phone-numbers:buy:mobile --country-code GB 
Zum Abschluss aktualisieren wir die Telefonnummer, so dass sie auf die Localhost-URL verweist. Wir müssen ngrok verwenden, um einen Tunnel zum Localhost-Port zu erstellen und ihn dem Internet verfügbar zu machen. In einem neuen Terminalfenster führen wir den folgenden Befehl aus:
$ ngrok http 8080 
Wir sollten eine Ausgabe mit einer Weiterleitungsadresse wie der folgenden erhalten. Wir kopieren die URL in die Zwischenablage. Wir müssen darauf achten, dass wir uns die https-URL notieren.
Forwarding https://xxxxxxxx.ngrok.io -> http://localhost:8080 
Zurück im Terminalfenster, in dem wir die Twilio-Nummer gekauft haben, aktualisieren wir unsere Telefonnummer, um eine HTTP-POST-Anfrage an den Server zu senden.
Wir führen den folgenden Befehl aus:
$ twilio phone-numbers:update $TWILIO_NUMBER --voice-url https://xxxxxxxx.ngrok.io 
Wir öffnen ein neues Terminalfenster und führen die index.js-Datei aus. Wenn wir jetzt unsere Twilio-Telefonnummer anrufen, sollten wir die folgende Aufforderung hören: „Ich streame die nächsten 60 Sekunden Audiodaten über das WebSocket.“ Im Terminal sollte Receiving Audio… (Audio wird empfangen) protokolliert werden.
HINWEIS: Wir müssen dafür sorgen, dass wir mindestens 2 Terminals ausführen, wenn das Protokoll nicht der erwarteten Antwort entspricht. In einem Terminal wird der Server (index.js) und im anderen Terminal wird ngrok ausgeführt.
Transkribieren von Sprache in Text
Wir haben jetzt erreicht, dass Audiodaten von unserem Anruf auf den Server gestreamt werden. In diesem Blog verwenden wir die Speech-to-Text-API der Google Cloud Platform, um die Sprachdaten des Telefonanrufs zu transkribieren.
Bevor wir aber beginnen können, müssen wir noch Folgendes ausführen.
  1. Das Cloud SDK installieren und initialisieren
  2. Ein neues GCP-Projekt einrichten
  • Ein Projekt erstellen oder auswählen
  • Die Google Speech-to-Text-API für das Projekt aktivieren
  • Ein Dienstkonto erstellen
  • Einen privaten Schlüssel als JSON herunterladen
  1. Wir legen die Umgebungsvariable GOOGLE_APPLICATION_CREDENTIALS auf den Dateipfad der JSON-Datei fest, die unseren Dienstkontoschlüssel enthält. Diese Variable gilt nur für die aktuelle Shell-Sitzung. Wenn wir eine neue Sitzung öffnen, müssen wir die Variable neu festlegen.
Wir führen den folgenden Befehl aus, um die Google Cloud Speech-to-Text-Clientbibliotheken zu installieren.
$ npm install --save @google-cloud/speech 
Jetzt endlich können wir die API in unserem Code verwenden.
Zuerst fügen wir den Speech-Client aus der Google Speech-to-Text-Bibliothek ein und dann konfigurieren wir einen Transcription Request. Damit wir die Ergebnisse der Live-Transkription erhalten, müssen wir interimResults auf den Wert „true“ festlegen. Außerdem habe ich den Sprachcode auf en-GB eingestellt. Das lässt sich aber ganz einfach in eine andere Sprachregion ändern.
const WebSocket = require("ws"); const express = require("express"); const app = express(); const server = require("http").createServer(app); const wss = new WebSocket.Server({ server }); //Include Google Speech to Text const speech = require("@google-cloud/speech"); const client = new speech.SpeechClient(); //Configure Transcription Request const request = { config: { encoding: "MULAW", sampleRateHertz: 8000, languageCode: "en-GB" }, interimResults: true }; // Handle Web Socket Connection wss.on("connection", function connection(ws) { console.log("New Connection Initiated"); ws.on("message", function incoming(message) { const msg = JSON.parse(message); switch (msg.event) { case "connected": console.log(`A new call has connected.`); break; case "start": console.log(`Starting Media Stream ${msg.streamSid}`); break; case "media": console.log(`Receiving Audio...`) break; case "stop": console.log(`Call Has Ended`); break; } }); }); //Handle HTTP Request app.get("/", (req, res) => res.send("Hello World"); app.post("/", (req, res) => { res.set("Content-Type", "text/xml"); res.send(` <Response> <Start> <Stream url="wss://${req.headers.host}/"/> </Start> <Say>I will stream the next 60 seconds of audio through your websocket</Say> <Pause length="60" /> </Response> `); }); console.log("Listening at Port 8080"); server.listen(8080); 
Jetzt erstellen wir einen neuen Stream, um Audiodaten vom Server an die Google-API zu senden. Wir nennen ihn recognizeStream und wir schreiben unsere Audiodatenpakete von unserem Telefonanruf in diesen Stream. Wenn der Anruf beendet ist, rufen wir .destroy() auf, um den Stream zu beenden.
Wir bearbeiten den Code so, dass diese Änderungen berücksichtigt werden.
const WebSocket = require("ws"); const express = require("express"); const app = express(); const server = require("http").createServer(app); const wss = new WebSocket.Server({ server }); //Include Google Speech to Text const speech = require("@google-cloud/speech"); const client = new speech.SpeechClient(); //Configure Transcription Request const request = { config: { encoding: "MULAW", sampleRateHertz: 8000, languageCode: "en-GB" }, interimResults: true }; // Handle Web Socket Connection wss.on("connection", function connection(ws) { console.log("New Connection Initiated"); let recognizeStream = null; ws.on("message", function incoming(message) { const msg = JSON.parse(message); switch (msg.event) { case "connected": console.log(`A new call has connected.`); // Create Stream to the Google Speech to Text API recognizeStream = client .streamingRecognize(request) .on("error", console.error) .on("data", data => { console.log(data.results[0].alternatives[0].transcript); }); break; case "start": console.log(`Starting Media Stream ${msg.streamSid}`); break; case "media": // Write Media Packets to the recognize stream recognizeStream.write(msg.media.payload); break; case "stop": console.log(`Call Has Ended`); recognizeStream.destroy(); break; } }); }); //Handle HTTP Request app.get("/", (req, res) => res.send("Hello World"); app.post("/", (req, res) => { res.set("Content-Type", "text/xml"); res.send(` <Response> <Start> <Stream url="wss://${req.headers.host}/"/> </Start> <Say>I will stream the next 60 seconds of audio through your websocket</Say> <Pause length="60" /> </Response> `); }); console.log("Listening at Port 8080"); server.listen(8080); 
Wir starten den Server neu, rufen unsere Twilio-Telefonnummer an und beginnen, ins Telefon zu sprechen. Im Terminal sollten wir jetzt Zwischenergebnisse der Transkription sehen.
Senden der Live-Transkription zum Browser
Ein Vorteil der Verwendung von WebSockets besteht darin, dass wir Nachrichten an andere Clients, einschließlich Browser, übertragen können.
Wir ändern jetzt den Code so, dass die Zwischenergebnisse der Transkription an alle Clients übertragen werden, zu denen eine Verbindung besteht. Außerdem ändern wir die GET-Route. Anstatt „Hello World“ senden wir eine HTML-Datei. Dazu benötigen wir auch das path-Paket, deshalb dürfen wir nicht vergessen, es anzufordern.
Wir ändern die index.js-Datei wie folgt.
const WebSocket = require("ws"); const express = require("express"); const app = express(); const server = require("http").createServer(app); const wss = new WebSocket.Server({ server }); const path = require("path"); //Include Google Speech to Text const speech = require("@google-cloud/speech"); const client = new speech.SpeechClient(); //Configure Transcription Request const request = { config: { encoding: "MULAW", sampleRateHertz: 8000, languageCode: "en-GB" }, interimResults: true }; // Handle Web Socket Connection wss.on("connection", function connection(ws) { console.log("New Connection Initiated"); let recognizeStream = null; ws.on("message", function incoming(message) { const msg = JSON.parse(message); switch (msg.event) { case "connected": console.log(`A new call has connected.`); //Create Stream to the Google Speech to Text API recognizeStream = client .streamingRecognize(request) .on("error", console.error) .on("data", data => { console.log(data.results[0].alternatives[0].transcript); wss.clients.forEach( client => { if (client.readyState === WebSocket.OPEN) { client.send( JSON.stringify({ event: "interim-transcription", text: data.results[0].alternatives[0].transcript }) ); } }); }); break; case "start": console.log(`Starting Media Stream ${msg.streamSid}`); break; case "media": // Write Media Packets to the recognize stream recognizeStream.write(msg.media.payload); break; case "stop": console.log(`Call Has Ended`); recognizeStream.destroy(); break; } }); }); //Handle HTTP Request app.get("/", (req, res) => res.sendFile(path.join(__dirname, "/index.html"))); app.post("/", (req, res) => { res.set("Content-Type", "text/xml"); res.send(` <Response> <Start> <Stream url="wss://${req.headers.host}/"/> </Start> <Say>I will stream the next 60 seconds of audio through your websocket</Say> <Pause length="60" /> </Response> `); }); console.log("Listening at Port 8080"); server.listen(8080); 
Wir richten jetzt eine Webseite ein, die die Zwischentranskriptionen verarbeitet und diese im Browser anzeigt.
Wir erstellen eine neue index.html-Datei und fügen ihr Folgendes hinzu:
<!DOCTYPE html> <html> <head> <title>Live Transcription with Twilio Media Streams</title> </head> <body> <h1>Live Transcription with Twilio Media Streams</h1> <h3> Call your Twilio Number, start talking and watch your words magically appear. </h3> <p id="transcription-container"></p> <script> document.addEventListener("DOMContentLoaded", event => { webSocket = new WebSocket("ws://localhost:8080"); webSocket.onmessage = function(msg) { const data = JSON.parse(msg.data); if (data.event === "interim-transcription") { document.getElementById("transcription-container").innerHTML = data.text; } }; }); </script> </body> </html> 
Wir starten den Server neu und laden localhost:8080 in den Browser. Wenn wir jetzt unsere Twilio-Telefonnummer anrufen, sehen wir, wie unsere gesprochenen Worte im Browser erscheinen.
Zusammenfassung
Gut gemacht! Wir haben gesehen, wie wir uns Twilio-Mediendatenströme zunutze machen können, um unsere Sprachanwendungen zu erweitern. Da wir jetzt eine Live-Transkription vorliegen haben, können wir versuchen, den Text mit der Google Translation-API zu übersetzen, um eine Live-Sprachübersetzung zu erstellen, oder eine Sentimentanalyse am Stream der Audiodaten durchzuführen, um die Emotionen hinter der Sprache zu ermitteln.
Wenn du noch Fragen oder Feedback hast oder mir einfach deine Ergebnisse zeigen willst, dann findest du mich unter: