logo
电话400 898 2016
预约试用
开放平台
En
尊敬的开发者,您好! 标贝科技的开发文档已更换新的地址,旧版文档地址将会在2023年12月31日下线, 给您带来不便,敬请谅解! 立即查看
产品简介 产品简介
基本概念 基本概念
平台新手指引 平台新手指引
计价模式 计价模式
开发者文档下拉
开放平台计价
定制服务计价
获取访问令牌 获取访问令牌
语音合成 语音合成
开发者文档下拉
接口说明
发音人列表
短文本合成 开发者文档下拉
Android SDK
iOS SDK
C++(Linux) SDK
RESTful API
Websocket API
长文本合成 开发者文档下拉
长文本合成RESTful API
长文本合成Websocket API
离线合成 开发者文档下拉
模型文件说明
离线合成(普通版) Android SDK
离线合成(普通版) iOS SDK
离线合成(精品版) Android SDK
离线合成(精品版) iOS SDK
XML标签
SSML标签
语音合成时间戳功能
语音识别 语音识别
开发者文档下拉
音频格式说明
一句话识别 开发者文档下拉
RESTful API
Websocket API
Android SDK
iOS SDK
实时长语音识别 开发者文档下拉
Websocket API
Android SDK
iOS SDK
录音文件识别 开发者文档下拉
RESTful API
自学习工具 开发者文档下拉
热词
个性化模型
声音复刻 声音复刻
开发者文档下拉
定制模型 开发者文档下拉
RESTful API
Android SDK
iOS SDK
定制声音合成 开发者文档下拉
短文本合成RESTful API
长文本合成Websocket API
声音转换 声音转换
开发者文档下拉
发音人列表
Websocket API
Android SDK
iOS SDK
离线声音转换 离线声音转换
开发者文档下拉
发音人列表
Android SDK
iOS SDK
声纹识别 声纹识别
开发者文档下拉
RESTful API
声音理解 声音理解
开发者文档下拉
RESTful API
协议规则 协议规则
开发者文档下拉
平台服务协议
平台通用规则
法律声明及隐私政策
服务等级协议SLA
常见问题 常见问题
开发者文档下拉
语音合成
语音识别

一句话识别Websocket API

功能介绍

识别不超过60s的语音,适用于语音交互、控制口令、对话聊天等场景。

  • 支持语言:中文普通话、中英自由说、英文、粤语、维语
  • 支持添加标点,支持将中文数字转换为阿拉伯数字进行输出
  • 支持连续返回中间识别结果,实现连续上屏效果

音频要求

  • 时长限制:60秒以内
  • 支持音频格式:wav, pcm
  • 音频采样率:8000Hz,16000Hz
  • 位深:16bits
  • 声道:单声道

使用方法

1. 创建账号和应用,详见平台新手指引,通过标贝开放平台/应用/服务获取client_id,client_secret

2. 发送请求获取access_token,详见获取访问令牌

3. 建立websocket连接

4. 实时发送需要识别的音频,具体参数详见 请求参数

5. 接收返回结果,具体参数定义详见响应结果说明。

6. 关闭连接。

7. 注意:

1)、 开始识别时,websocket握手建立会话根据网络环境会有一定延迟,需根据实际测试进行处理

2)、 结束识别时,不要立刻关闭麦克风,可以再延迟几百毫秒,收一部分环境静音,有助于vad快速结束。

3)、 最后一个数据包发送出去后,等待最终识别结果返回,返回参数end_flag=1时为最后一个包,不要提前关闭会话。

服务地址

访问类型 说明 URL Host
外网访问 支持中文普通话、中英自由说、英文、粤语、维语 wss://openapi.data-baker.com/asr/wsapi/ openapi.data-baker.com

交互流程

测试音频

测试音频

请求参数

请求消息和回应消息均序列化为json格式。
参数名称 类型 是否必填项 说明
access_token string yes 通过client_id,client_secret调用授权服务获得见 获取访问令牌
version string yes 版本信息,目前为1.0
asr_params jsonObject,其中 audio_data字段是必需字段且需使用base64进行编码 yes asr相关参数

asr相关参数如下:

参数名称 类型 是否必填项 说明
audio_data string 流式接口需要将音频分包发送,具体步骤如下:
1. 除最后一包外,每包长度固定为5120字节,最后一包如音频长度不足5120字节时,可直接发送剩余全部数据
2. 对音频进行base64编码
audio_format string 音频编码格式
pcm
wav
sample_rate int 音频采样率
8000
16000
req_idx int 音频序号索引,步长为1递增 0:起始音频帧 >0:中间音频帧(如1 2 3 4 … 1000) -n:结束音频帧(如-1001)
domain string 模型名称
16k采样率支持模型:
中文通用模型 "common",默认值
中英自由说模型 "cn-en-mixed"
英文模型 "english",
粤语模型 "cantonese",
维语模型 "uighur",
8k采样率支持模型:
中文客服模型 "kefu"
add_pct boolean no true: 加标点,默认值
false:不添加标点
hotwordid string no 配置的热词组的id
diylmid string no asr个性化模型的id
enable_vad bool False: 关闭静音检测(默认)
True:开启静音检测
max_begin_silence int 当enable_vad为true时有效,表示允许的最大开始静音时长
单位:毫秒,取值范围[200,60000],输入超过范围取临近值,该值是一个参考值,具体可能会根据音频不同有少量浮动。
超出规定范围后,即开始识别后多长时间没有检测到语音,服务端将会发送错误码90002,表示没有检测到语音,结束本次识别。
max_end_silence int 当enable_vad为true时有效,表示允许的最大结束静音时长
单位:毫秒,取值范围[200, 5000],输入超过范围取临近值,该值是一个参考值,具体可能会根据音频不同有少量浮动。
超出规定范围后,即在上句话识别后,间隔多长时间没有检测到语音,结束本次识别,间隔后如果还有后续语音则不会被识别。
enable_itn boolean 中文数字转换为阿拉伯数字(仅支持3位及以上数字串)
‘true’: 开启
‘false’: 关闭

请求示例

{
    "access_token":" 1ccbbc2d************0cfce63eec55",
    "version":"1.0",
    "asr_params":{
        "audio_data":"AAAAAAAAAA",
        "audio_format":"pcm",
        "sample_rate":16000,
        "req_idx":0,
        "domain":"xxx"  //非必填
    }
}

Python示例代码

代码地址:Github

Python3示例:

import argparse
import json
import base64
from threading import Thread

import requests
import websocket


class Client:
    def __init__(self, data, uri):
        self.data = data
        self.uri = uri

    #建立连接
    def connect(self):
        ws_app = websocket.WebSocketApp(uri,
                                        on_open=self.on_open,
                                        on_message=self.on_message,
                                        on_error=self.on_error,
                                        on_close=self.on_close)
        ws_app.run_forever()

    # 建立连接后发送消息
    def on_open(self, ws):
        print("sending..")
        def run(*args):
            for i in range(len(self.data)):
                ws.send(self.data[i])

        Thread(target=run).start()

    # 接收消息
    def on_message(self, ws, message):
        code = json.loads(message).get("code")

        if code != 90000:
            # 打印接口错误
            print(message)
        if json.loads(message).get('data')['end_flag'] == 1:
            print(json.loads(message).get('data')['nbest'][0])

    # 打印错误
    def on_error(slef, ws, error):
        print("error: ", str(error))

    # 关闭连接
    def on_close(ws):
        print("client closed.")


# 准备数据
def prepare_data(args, access_token):
    # 读取音频文件
    with open(args.file_path, 'rb') as f:
        file = f.read()

    # 填写Header信息
    audio_format = args.audio_format
    sample_rate = args.sample_rate

    splited_data = [str(base64.b64encode(file[i:i + 5120]), encoding='utf-8') for i in range(0, len(file), 5120)]
    asr_params = {"audio_format": audio_format, "sample_rate": int(sample_rate), "speech_type": 1}

    json_list = []
    for i in range(len(splited_data)):
        if i != len(splited_data) - 1:
            asr_params['req_idx'] = i
        else:
            asr_params['req_idx'] = -len(splited_data) + 1
        asr_params["audio_data"] = splited_data[i]
        data = {"access_token": access_token, "version": "1.0", "asr_params": asr_params}
        json_list.append(json.dumps(data))

    return json_list


# 获取命令行输入参数
def get_args():
    parser = argparse.ArgumentParser(description='ASR')
    parser.add_argument('-client_secret', type=str, required=True)
    parser.add_argument('-client_id', type=str, required=True)
    parser.add_argument('-file_path', type=str, required=True)
    parser.add_argument('--audio_format', type=str, default='wav')
    parser.add_argument('--sample_rate', type=str, default='16000')
    args = parser.parse_args()

    return args


# 获取access_token用于鉴权
def get_access_token(client_secret, client_id):
    grant_type = "client_credentials"
    url = "https://openapi.data-baker.com/oauth/2.0/token?grant_type={}&client_secret={}&client_id={}" \
        .format(grant_type, client_secret, client_id)

    try:
        response = requests.post(url)
        response.raise_for_status()
    except Exception as e:
        print(response.text)
        raise Exception
    else:
        access_token = json.loads(response.text).get('access_token')
        return access_token


if __name__ == '__main__':
    try:
        args = get_args()

        # 获取access_token
        client_secret = args.client_secret
        client_id = args.client_id
        access_token = get_access_token(client_secret, client_id)

        # 准备数据
        data = prepare_data(args, access_token)

        uri = "wss://openapi.data-baker.com/asr/wsapi/"
        # 建立Websocket连接
        client = Client(data, uri)
        client.connect()
    except Exception as e:
        print(e)

命令行执行:

默认wav格式,16000采样率

python single_sentence_recognition.py -client_secret=你的client_secret -client_id=你的client_id -file_path=test.wav

如有需要可自行修改参数

python single_sentence_recognition.py -client_secret=你的client_secret -client_id=你的client_id -file_path=test.wav --audio_format=wav --sample_rate=16000

JAVA示例代码

package com.databaker.web.asr;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import okhttp3.*;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;

/**
 * 一句话语音识别 webSocket接口调用示例
 * 附:一句话识别Websocket API文档 【https://www.data-baker.com/specs/file/asr_word_api_websocket】
 *
 * 注意:
 * 1.作为demo,进行了一些简化,采用直接从文件中读取的方式获取音频流,实际场景很可能是从麦克风获取音频流。
 * 2.如果从麦克风获取音频流,请注意每次发送的数据流大小为52K,不足时补静音段。
 * 3.本demo仅完成基本的接口调用,失败重试、token过期重新获取、日志打印等优化工作需要开发者自行完成。
 *
 * @author data-baker
 */

public class AsrWebSocketDemo extends WebSocketListener {
    /**
     * 授权:需要在开放平台获取【https://ai.data-baker.com/】
     */
    private static final String clientId = "YOUR_CLIENT_ID";
    private static final String clientSecret = "YOUR_CLIENT_SECRET";
    /**
     * 获取token的地址信息
     */
    public static String tokenUrl = "https://openapi.data-baker.com/oauth/2.0/token?grant_type=client_credentials&client_secret=%s&client_id=%s";

    private static final String hostUrl = "wss://openapi.data-baker.com/asr/wsapi";
    /**
     * 文件路径【开发者需要根据实际路径调整。支持的音频编码格式:PCM(无压缩的PCM文件或WAV文件),采样率8K或16K,位深16bit,单声道】
     */
    private static final String file = "/home/asr/16bit_16k.pcm";


    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss.SSS");

    // 开始时间
    private static ThreadLocal<Date> timeBegin = ThreadLocal.withInitial(() -> new Date());

    // 结束时间
    private static ThreadLocal<Date> timeEnd = ThreadLocal.withInitial(() -> new Date());

    private Date startTime;

    private String accessToken = getAccessToken();

    private StringBuilder resultAsr = new StringBuilder();

    @Override
    public void onOpen(WebSocket webSocket, Response response) {
        super.onOpen(webSocket, response);
        this.startTime = timeBegin.get();
        //该demo直接从文件中读取音频流【实际场景可能是实时从麦克风获取音频流,开发者自行修改获取音频流的逻辑即可】
        new Thread(() -> {
            //连接成功,开始发送数据
            //每一帧音频的大小,固定值,具体参考接口文档
            int frameSize = 5120;
            int interval = 40;
            // 音频的序号
            int req_idx = 0;
            try (FileInputStream fs = new FileInputStream(file)) {
                byte[] buffer = new byte[frameSize];
                // 发送音频
                while (true) {
                    int len = fs.read(buffer);
                    if (len < frameSize) {
                        //文件已读完
                        req_idx = -1 - req_idx;
                    }
                    //发送音频
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("access_token", accessToken);
                    jsonObject.put("version", "1.0");
                    //填充asr_params
                    JSONObject asrParams = new JSONObject();
                    //domain非必填
                    asrParams.put("domain", "common");
                    asrParams.put("audio_format", "pcm");
                    asrParams.put("sample_rate", 16000);
                    asrParams.put("req_idx", req_idx);
                    asrParams.put("audio_data", Base64.getEncoder().encodeToString(Arrays.copyOf(buffer, len<0?0:len)));
                    jsonObject.put("asr_params", asrParams);
                    webSocket.send(jsonObject.toString());
                    if (req_idx >= 0) {
                        req_idx++;
                    }else {
                        break;
                    }
                    //模拟音频采样延时【如果从麦克风获取音频流,可删除这句代码】
                    Thread.sleep(interval);
                }
                System.out.println("all data is send");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

    @Override
    public void onMessage(WebSocket webSocket, String text) {
        super.onMessage(webSocket, text);
        ResponseData resp = JSON.parseObject(text, ResponseData.class);
        if (resp != null) {
            if (resp.getCode() != 90000) {
                System.out.println("code=>" + resp.getCode() + " error=>" + resp.getMessage() + " trace_id=" + resp.getTrace_id());
                //关闭连接
                webSocket.close(1000, "");
                System.out.println("发生错误,关闭连接");
                return;
            }
            if (resp.getData() != null) {
                if (Integer.valueOf(1).equals(resp.getData().getEnd_flag())) {
                    resultAsr.append(resp.getData().nbest);
                    System.out.println("当前句子识别结束,识别结果 ==》" + resp.getData().nbest);
                } else {
                    System.out.println("当前句子识别未结束,中间识别结果 ==》" + resp.getData().nbest);
                }
            } else {
                // todo 根据返回的数据处理
            }
        }
    }

    @Override
    public void onFailure(WebSocket webSocket, Throwable t, Response response) {
        super.onFailure(webSocket, t, response);
        try {
            if (null != response) {
                int code = response.code();
                System.out.println("onFailure code:" + code);
                System.out.println("onFailure body:" + response.body().string());
                if (101 != code) {
                    System.out.println("connection failed");
                    System.exit(0);
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 测试方法
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        OkHttpClient client = new OkHttpClient.Builder().build();
        Request request = new Request.Builder().url(hostUrl).build();
        client.newWebSocket(request, new AsrWebSocketDemo());
    }

    public static String getAccessToken() {
        String accessToken = "";
        OkHttpClient client = new OkHttpClient();
        //request 默认是get请求
        String url = String.format(tokenUrl, clientSecret, clientId);
        Request request = new Request.Builder().url(url).build();
        JSONObject jsonObject;
        try {
            Response response = client.newCall(request).execute();
            if (response.isSuccessful()) {
                //解析
                String resultJson = response.body().string();
                jsonObject = JSON.parseObject(resultJson);
                accessToken = jsonObject.getString("access_token");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return accessToken;
    }

    public static class ResponseData {
        /**
         * 状态码(4xxxx表示客户端参数错误,5xxxx表示服务端内部错误)
         */
        private Integer code;
        /**
         * 错误描述
         */
        private String message;
        /**
         * 任务id
         */
        private String trace_id;
        /**
         * 会话id
         */
        private String sid;
        /**
         * payload数据
         */
        private Data data;


        public Integer getCode() {
            return code;
        }

        public void setCode(Integer code) {
            this.code = code;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }

        public String getTrace_id() {
            return trace_id;
        }

        public void setTrace_id(String trace_id) {
            this.trace_id = trace_id;
        }

        public String getSid() {
            return sid;
        }

        public void setSid(String sid) {
            this.sid = sid;
        }

        public Data getData() {
            return data;
        }

        public void setData(Data data) {
            this.data = data;
        }
    }

    /**
     * payload类
     */
    public static class Data{
        /**
         * 数据块序列号,请求内容会以流式的数据块方式返回给客户端。服务器端生成,从1递增
         */
        private Integer res_idx;
        /**
         * 识别结果
         */
        private String nbest;
        /**
         * 识别结果预测
         */
        private String uncertain;
        /**
         * 是否是最后一个数据块,0:否,1:是
         */
        private Integer end_flag;

        public Integer getRes_idx() {
            return res_idx;
        }

        public void setRes_idx(Integer res_idx) {
            this.res_idx = res_idx;
        }

        public String getNbest() {
            return nbest;
        }

        public void setNbest(String nbest) {
            this.nbest = nbest;
        }

        public String getUncertain() {
            return uncertain;
        }

        public void setUncertain(String uncertain) {
            this.uncertain = uncertain;
        }

        public Integer getEnd_flag() {
            return end_flag;
        }

        public void setEnd_flag(Integer end_flag) {
            this.end_flag = end_flag;
        }
    }
}
        

PHP示例代码

<?php
    ini_set("max_execution_time", "300");
    require_once "websocket-php/vendor/autoload.php";
    //1.获取token
    $client_secret = '***'; //应用secret
    $client_id = '***'; //应用id
    $grant_type = 'client_credentials'; //固定格式
    $url = 'https://openapi.data-baker.com/oauth/2.0/token?grant_type='.$grant_type.'&client_id='.$client_id.'&client_secret='.$client_secret;

    //curl get请求
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //信任任何证书
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); // 检查证书中是否设置域名,0不验证
    $token_res = curl_exec($ch);

    //如果错误 查看错误信息
    if(curl_errno($ch))
    {
        print curl_error($ch);
    }
    curl_close($ch);

    //进行token信息解析
    $token_info = json_decode($token_res,1);
    $access_token = $token_info['access_token'];    //获取到的token信息
    //var_dump($access_token);die;
    //$access_token = '***';

    //2.一句话识别请求
    $url = 'wss://openapi.data-baker.com/asr/wsapi/';
    $file_path = './test.wav';//文件路径

    //定义请求方法
    $data_param['access_token'] = $access_token;
    $data_param['version'] = '1.0';
    $data_param['asr_params']['audio_format'] = 'wav';
    $data_param['asr_params']['sample_rate'] = 16000;


    //读取文件 发送数据请求
    $client = new \WebSocket\Client($url); //实例化
    $file_size = filesize($file_path); //获取文件大小
    if(!$file_size) die("文件大小不能为0");
    $package_nums = ceil($file_size/5120); //计算要发送的包数量
    $handle = fopen($file_path,'rb');
    $i=0; //起始音频编号

    while (!feof($handle)){
        if($i == $package_nums-1){
            $data_param['asr_params']['req_idx'] = -$i;
        }else{
            $data_param['asr_params']['req_idx'] = $i;
        }
        $audio_info = fread($handle,5120);
        $data_param['asr_params']['audio_data'] = base64_encode($audio_info);
        $data = json_encode($data_param);
        $client->send($data); //发送数据
        $i++;
    }

    //监听数据
    $flag = true;
    while ($flag) {
        try {
            $message = $client->receive();//获取数据
            $result_info = json_decode($message,1);
            if($result_info['code'] == 90000){
                if($result_info['data']['end_flag']==1){
                    //(说明,根据业务逻辑情况使用,本示例对返回完整信息进行展示)
                    $flag=false;//最后一包数据获取完成 停止获取数据
                    var_dump($result_info['data']['nbest']);
                }
            }else{
                throw new Exception($result_info['message']);
            }

        } catch (\WebSocket\ConnectionException $e) {
            die($e->getMessage());
        }
    }

GO示例代码

package main

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/gorilla/websocket"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"time"
)

type AuthInfo struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
	Scope       string `json:"scope"`
}

type ReqParams struct {
	AudioData       string `json:"audio_data"`
	AudioFormat     string `json:"audio_format"`
	SampleRate      int    `json:"sample_rate"`
	ReqIdx          int    `json:"req_idx"`
	Domain          string `json:"domain,omitempty"`
	AddPct          bool   `json:"add_pct"`
	Hotwordid       string `json:"hotwordid,omitempty"`
	Diylmid         string `json:"diylmid,omitempty"`
	EnableVad       bool   `json:"enable_vad"`
	MaxBeginSilence int    `json:"max_begin_silence,omitempty"`
	MaxEndSilence   int    `json:"max_end_silence,omitempty"`
}
type WsReqParam struct {
	AccessToken string    `json:"access_token"`
	Version     string    `json:"version"`
	Params      ReqParams `json:"asr_params"`
}

type WsMsgData struct {
	ResIdx    int      `json:"res_idx"`
	Nbest     []string `json:"nbest"`
	Uncertain []string `json:"uncertain"`
	EndFlag   int      `json:"end_flag"`
}
type WsMsg struct {
	Code    int       `json:"code"`
	Message string    `json:"message"`
	TraceId string    `json:"trace_id"`
	Data    WsMsgData `json:"data"`
}

const grantType string = "client_credentials"

func GetToken(reqUrl, clientId, clientSecret string) (string, error) {
	// 超时时间:60秒
	client := &http.Client{Timeout: 60 * time.Second}
	urlParams := url.Values{}
	urlParams.Add("grant_type", grantType)
	urlParams.Add("client_id", clientId)
	urlParams.Add("client_secret", clientSecret)
	httpUrl := reqUrl
	httpUrl += "?"
	httpUrl += urlParams.Encode()
	fmt.Printf("httpUrl: %s\n\n", httpUrl)
	resp, err := client.Get(httpUrl)
	if err != nil {
		fmt.Printf("send http get token request failed, err: %s\n", err)
		return "", err
	}
	defer resp.Body.Close()
	result, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("http get token request, readall body failed, err: %s\n", err)
		return "", err
	}
	authInfo := AuthInfo{}
	err = json.Unmarshal(result, &authInfo)
	if err != nil {
		fmt.Printf("auth info json.Unmarshal err: %s \n", err)
		return "", err
	}
	if len(authInfo.AccessToken) <= 0 {
		return "", errors.New("access token is null")
	}
	fmt.Printf("get token success. info: %#v \n", authInfo)
	return authInfo.AccessToken, nil
}

func RecvMsg(conn *websocket.Conn, chn chan error) {
	var err error
	for {
		msg := WsMsg{}
		err = conn.ReadJSON(&msg)
		if err != nil {
			fmt.Printf("websocket read json failed.\n")
			break
		}
		if msg.Code != 90000 {
			err = fmt.Errorf("error_code %d, msg: %s traceId: %s ", msg.Code, msg.Message, msg.TraceId)
			break
		}
		fmt.Printf("ResIdx:\t%d\nnbest:\t%v\n", msg.Data.ResIdx, msg.Data.Nbest)
		if len(msg.Data.Uncertain) > 0 {
			fmt.Printf("uncertain:\t%v\n", msg.Data.Uncertain)
		}
		fmt.Printf("endflag:\t%d\n\n", msg.Data.EndFlag)
		if msg.Data.EndFlag == 1 {
			break
		}
	}
	chn <- err
}

func SendAsr(urlStr, file string, reqParam WsReqParam) error {
	conn, _, err := websocket.DefaultDialer.Dial(urlStr, nil)
	if err != nil {
		fmt.Printf("websocket dial failed. url: %s\n", urlStr)
		return err
	}
	defer conn.Close()

	audioData, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Println("read file failed!!!")
		return err
	}
	// channel 等待返回使用
	chn := make(chan error)
	// 开启接收websocket 消息 协程
	go RecvMsg(conn, chn)

	for index := 0; index < len(audioData); index += 5120 {
		reqIdx := index / 5120
		if index+5120 >= len(audioData) {
			reqIdx = -reqIdx
			audio := audioData[index:]
			reqParam.Params.AudioData = base64.StdEncoding.EncodeToString(audio)
		} else {
			audio := audioData[index : index+5120]
			reqParam.Params.AudioData = base64.StdEncoding.EncodeToString(audio)
		}
		reqParam.Params.ReqIdx = reqIdx
		fmt.Printf("send req_idx: %d\n", reqIdx)
		err = conn.WriteJSON(reqParam)
		if err != nil {
			fmt.Printf("websocket write json failed. url: %s\n", urlStr)
			chn <- err
			break
		}
	}
	fmt.Println("send audio data finish.")
	// 等待channel 返回
	err = <-chn
	return err
}

func main() {
	var (
		clientId, clientSecret, file, AudioFormat, domain, hotwordid, diylmid string
		SampleRate, maxBeginSilence, maxEndSilence                            int
		addPct, enableVad                                                     bool
	)
	flag.StringVar(&clientId, "cid", "", "client id")
	flag.StringVar(&clientSecret, "cs", "", "client secret")
	flag.StringVar(&file, "f", "", "识别音频文件")
	flag.StringVar(&AudioFormat, "audio_format", "wav", "音频编码格式wav, pcm")
	flag.IntVar(&SampleRate, "sample_rate", 16000, "音频采样率8000, 16000")
	flag.StringVar(&domain, "domain", "", "模型名称")
	flag.BoolVar(&addPct, "add_pct", true, "加标点")
	flag.StringVar(&hotwordid, "hotwordid", "", "配置的热词组的id")
	flag.StringVar(&diylmid, "diylmid", "", "asr个性化模型的id")
	flag.BoolVar(&enableVad, "enable_vad", false, "静音检测")
	flag.Parse()
	if len(os.Args) < 2 {
		flag.Usage()
		return
	}
	if len(clientId) <= 0 || len(clientSecret) <= 0 || len(file) <= 0 || len(AudioFormat) <= 0 {
		fmt.Println("parameter error!!!")
		return
	}
	accessToken, err := GetToken("https://openapi.data-baker.com/oauth/2.0/token",
		clientId,
		clientSecret,
	)
	if err != nil {
		fmt.Println("get access token failed!!!! please check your client_id and client_secret.")
		return
	}
	param := WsReqParam{
		AccessToken: accessToken,
		Version:     "1.0",
		Params: ReqParams{
			AudioFormat:     AudioFormat,
			SampleRate:      SampleRate,
			ReqIdx:          0,
			Domain:          domain,
			AddPct:          addPct,
			Hotwordid:       hotwordid,
			Diylmid:         diylmid,
			EnableVad:       enableVad,
			MaxBeginSilence: maxBeginSilence,
			MaxEndSilence:   maxEndSilence,
		},
	}
	err = SendAsr("wss://openapi.data-baker.com/asr/wsapi", file, param)
	if err != nil {
		fmt.Printf("send websocket request failed. err: %s \n", err.Error())
		return
	}
	fmt.Println("send websocket request success.")
}

C示例代码

代码地址:Github

响应结果参数

1.成功时
参数名称 类型 描述
code int 错误码4xxxx表示客户端参数错误,5xxxx表示服务端内部错误
message string 错误描述
trace_id string 任务id 用于定位错误跟踪日志
data Object 识别结果

data内部结构:

参数名称 类型 描述
res_idx int 数据块序列号,请求内容会以流式的数据块方式返回给客户端。服务器端生成,从1递增
nbest stringArray 识别结果
end_flag int 是否是最后一个数据块,0:否,1:是
text string 语音识别结果,失败时为空
confidence string 整句置信度[0-100],值越大表示置信度越高。
speed int 语速,取值[0-2000]
speed_label string 语速标签:
15以下:FAST 快
15-30 : MEDIUM 适中
30-2000:SLOW 慢
volume int 音量,取值[0-100]
volume_label string 音量标签:
SILENT:0-15 静音
XSOFT:15-30 音量很小
SOFT:30-50 音量小
MEDIUM:50-70 音量适中
LOAD:70-85 音量大
XLOUD:85-100 音量很大
words array 词级别识别结果

words内部结构:

参数名称 类型 描述
confidence string 词置信度[0-1],值越大表示置信度越高。
sos string 词在音频中的绝对开始时间点 单位:秒
eos string 词在音频中的绝对结束时间点 单位:秒
word string

响应结果示例

{
	"code": 90000,
	"data": {
		"confidence": "68.31",
		"end_flag": 1,
		"nbest": ["欢迎使用标贝科技开放平台。"],
		"res_idx": 20,
		"speed": 26,
		"speed_label": "MEDIUM",
		"text": "欢迎使用标贝科技开放平台。",
		"volume": 45,
		"volume_label": "SOFT",
		"words": [{
			"confidence": "0.40",
			"eos": "0.51",
			"sos": "0.06",
			"word": "欢迎"
		}, {
			"confidence": "0.80",
			"eos": "1.05",
			"sos": "0.51",
			"word": "使用"
		}, {
			"confidence": "0.40",
			"eos": "1.52",
			"sos": "1.05",
			"word": "标贝"
		}, {
			"confidence": "1.00",
			"eos": "1.95",
			"sos": "1.52",
			"word": "科技"
		}, {
			"confidence": "0.50",
			"eos": "2.40",
			"sos": "1.95",
			"word": "开放"
		}, {
			"confidence": "1.00",
			"eos": "3.12",
			"sos": "2.40",
			"word": "平台"
		}]
	},
	"message": "Success",
	"trace_id": "1652148314904999"
}

错误码

错误码 描述 解决方案
90000 文本数据
90001 检测到音频 正常
90002 未检测到音频,超过最大开始静音时长 请检查音频有效长度,或者调整最大开始静音时长参数
30001 HTTP请求参数错误 服务器内部错误,提交traceid,标贝后台进行排查。
30002 服务内部错误
30003 识别结果解析出错
30004 应用包名未知
30005 语音质量问题
30006 输入语音过长
30007 连接识别引擎失败
30008 会话id不存在
30009 Rpc调用非法
30010 redis rpop操作返回空
30011 redis rpop值不合法
30012 rpc调用识别引擎失败
30013 Redis rpop操作失败
30014 redis lpush操作失败
30015 单个语音分片过长
30016 回调url失败
40001 json解析失败 将请求序列化为json结构
40002 json字段不全 检查对应的参数是否正确
40003 版本错误
40004 json字段值类型错误
40005 参数错误
40006 idx超时 相邻idx间隔超过设置超时值(默认60s)
40007 idx顺序错误 idx乱序
40008 token校验失败
40009 token处于未激活状态 检查相应的client_id
40010 token已过期 重新获取token
40011 使用量已超过购买量 检查相应的client_id
40012 qps错误 增大qps
50001 处理超时
50002 内部 rpc 调用失败
50003 服务端繁忙
50004 其他内部错误
50005 vad检测过程异常报错 服务内部错误,提交traceid,标贝后台进行排查。