腾讯、百度、讯飞 语音识别

一、腾讯语音识别—一句话语音识别

1、账号申请

(1)搜索腾讯云官网

https://cloud.tencent.com/?fromSource=gwzcw.2212127.2212127.2212127&utm_medium=cpd&utm_id=gwzcw.2212127.2212127.2212127

(2)打开语音识别
在这里插入图片描述
腾讯云语音识别(Automatic Speech Recognition,ASR)为开发者提供语音转文字服务的最佳体验。经公司内部微信、QQ 、腾讯视频、王者荣耀等大体量业务落地验证,日服务亿级用户,性能稳定。腾讯语音识别技术开放实时语音识别、一句话识别和录音文件识别服务,满足不同类型开发者需求。除公有云接入外,腾讯语音识别技术也支持私有化部署。

小编:微信和王者荣耀的语音识别还是很强大的,其它的虽然并不清楚

(3)先来看下语音识别的接口文档

![1552371855329](C:\Users\lichuanran\AppData\Roaming\Typora\typora-user-images\15523718在这里插入图片描述

1>通过post请求来发送语音数据,从而得到结果

2>请求大小不能超过600k

​ 超过600k不会报错,但是识别时长会有点长

3>音频时长不可超过60s

4>支持音频格式 wav 、mp3

5>比特率128k或者256k

​ 256k比128k保存的数据更具体,对应的文件大小也更大

6>音频流的采样率8000或者16000 (单位时间内有多少数据来描述采样点)

​ 分别对应接口文档的EngSerViceType(引擎类型)的8K或者16k

​ 16000的采样率识别准确率比8000采样率更高

7>请求频率25次/s

​ 使用多线程进行死循环发现,达到25次/s会把请求延迟一点,并不会报错

8>单声道

注:下文小编会介绍怎么把 任意一个音频文件 转换为一个 符合腾讯云识别标准 的音频文件

2、java 实现语音识别

(1)下载SDK

https://cloud.tencent.com/document/product/441/19814

1>打开SDK会发现有两个类 SASRsdk 和 SASRtest 一个wav格式的语音文件

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import java.net.URLEncoder;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.BufferedReader;
import java.net.URL;
import java.net.HttpURLConnection;
import java.io.InputStreamReader;

public class SASRsdk {
    private static String SecretId, SecretKey, EngSerViceType, SourceType, VoiceFormat, fileURI;
public static String formSignstr(String serverUrl, Map<String, String> mapReq) {
    StringBuilder strBuilder = new StringBuilder(serverUrl);

    // to make that all the parameters are sorted by ASC order
    TreeMap<String, String> sortedMap = new TreeMap(mapReq);

    for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
        strBuilder.append(entry.getKey());
        strBuilder.append('=');
        strBuilder.append(entry.getValue());
        strBuilder.append('&');
    }

    if (mapReq.size() > 0) {
        strBuilder.setLength(strBuilder.length() - 1);
    }

    //System.out.println("sign str: " + strBuilder);

    return strBuilder.toString();
}

public static String formPostbody(Map<String, String> mapReq) {
    StringBuilder stringBuilder = new StringBuilder();
    // to make that all the parameters are sorted by ASC order
    TreeMap<String, String> sortedMap = new TreeMap(mapReq);
    for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
        try {
            stringBuilder.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
            stringBuilder.append('=');
            stringBuilder.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
            stringBuilder.append('&');
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
    return stringBuilder.toString();
}

public static String base64_hmac_sha1(String value, String keyStr) {
    String encoded = "";
    String type = "HmacSHA1";
    try {
        byte[] key = (keyStr).getBytes("UTF-8");
        byte[] Sequence = (value).getBytes("UTF-8");

        Mac HMAC = Mac.getInstance(type);
        SecretKeySpec secretKey = new SecretKeySpec(key, type);

        HMAC.init(secretKey);
        byte[] Hash = HMAC.doFinal(Sequence);

        encoded = Base64.getEncoder().encodeToString(Hash);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return encoded;
}

/* - 获得unix时间戳 */
  public static String toUNIXEpoch() {
  long unixTime = System.currentTimeMillis() / 1000L;
  return unixTime + "";
  }

/* - 生成随机nonce */
  public static String toUNIXNonce() {
  long unixTime = System.currentTimeMillis() / 1000L;
  String str = unixTime + "";
  String nonce = str.substring(0, 4);
  return nonce;
  }

public static String createSign(String signStr, String secretKey) {
    return base64_hmac_sha1(signStr, secretKey);
}

private static String getRandomString(int length) {
    //定义一个字符串(A-Z,a-z,0-9)即62位;
    String str = "zxcvbnmlkjhgfdsaqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
    //由Random生成随机数
    Random random = new Random();
    StringBuffer sb = new StringBuffer();
    //长度为几就循环几次
    for (int i = 0; i < length; ++i) {
        //产生0-61的数字
        int number = random.nextInt(62);
        //将产生的数字通过length次承载到sb中
        sb.append(str.charAt(number));
    }
    //将承载的字符转换成字符串
    return sb.toString();
}

public static int setConfig(
        String SecretId,
        String SecretKey,
        String EngSerViceType,
        String SourceType,
        String VoiceFormat,
        String fileURI
) {
    if (SecretId.length() <= 0) {
        System.out.println("SecretId can not be empty!");
        return -1;
    }
    if (SecretKey.length() <= 0) {
        System.out.println("SecretKey can not be empty!");
        return -1;
    }
    if (EngSerViceType.length() <= 0 || (EngSerViceType.compareTo("8k") != 0 && EngSerViceType.compareTo("16k") != 0)) {
        System.out.println("EngSerViceTyp is not valied !");
        return -1;
    }
    if (SourceType.length() <= 0 || (SourceType.compareTo("0") != 0 && SourceType.compareTo("1") != 0)) {
        System.out.println("SourceType is not valied !");
        return -1;
    }
    if (VoiceFormat.length() <= 0 || (VoiceFormat.compareTo("mp3") != 0 && VoiceFormat.compareTo("wav") != 0)) {
        System.out.println("VoiceFormat is not valied !");
        return -1;
    }
    if (fileURI.length() <= 0) {
        System.out.println("fileURI can not be empty!");
        return -1;
    }
    SASRsdk.SecretId = SecretId;
    SASRsdk.SecretKey = SecretKey;
    SASRsdk.EngSerViceType = EngSerViceType;
    SASRsdk.SourceType = SourceType;
    SASRsdk.VoiceFormat = VoiceFormat;
    SASRsdk.fileURI = fileURI;
    return 0;
}

public static int sendVoice() {
    Map<String, String> reqMap = new TreeMap();
    reqMap.put("Action", "SentenceRecognition");
    reqMap.put("SecretId", SecretId);
    reqMap.put("Timestamp", toUNIXEpoch());
    reqMap.put("Nonce", toUNIXNonce());
    reqMap.put("Version", "2018-05-22");
    reqMap.put("ProjectId", "0");
    reqMap.put("SubServiceType", "2");
    reqMap.put("EngSerViceType", EngSerViceType);
    reqMap.put("SourceType", SourceType);
    if (SourceType.compareTo("0") == 0) {
        try {
            String Url = URLEncoder.encode(fileURI, "UTF-8");
            reqMap.put("Url", Url);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    } else if (SourceType.compareTo("1") == 0) {
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(new File(fileURI));
            int datalen = fileInputStream.available();
            byte[] dataPacket = new byte[datalen];
            int n = fileInputStream.read(dataPacket);
            //System.out.println("n :"+n);
            String Data = Base64.getEncoder().encodeToString(dataPacket);
            String DataLen = datalen + "";
            // System.out.println("data len: "+DataLen);
            reqMap.put("Data", Data);
            reqMap.put("DataLen", DataLen);

} catch (Exception e) {
            e.printStackTrace();
        }
    } else {
        return -3;
    }
    reqMap.put("VoiceFormat", VoiceFormat);
    String UsrAudioKey = getRandomString(16);
    reqMap.put("UsrAudioKey", UsrAudioKey);
    String _url = "POSTaai.tencentcloudapi.com/?";
    String signstr = formSignstr(_url, reqMap);
    // System.out.println("signstr: " + signstr);
    String signing = createSign(signstr, SecretKey);
    // System.out.println("签名: " + signing);
    String tmppostdata = formPostbody(reqMap);
    String sign = "";
    try {
        sign = URLEncoder.encode(signing, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    StringBuilder postdata = new StringBuilder(tmppostdata);
    postdata.append("Signature=");
    postdata.append(sign);
    String post = postdata.toString();
    //System.out.println("post : "+post);
    String serverUrl = "https://aai.tencentcloudapi.com";

    HttpURLConnection con = null;
    StringBuilder sbResult = new StringBuilder();
    try {
        URL url = new URL(serverUrl);
        con = (HttpURLConnection) url.openConnection();
        con.setRequestMethod("POST");
        con.setDoOutput(true);
        con.setDoInput(true);
        con.setUseCaches(false);
        con.setRequestProperty("Host", "aai.tencentcloudapi.com");
        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        con.setRequestProperty("Charset", "utf-8");

        // 往服务器写入数据
        OutputStream out = con.getOutputStream();
        out.write(post.getBytes());
        out.flush();

        // 接收服务器返回的数据
        InputStream in = con.getInputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        String line;// 每一行的数据
        while ((line = br.readLine()) != null) {
            sbResult.append(line);
        }
        System.out.println(sbResult.toString());
    } catch (Exception e) {
        throw new RuntimeException(e);
    } finally {
        if (con != null) {
            con.disconnect();
            con = null;
        }
    }

    return 0;
}

}

(2)进行初步测试 --wav 采样率16000 比特率256kbps 远程文件

public static void main(String[] args) {

    //用户需修改为自己的SecretId,SecretKey
    String SecretId = "AKID31NbfXbpBLJ4kGJrytc9UfgVAlGltJJ8";
    String SecretKey = "kKm26uXCgLtGRWVJvKtGU0LYdWCgOvGP";


    // 识别引擎 8k or 16k
    String EngSerViceType = "16k";

    // 语音数据来源 0:语音url or 1:语音数据bodydata(data数据大小要小于800k)
    String SourceType = "0";

    //音频格式 wav,mp3
    String VoiceFormat = "wav";

    // 语音数据地址
    String fileURI="http://liqiansunvoice-1255628450.cosgz.myqcloud.com/30s.wav";

    //调用setConfig函数设置相关参数
    int res = SASRsdk.setConfig(SecretId, SecretKey, EngSerViceType, SourceType, VoiceFormat, fileURI);
    if (res < 0) {
        return;
    }

    //调用sendVoice函数获得音频识别结果
    SASRsdk.sendVoice();

}

1>测试结果

{"Response":{"Result":"雨是最寻常的一下就是三两天。可别恼看像牛毛像花针像细丝密密地斜织着。人家屋顶上全笼着一层烟雾树叶却绿的发亮,小草也青得逼你的眼,傍晚的时候上灯了。一点点黄晕的光。烘托出一片安静而和平的夜在乡下在小路上石桥边默默撑着伞的人。","RequestId":"b08e7b64-3b0b-4658-a478-a4650f2a7c48"}}

2>根据语音数据地址,下载文件

​ 文件的比特率为256kbps 因此 识别引擎(EngSerViceType)16k 、语音数据来源 (SourceType)0

3>SecretId和SecretKey 通过腾讯云可以新建

![1552377914206](C:\Users\li在这里插入图片描述

(3)继续测试 --wav 采样率8000 比特率128kbps 本地文件

public static void main(String[] args) {

    //用户需修改为自己的SecretId,SecretKey
    String SecretId = "AKID31NbfXbpBLJ4kGJrytc9UfgVAlGltJJ8";
    String SecretKey = "kKm26uXCgLtGRWVJvKtGU0LYdWCgOvGP";


    // 识别引擎 8k or 16k
    String EngSerViceType = "8k";

    // 语音数据来源 0:语音url or 1:语音数据bodydata(data数据大小要小于800k)
    String SourceType = "1";

    //音频格式 wav,mp3
    String VoiceFormat = "wav";

    // 语音数据地址
    String fileURI = "D:\\test.wav";
    //调用setConfig函数设置相关参数
    int res = SASRsdk.setConfig(SecretId, SecretKey, EngSerViceType, SourceType, VoiceFormat, fileURI);
    if (res < 0) {
        return;
    }

    //调用sendVoice函数获得音频识别结果
    SASRsdk.sendVoice();
}

1>测试结果

{"Response":{"Result":"张先生,您好。那个为了规范保险从业人员的销售行为。也为了更好的保护您的合法权益。根据保监会规定我们将。哎。您能不能说话客气点。你会不会好好说话,会不会会不会会不会好像说。","RequestId":"2b93994a-7c01-4237-9c3a-07dfa14eb34d"}}

2>使用的语音文件为SDK自带

​ 文件比特率为128kbps、采样率为8000 即 识别引擎(EngSerViceType)8k 、本地文件 SourceType=1

3>如果不按上面所示进行测试,得到的结果会识别不准确哦

(4)使用自己的文件进行测试(MP3)

1>使用16k识别引擎

{"Response":{"Result":"�","RequestId":"6c18c00a-1a6e-4da4-b3a7-701088c864c8"}}

2>使用8k识别引擎

{"Response":{"Result":"嗯。嗯嗯。呃,嗯。呃。嗯嗯。嗯嗯,呃。呃。嗯。嗯嗯。嗯嗯,呃。呃。嗯。嗯。嗯。嗯。嗯。","RequestId":"7d03d1fc-a586-470a-bdfb-81b4ccbf8fc5"}}

3>我们发现,识别结果有严重的误差

​ 原因:语音文件为320kbps,采样率远远大于41000。导致识别失败,或者结果全是语气词

4>将自己的语音文件转换成 比特率为128kbps,采样率为16000,单声道 (下面会介绍用java怎么转换)

​ 使用16k识别引擎测试—>识别成功

5>将自己的语音文件转换成 比特率为128kbps,采样率为8000 ,单声道

​ 使用8k识别引擎测试—>识别成功

​ 缺点:较16k识别引擎。准确率更低,文件的大小却无差别

3、文件转换

(1)、把 任意格式的语音文件 转换为 符合腾讯接口的格式文件

1>导入jar包

<!-- https://mvnrepository.com/artifact/com.github.dadiyang/jave -->
<dependency>
    <groupId>com.github.dadiyang</groupId>
    <artifactId>jave</artifactId>
    <version>1.0.2</version>
</dependency>

2> JAVE 简介 和 作用

​ J 即java ,A 即 audio , V 即 voice ,E 即 encoding。它一个工具,一个用纯java语言写的跨平台的,一通用的Ascii码图形化的文本编辑器。其功能和微软的记事本有些类似,主要是一个字处理工具,但提供图形化输入;操作像是微软的画图软件,因为该软件主要是通过鼠标进行文本的编辑。进一步看了一下该软件的功能,操作和功能并不复杂。该软件主要的卖点是授权开发源代码方式,其用百分之百java语言开发的跨平台特点。

​ 可以用来给音频,视频 重新编码 , 视频剪辑 , 视频格式的转换等一系列强大的功能。

3>

try {
            //File source = new File("file path source");
            //File target = new File("file path target");

            // Audio Attributes/音频编码属性

            AudioAttributes audio = new AudioAttributes();
            /* * 它设置将用于音频流转码的编解码器的名称。您必须从当前Encoder实例的getAudioEncoders()方法返回的列表中选择一个值。否则, * 您可以传递AudioAttributes.DIRECT_STREAM_COPY特殊值,该值需要源文件中原始音频流的副本。 */
            audio.setCodec("libmp3lame");
            /* * 它设置新重新编码的音频流的比特率值。如果未设置比特率值,编码器将选择默认值。该值应以每秒位数表示。例如,如果你想要128 kb / * s比特率,你应该调用setBitRate(new Integer(128000))。 */
            audio.setBitRate(128000);
            /* 它设置将在重新编码的音频流中使用的音频通道的数量(1 =单声道,2 =立体声)。如果未设置通道值,编码器将选择默认值。 */
            audio.setChannels(1);
            /* * 它设置新重新编码的音频流的采样率。如果未设置采样率值,编码器将选择默认值。该值应以赫兹表示。例如,如果您想要类似CD的44100 * Hz采样率,则应调用setSamplingRate(new Integer(44100))。 */
            //audio.setSamplingRate(44100);
            audio.setSamplingRate(16000);
            /* 可以调用此方法来改变音频流的音量。值256表示没有音量变化。因此,小于256的值是音量减小,而大于256的值将增加音频流的音量。 */
            audio.setVolume(new Integer(256));

            // Encoding attributes/编码属性
            EncodingAttributes attrs = new EncodingAttributes();
            /* * 它设置将用于新编码文件的流容器的格式。给定参数表示格式名称。 * 编码格式名称有效且仅在它出现在正在使用的Encoder实例的getSupportedEncodingFormats()方法返回的列表中时才受支持。 */
            attrs.setFormat("mp3");
            /* 它设置音频编码属性。如果从未调用过新的EncodingAttributes实例,或者给定参数为null,则编码文件中不会包含任何音频流 */
            attrs.setAudioAttributes(audio);
            /* * 它为转码操作设置偏移量。源文件将从其开始的偏移秒开始重新编码。例如,如果您想剪切源文件的前五秒, * 则应在传递给编码器的EncodingAttributes对象上调用setOffset(5)。 */
            // attrs.setOffset(5F);
            /* * 它设置转码操作的持续时间。只有源的持续时间秒才会在目标文件中重新编码。例如,如果您想从源中提取和转码30秒的一部分, * 则应在传递给编码器的EncodingAttributes对象上调用setDuration(30) */
            // attrs.setDuration(30F);

            // Encode/编码
            Encoder encoder = new Encoder();
            encoder.encode(source, target, attrs);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

二、百度语音识别

1、应用创建

(1)、搜索 百度AI开放平台

(2)、创建应用
在这里插入图片描述
(3)、查看技术文档
在这里插入图片描述

(4)、选择java SDK
在这里插入图片描述

(5)、SDK下载 选择java SDK进行下载
在这里插入图片描述

2、使用百度Api进行开发

(1)、创建maven工程

(2)、加入依赖

<dependency>
    <groupId>com.baidu.aip</groupId>
    <artifactId>java-sdk</artifactId>
    <version>4.1.1</version>
</dependency>

(3)、创建wav语音格式的音频

​ 目前格式仅仅支持pcm,wav或amr,填写mp3即会有此错误(mp3转pcm会出现音频质量过差的错误)

​ 如何获取以上某种格式的音频,用来测试呢

​ 手机下载录音专家,录音后会有形成wav格式的音频,在 手机文件存储 (一般是过1分钟左右,会出现在音频文件中) 中找到生成的wav格式文件,发送到电脑上

​ 音频文件不能过大,小于4M,音频长度要小于60s

(4)、

**

public class Sample {
   //设置APPID/AK/SK
    public static final String APP_ID = "15598353";
   public static final String API_KEY = **"IwqPzfgVlGLKMbUIw3TVlF77";
   public static final String SECRET_KEY = "xxxxxxxxxxxxxxx";

   public static void main(String[] args) {
        // 初始化一个AipSpeech 
    AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);

        // 可选:设置网络连接参数 
    client.setConnectionTimeoutInMillis(2000);
   client.setSocketTimeoutInMillis(60000);

   // 可选:设置代理服务器地址, http和socket二选一,或者均不设置 //client.setHttpProxy("proxy_host", proxy_port); // 设置http代理 //client.setSocketProxy("proxy_host", proxy_port); // 设置socket代理 
    // 可选:设置log4j日志输出格式,若不设置,则使用默认配置 
    //也可以直接通过jvm启动参数设置此环境变量 
    System.setProperty("aip.log4j.conf", "log4j.properties");

    new Sample().asr(client);

     // 调用接口 
    //JSONObject res = client.asr("test.pcm", "pcm", 16000, null); //System.out.println(res.toString(2)); 
}

    public void asr(AipSpeech client){
        // 对本地语音文件进行识别 
    	String path = "D:\\测试文件\\970f1c080e0c78476d337a80ba5114f9.wav";
       JSONObject asrRes = client.asr(path, **"wav"**, 16000, **null**);
       System.out.println(asrRes);

       // 对语音二进制数据进行识别 
    	byte[] data = new byte[0];     
    	//readFileByBytes仅为获取二进制数据示例 
    	try {
            data = Util.readFileByBytes(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        JSONObject asrRes2 = client.asr(data, "wav", 16000, null);
        System.out.println(asrRes.getInt("err_no")==0);
        System.out.println(asrRes2.get("result"));
    }
}

​ 注:APP_ID API_KEY SECRET_KEY 在百度AI开放平台 注册服务后,会有这3个的值
在这里插入图片描述

(5)、测试结果:
在这里插入图片描述

三、讯飞语音识别

1、应用创建

(1)、在这里插入图片描述

1>我们可以看到有语音听写、语音转写

​ 语音听写:一分钟以内 人机对话、输入法、语音搜索

​ 语音转写:五小时以内 日常对话、演讲

2>支持格式

​ 语音听写:采样率为8kHz或16kHz,位长16bit,单声道的wav、pcm

​ 语音转写:单声道、多声道的wav、flac、opus、m4a、mp3

3>语音听写创建

​ 1、在这里插入图片描述

​ 2、在这里插入图片描述

​ 3、
在这里插入图片描述

​ 4、
在这里插入图片描述
​ 5、

在这里插入图片描述

4>语音转写创建

​ 1、在这里插入图片描述

​ 2、
在这里插入图片描述

​ 3、语音转写可以选择java哦,我这个是webapi的

总结:

​ 本文介绍了腾讯、百度、讯飞的语音识别。百度和讯飞使用简单,腾讯相对使用就比较复杂点了。

腾讯一句话识别是最突出的功能,支持识别mp3格式的语音文件。腾讯的语音识别在王者荣耀、微信

都有大量的使用,并发还是可以的,虽然给客户1s 25次的请求比较少,但是出问题的可能性比较小。

并且腾讯语音识别时间是非常短的,本地测试2s多一点,真正使用也就是1s左右。讯飞的语音转写时

间就比较长了,在8s左右一个请求。讯飞的语音听写时间在3s左右,百度的语音听写类似。