从零开始玩人工智能—语音API-02

文本转语音然后播放出来貌似并不困难,那么反过来让人工智能听到我们说的话,变成文本呢?其实也不复杂,只不过需要想法解决录制音频的问题。


关于录制声音的说明

前面我们使用的playsound用于简单的播放声音,但如果我们想通过麦克风录制声音然后让语音认知服务进行识别的时候,就需要一个支持麦克风输入的库了。所以我们来安装一个新的库:

pip install pyaudio
  • 在macOS上运行pip安装的时候,会报错提示找不到 portaudio.h 。解决这个问题并不复杂,使用如下 brew 命令行安装 portaudio 即可:
brew install portaudio
  • 在macOS Catalina上运行代码录音时,您也许会发现录制的WAV文件没有任何声音。估计这是由于系统偏好设置中,关于隐私的配置的问题。如果终端没有提示要访问麦克风,加入到麦克风的允许访问列表,运行代码时就不能访问麦克风。一个可以参考的做法是,安装一个命令行下访问麦克风的软件包,然后尝试触发终端的麦克风权限请求。
brew install sox

安装之后,就可以直接运行 sox 尝试让终端访问麦克风。还可以检查这个命令行生成的 test.wav 文件是否已经成功地录入了声音。

sox -d test.wav

按照网上的建议,还可以手动打开一个终端再运行上述步骤:

open /System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal

如果已经能够通过终端录制声音,我们就可以进入正式的代码尝试了。


关于语音到文字的智能识别,在微软的示例代码中却没找到 Python 的,所以参考Speech-to-Text REST API文档中 C# 的示例代码,写出了以下 Python 的实现。

如同之前做的那样,我们运行代码输入服务订阅密钥和服务区域。

import os, requests
subscription_key = input('Please input your Service Key:')

仍然可以使用之前尝试文字转语音时使用的语音认知服务资源。

service_region = input('please input your Service Region:')

尽管与文字转语音的API不同,语音转文字的API可以使用 服务订阅密钥 来验证,我们还是再熟悉一遍使用POST得到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)
access_token = str(response.text)
print(access_token)

为了尽可能简化代码,我们可以首先用 pyaudio 来按照录制一个WAV文件,Speech-to-Text REST API文档中介绍,通过REST API调用时,语音认知服务接受两种文件编码格式:

Format Codec Bit rate Sample Rate
WAV PCM 256 kbps 16 kHz, mono
OGG OPUS 256 kpbs 16 kHz, mono

因此,我们使用 pyaudio 录制语音以及保存为WAV文件时,应该按照这个标准设置好参数。录制将使用16 kHz的采样率、16 bit的比特率,单声道。为了减少等待时间,我们将录制的时间定义为 5 秒钟。如果需要,可以自行修改这个参数。 我们使用一个名为 'test.wav' 的文件保存录制的语音。

import pyaudio
import wave

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "test.wav"

为了在包含多个支持录音的音频设备的计算机上运行这些代码,我们使用 pyaudio 来枚举这些设备,并让您选择使用哪一个。

当看到 'Recording...' 的提示时,表示录音已经开始。您可以说一句简单的英文(因为后面的代码我们选择的语言是 en-US,如果您录制的是中文,请修改后续的代码)。大约 5 秒钟左右,'End record.' 会显示,表示录音结束。当然,录音时长可在前面的代码处修改。

录制结束后,自带的 wave 库就会将生成的流写为一个WAV文件,供后续使用。

audio = pyaudio.PyAudio()

print("----------------------record device list---------------------")
info = audio.get_host_api_info_by_index(0)
numdevices = info.get('deviceCount')
for i in range(0, numdevices):
    if (audio.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0:
        print("Input Device id ", i, " - ", audio.get_device_info_by_host_api_device_index(0, i).get('name'))

print("-------------------------------------------------------------")

index = int(input("Please select device id:"))
print("recording via index "+str(index))
stream = audio.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)

print("Recording...")
frames = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)
print("End record.")

stream.stop_stream()
stream.close()
audio.terminate()

wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(audio.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

接下来就是正式的语音认知服务的尝试了。我们会按照Speech-to-Text REST API文档的要求,提交一个语音到文本的认知服务请求给 REST API。

首先需要提供的是服务的终结点URL,形如:

https://<REGION_IDENTIFIER>.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1

我们将使用 base_url 加上 path 构造出完整的服务终结点URL。

⚠️ 需要注意的是,必须将语言参数追加到 URL 以避免收到 4xx HTTP 错误。

接着我们提供必要的参数给POST方法。重要的参数主要有这几个:

参数 说明 必需/可选
language 标识所要识别的口语。 请参阅支持的语言。 必须
format 指定结果格式。 接受的值为 simple 和 detailed。 简单结果包括 RecognitionStatus、DisplayText、Offset 和 Duration。 Detailed 响应包括显示文本的四种不同的表示形式。 默认设置为 simple。 可选

一个标准的HTTP的POST方法还需要提供头部headers的参数。以下是主要的参数:

标头 说明 必需/可选
Ocp-Apim-Subscription-Key 语音服务订阅密钥。 此标头或 Authorization 是必需的。
Authorization 前面带有单词 Bearer 的授权令牌。 有关详细信息,请参阅身份验证。 此标头或 Ocp-Apim-Subscription-Key 是必需的。
Content-type 描述所提供音频数据的格式和编解码器。 接受的值为 audio/wav; codecs=audio/pcm; samplerate=16000 和 audio/ogg; codecs=opus。 必需
Accept 如果提供此标头,则值必须是 application/json。 语音服务以 JSON 格式提供结果。 某些请求框架提供不兼容的默认值。 最好始终包含 Accept。 可选,但建议提供。

与文本到语音不同,除了 token,语音到文本也支持订阅密钥的直接验证。两者二选一即可。

最后是POST这个请求最重要的部分,我们将打开之前录制的WAV文件,将其作为请求的body使用。并且对返回的响应进行阅读和分析。

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'
        }
body = open('test.wav','rb').read()
response = requests.post(constructed_url, params=params, headers=headers, data=body)
if response.status_code == 200:
    print(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")

如果一切正常,语音认知服务将返回JSON格式的数据。 如果前面参数我们选择的是 'simple' ,就能看到以下数据:

参数 说明
RecognitionStatus 状态,例如 Success 表示成功识别。
DisplayText 经过大小写转换、添加标点、执行反向文本规范化(将口头文本转换为短形式,例如,200 表示“two hundred”,或“Dr.Smith”表示“doctor smith”)和屏蔽亵渎内容之后的识别文本。 仅在成功时提供。

如果像上面代码中,选择了 'detailed' 参数,就能看到:

参数 说明
Confidence 条目的置信度评分,从 0.0(完全不可信)到 1.0(完全可信)
Lexical 已识别文本的词法形式:识别的实际单词。
ITN 已识别文本的反向文本规范化(“规范”)形式,已应用电话号码、数字、缩写(“doctor smith”缩写为“dr smith”)和其他转换。
MaskedITN 可根据请求提供应用了亵渎内容屏蔽的 ITN 形式。
Display 已识别文本的显示形式,其中添加了标点符号和大小写形式。 此参数与将格式设置为 simple 时提供的 DisplayText 相同。

您在返回的数据中,找到了语音认知服务识别出的文本了吗?

同样,运行完毕后,您可以输入 y 或 n 来决定是否删除使用过的 test.wav 文件。

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

猜你喜欢

转载自blog.51cto.com/haohu/2518557