语音文字相互转换
汉字转化为语音
from aip import AipSpeech
""" 你的 APPID AK SK """
APP_ID = '15420891'
API_KEY = 'clBfcG0eTy4GGHzpynHAUp8E'
SECRET_KEY = 'IaVWwTW7aKamjHVBv84pmTf8nKPrv2WX'
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
result = client.synthesis('北国风光,千里冰封,万里雪飘。', 'zh', 1,
{
'vol': 5,
'spd': 3,
'pit': 9,
'per': 4
})
# 识别正确返回语音二进制 错误则返回dict 参照下面错误码
if not isinstance(result, dict):
with open('audio.mp3', 'wb') as f:
f.write(result)
参数 | 类型 | 描述 | 是否必须 |
---|---|---|---|
tex | String | 合成的文本,使用UTF-8编码, 请注意文本长度必须小于1024字节 | 是 |
cuid | String | 用户唯一标识,用来区分用户, 填写机器 MAC 地址或 IMEI 码,长度为60以内 | 否 |
spd | String | 语速,取值0-9,默认为5中语速 | 否 |
pit | String | 音调,取值0-9,默认为5中语调 | 否 |
vol | String | 音量,取值0-15,默认为5中音量 | 否 |
per | String | 发音人选择, 0为女声,1为男声, 3为情感合成-度逍遥,4为情感合成-度丫丫,默认为普通女 | 否 |
语音转化为汉字
from aip import AipSpeech
import os
""" 你的 APPID AK SK """
APP_ID = '15420336'
API_KEY = 'VwSGcqqwsCl282LGKnFwHDIA'
SECRET_KEY = 'h4oL6Y9yRuvmD0oSdQGQZchNcix4TF5P'
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
# 读取文件
def get_file_content(filePath):
os.system(f"ffmpeg -y -i {filePath} -acodec pcm_s16le -f s16le -ac 1 -ar 16000 {filePath}.pcm")
with open(f"{filePath}.pcm", 'rb') as fp:
return fp.read()
# 识别本地文件
res = client.asr(get_file_content('wyn.wma'), 'pcm', 16000, {
'dev_pid': 1536,
})
print(res.get("result")[0])
# 结果可能是一个列表取其中最相似的即可
参数 | 类型 | 描述 | 是否必须 |
---|---|---|---|
speech | Buffer | 建立包含语音内容的Buffer对象, 语音文件的格式,pcm 或者 wav 或者 amr。不区分大小写 | 是 |
format | String | 语音文件的格式,pcm 或者 wav 或者 amr。不区分大小写。推荐pcm文件 | 是 |
rate | int | 采样率,16000,固定值 | 是 |
cuid | String | 用户唯一标识,用来区分用户,填写机器 MAC 地址或 IMEI 码,长度为60以内 | 否 |
dev_pid | Int | 不填写lan参数生效,都不填写,默认1537(普通话 输入法模型),dev_pid参数见本节开头的表格 | 否 |
lan(已废弃) | String | 历史兼容参数,请使用dev_pid。如果dev_pid填写,该参数会被覆盖。语种选择,输入法模型,默认中文(zh)。 中文=zh、粤语=ct、英文=en,不区分大小写。 | 否 |
dev_pid 参数列表
dev_pid | 语言 | 模型 | 是否有标点 | 备注 |
---|---|---|---|---|
1536 | 普通话(支持简单的英文识别) | 搜索模型 | 无标点 | 支持自定义词库 |
1537 | 普通话(纯中文识别) | 输入法模型 | 有标点 | 不支持自定义词库 |
1737 | 英语 | 有标点 | 不支持自定义词库 | |
1637 | 粤语 | 有标点 | 不支持自定义词库 | |
1837 | 四川话 | 有标点 | 不支持自定义词库 | |
1936 | 普通话远场 | 远场模型 | 有标点 | 不支持 |
返回样例:
// 成功返回
{
"err_no": 0,
"err_msg": "success.",
"corpus_no": "15984125203285346378",
"sn": "481D633F-73BA-726F-49EF-8659ACCC2F3D",
"result": ["北京天气"]
}
// 失败返回
{
"err_no": 2000,
"err_msg": "data empty.",
"sn": null
}
错误返回格式
若请求错误,服务器将返回的JSON文本包含以下参数:
- error_code:错误码。
- error_msg:错误描述信息,帮助理解和解决发生的错误。
错误码
错误码 | 用户输入/服务端 | 含义 | 一般解决方法 |
---|---|---|---|
3300 | 用户输入错误 | 输入参数不正确 | 请仔细核对文档及参照demo,核对输入参数 |
3301 | 用户输入错误 | 音频质量过差 | 请上传清晰的音频 |
3302 | 用户输入错误 | 鉴权失败 | token字段校验失败。请使用正确的API_KEY 和 SECRET_KEY生成 |
3303 | 服务端问题 | 语音服务器后端问题 | 请将api返回结果反馈至论坛或者QQ群 |
3304 | 用户请求超限 | 用户的请求QPS超限 | 请降低识别api请求频率 (qps以appId计算,移动端如果共用则累计) |
3305 | 用户请求超限 | 用户的日pv(日请求量)超限 | 请“申请提高配额”,如果暂未通过,请降低日请求量 |
3307 | 服务端问题 | 语音服务器后端识别出错问题 | 目前请确保16000的采样率音频时长低于30s,8000的采样率音频时长低于60s。如果仍有问题,请将api返回结果反馈至论坛或者QQ群 |
3308 | 用户输入错误 | 音频过长 | 音频时长不超过60s,请将音频时长截取为60s以下 |
3309 | 用户输入错误 | 音频数据问题 | 服务端无法将音频转为pcm格式,可能是长度问题,音频格式问题等。 请将输入的音频时长截取为60s以下,并核对下音频的编码,是否是8K或者16K, 16bits,单声道。 |
3310 | 用户输入错误 | 输入的音频文件过大 | 语音文件共有3种输入方式: json 里的speech 参数(base64后); 直接post 二进制数据,及callback参数里url。 分别对应三种情况:json超过10M;直接post的语音文件超过10M;callback里回调url的音频文件超过10M |
3311 | 用户输入错误 | 采样率rate参数不在选项里 | 目前rate参数仅提供8000,16000两种,填写4000即会有此错误 |
3312 | 用户输入错误 | 音频格式format参数不在选项里 | 目前格式仅仅支持pcm,wav或amr,如填写mp3即会有此错误 |
错误码常见问题及具体分析
3300 错误
语音识别api使用的是HTTP POST方法, BODY里直接放置json, Content-Type头部为 application/json。 并非常见的浏览器表单请求(application/x-www-form-urlencoded或者multipart/x-www-form-urlencoded)。
必填字段:format rate channel cuid token cuid token cuid token cuid token,请勿漏填。此外 (speech, len) 及 (url, callback) 这两组参数必须二选一,如果都填,默认处理第一组。 channel cuid token,请勿漏填。此外 (speech, len) 及 (url, callback) 这两组参数必须二选一,如果都填,默认处理第一种。 channel cuid token,请勿漏填。此外 (speech, len) 及 (url, callback) 这两组参数必须二选一,如果都填,默认处理第一种。
必填字段如format rate channel cuid token,请勿漏填。此外 (speech, len) 及 (url, callback) 这两组参数必须二选一,如果都填,默认处理第一种,并确认 音频时长截取为60s以下。
3309错误
wav和amr的音频,服务端会自动转为pcm,这个过程中导致转码出错。请确认下format及rate参数与音频一致,并确认音频时长截取为60s以下。
3301 错误
识别结果实际为空。可能是音频质量过差,不清晰,或者是空白音频。 有时也可能是pcm填错采样率。如16K采样率的pcm文件,填写的rate参数为8000。
语音识别相互转换
""" 你的 APPID AK SK """
APP_ID = '15420336'
API_KEY = 'VwSGcqqwsCl282LGKnFwHDIA'
SECRET_KEY = 'h4oL6Y9yRuvmD0oSdQGQZchNcix4TF5P'
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
# 读取文件
def get_file_content(filePath):
os.system(f"ffmpeg -y -i {filePath} -acodec pcm_s16le -f s16le -ac 1 -ar 16000 {filePath}.pcm")
with open(f"{filePath}.pcm", 'rb') as fp:
return fp.read()
def audio2text(filepath):
# 识别本地文件
res = client.asr(get_file_content(filepath), 'pcm', 16000, {
'dev_pid': 1536,
})
print(res.get("result")[0])
return res.get("result")[0]
def text2audio(text):
filename = f"{time.time()}.mp3"
result = client.synthesis(text, 'zh', 1, {
'vol': 5,
"spd": 3,
"pit": 7,
"per": 4
})
# 识别正确返回语音二进制 错误则返回dict 参照下面错误码
if not isinstance(result, dict):
with open(filename, 'wb') as f:
f.write(result)
return filename
text = audio2text("wyn.wma")
filename = text2audio(text)
os.system(filename)
图灵机器人
import requests
args = {
"reqType": 0,
"perception": {
"inputText": {
"text": "北京天气怎么样"
},
"inputImage": {
"url": "imageUrl"
},
"selfInfo": {
"location": {
"city": "北京",
"province": "北京",
"street": "信息路"
}
}
},
"userInfo": {
"apiKey": "47dead7dcee74a1da3bcc980d147598c",
"userId": "zzy"
}
}
url = 'http://openapi.tuling123.com/openapi/api/v2'
res = requests.post(url=url, json=args)
text = res.json().get('results')[0].get('values').get('text')
print(text)
参数说明
参数 | 类型 | 是否必须 | 取值范围 | 说明 |
---|---|---|---|---|
reqType | int | N | - | 输入类型:0-文本(默认)、1-图片、2-音频 |
perception | - | Y | - | 输入信息 |
userInfo | - | Y | - | 用户参数 |
perception
参数 | 类型 | 是否必须 | 取值范围 | 说明 |
---|---|---|---|---|
inputText | - | N | - | 文本信息 |
inputImage | - | N | - | 图片信息 |
inputMedia | - | N | - | 音频信息 |
selfInfo | - | N | - | 客户端属性 |
注意:输入参数必须包含inputText或inputImage或inputMedia!
*inputText*
参数 | 类型 | 是否必须 | 取值范围 | 说明 |
---|---|---|---|---|
text | String | Y | 1-128字符 | 直接输入文本 |
*inputImage*
参数 | 类型 | 是否必须 | 取值范围 | 说明 |
---|---|---|---|---|
url | String | Y | 图片地址 |
*inputText*
参数 | 类型 | 是否必须 | 取值范围 | 说明 |
---|---|---|---|---|
text | String | Y | 1-128字符 | 直接输入文本 |
*inputImage*
参数 | 类型 | 是否必须 | 取值范围 | 说明 |
---|---|---|---|---|
url | String | Y | 图片地址 |
*inputMedia*
参数 | 类型 | 是否必须 | 取值范围 | 说明 |
---|---|---|---|---|
url | String | Y | 音频地址 |
*selfInfo*
参数 | 类型 | 是否必须 | 取值范围 | 说明 |
---|---|---|---|---|
location | - | N | - | 地理位置信息 |
*location*
参数 | 类型 | 是否必须 | 取值范围 | 说明 |
---|---|---|---|---|
city | String | Y | - | 所在城市 |
province | String | N | - | 省份 |
street | String | N | - | 街道 |
userInfo
参数 | 类型 | 是否必须 | 取值范围 | 说明 |
---|---|---|---|---|
apiKey | String | Y | 32位 | 机器人标识 |
userId | String | Y | 长度小于等于32位 | 用户唯一标识 |
groupId | String | N | 长度小于等于64位 | 群聊唯一标识 |
userIdName | String | N | 长度小于等于64位 | 群内用户昵称 |
输出参数
输出示例:
{
"intent": {
"code": 10005,
"intentName": "",
"actionName": "",
"parameters": {
"nearby_place": "酒店"
}
},
"results": [
{
"groupType": 1,
"resultType": "url",
"values": {
"url": "http://m.elong.com/hotel/0101/nlist/#indate=2016-12-10&outdate=2016-12-11&keywords=%E4%BF%A1%E6%81%AF%E8%B7%AF"
}
},
{
"groupType": 1,
"resultType": "text",
"values": {
"text": "亲,已帮你找到相关酒店信息"
}
}
]
}
参数说明
参数 | 类型 | 是否必须 | 取值范围 | 说明 |
---|---|---|---|---|
intent | - | Y | - | 请求意图 |
results | - | N | - | 输出结果集 |
intent
参数 | 类型 | 是否包含 | 取值范围 | 说明 |
---|---|---|---|---|
code | int | Y | - | 输出功能code |
intentName | String | N | - | 意图名称 |
actionName | String | N | - | 意图动作名称 |
parameters | Map | N | - | 功能相关参数 |
results
参数 | 类型 | 是否包含 | 取值范围 | 说明 |
---|---|---|---|---|
resultType | String | Y | 文本(text);连接(url);音频(voice);视频(video);图片(image);图文(news) | 输出类型 |
values | - | Y | - | 输出值 |
groupType | int | Y | - | ‘组’编号:0为独立输出,大于0时可能包含同组相关内容 (如:音频与文本为一组时说明内容一致) |
异常返回码
异常返回格式
{
'intent':
{
'code':5000
}
}
异常返回说明
异常码 | 说明 |
---|---|
5000 | 无解析结果 |
6000 | 暂不支持该功能 |
4000 | 请求参数格式错误 |
4001 | 加密方式错误 |
4002 | 无功能权限 |
4003 | 该apikey没有可用请求次数 |
4005 | 无功能权限 |
4007 | apikey不合法 |
4100 | userid获取失败 |
4200 | 上传格式错误 |
4300 | 批量操作超过限制 |
4400 | 没有上传合法userid |
4500 | userid申请个数超过限制 |
4600 | 输入内容为空 |
4602 | 输入文本内容超长(上限150) |
7002 | 上传信息失败 |
8008 | 服务器错误 |
0 | 上传成功 |
图灵结合语音
from aip import AipSpeech,AipNlp
import time,os
""" 你的 APPID AK SK """
APP_ID = '15420336'
API_KEY = 'VwSGcqqwsCl282LGKnFwHDIA'
SECRET_KEY = 'h4oL6Y9yRuvmD0oSdQGQZchNcix4TF5P'
nlp = AipNlp(APP_ID, API_KEY, SECRET_KEY)
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
# 读取文件
def get_file_content(filePath):
os.system(f"ffmpeg -y -i {filePath} -acodec pcm_s16le -f s16le -ac 1 -ar 16000 {filePath}.pcm")
with open(f"{filePath}.pcm", 'rb') as fp:
return fp.read()
def audio2text(filepath):
# 识别本地文件
res = client.asr(get_file_content(filepath), 'pcm', 16000, {
'dev_pid': 1536,
})
print(res.get("result")[0])
return res.get("result")[0]
def text2audio(text):
filename = f"{time.time()}.mp3"
result = client.synthesis(text, 'zh', 1, {
'vol': 5,
"spd": 3,
"pit": 7,
"per": 4
})
# 识别正确返回语音二进制 错误则返回dict 参照下面错误码
if not isinstance(result, dict):
with open(filename, 'wb') as f:
f.write(result)
return filename
def to_tuling(text):
import requests
args = {
"reqType": 0,
"perception": {
"inputText": {
"text": text
}
},
"userInfo": {
"apiKey": "9a9a026e2eb64ed6b006ad99d27f6b9e",
"userId": "1111"
}
}
url = "http://openapi.tuling123.com/openapi/api/v2"
res = requests.post(url, json=args)
text = res.json().get("results")[0].get("values").get("text")
return text
# res = nlp.simnet("你叫什么名字","你的名字是什么")
# print(res)
text = audio2text("bjtq.wma")
if nlp.simnet("你叫什么名字",text).get("score") >= 0.68 :
text = "我的名字叫银角大王8"
else:
text = to_tuling(text)
filename = text2audio(text)
os.system(filename)
整合后的项目
from flask import Flask, render_template, request, jsonify, send_file
from uuid import uuid4
import baidu_ai
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html")
@app.route("/ai", methods=["POST"])
def ai():
# 1.保存录音文件
audio = request.files.get("record")
filename = f"{uuid4()}.wav"
audio.save(filename)
# 2.将录音文件转换为PCM发送给百度进行语音识别
q_text = baidu_ai.audio2text(filename)
# 3.将识别的问题交给图灵或自主处理获取答案
a_text = baidu_ai.to_tuling(q_text)
# 4.将答案发送给百度语音合成,合成音频文件
a_file = baidu_ai.text2audio(a_text)
# 5.将音频文件发送给前端播放
return jsonify({"filename": a_file})
@app.route("/get_audio/<filename>")
def get_audio(filename):
return send_file(filename)
if __name__ == '__main__':
app.run("0.0.0.0", 9527, debug=True)