從零開始玩人工智能—語音API-03

還在擔憂本身的英語發音不標準?請個外教教發音太貴?有語音認知服務還要啥自行車啊~ 既然放音和錄音咱們都嘗試過了,那麼來一個更有難度的實驗吧。python

發音評估

實際上,語音轉文本的服務中,提供了一個發音評估參數。利用這個參數,就可以對發送的語音進行發音評估。頗有趣吧?咱們看看Speech-to-Text REST API是怎麼說明的。git

要實現發音評估功能,只需簡單在提交語音轉文本請求的時候,在頭部header中添加 'Pronunciation-Assessment' 這個字段便可。該字段指定用於在識別結果中顯示發音評分的參數,這些參數可評估語音輸入的發音質量,並顯示準確性、熟練、完整性等。此參數是 base64 編碼的 json,其中包含多個詳細參數。github

和前面的內容同樣,咱們首先作些準備工做,首先把代碼環境設置好。json

import requests
import pyaudio, wave
import os, json, base64
from xml.etree import ElementTree

# constents for WAV file
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
RECORD_SECONDS = 5

# speech service information
subscription_key = input("Please input Service Key: ")
service_region = input("Please input Service Region: ")
#發音評估功能當前僅適用於 `westus` `eastasia` 和 `centralindia` 區域。 此功能目前僅適用於 `en-US` 語言。

# generate speech service token
fetch_token_url = "https://"+service_region+".api.cognitive.microsoft.com/sts/v1.0/issueToken"
headers = {
        'Ocp-Apim-Subscription-Key': subscription_key
        }
response = requests.post(fetch_token_url, headers=headers)
if response.status_code == 200:
    access_token = str(response.text)
    print("Access token granted.")
else:
    print("\nStatus code: " + str(response.status_code) + "\nSomething went wrong. Check your subscription key and region.\n")
    print("Reason: " + str(response.reason) + "\n")

RECORD_SECONDS 表明的是後面錄製聲音的時長,能夠自行調整。若是須要的時間特別長,建議參考文檔對音頻進行分塊發送。分塊發送的音頻在處理響應上會有更好的表現。 音頻WAV文件的參數設置好了,認知服務須要的服務訂閱密鑰和服務區域也設置好了,訪問語音服務的 token 也成功生成了,接下里咱們輸入一句英文的文本,看看人工智能如何朗讀。api

sentence_str = input("Please enter the sentence to test: ")

base_url = 'https://'+service_region+'.tts.speech.microsoft.com/'
path = 'cognitiveservices/v1'
constructed_url = base_url + path
headers = {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type': 'application/ssml+xml',
        'X-Microsoft-OutputFormat': 'riff-24khz-16bit-mono-pcm',
        'User-Agent': 'YOUR_RESOURCE_NAME'
        }
xml_body = ElementTree.Element('speak', version='1.0')
xml_body.set('{http://www.w3.org/XML/1998/namespace}lang', 'en-us')
voice = ElementTree.SubElement(xml_body, 'voice')
voice.set('{http://www.w3.org/XML/1998/namespace}lang', 'en-US')
voice.set('name', 'en-US-AriaRUS') 
voice.text = sentence_str
body = ElementTree.tostring(xml_body)

與以前相似,咱們須要提供一個包含兩部份內容的SSML格式bodyapp

  • 首先須要按照 w3.org 的規範,給出文檔的描述。
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="string"></speak>
  • 而後須要對具體使用的語音進行描述。因爲目前發音評估只支持英文,按照認知語音服務對於區域所支持的語音,咱們選擇標準語音的Aria的聲音en-US-AriaRUS
<voice name="zh-CN-HuihuiRUS"></voice>
  • 最後,咱們傳遞以前輸入的文本到這個XML文件中。最終的文檔就形如:
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="string">
    <voice name="en-US-AriaRUS">
        Hello World!
    </voice>
</speak>

接下來的工做,就是把構造好的頭部headers、包含SSML的bady提交到REST API的服務終結點地址了。ide

response = requests.post(constructed_url, headers=headers, data=body)
if response.status_code == 200:
    with open('text.wav', 'wb') as audio:
        audio.write(response.content)
        print("Please listen AI speak the sentence.")
        audio.close
    wf = wave.open('text.wav', 'rb')
    p = pyaudio.PyAudio()
    stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                    channels=wf.getnchannels(),
                    rate=wf.getframerate(),
                    output=True)
    data = wf.readframes(CHUNK)
#    while data != '':
    while len(data) > 0:
        stream.write(data)
        data = wf.readframes(CHUNK)
    stream.stop_stream()
    stream.close()
    p.terminate()
else:
    print("\nStatus code: " + str(response.status_code) + "\nSomething went wrong. Check your subscription key and headers.\n")
    print("Reason: " + str(response.reason) + "\n")

聽完了 Aria 的示範朗讀,接下來該咱們本身讀一邊句子了。代碼將會提示咱們前面設置的錄音時長,重複一遍,若是時間不夠,能夠回到以前的代碼,修改 RECORD_SECONDS 的值。post

# Recording 
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)
print("Please speak this sentence yourself.\n")
print("You have "+str(RECORD_SECONDS)+" seconds to record...")
frames = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)
print("Recording end. Please wait...\n")
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open('test.wav', 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

一切順利的話,錄製完畢就能夠獲得一個WAV文件。 在macOS好比Catalina上,若是使用VS Code直接運行代碼而又沒法錄製到聲音,一個能夠參考的臨時作法是以管理權限運行VS Code。fetch

sudo /Applications/Visual\ Studio\ Code.app/Contents/MacOS/Electron

得到錄製的WAV文件以後,咱們就能夠向語音認知服務提交發音評估的請求了。ui

發音評估有個不同的要求:提交的 'Pronunciation-Assessment' 必須是一個基於BASE64編碼的JSON數據,而JSON自己包含了幾個發音評估參數。

參數 說明 必需/可選
ReferenceText 將對發音進行計算的文本 必選
GradingSystem 用於分數校準的點系統。 接受的值爲 FivePoint 和 HundredMark。 默認設置爲 FivePoint。 可選
粒度 計算粒度。 接受的值爲 Phoneme ,其中顯示了全文本、單詞和音素級別上的分數, Word 其中顯示了整個文本和 word 級別的分數, FullText 只顯示了完整文本級別的分數。 默認設置爲 Phoneme。 可選
維度 定義輸出條件。 接受的值爲 Basic ,只顯示精確度評分, Comprehensive 顯示更多維度上的分數 (例如,熟練分數和完整文本級別的完整性分數,word 級別上的錯誤類型) 。 檢查響應參數以查看不一樣分數維度和 word 錯誤類型的定義。 默認設置爲 Basic。 可選
EnableMiscue 啓用 miscue 計算。 啓用此功能後,會將發音爲的單詞與引用文本進行比較,並根據比較結果標記爲省略/插入。 接受的值爲 False 和 True。 默認設置爲 False。 可選
ScenarioId 指示自定義點系統的 GUID。 可選

下面就是個經常使用的例子,'Hello World!' 就是用於評估發音的文本:

{
  "ReferenceText": "Good morning.",
  "GradingSystem": "HundredMark",
  "Granularity": "FullText",
  "Dimension": "Comprehensive"
}

咱們須要構造這個JSON,而後使之採用標準的UTF8編碼,再對其進行BASE64編碼。

# Pronunciation Assessment request
paJson = {'ReferenceText': sentence_str, 
        'GradingSystem':'HundredMark', 
        'Granularity':'FullText',
        'Dimension':'Comprehensive'
        }
paHead = base64.b64encode(json.dumps(paJson).encode('utf8')).decode('ascii')

有了這個頭部header字段,咱們就能夠像其餘語音轉文本請求同樣,調用語音認知服務了。

rs=''

base_url = "https://"+service_region+".stt.speech.microsoft.com/"
path = 'speech/recognition/conversation/cognitiveservices/v1'
constructed_url = base_url + path
params = {
        'language': 'en-US',
        'format': 'detailed'
        }
headers = {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type': 'audio/wav; codecs=audio/pcm; samplerate=16000',
        'Accept': 'application/json;text/xml',
        'Pronunciation-Assessment': paHead
        }
body = open('test.wav','rb').read()
response = requests.post(constructed_url, params=params, headers=headers, data=body)
if response.status_code == 200:
    rs = response.json()
else:
    print("\nStatus code: " + str(response.status_code) + "\nSomething went wrong. Check your subscription key and headers.\n")
    print("Reason: " + str(response.reason) + "\n")

if rs != '':
    print(rs)

順利的話,運行到這裏已經可以看到語音認知服務返回的發音評估結果了。但是一堆JSON看着很混亂不是嗎?不要緊,咱們對信息作一些規範化。

if rs != '':
    print(" The testing sentence: "+rs['NBest'][0]['Display'])
    print("       Accuracy Score: "+str(rs['NBest'][0]['AccuracyScore']))
    print("        Fluency Score: "+str(rs['NBest'][0]['FluencyScore']))
    print("   Completeness Score: "+str(rs['NBest'][0]['CompletenessScore']))
    print("  Pronunciation Score: "+str(rs['NBest'][0]['PronScore']))

怎麼樣?一個簡單的聽讀英語句子並對發音進行評估打分的代碼就這麼簡單的實現了吧。 最後,須要的話,清理一下生成的WAV文件。

delfile = input("Delete the WAV file? (y/n):")
if delfile=='y':
    os.remove('test.wav')
    os.remove('text.wav')

本文代碼已經使用 jupyter notebook 提交到 Github/HaoHoo/F02AI,可訪問下載。

相關文章
相關標籤/搜索