[譯] 使用 Nexmo 和微軟語音翻譯 API 構建 Babel Fish

若是在過去的幾個月時間裏你關注過互聯網上的變化,那你就會注意到 Google 的即時翻譯 Pixel Buds。它是一個像給 Galaxy 的 Hitchhiker 指南中 Bable Fish 同樣的技術,能夠爲穿戴者翻譯任何可感知的語言並讓他們像虛擬人類同樣與穿戴者進行交流。但使用 Google 的 Pixel Buds 代價昂貴 —— 那麼咱們爲什麼不本身動手構建呢?這也是 Danielle 和我最近在 hackference 上所構想的。咱們想要去建立一個讓電話交流的雙方能夠根據本身所需,聽到彼此說話內容的翻譯版本的 Nexmo Babel Fish。html

Image of a Babel fish from The Hitchhiker's Guide to the Galaxy

來自給 Galaxy 的 Hitchhiker 的指南中的 Bable Fish 的圖片。前端

在這篇博客中,咱們將介紹搭建 Babel Fish 系統的步驟。首先咱們須要設置和配置環境。而後咱們會設置一個 Nexmo number 來處理調用。在此以後,咱們會搭建一個經過 WebSocket 接收語音,並將其從 Nexmo number 傳送到微軟語言翻譯 API 的 Python 服務器。咱們會使用語音翻譯 API 來處理轉錄和翻譯。在此基礎上,咱們將實現管理雙向對話的邏輯,並指示 Nexmo number 說出翻譯內容。爲了便於實現,雙方必須都調用咱們的 Nexmo number。你能夠參考下面的高級圖表,它展現了來自任何一方的語音實例是如何處理的。注意在本教程中,我會使用一個德語/英語的示例。python

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.

該圖表展現了消息如何在系統中傳遞。一個德語調用者經過 Nexmo 發送一條德語消息給 Python 服務器。Python 服務器將德語音頻發送給微軟語音 API。語音 API 將英文翻譯做爲文本發送到 Python 服務器來響應請求。Python 服務器會向 Nexmo 發送請求,向英語調用者說出英語消息。此時英語調用者便會聽到翻譯後的英語信息。android

若是你只想看代碼,能夠在 Github 上找到。ios

準備條件

你須要同時安裝 Python 2.x 或 3.x 和 HTTP 通道軟件 ngrok。咱們會列出你在安裝過程當中須要的全部命令。git

開始

配置你的環境

咱們從使用 Virtualenv 來爲此項目配置虛擬環境的 DIY Babel Fish 的解決方案開始。Virtualenv 容許咱們將此項目與其餘項目隔離。繼續爲此項目建立目錄,並將如下依賴列表複製到你項目目錄中命名爲 requirements.txt 的文件中:github

nexmo
tornado>=4.4.2
requests>=2.12.4
複製代碼

爲了建立並激活你的虛擬環境,請在終端運行如下命令:web

virtualenv venv  # sets up the environment
source venv/bin/activate  # activates the environment
pip install -r requirement.txt  # installs our dependencies
 
# 若是你使用的是 Python 3,請使用如下命令
pip3 install -r requirement.txt
複製代碼

此時,經過在單獨打開一個終端的窗口中運行下列命令來啓動 ngrok。ngrok 容許咱們將本地 5000 端口暴露給外部請求。爲此,你須要讓 ngrok 在後臺保持正常運行。你能夠閱讀更多關於用 Nexmo 連接 ngrok 的信息json

ngrok http 5000
複製代碼

一旦你運行上述命令,你的終端就會和下面的截圖相似。下一步,你須要在配置你的 Nexmo 應用程序和編號 時轉發 URL。後端

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

在終端運行的 ngrok 以及展現轉發來自 「016a0331.ngrok.io」 URL 的屏幕截圖。

獲取一個 Nexmo number

使用咱們的翻譯服務須要一個 Nexmo Number。若是你尚未帳號,能夠在 dashboard.nexmo.com/sign-up 進行註冊,請前往 dashboard.nexmo.com/buy-numbers 購買一個具備語音功能的 Nexmo number。

Screen capture of a user buying a number using the Nexmo buy numbers menu. A user selects their country, Voice as the feature, and mobile as the type and clicks on the search button. The user then clicks on buy for the first number that comes up and confirms the purchase.

該截屏展現了用戶如何使用 Nexmo 的購買編號菜單來購買 Nexmo number。用戶須要選擇國家和語音將做爲特徵,移動設備做爲類型,最後點擊搜索按鈕。而後點擊第一個數字邊的購買連接,即可確認購買。

建立 Nexmo 應用程序

進入你的應用程序,而後新增一個。對事件 URL 和應答 URL 使用 Ngrok 轉發 URL,添加 /event 做爲事件 URL(e.g. http://016a0331.ngrok.io/event) 的路徑,對應答 URL(e.g. http://016a0331.ngrok.io/ncco) 使用 /ncco。咱們以後會設置這些端點。在你的電腦上經過用戶接口生成並存儲一對公鑰/私鑰對。

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, `http://016a0331.ngrok.io/event` as the Event URL, and `http://016a0331.ngrok.io/ncco` as the Answer URL. The user then clicks on the `Generate public/private key pair` link, saves the key when prompted, and finally clicks on create application.」 The last step for our number setup is to link the number you purchased earlier to your application. Use the application dashboard to link the number.

使用 Nexmo 應用程序目錄建立應用程序的用戶屏幕截圖。用戶點擊新增應用程序。在顯示的表單中,用戶輸入 Babelfish 做爲應用程序名,http://016a0331.ngrok.io/event 做爲事件的 URL,http://016a0331.ngrok.io/ncco 做爲應答 URL。而後用戶單擊 Generate public/private key pair 連接,在提示時保存密鑰,最後單擊建立應用程序。編號設置的最後一步是將你以前購買的編號連接到你的應用程序。使用應用程序儀表板來連接編號。

獲取微軟語音翻譯 API 的密鑰

咱們須要設置的另外一個服務是微軟的語音服務 API。在 azure.com 上註冊一個免費的微軟 Azure 帳號,而後跳轉到 portal.azure.com,建立一個語音翻譯 API 資源。你須要它爲下一步生成的密鑰。

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.

設置微軟語音翻譯 API 的用戶屏幕截圖。用戶在微軟 Azure 上的市場搜索中輸入語言翻譯。而後,用戶單擊出現的語音翻譯 API 選項,再單擊 API 概述屏幕上的建立按鈕。以後,用戶填寫資源的表單,使用 babelfish 做爲應用程序名,Pay-as-you-go 做爲訂閱,F0(10 小時的音頻輸入)做爲訂價層,babelfish —— 資源做爲資源組組名。選中用戶已經「閱讀並理解注意事項」的選框,並檢查添加到儀表板,用戶單擊建立並重定向到儀表板。部署完成後,用戶單擊已部署的資源,會顯示一個資源儀表板。在資源儀表板中,抓取用戶單擊的鍵並複製鍵 1。

管理密鑰和配置

咱們如今有了本身的 Nexmo number 和語音翻譯 API 密鑰。咱們如今要作的就是去設置一個包含全部這些重要細節的密碼和配置,這樣咱們就沒必要繼續編輯它們,能夠對它們進行單獨的管理。將下列內容存儲在你項目目錄的 secrets.py 中,而後用你的值來替換佔位符值。

# 用你的值替換下面的值
# 你的 API 密鑰和密碼能夠在這裏找到 https://dashboard.nexmo.com/getting-started-guide
NEXMO_API_KEY = "<your-api-key>"
NEXMO_API_SECRET = "<your-api-secret>"
# 你的 nexmo 編號
NEXMO_NUMBER = "+447512345678"
# 這能夠在你的 Nexmo 應用程序面板上找到
NEXMO_APPLICATION_ID = "<nexmo-application-id>"
# 這是設置你的應用程序時下載的私鑰
NEXMO_PRIVATE_KEY = '''-----BEGIN PRIVATE KEY----- <your-private-key> -----END PRIVATE KEY-----'''
 
# 你必須註冊一個免費的微軟帳號才能使用微軟語音翻譯 API:http://docs.microsofttranslator.com/speech-translate.html
MICROSOFT_TRANSLATION_SPEECH_CLIENT_SECRET = "<your-api-key>"
複製代碼

以後,在你的項目目錄中,在 config.py 中存儲如下內容,而後再用值替換佔位符值。注意,你也能夠選擇如下語言的其餘語言。你也能夠在以後的任意時間更改這些內容。

HOSTNAME = '<your-value>.ngrok.io'
 
# 用相同格式的數字替換變量賦值
CALLER = '447812345678'
 
# 用語言替換變量值
LANGUAGE1 = 'de-DE'
 
 
# 將變量賦值替換爲你的語言的相應名稱。能夠在這裏找到:
# https://developer.nexmo.com/api/voice/ncco#voice-names
VOICE1 = 'Marlene'
 
# 其餘語言和語音
LANGUAGE2 = 'en-US'
VOICE2 = 'Kimberly'
複製代碼

教程步驟

咱們將首先介紹如何使用語言翻譯 API 進行身份認證。而後咱們將使用提供的模版來設置咱們的 Tornado Web 服務器。以後,咱們要實現 CallHandlerEventHandler 以及 WSHandlerCallHandler 將爲咱們處理 Nexmo number 的調用。在此基礎上,EventHandler 將被用於處理 Nexmo 發送的事件,例如開始或完成的調用。在每一個事件中,Nexmo 都會發送關於啓動或完成調用的執行者的信息。咱們會使用這些信息來存儲特定調用中的人。WSHandler 同時被用來打開 WebSocket,Nexmo 和咱們的 Python 服務器經過它進行通訊。Python 服務器將建立音頻片斷並將其發送到語言翻譯 API。處理器將使用 EventHandler 收集信息來正確地路由。下面的部分會進一步解釋這些概念,並顯示相應的實現。

使用微軟語言翻譯 API 的身份認證

要使用語音翻譯 API,咱們須要一個名爲 MICROSOFT_TRANSLATION_SPEECH_CLIENT_SECRET 的 token。幸運的是,微軟提供了一個 Python AzureAuthClient,咱們會使用它,不會作任何更改。請將如下內容複製並保存到你的項目目錄中名爲 azure_auth_client.py 的文件中。

""" 從 Azure 平臺獲取示例 A 的代碼。 訪問 http://docs.microsofttranslator.com/oauth-token.html 來查看 微軟 Azure 認知服務的身份驗證服務 API 參考資料。 """
 
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
複製代碼

建立服務器

計算機通訊協議 WebSocket 容許咱們在一個 TCP 鏈接中擁有一個雙向通訊管道。Nexmo 的 Voice API 容許你將電話調用連接到這樣的 WebScoket 端點。咱們會使用 Tornado Web 服務器 web 框架來實現咱們的 WebSocket 協議。

若是你一直按照步驟來,並且全部的文件都如咱們所描述的建立,那麼你能夠從下面的 Tornado Web 服務器配置開始。這個代碼會處理全部的導入,配置 Nexmo 客戶端以及 azure auth 客戶端,並使用 5000 端口啓動服務器。注意這個服務器目前還未執行任何有用操做。它有 3 個端點:nccoeventsocket,它們會分別調用 CallHandlerEventHandlerWSHandler。咱們會在下面的部分實現處理器。

在你的項目文件夾中建立一個名爲 main.py 的文件,並將如下代碼複製進去。

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()
複製代碼

實現 CallHandler

爲了將電話調用鏈接到 WebSocket 端點,Nexmo 的 Voice API 使用 Nexmo Call Control Object (NCCO) 或 API 調用。當有人調用你的 Nexmo number 時,Nexmo 就會向你在設置 Nexmo Voice 應用程序時提供的 URL 發起 GET 請求。咱們將應用程序指向服務器,服務器如今須要經過 NCCO 來響應這個請求。這個 NCCO 應該指示 Nexmo 給調用者發生一個簡短的歡迎消息,而後將調用者鏈接到 WebSocket。

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.

顯示用戶、Nexmo 以及 web 服務器之間的交互圖。當用戶調用 Nexmo number 時,Nexmo 就會向 web 服務器/ncco 發送一個 GET 請求。web 服務器會指示 Nexmo 打開自身的 socket 來讓 NCCO 進行響應。

接着將如下的 NCCO 保存到你項目中名爲 ncco.json 的文件中。它包含執行請求動做所需的模版。可是,它也包括一些咱們之後使用時須要替換的佔位符變量($hostname$whoami$cid)。

[
  {
    "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"
        }
      }
    ]
  }
]
複製代碼

在服務器模版中,如下再現部分設置了 /ncco 端點和 CallHandler 之間的映射。這個映射確保了在 /ncco 接收到 GET 請求時,CallHandler 的 get 方法由服務器執行。

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

當服務器執行方法時,它會使用如下代碼返回一個組裝的 NCCO。首先,咱們從 data 變量中查詢(即 GET 請求)收集數據。咱們還存儲conversation_uuid,以便以後的使用。在這種狀況下,有一個打印語句,能夠在你測試服務器時看見 conversation_uuid。接下來,代碼從咱們建立的 ncco.json 文件中加載 NCCO。爲了完成加載 NCCO,咱們用從數據變量中手機的值替換佔位符變量($hostname$cid$whoami)。替換以後,咱們已經準備好將其返回給 Nexmo 了。

將上述模版中的 CallHandler 替換爲如下代碼:

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()
複製代碼

不管什麼時候,只要有人調用 Nexmo number,Nexmo 就會向咱們的 /ncco 端點發送 GET 請求,CallHandler 將組裝併發送 NCCO。Nexmo 以後會執行 NCCO 中所設計的動做。在這種狀況下,這意味着調用者會聽到**「請稍侯,咱們正在與你創建鏈接。」**以後,Nexmo 會嘗試將調用鏈接到提供的 socket 端點。它也會提供了 Nexmo 要使用的 event 端點。若是你如今經過在終端窗口運行 python main.py 來啓動服務器,你就會發現你能聽到消息,但調用會在消息以後結束。這是由於咱們沒有實現 EventHandlerWSHandler。咱們開始實現吧!

實現 EventHandler

EventHandler 處理 Nexmo 發送的時間。咱們對任何調用都感興趣,所以咱們會檢查任何請求,以肯定其主體是否包含 direction,以及該目錄是否爲 incoming。若是是的話,咱們會存儲 uuid 並完成上下文請求。call_id_by_conversation_id 字典將用於 WSHandler 中調用方之間的消息路由。

用如下模版代碼替換 EventHandler

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()
複製代碼

實現 WSHandler

CallHandlerEventHandler 容許咱們的應用程序來設置調用。WSHandler 如今將關注調用的音頻流。語音的主調用者將經過語音翻譯 API 轉錄並翻譯,結果文本將由另外一端的 Nexmo 語音說出。所以第二我的就能夠用他們所明白的語言來傾聽調用者的語音了,而後再做出響應。語言翻譯 API 將依次翻譯響應,以便第一格人能夠聽到他們的語言。這個工做流就是咱們要實現的部分。

當 Nexmo Voice API 鏈接 WebSocket 時,Nexmo 會向端點發送一個初始化的 HTTP GET 請求。咱們的服務器響應 HTTP 101 來切換協議,服務器以後會使用 TCP 鏈接 Nexmo。鏈接會經過 Tornado 來爲咱們處理升級。不管什麼時候有人調用 Nexmo number,Nexmo 都會在調用期間打開 WebSocket。當 WebSocket 被打開而且最後被關閉時,Tornado 框架將調用下面的 openclose 方法。咱們不須要在這兩種狀況下作任何事情,但咱們會打印消息,這樣咱們就能夠在服務器運行時跟蹤掌握所發生的一切。

如今咱們打開一個鏈接,Nexmo 會在 on_message 方法中處理咱們發送的信息。咱們從 Nexmo 收到第一個消息是帶有元數據的純文本。在收到這一消息後,咱們會設置 WSHandlerwhoami 屬性,以便能識別發言人。以後,咱們會建立一個咱們發送到語音翻譯 API 的 wave 標題。爲了向語音翻譯 API 發送消息,咱們將建立一個 translator_future。根據調用者的不一樣,例如,消息來源,咱們將使用相應的語言變量建立 translator_future,以便 API 瞭解從哪一種語言翻譯成哪一種其餘的語言。

translator_future 是鏈接到語音翻譯 API 的另外一個 WebSocket。咱們使用它來傳遞咱們從 Nexmo Voice API 接收到的消息。在它建立以後,translator_future 被存儲在變量 ws 中,被用來發送咱們以前建立的 wave 標題。來自 Nexmo 的每一個後續消息都是二進制消息。這些二進制消息使用 translator_future 傳遞語音翻譯 API,它會處理音頻並返回轉錄的翻譯。

當咱們初始化 translator_future 時,咱們聲明語言翻譯 API 處理咱們時,它應該會調用 speech_to_translation_completed 方法。這個方法在接收到消息後,會檢查消息是否爲空,而後以消息接收語言語音出消息內容。它只會對其餘調用者說出消息,而不是最初說話的人。此外,咱們還會將翻譯內容打印到終端。

將模版中的 WSHandler 替換爲如下代碼:

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")
複製代碼

咱們使用名爲 make_wave_header 的函數來建立語言翻譯 API 所指望的標題。用於建立 WAV 頭的代碼複製於 Python-Speech-Translate 項目,以下簡介。

make_wave_header 函數複製到 main.py 文件末尾:

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
複製代碼

最後,上述說起的 speak 函數實際上是在 nexmo_client 方法 send_speech 周圍的進行的簡單封裝。正如你在下面所看到的那樣,它會打印打印一些在運行代碼時可能對你有用的信息,而後使用 Nexmo API 指示 Nexmo 使用給定的 voice_name 來播放 text

將下列 speak 函數複製到你的 main.py 文件末尾。

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

結論

若是你一直是按照步驟作的,那麼如今應該已經成功構建了本身的 Babel Fish!若是你沒有遵循步驟,也能夠在這裏找到源代碼。

經過在終端中輸入 python main.py 來運行。如今和別人合做(或者使用兩部手機)。從兩條線上撥打你的 Nexmo 號碼。你應該能夠聽到歡迎信息,而後就能夠用你選擇的兩種語音進行交流了。

咱們歸納一下:咱們首先配置了環境, Nexmo 應用程序和微軟的語言翻譯 API。而後構建了本身的 Tornado WebServer,它容許咱們使用 WebSocket 來處理語音調用,能夠將語音調用的語音傳遞給語音翻譯 API。API 爲咱們翻譯並轉錄語音。獲得結果後,咱們用新語言說出信息。咱們的路由邏輯使得咱們的服務能夠處理雙向調用,即咱們的服務在鏈接兩個調用者後,會先翻譯任何一我的的語音以確保他們彼此能夠選擇彼此須要的語言來進行溝通。

咱們如今作到了。咱們正在運行的 Babel Fish!恐怕咱們的 DIY Babel Fish 並不會像電影中的那樣可愛,但這是一種可行性的選擇。

若是你有任何疑問,請聯繫 @naomi_pen 或在 naomi.codes 上找我。

下一步?

若是你對此有深刻了解的興趣,那麼爲何不實現容許用戶在調用開始時能夠選擇語言的邏輯呢?這種邏輯也可能會消除咱們硬編碼主要電話號碼的必要性。對於一個有趣的項目來講,你也能夠探索爲電話會議工做以及爲每一個電話建立記錄。最後,我設想你可能想要確保你本身服務的安全性以及不讓任何人都有機會調用你的服務。你能夠經過只容許某個號碼(或多個)來使用你的服務,或者使用第二階段的內部調用的邏輯來容許你邀請沒有給定 Bable Fish 服務權限的用戶。我很想知道你在 Twitter 上構建的內容 —— @naomi_pen

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索