还在担忧本身的英语发音不标准?请个外教教发音太贵?有语音认知服务还要啥自行车啊~ 既然放音和录音咱们都尝试过了,那么来一个更有难度的实验吧。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格式body
。app
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="string"></speak>
en-US-AriaRUS
。<voice name="zh-CN-HuihuiRUS"></voice>
<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,可访问下载。