https://d226lax1qjow5r.cloudfront.net/blog/blogposts/speech-voice-translation-microsoft-dr/babel-fish-tutorial.png

Bauen Sie einen Babel Fish mit Nexmo und der Microsoft Translator Speech API

Zuletzt aktualisiert am May 12, 2021

Lesedauer: 12 Minuten

Wenn Sie in den letzten Monaten im Internet waren, haben Sie wahrscheinlich Googles Echtzeit-Übersetzung Pixel Buds gesehen. Eine Technologie ähnlich wie der Babel-Fisch in Per Anhalter durch die Galaxis die jede empfindungsfähige Sprache für ihren Träger übersetzen kann und es ihm so ermöglicht, mit praktisch jedem Wesen zu kommunizieren. Die Google Pixel Buds haben natürlich ihren Preis - warum also nicht unsere eigenen bauen? Das ist es, was Danielle und ich bei der letzten hackference. Wir haben daraufhin einen Nexmo Babel-Fisch entwickelt, mit dem zwei Personen telefonieren können, wobei jede Partei eine übersetzte Version dessen hört, was die jeweils andere Partei sagt.

BabelfishBabelfish

In diesem Blogpost gehen wir Schritt für Schritt auf die Funktionsweise dieses Babel-Fish-Systems ein und beginnen mit der erforderlichen Einrichtung und Konfiguration. Dann werden wir eine Nexmo-Nummer für die Bearbeitung eingehender Anrufe einrichten. Danach werden wir einen Python-Server implementieren, der Sprache über einen WebSocket empfängt und die eingehende Sprache von der Nexmo-Nummer an die Microsoft Translator Speech API weiterleitet. Wir werden die Translator Speech API verwenden, um die Transkription und Übersetzung durchzuführen. Darüber hinaus werden wir eine Logik implementieren, um einen bidirektionalen Dialog zu führen und die Nexmo-Nummer anzuweisen, die Übersetzungen zu sprechen. Um die Implementierung zu vereinfachen, müssen beide Parteien die Nexmo-Nummer unseres Dienstes anrufen. Unten sehen Sie ein Systemdiagramm auf hoher Ebene, das zeigt, wie eine Instanz von Sprache von beiden Seiten verarbeitet wird. Beachten Sie, dass ich in diesem Tutorial das Beispiel einer deutsch-britisch-englischen Konversation verwenden werde.

Diagram that shows how a message passes through the system. A German caller speaks a message in German which Nexmo passes through to a Python server. The Python server sends the German audio to the Microsoft Speech API. The Speech API responds by sending the English translation as text to the Python server. The Python server then sends a request to Nexmo to speak the English message to the British caller. At this point the British caller hears the translated message in English.

Wenn Sie lieber nur den Code sehen möchten, finden Sie ihn auf GitHub hier.

Voraussetzungen

Sie benötigen sowohl Python 2.x oder 3.x als auch die HTTP-Tunnelsoftware ngrok installiert haben, um der Anleitung folgen zu können. Wir werden alle Befehle auflisten, die Sie benötigen, um alles andere zu installieren, während Sie dem Kurs folgen.

Erste Schritte

Richten Sie Ihre Umgebung ein

Beginnen wir mit unserer DIY-Babel-Fisch-Lösung, indem wir eine virtuelle Umgebung für dieses Projekt einrichten. Virtualenv. Virtualenv ermöglicht es uns, die Abhängigkeiten dieses Projekts von unseren anderen Projekten zu isolieren. Legen Sie ein Verzeichnis für dieses Projekt an und kopieren Sie die folgende Liste von Abhängigkeiten in eine Datei in Ihrem Projektverzeichnis mit dem Namen requirements.txt:

nexmo
tornado>=4.4.2
requests>=2.12.4

Um Ihre virtuelle Umgebung zu erstellen und zu aktivieren, führen Sie die folgenden Befehle in Ihrem Terminal aus:

virtualenv venv  # sets up the environment
source venv/bin/activate  # activates the environment
pip install -r requirement.txt  # installs our dependencies

# if you are running python3 please run the following instead
pip3 install -r requirement.txt

Starten Sie nun ngrok in einem separaten Terminalfenster, indem Sie den unten stehenden Befehl ausführen. ngrok ermöglicht es uns, unseren localhost an Port 5000 für eingehende Anfragen freizugeben. Sie müssen ngrok im Hintergrund laufen lassen, damit dies funktioniert. Sie können mehr über die Verbindung von ngrok mit Nexmo lesen hier.

ngrok http 5000

Sobald Sie den obigen Befehl ausgeführt haben, sollte Ihr Terminal ähnlich wie im folgenden Screenshot aussehen. Sie benötigen die Weiterleitungs-URL, wenn Sie Ihre Nexmo-Anwendung und -Nummer in den nächsten Schritten konfigurieren.

Screenshot of ngrok running in a terminal and displaying a forwarding URL of the form “http://016a0331.ngrok.io”.

Vonage API-Konto

Um dieses Tutorial durchzuführen, benötigen Sie ein Vonage API-Konto. Wenn Sie noch keines haben, können Sie sich noch heute anmelden und mit einem kostenlosen Guthaben beginnen. Sobald Sie ein Konto haben, finden Sie Ihren API-Schlüssel und Ihr API-Geheimnis oben auf dem Vonage-API-Dashboard.

In diesem Lernprogramm wird auch eine virtuelle Telefonnummer verwendet. Um eine zu erwerben, gehen Sie zu Rufnummern > Rufnummern kaufen und suchen Sie nach einer Nummer, die Ihren Anforderungen entspricht.

Eine Nexmo-Anwendung erstellen

Gehen Sie zu Ihren Applications und fügen Sie eine neue Applikation hinzu. Verwenden Sie die Ngrok-Weiterleitungs-URL sowohl für die Ereignis-URL als auch für die Antwort-URL und fügen Sie /event als Pfad für die Ereignis-URL (z.B. http://016a0331.ngrok.io/event) und /ncco für die Antwort-URL (z.B. http://016a0331.ngrok.io/ncco). Wir werden diese Endpunkte später einrichten. Erzeugen Sie ein öffentliches/privates Schlüsselpaar über die Benutzeroberfläche und speichern Sie den Schlüssel auf Ihrem Computer.

Screen capture of a user creating an application using the Nexmo application menu. A user clicks on add new application. In the form that appears the user enters babelfish as the application name,  as the Event URL, and  as the Answer URL. The user then clicks on the  link, saves the key when prompted, and finally clicks on create application.

Der letzte Schritt bei der Einrichtung der Nummer besteht darin, die zuvor erworbene Nummer mit Ihrer Anwendung zu verknüpfen. Verwenden Sie das Dashboard der Anwendung, um die Nummer zu verknüpfen.

Screen capture of a user linking a purchased number in the edit application menu on Nexmo. A user clicks on the NUMBERS rider at the top of the application dashboard. Then the user clicks on the link button for the previously purchased number.

Schlüssel für Microsofts Translator Speech API erhalten

Der andere Dienst, den wir einrichten müssen, ist Microsofts Übersetzer-Sprach-API. Melden Sie sich für einen kostenlosen Microsoft Azure Account an unter azure.com und gehen Sie anschließend zu portal.azure.com und erstellen Sie eine Translator Speech API-Ressource. Den dabei erzeugten Schlüssel benötigen Sie für den nächsten Schritt.

Screen capture of a user setting up the Microsoft Translator Speech API. A user types translator speech into the Marketplace search on the Microsoft Azure portal. The user then clicks on the Translator Speech API option that comes up and clicks on the create button on the API overview screen. The user then fills in the form for the resource using babelfish as the name, Pay-as-you-go as the subscription, F0 (10 Hours of audio input) as the pricing tier, and babelfish-resource as the resource group name. After checking the box that the user has 'read and understood the notice' and checking add to dashboard, the user clicks on create and is redirected to the dashboard. After the deployment finishes, the user clicks on the deployed resource and is presented with a resource dashboard. On the resource dashboard under the section grab the keys the user clicks on keys and copies key 1.

Verwalten von Geheimnissen und Konfigurationen

Jetzt, da wir unsere Nexmo-Nummer und unseren Translator Speech API-Schlüssel haben, müssen wir nur noch eine Secrets- und eine Config-Datei mit all diesen wichtigen Details einrichten, damit wir sie nicht ständig neu schreiben müssen und sie separat verwalten können. Speichern Sie die untenstehende Datei in secrets.py in Ihrem Projektordner und ersetzen Sie die Platzhalterwerte durch Ihre Werte.

# Replace the below values with your values
# Your API key and secret can be found here ${CUSTOMER_DASHBOARD_URL}/getting-started-guide
NEXMO_API_KEY = "<your-api-key>"
NEXMO_API_SECRET = "<your-api-secret>"
# Your nexmo number
NEXMO_NUMBER = "+447512345678"
# This is found on your Nexmo application’s dashboard
NEXMO_APPLICATION_ID = "<nexmo-application-id>"
# This is the private key you downloaded when setting up your application
NEXMO_PRIVATE_KEY = '''-----BEGIN PRIVATE KEY-----
<your-private-key>
cobranding_allowed: false
-----END PRIVATE KEY-----'''

# You will have to sign up for a free Microsoft account to use the Microsoft Translator Speech API: http://docs.microsofttranslator.com/speech-translate.html
MICROSOFT_TRANSLATION_SPEECH_CLIENT_SECRET = "<your-api-key>"

Speichern Sie anschließend die unten stehenden Dateien in config.py in Ihrem Projektordner und ersetzen Sie wiederum die Platzhalterwerte durch Ihre Werte. Beachten Sie, dass Sie auch andere Sprachen als die unten aufgeführten wählen können. Sie können diese auch später jederzeit ändern.

HOSTNAME = '<your-value>.ngrok.io'

# Replace the variable assignment with your number in the same format
CALLER = '447812345678'

# Replace the variable assignment with your languages
LANGUAGE1 = 'de-DE'


# Replace the variable assignments with the respective name for your language. They can be found here:
# https://developer.nexmo.com/api/voice/ncco#voice-names
VOICE1 = 'Marlene'

# the other person's language and voice
LANGUAGE2 = 'en-US'
VOICE2 = 'Kimberly'

Tutorial-Schritte

Im Folgenden werden wir zunächst die Authentifizierung bei der Translator Speech API erläutern. Dann werden wir unseren Tornado-Webserver mit Hilfe einer mitgelieferten Vorlage einrichten. Anschließend implementieren wir die CallHandler, die EventHandlerund die WSHandler. Die CallHandler wird eingehende Anrufe an die Nexmo-Nummer für uns bearbeiten. Darüber hinaus wird die EventHandler verwendet, um Ereignisse zu verarbeiten, die Nexmo sendet, wie z.B. den Beginn oder das Ende eines Anrufs. Mit jedem Ereignis sendet Nexmo Informationen über den Akteur, der den Anruf begonnen oder beendet hat. Wir verwenden diese Informationen, um zu speichern, wer an einem bestimmten Anruf beteiligt ist. Die WSHandler wird in der Zwischenzeit verwendet, um den WebSocket zu öffnen, über den Nexmo und unser Python-Server kommunizieren. Der Python-Server erstellt Audioschnipsel und sendet sie an die Translator Speech API. Der Handler verwendet die Informationen, die der EventHandler sammelt, um Nachrichten korrekt weiterzuleiten. In den folgenden Abschnitten werden diese Konzepte näher erläutert und die jeweilige Implementierung gezeigt.

Authentifizierung mit der Translator Speech API von Microsoft

Um die Translator Speech API zu verwenden, benötigen wir ein Token, das wir als MICROSOFT_TRANSLATION_SPEECH_CLIENT_SECRET. Glücklicherweise bietet Microsoft einen Python AzureAuthClient den wir ohne Änderungen verwenden werden. Bitte kopieren Sie den folgenden Text und speichern Sie ihn in einer Datei namens azure_auth_client.py in Ihrem Projektverzeichnis.

"""
Code example for getting a A from the Azure Platform.
Visit http://docs.microsofttranslator.com/oauth-token.html to view the API reference
for Microsoft Azure Cognitive Services authentication service.
"""

from datetime import timedelta
from datetime import datetime

import requests

class AzureAuthClient(object):
    """
    Provides a client for obtaining an OAuth token from the authentication service
    for Microsoft Translator in Azure Cognitive Services.
    """

    def __init__(self, client_secret):
        """
        :param client_secret: Client secret.
        """

        self.client_secret = client_secret
        # token field is used to store the last token obtained from the token service
        # the cached token is re-used until the time specified in reuse_token_until.
        self.token = None
        self.reuse_token_until = None

    def get_access_token(self):
        '''
        Returns an access token for the specified subscription.
        This method uses a cache to limit the number of requests to the token service.
        A fresh token can be re-used during its lifetime of 10 minutes. After a successful
        request to the token service, this method caches the access token. Subsequent
        invocations of the method return the cached token for the next 5 minutes. After
        5 minutes, a new token is fetched from the token service and the cache is updated.
        '''

        if (self.token is None) or (datetime.utcnow() > self.reuse_token_until):

            token_service_url = 'https://api.cognitive.microsoft.com/sts/v1.0/issueToken'

            request_headers = {'Ocp-Apim-Subscription-Key': self.client_secret}

            response = requests.post(token_service_url, headers=request_headers)
            response.raise_for_status()

            self.token = response.content
            self.reuse_token_until = datetime.utcnow() + timedelta(minutes=5)

        return self.token

Einen Server erstellen

Das Computerkommunikationsprotokoll WebSockets ermöglicht einen zweiseitigen Kommunikationskanal über eine einzige TCP-Verbindung. Die Voice API von Nexmo ermöglicht es, Telefonanrufe mit solchen WebSocket-Endpunkten zu verbinden. Wir werden das Tornado Web Server Web-Framework verwenden, da es das WebSocket-Protokoll für uns implementiert.

Wenn Sie den Anweisungen gefolgt sind und alle Dateien wie beschrieben benannt haben, können Sie mit der folgenden Einrichtung des Tornado-Webservers beginnen. Dieser Code verarbeitet alle unsere Importe, richtet den Nexmo-Client und den Azure-Auth-Client ein und startet einen Server an Port 5000. Beachten Sie, dass dieser Server noch nichts Nützliches tut. Er hat drei Endpunkte: ncco, event, und socket die den CallHandler, EventHandler, und WSHandler aufrufen. Wir werden die Handler in den folgenden Abschnitten implementieren.

Erstellen Sie eine Datei namens main.py in Ihrem Projektverzeichnis und kopieren Sie diesen Code hinein.

from string import Template
import json
import os
import requests
import struct
import StringIO

from tornado import httpserver, httpclient, ioloop, web, websocket, gen
from xml.etree import ElementTree
import nexmo

from azure_auth_client import AzureAuthClient
from config import HOSTNAME, CALLER, LANGUAGE1, VOICE1, LANGUAGE2, VOICE2
from secrets import NEXMO_APPLICATION_ID, NEXMO_PRIVATE_KEY, MICROSOFT_TRANSLATION_SPEECH_CLIENT_SECRET, NEXMO_NUMBER


nexmo_client = nexmo.Client(application_id=NEXMO_APPLICATION_ID, private_key=NEXMO_PRIVATE_KEY)
azure_auth_client = AzureAuthClient(MICROSOFT_TRANSLATION_SPEECH_CLIENT_SECRET)

conversation_id_by_phone_number = {}
call_id_by_conversation_id = {}


class CallHandler(web.RequestHandler):
    @web.asynchronous
    def get(self):
        self.write("Hello world")


class EventHandler(web.RequestHandler):
    @web.asynchronous
    def post(self):
        self.write("Hello world")


class WSHandler(websocket.WebSocketHandler):
    def open(self):
        print("WebSocket opened")

    def on_message(self, message):
        self.write_message(u"You said: " + message)

    def on_close(self):
        print("WebSocket closed")


def main():
    application = web.Application([
        (r"/event", EventHandler),
        (r"/ncco", CallHandler),
        (r"/socket", WSHandler),
    ])

    http_server = httpserver.HTTPServer(application)
    port = int(os.environ.get("PORT", 5000))
    http_server.listen(port)
    print("Running on port: " + str(port))

    ioloop.IOLoop.instance().start()


if __name__ == "__main__":
    main()

Implementierung des CallHandlers

Um Telefonanrufe mit WebSocket-Endpunkten zu verbinden, verwendet die Voice API von Nexmo eine Nexmo Calle Controlle Objekt (NCCO) oder einen API-Aufruf. Wenn jemand Ihre Nexmo-Nummer anruft, sendet Nexmo eine Anfrage an die Antwort-URL, die Sie bei der Einrichtung Ihrer Nexmo Voice Application angegeben haben. Wir haben unsere Anwendung auf unseren Server verwiesen, der nun diese Anfrage beantworten muss, indem er ein NCCO. Diese NCCO sollte Nexmo anweisen, dem Anrufer eine kurze Begrüßungsnachricht zu geben und den Anrufer dann mit dem WebSocket zu verbinden.

Diagram that shows the interactions between a user, Nexmo, and the web server. When the user calls the Nexmo number, Nexmo sends a GET request to the web server's /ncco endpoint. The web server responds with an NCCO that instructs Nexmo to open a socket with the web server.Diagram that shows the interactions between a user, Nexmo, and the web server. When the user calls the Nexmo number, Nexmo sends a GET request to the web server's /ncco endpoint. The web server responds with an NCCO that instructs Nexmo to open a socket with the web server.

Speichern Sie nun die folgende NCCO in eine Datei namens ncco.json in Ihrem Projektverzeichnis. Sie enthält eine Vorlage, die die erforderlichen Aktionen ausführt. Sie enthält jedoch auch einige Platzhaltervariablen ($hostname, $whoami, und $cid), die wir später ersetzen müssen, wenn wir sie verwenden.

[
  {
    "action": "talk",
    "text": "Please wait while we connect you."
  },
  {
    "action": "connect",
    "eventUrl": [
      "http://$hostname/event"
    ],
    "from": "12345",
    "endpoint": [
      {
        "type": "websocket",
        "uri" : "ws://$hostname/socket",
        "content-type": "audio/l16;rate=16000",
        "headers": {
          "whoami": "$whoami",
          "cid": "$cid"
        }
      }
    ]
  }
]

In der Vorlage für den Server richtet der unten abgebildete Abschnitt die Zuordnung zwischen dem /ncco Endpunkt und dem CallHandler. Dieses Mapping stellt sicher, dass wenn der /ncco Endpunkt eine GET-Anfrage erhält, die CallHandlerMethode von get vom Server ausgeführt wird.

application = web.Application([
    (r"/event", EventHandler),
    (r"/ncco", CallHandler),
    (r"/socket", WSHandler),
])

Wenn der Server die Methode ausführt, gibt er eine zusammengesetzte NCCO unter Verwendung des unten stehenden Codes zurück. Zunächst sammeln wir die Daten aus der Abfrage (d. h. der GET-Anfrage) in einer data Variable. Wir speichern auch die conversation_uuid für die spätere Verwendung. In diesem Fall gibt es eine Print-Anweisung, damit Sie die Daten sehen können. conversation_uuid sehen können, wenn Sie Ihren Server testen. Im nächsten Schritt lädt der Code die NCCO aus der ncco.json Datei, die wir erstellt haben. Um den geladenen NCCOzu vervollständigen, ersetzen wir die Platzhaltervariablen ($hostname, $cid, und $whoami) durch die gesammelten Werte aus der Datenvariable. Nach der Ersetzung sind wir bereit, die Daten an Nexmo zurückzusenden.

Ersetzen Sie die CallHandler aus der obigen Vorlage durch diesen Code:

class CallHandler(web.RequestHandler):
    @web.asynchronous
    def get(self):
        data={}
        data['hostname'] = HOSTNAME
        data['whoami'] = self.get_query_argument('from')
        data['cid'] = self.get_query_argument('conversation_uuid')
        conversation_id_by_phone_number[self.get_query_argument('from')] = self.get_query_argument('conversation_uuid')
        print(conversation_id_by_phone_number)
        filein = open('ncco.json')
        src = Template(filein.read())
        filein.close()
        ncco = json.loads(src.substitute(data))
        self.write(json.dumps(ncco))
        self.set_header("Content-Type", 'application/json; charset="utf-8"')
        self.finish()

Wann immer nun jemand die Nexmo-Nummer anruft, sendet Nexmo eine GET-Anfrage an unseren /ncco Endpunkt und die CallHandler stellt die Anfrage zusammen und sendet die NCCO. Nexmo führt dann die Aktionen aus, die in der NCCO. In diesem Fall bedeutet das, dass der Anrufer Folgendes hören wird "Bitte warten Sie, während wir Sie verbinden.". Danach wird Nexmo versuchen, den Anruf mit dem angegebenen Endpunkt zu verbinden. socket Endpunkt zu verbinden. Er teilt Nexmo auch den event Endpunkt, der verwendet werden soll. Wenn Sie nun Ihren Server starten, indem Sie python main.py in Ihrem Terminal-Fenster aufrufen, werden Sie feststellen, dass Sie die Nachricht hören, aber der Anruf danach endet. Das liegt daran, dass wir den EventHandler oder die WSHandler. Das sollten wir jetzt nachholen!

Implementierung des EventHandlers

Die EventHandler behandelt Ereignisse, die Nexmo sendet. Wir sind an allen eingehenden Aufrufen interessiert und überprüfen daher jede eingehende Anfrage, um zu sehen, ob ihr Körper eine direction enthält und ob diese Richtung incoming. Wenn ja, wollen wir die uuid speichern und den Anfragekontext beenden. Das call_id_by_conversation_id Wörterbuch wird für die Weiterleitung von Nachrichten zwischen den Anrufern in der WSHandler.

Ersetzen Sie die EventHandler aus der Vorlage durch diesen Code:

class EventHandler(web.RequestHandler):
    @web.asynchronous
    def post(self):
        body = json.loads(self.request.body)
        if 'direction' in body and body['direction'] == 'inbound':
            if 'uuid' in body and 'conversation_uuid' in body:
                call_id_by_conversation_id[body['conversation_uuid']] = body['uuid']
        self.content_type = 'text/plain'
        self.write('ok')
        self.finish()

Implementierung des WSHandlers

Die CallHandler und die EventHandler haben es unserer Anwendung ermöglicht, den Anruf einzurichten. Die WSHandler kümmert sich nun um den Audiostrom des Anrufs. Die Sprache auf der Seite des primären Anrufers wird von der Translator Speech API transkribiert und übersetzt, und der daraus resultierende Text wird von einer Nexmo-Stimme am anderen Ende der Leitung gesprochen. Die zweite Person kann so den Anrufer in einer Sprache hören, die sie versteht, und anschließend antworten. Die Translator Speech API wird die Antwort wiederum so übersetzen, dass die erste Person sie in ihrer Sprache hört. Diesen Arbeitsablauf werden wir nun implementieren.

Wenn die Nexmo Voice API eine Verbindung zu einem WebSocket herstellt, sendet Nexmo eine erste HTTP GET-Anfrage an den Endpunkt. Unser Server antwortet mit einem HTTP 101, um das Protokoll zu wechseln, und der Server verbindet sich anschließend mit Nexmo über TCP. Dieser Verbindungsaufbau wird für uns von Tornado durchgeführt. Immer wenn jemand unsere Nexmo-Nummer anruft, öffnet Nexmo einen WebSocket für die Dauer des Anrufs. Wenn ein WebSocket geöffnet und schließlich geschlossen wird, ruft das Tornado-Framework die Funktion open und close Methoden unten auf. Wir brauchen in beiden Fällen nichts zu tun, aber wir werden Nachrichten ausgeben, damit wir verfolgen können, was passiert, wenn wir den Server laufen lassen.

Da wir nun eine offene Verbindung haben, wird Nexmo Nachrichten senden, die wir in der on_message Methode behandeln. Die erste Nachricht, die wir von Nexmo erhalten, ist ein einfacher Text mit Metadaten. Wenn wir diese Nachricht erhalten, setzen wir die whoami Eigenschaft der WSHandler um den Sprecher identifizieren zu können. Anschließend erstellen wir einen Wave-Header, den wir an die Translator Speech API senden. Um Nachrichten an die Translator Speech API zu senden, erstellen wir eine translator_future. Je nach Anrufer, d. h. der Person, von der die Nachricht kommt, erstellen wir den translator_future mit den entsprechenden Sprachvariablen, damit die API weiß, aus welcher Sprache sie in welche andere Sprache übersetzen muss.

A translator_future ist ein weiterer WebSocket, der mit der Speech Translator API verbunden ist. Wir verwenden es, um die Nachrichten weiterzuleiten, die wir von der Nexmo Voice API erhalten. Nach seiner Erstellung wird die translator_future in der Variable ws gespeichert und verwendet, um den Wave-Header zu senden, den wir zuvor erstellt haben. Jede nachfolgende Nachricht von Nexmo ist eine binäre Nachricht. Diese binären Nachrichten werden an die Translator Speech API mit Hilfe der Funktion translator_future die das Audio verarbeitet und die transkribierte Übersetzung zurückgibt.

Bei der Initialisierung der translator_futureinitialisieren, geben wir an, dass die Translator Speech API nach der Verarbeitung unserer Nachricht die Methode speech_to_translation_completed. Diese Methode prüft beim Empfang einer Nachricht, ob die Nachricht nicht leer ist, und spricht dann die Nachricht in der Sprache des Empfängers der Nachricht. Sie wird die Nachricht nur für den anderen Anrufer sprechen, nicht für die Person, die zuerst gesprochen hat. Außerdem wird die Übersetzung auf dem Terminal ausgedruckt.

Ersetzen Sie die WSHandler aus der Vorlage durch diesen Code:

class WSHandler(websocket.WebSocketHandler):
    whoami = None

    def open(self):
        print("Websocket Call Connected")

    def translator_future(self, translate_from, translate_to):
        uri = "wss://dev.microsofttranslator.com/speech/translate?from={0}&to={1}&api-version=1.0".format(translate_from[:2], translate_to)
        request = httpclient.HTTPRequest(uri, headers={
            'Authorization': 'Bearer ' + azure_auth_client.get_access_token(),
        })
        return websocket.websocket_connect(request, on_message_callback=self.speech_to_translation_completed)

    def speech_to_translation_completed(self, new_message):
        if new_message == None:
            print("Got None Message")
            return
        msg = json.loads(new_message)
        if msg['translation'] != '':
            print("Translated: " + "'" + msg['recognition'] + "' -> '" + msg['translation'] + "'")
            for key, value in conversation_id_by_phone_number.iteritems():
                if key != self.whoami and value != None:
                    if self.whoami == CALLER:
                        speak(call_id_by_conversation_id[value], msg['translation'], VOICE2)
                    else:
                        speak(call_id_by_conversation_id[value], msg['translation'], VOICE1)

    @gen.coroutine
    def on_message(self, message):
        if type(message) == str:
            ws = yield self.ws_future
            ws.write_message(message, binary=True)
        else:
            message = json.loads(message)
            self.whoami = message['whoami']
            print("Sending wav header")
            header = make_wave_header(16000)

            if self.whoami == CALLER:
                self.ws_future = self.translator_future(LANGUAGE1, LANGUAGE2)
            else:
                self.ws_future = self.translator_future(LANGUAGE2, LANGUAGE1)

            ws = yield self.ws_future
            ws.write_message(header, binary=True)

    @gen.coroutine
    def on_close(self):
        print("Websocket Call Disconnected")

Im obigen Beispiel verwenden wir eine Funktion namens make_wave_header um den Header zu erstellen, den die Translator Speech API erwartet. Der Code zur Erstellung eines WAV-Headers wurde aus der Datei Python-Speech-Translate Projekt kopiert und ist im Folgenden wiedergegeben.

Kopieren Sie die make_wave_header Funktion an das Ende Ihrer main.py Datei:

def make_wave_header(frame_rate):
    """
    Generate WAV header that precedes actual audio data sent to the speech translation service.
    :param frame_rate: Sampling frequency (8000 for 8kHz or 16000 for 16kHz).
    :return: binary string
    """

    if frame_rate not in [8000, 16000]:
        raise ValueError("Sampling frequency, frame_rate, should be 8000 or 16000.")

    nchannels = 1
    bytes_per_sample = 2

    output = StringIO.StringIO()
    output.write('RIFF')
    output.write(struct.pack('<L', 0))
    output.write('WAVE')
    output.write('fmt ')
    output.write(struct.pack('<L', 18))
    output.write(struct.pack('<H', 0x0001))
    output.write(struct.pack('<H', nchannels))
    output.write(struct.pack('<L', frame_rate))
    output.write(struct.pack('<L', frame_rate * nchannels * bytes_per_sample))
    output.write(struct.pack('<H', nchannels * bytes_per_sample))
    output.write(struct.pack('<H', bytes_per_sample * 8))
    output.write(struct.pack('<H', 0))
    output.write('data')
    output.write(struct.pack('<L', 0))

    data = output.getvalue()
    output.close()

    return data

Schließlich ist die speak Funktion, die oben verwendet wurde, eine einfache Umhüllung der nexmo_client Methode send_speech. Wie Sie unten sehen können, wird sie einige Informationen ausgeben, die für Sie nützlich sein können, wenn Sie den Code ausführen, und dann die Nexmo-API verwenden, um Nexmo anzuweisen, eine bestimmte text mit einer gegebenen voice_name. Kopieren Sie die speak Funktion unten an das Ende Ihrer main.py Datei.

def speak(uuid, text, vn):   
    print("speaking to: " + uuid  + " " + text)
    response = nexmo_client.send_speech(uuid, text=text, voice_name=vn)
    print(response)

Schlussfolgerung


Wenn du mitgemacht hast, hast du jetzt erfolgreich deinen eigenen Babel-Fisch gebaut! Wenn du nicht mitgemacht hast, findest du den endgültigen Code hier. Starten Sie ihn durch Eingabe von python main.py in dein Terminal eingibst. Tun Sie sich nun mit einem anderen Menschen zusammen (oder benutzen Sie zwei Telefone) und rufen Sie Ihre Nexmo-Nummer von zwei Leitungen aus an. Sie sollten Ihre Begrüßungsnachricht hören und dann in der Lage sein, in den beiden von Ihnen gewählten Sprachen miteinander zu sprechen. Lassen Sie uns rekapitulieren: Wir begannen mit der Einrichtung unserer Umgebung, unserer Nexmo-Applikation und der Microsoft Translator Speech API. Dann bauten wir unseren Tornado WebServer auf, der es uns ermöglichte, WebSockets zu verwenden, um Sprachanrufe zu bearbeiten und die Sprache des Sprachanrufs an die Translator Speech API weiterzuleiten. Die API übersetzt und transkribiert dann die Sprache für uns. Nachdem wir das Ergebnis erhalten haben, haben wir die Nachricht in der neuen Sprache gesprochen. Unser Dienst verarbeitet bidirektionale Anrufe aufgrund unserer Routing-Logik, was bedeutet, dass unser Dienst, nachdem er zwei Anrufer verbunden hat, die Sprache beider Personen übersetzt, bevor er sie weiterleitet, so dass sie in ihren gewählten Sprachen kommunizieren können. Und da haben wir ihn! Unser funktionierender Babel-Fisch! Ich fürchte, unser selbstgebauter Babel-Fisch sieht nicht ganz so liebenswert aus wie der aus dem Film, aber er ist eine funktionierende Alternative. Wenn ihr irgendwelche Fragen habt, wendet euch bitte an @naomi_pen oder finden Sie mich auf naomi.codes.

Wie geht es weiter?

Wenn Sie daran interessiert sind, dies weiter zu erforschen, warum implementieren Sie nicht eine Logik, die es den Benutzern ermöglicht, zu Beginn des Anrufs eine Sprache zu wählen. Eine solche Logik könnte auch die Notwendigkeit beseitigen, unsere Haupttelefonnummer fest zu kodieren. Als unterhaltsames Projekt könnten Sie auch untersuchen, wie dies bei Telefonkonferenzen funktioniert, und für jedes Gespräch eine Abschrift erstellen. Schließlich würde ich erwarten, dass Sie an der Sicherheit Ihres Dienstes arbeiten und nicht zulassen wollen, dass zufällige Personen Ihren Dienst anrufen. Sie könnten dies erreichen, indem Sie nur einer bestimmten Anzahl (oder mehreren) die Nutzung Ihres Dienstes gestatten und über eine Logik verfügen, um einen zweiten Teil des Anrufs aus dem Anruf heraus zu initiieren, damit Sie andere Benutzer einladen können, ohne ihnen das Privileg zu geben, Ihren Babel-Fischdienst zu nutzen. Ich würde mich freuen zu hören, was Sie auf Twitter bauen @naomi_pen!

Teilen Sie:

https://a.storyblok.com/f/270183/384x384/991644ad36/naomi-pentrel.png
Naomi Pentrel

Naomi Pentrel is a Software Engineer with a passion for organizing and attending hackathons. She has worked at companies such as Google, Microsoft, and Palantir. In her free time, she occasionally blogs about her side projects.