实时语音识别

最近更新时间:2020-09-17 17:24:30

接口描述

本接口服务对实时音频流进行识别,同步返回识别结果,达到“边说边出文字”的效果。接口是 HTTP RESTful 形式,在使用该接口前,需要在语音识别控制台开通服务,并进入 API 密钥管理页面 新建密钥,生成 AppID、SecretID 和 SecretKey,用于 API 调用时生成签名,签名将用来进行接口鉴权。

接口要求

集成实时语音识别 API 时,需按照以下要求。

内容 说明
支持语言 中文普通话、英文、粤语、韩语、日语、上海话方言
支持行业 通用、金融
音频属性 采样率:16000Hz或8000Hz、采样精度:16bits、声道:单声道
音频格式 wav、pcm、opus、speex、silk、mp3
数据长度 音频流中每个数据包的音频分片建议为200ms,8k采样率对应的音频分片大小为3200字节,16k采样率对应的音频分片大小为6400字节
请求协议 HTTP
请求地址 http://asr.cloud.tencent.com/asr/v1/<appid>?{请求参数}
接口鉴权 签名鉴权机制,详见 签名生成
响应格式 统一采用 JSON 格式
开发语言 任意,只要可以向腾讯云服务发起 HTTP 请求的均可
请求频率限制 50次/秒

请求结构

请求结构主要由请求方法、请求 URL、请求头部、请求正文组成。

请求方法

HTTPS 请求方法,实时语音识别的请求方法为 POST

请求 URL

RESTful 形式的 URL 结构示例如下:

http://asr.cloud.tencent.com/asr/v1/<appid>?
projectid=xxx&
sub_service_type=xxx&
engine_model_type=xxx&
result_text_format=xxx&
res_type=xxx&
voice_format=xxx&
secretid=xxx&
timestamp=xxx&
expired=xxx&
needvad=xxx&
nonce=xxx&
seq=xxx&
end=xxx&
source=xxx&
voice_id=xxx&
timeout=xxx

URL 中各字段含义如下:

参数名称 必选 类型 描述
AppId Int 用户在腾讯云注册账号的 AppId,可以进入 API 密钥管理页面 获取。
projectid Int 腾讯云项目 ID,语音识别目前不区分项目,所以填0即可。
secretid String 用户在腾讯云注册账号 AppId 对应的 SecretId,可以进入 API 密钥管理页面 获取。
sub_service_type Int 子服务类型。1:实时流式识别。
engine_model_type String 引擎模型类型。
电话场景:
• 8k_en:电话 8k 英语;
• 8k_zh:电话 8k 中文普通话通用;
• 8k_zh_finance:电话 8k 金融领域模型;
非电话场景:
• 16k_zh:16k 中文普通话通用;
• 16k_en:16k 英语;
• 16k_ca:16k 粤语;
• 16k_ko:16k 韩语;
• 16k_zh-TW:16k 中文普通话繁体;
• 16k_ja:16k 日语;
• 16k_wuu-SH:16k上海话方言。
hotword_id String 热词 id。用于调用对应的热词表,如果在调用语音识别服务时,不进行单独的热词 id 设置,自动生效默认热词;如果进行了单独的热词 id 设置,那么将生效单独设置的热词 id。
result_text_format Int 识别结果文本编码方式。0:UTF-8。
res_type Int 结果返回方式。 0:同步返回;1:尾包返回。
voice_format Int 语音编码方式,可选,默认值为 4。1:wav(pcm);4:speex(sp);6:silk;8:mp3;10:opus(opus 格式音频流封装说明)。
needvad Int 0:关闭 vad,1:开启 vad。
如果音频流总时长超过60秒,用户需开启 vad。
vad_silence_time Int 语音断句检测阈值,静音时长超过该阈值会被认为断句(多用在智能客服场景,需配合 needvad=1 使用),取值范围150-2000,单位 ms,目前仅支持 8k_zh、8k_zh_finance、16k_zh 引擎模型。
seq Int 语音分片的序号,序号从 0 开始,每次请求递增1, 两个seq之间间隔不能超过6秒。
end Int 是否为最后一片,最后一片语音片为 1,其余为 0。
source Int 默认值为 0。
voice_id String 16 位 String 串作为每个音频的唯一标识,用户自己生成。
timestamp Int 当前 UNIX 时间戳,可记录发起 API 请求的时间。如果与当前时间相差过大,会引起签名过期错误。可以取值为当前请求的系统时间戳即可。
expired Int 签名的有效期,是一个符合 UNIX Epoch 时间戳规范的数值,单位为秒;Expired 必须大于 Timestamp 且 Expired - Timestamp 小于90天。
nonce Int 随机正整数。用户需自行生成,最长 10 位。
filter_dirty Interger 是否过滤脏词(目前支持中文普通话引擎)。默认为0。0:不过滤脏词;1:过滤脏词;2:将脏词替换为 * 。
filter_modal Interger 是否过滤语气词(目前支持中文普通话引擎)。默认为0。0:不过滤语气词;1:部分过滤;2:严格过滤 。
filter_punc Interger 是否过滤标点符号(目前支持中文普通话引擎)。0:不过滤,1:过滤句末标点,2:过滤所有标点。默认为0。
convert_num_mode Int 是否进行阿拉伯数字智能转换。0:全部转为中文数字;1:根据场景智能转换为阿拉伯数字。
word_info Int 是否显示词级别时间戳。0:不显示;1:显示。支持引擎:8k_zh, 8k_zh_finance, 16k_zh, 16k_en, 16k_ca,默认为0。

请求头部

请求头部,包括 Host,Authorization,Content-Type,Content-Length 四个参数。

参数名称 必选 类型 描述
Host String 语音识别服务域名,固定为 asr.cloud.tencent.com
Authorization String 用户的有效签名,用于鉴权。对应签名鉴权中得到的签名字符串
Content-Type String application/octet-stream
Content-Length Int 请求长度,此处对应语音数据字节数,单位:字节

请求正文

请求正文主要包含实时语音识别的数据,每个数据包的音频分片最大不能超过200KB。

请求示例

用户通过 签名生成 的签名为 qPHg2LZkMRZyC5pyVD8egRqxPc0=,上传语音分片 @/mnt/d/1.silk_down,分片为流式语音的第一个分片(seq=0),采样率为16k,请求实时语音识别服务16k通用(engine_model_type = 16k_0)引擎模型,结果返回方式为同步返回。具体的示例生成方式请参考下面 PHP 代码

curl --data "@/mnt/d/1.silk_down" -H "Authorization:qPHg2LZkMRZyC5pyVD8egRqxPc0=" -X POST "http://asr.cloud.tencent.com/asr/v1/1259228442?end=0&engine_model_type=16k_0&expired=1561548973&needvad=0&nonce=1561&projectid=1013976&res_type=0&result_text_format=0&secretid=AKIDoQq1zhZMN8dv0psmvud6OUKuGPO*****&seq=0&source=0&sub_service_type=1&timeout=100&timestamp=1561462573&voice_format=1&voice_id=yp1fifJmEx4Lh3OX"

签名生成

这里以 Appid=1259228442, SecretId=AKIDoQq1zhZMN8dv0psmvud6OUKuGPO* 为例拼接签名原文,则拼接的签名原文为:

POSTasr.cloud.tencent.com/asr/v1/1259228442?end=1&engine_model_type=16k_0&expired=1561022467&needvad=1&nonce=52811334&projectid=0&res_type=1&result_text_format=0&secretid=AKIDoQq1zhZMN8dv0psmvud6OUKuGPO*****&seq=0&source=0&sub_service_type=1&timeout=5000&timestamp=1561016467&voice_format=4&voice_id=f658c689c3b4db74

对签名原文和 SecretKey=kFpwoX5RYQ2SkqpeHgqmSzHK7h3A2fni,使用 HmacSha1 算法进行加密处理:

签名串=Base64Encode(HmacSha1(签名原文,SecretKey))

最终得到签名串为:

LsgT3TbVPb2nS3ac1U34stZ/D5k=

Opus 音频流封装说明

压缩 FrameSize 固定640,即一次压缩640 short,否则解压会失败。传到服务端可以是多帧的拼接组合,每一帧需满足下面格式。
每一帧压缩数据封装如下:

OpusHead(4字节) 帧数据长度(2字节) Opus 一帧压缩数据
opus 长度 len 对应 len 长的 opus decode data

返回结构

返回结果

实时语音识别的 RESTfulAPI 请求返回结果如下表所示:

参数名称 描述
code 0:正常,其他,发生错误
message 0:success;不为0:其他
voice_id 表示这通音频的标记,同一个音频流这个标记一样
seq 语音分片的信号。
如果请求参数 needvad 为0的话,表示不需要后台做 vad,这里的 seq 就是发送过来的 seq 的序号。如果请求参数 needvad 为1的话,表示需要后台做 vad,vad 会重新分片,送入识别的 seq 会和发送过来的 seq 不一样。
text 语音分片的识别结果
如果请求参数 needvad 为0的话,表示不需要后台做 vad, text 为完整识别结果。
如果请求参数 needvad 为1的话,表示需要后台做 vad,text 为空,需要从 result_list 字段中获取分片识别结果。
result_number 表示后面的 result_list 里面有几段结果,如果是0表示没有结果,遇到中间是静音。
如果是1表示 result_list 有一个结果, 在发给服务器分片很大的情况下可能会出现多个结果,正常情况下都是1个结果。
result_list slice_type: 返回分片类型标记, 0表示一小段话开始,1表示在小段话的进行中,2表示小段话的结束
index 表示第几段话
start_time 这个分片在整个音频流中的开始时间
end_time 这个分片在整个音频流中的结束时间
voice_text_str 识别结果
final 0 表示还在整个音频流的中间部分
1 表示是整个音频流的最后一个包。
主要是在电信场景中,客户端发送完了之后,要知道是否返回的是最后一个包。
word_size 表示后面的 word_list 的长度,即有多少个词。
word_list 词时间戳列表:
word 表示这个词的内容,
start_time 表示该词在整个音频中的起始时间,
end_time 表示该词在整个音频中的结束时间,
stable_flag 表示词的稳态结果,0:该词在后续识别中可能发生变化,1:表示该词在后续识别过程中不会变化。

返回示例

第一个分片:

{"code":0,"message":"success","voice_id":"8qiS3yeVnwHHbQ9F","seq":0,"text":"","result_number":1,"result_list":[{"slice_type":0,"index":1,"start_time":0,"end_time":256,"voice_text_str":""}],"final":0}

中间某个分片:

{"code":0,"message":"success","voice_id":"8qiS3yeVnwHHbQ9F","seq":2,"text":"吃饭了。","result_number":1,"result_list":[{"slice_type":0,"index":1,"start_time":512,"end_time":768,"voice_text_str":"吃饭了。"}],"final":0}

最后一个分片:

{"code":0,"message":"success","voice_id":"K3lRwC6tWkKjvnCL","seq":6,"text":"吃饭了吗。","result_number":1,"result_list":[{"slice_type":0,"index":1,"start_time":3072,"end_time":3093,"voice_text_str":"吃饭了吗。"}],"final":1}

错误码

数值 说明
100 获取语音分片信息失败
101 语音分片过大
102 参数不合法,具体详情参考 message
103 访问数据库失败
104 AppID 服务未开通,请在控制台开通服务
105 模板不存在
106 模板停用
107 鉴权失败
108 拼接签名串失败
109 l5获取 IP、port 失败
110 后台识别服务器故障,请从 seq=0重传
111 后台识别模块回包格式错误
112 语音分片为空
113 后台服务器识别超时
114 引擎编号不合法
115 时长计算时音频类型不合法
116 无可使用的免费额度
117 禁止访问
118 请求限流
119 账户欠费停止服务,请及时充值
120 获取 rpcClient 错误
121 后台识别服务器错误,请从seq=0重传
122 后台识别服务器收到的包格式错误
123 后台识别服务器音频解压失败,请从seq=0重传
124 后台识别服务器识别失败,请从seq=0重传
125 后台识别服务器识别失败,请重新尝试
126 后台识别服务器音频分片等待超时,请从seq=0重传
127 后台识别服务器音频分片重复

PHP 代码示例

//filepath 音频文件路径
//printCutResponse 是否打印返回结果
function sendvoice($filepath, $printCutResponse) {
    if (empty ($filepath)) {
        echo "filepath can not be empty";
        return -1;
    }

    $reqArr = array ();
    $reqArr['appid'] = Config :: $APPID;
    $reqArr['projectid'] = 0;
    $reqArr['sub_service_type'] = 1;
    $reqArr['engine_model_type'] = Config :: $ENGINE_MODEL_TYPE;
    $reqArr['res_type'] = Config :: $RES_TYPE;
    $reqArr['result_text_format'] = Config :: $RESULT_TEXT_FORMAT;
    $reqArr['voice_id'] = randstr(16);
    $reqArr['needvad'] = 0;
    $reqArr['timeout'] = 20000;
    $reqArr['source'] = 0;
    $reqArr['secretid'] = Config :: $SECRET_ID;
    $reqArr['timestamp'] = time();
    $reqArr['expired'] = time() + 24 * 60 * 60;
    $reqArr['nonce'] = rand(1, 10000);
    $reqArr['voice_format'] = Config :: $VOICE_FORMAT;
    $secretKey = Config :: $SECRET_KEY;
    $voicedata = file_get_contents($filepath);
    $datalen = strlen($voicedata);
    $cutlegth = Config :: $CUTLENGTH;
    $seq = 0;
    while ($datalen > 0) {
        $end = 0;
        if ($datalen < $cutlegth)
            $end = 1;
        $serverUrl = "http://asr.cloud.tencent.com/asr/v1/";
        $reqArr['end'] = $end;
        $reqArr['seq'] = $seq;
        $serverUrl .= $reqArr['appid'] . "?";
        $serverUrl .= "projectid=" . $reqArr['projectid'] . "&";
        if (isset ($reqArr['template_name'])) {
            $serverUrl .= "template_name=" . $reqArr['template_name'] . "&";
        }
        $serverUrl .= "sub_service_type=" . $reqArr['sub_service_type'] . "&";
        $serverUrl .= "engine_model_type=" . $reqArr['engine_model_type'] . "&";
        $serverUrl .= "res_type=" . $reqArr['res_type'] . "&";
        $serverUrl .= "result_text_format=" . $reqArr['result_text_format'] . "&";
        $serverUrl .= "voice_id=" . $reqArr['voice_id'] . "&";
        $serverUrl .= "seq=" . $seq . "&";
        $serverUrl .= "end=" . $end . "&";
        $serverUrl .= "timeout=" . $reqArr['timeout'] . "&";
        $serverUrl .= "source=" . $reqArr['source'] . "&";
        $serverUrl .= "secretid=" . $reqArr['secretid'] . "&";
        $serverUrl .= "timestamp=" . $reqArr['timestamp'] . "&";
        $serverUrl .= "expired=" . $reqArr['expired'] . "&";
        $serverUrl .= "nonce=" . $reqArr['nonce'] . "&";
        $serverUrl .= "needvad=" . $reqArr['needvad'] . "&";
        $serverUrl .= "voice_format=" . $reqArr['voice_format'];

        $autho = createSign($reqArr, "POST", "asr.cloud.tencent.com", "/asr/v1/", $secretKey);
        if ($datalen < $cutlegth) {
            $data = file_get_contents($filepath, NULL, NULL, $seq * $cutlegth, $cutlegth);
        } else {
            $data = file_get_contents($filepath, NULL, NULL, $seq * $cutlegth, $cutlegth);
        }
        $seq = $seq +1;
        $datalen = $datalen - $cutlegth;

        $header = array (
            'Authorization: ' . $autho,
            'Content-Length: ' . strlen($data),
        );

        $rsp_str = "";
        $http_code = -1;
        $ret = http_curl_exec($serverUrl, $data, $rsp_str, $http_code, 'POST', 10, array(), $header);
        if ($ret != 0) {
            echo "http_curl_exec failed \n";
            return false;
        }

        if($printCutResponse)
            echo "rsp_str : \n" . $rsp_str . "\n";
    }
    return $rsp_str;
}
目录