实时语音识别

最近更新时间:2019-08-21 10:54:36

接口描述

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

接口要求

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

内容 说明
请求协议 HTTP
请求地址 http://asr.cloud.tencent.com/asr/v1/<appid>?{请求参数}
接口鉴权 签名鉴权机制,详见 签名生成
响应格式 统一采用 JSON 格式
开发语言 任意,只要可以向腾讯云服务发起 HTTP 请求的均可
音频属性 采样率16k或8k、位长16bits、单声道
音频格式 wav、pcm、speex、silk
数据长度 每个数据包的音频分片最大不能超过200KB
对实时性要求高的场景,建议8k采样率每个数据包大小4KB,16k采样率每个数据包大小8KB
语言种类 中文普通话、带有一定方言口音的普通话

请求结构

请求结构主要由请求方法、请求 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_0:8k 通用,16k_0:16k 通用。
result_text_format Int 识别结果文本编码方式。0:UTF-8;1:GB2312;2:GBK;3:BIG5
res_type Int 结果返回方式。 0:同步返回;1:尾包返回。
voice_format Int 语音编码方式,可选,默认值为 4。1:wav(pcm);4:speex(sp);6:silk;8:mp3(仅16k_0模型支持)。
needvad Int 0:不需要 VAD,1:需要 VAD。
VAD(Voice Activity Detection)是指对音频进行分段的技术,如果一段音频中包含了多句话,VAD 可以检测多句话之间的停顿进行自动断句,从而达到更高的识别准确度。
如果设置 needvad 为0,需要开发者自己控制单句语音长度不超过60秒;如果设置 needvad 为1,系统会自动根据语音的停顿来分句,但是如果一句话超过60秒没有停顿仍然会识别失败,整个语音流时长没有限制。
seq Int 语音分片的序号,序号从 0 开始。
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天。
timeout Int 设置超时时间,单位为毫秒。
nonce Int 随机正整数。用户需自行生成,最长 10 位。

请求头部
请求头部,包括 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=AKIDoQq1zhZMN8dv0psmvud6OUKuGPO7pu0r&seq=0&source=0&sub_service_type=1&timeout=100&timestamp=1561462573&voice_format=1&voice_id=yp1fifJmEx4Lh3OX"


签名生成
这里以 Appid=1259228442, SecretId=AKIDoQq1zhZMN8dv0psmvud6OUKuGPO7pu0r 为例拼接签名原文,则拼接的签名原文为:

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=AKIDoQq1zhZMN8dv0psmvud6OUKuGPO7pu0r&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=

返回结构

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

参数名称 描述
code 0:正常,其他,发生错误
message 0:正常(成功),其他为错误的具体信息
voice_id 表示这通音频的标记,同一个音频流这个标记一样
seq 语音分片的信号。
如果请求参数 needvad 为0的话,表示不需要后台做 vad,这里的 seq 就是发送过来的 seq 的序号。
如果请求参数 needvad 为1的话,表示需要后台做 vad,因为后台做 vad 的话,vad 会重新分片,送入识别的 seq 会和发送过来的 seq 不一样,这里返回的 seq 就为
text 如果请求参数 needvad 为0的话,表示不需要后台做 vad,这里的 text 的值是这个分片的识别结果
如果请求参数 needvad 为1的话,表示需要后台做 vad,因为后台做 vad 的话,vad 会重新分片,送入识别的 seq 会和发送过来的 seq 不一样,text 为""
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 表示是整个音频流的最后一个包。
主要是在电信场景中,客户端发送完了之后,要知道是否返回的是最后一个包。

返回示例
第一个分片:

{"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 参数不合法
103 访问数据库失败
104 appid未注册
105 模板不存在
106 模板停用
107 鉴权失败
108 拼接签名串失败
109 l5获取 IP、port 失败
110 后台识别服务器故障,请从 seq=0重传
111 后台识别模块回包格式错误
112 语音分片为空

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;
}