百度语音识别服务 —— 语音识别 REST API 开发笔记

在以前的项目中用到了百度语音识别服务,在这里做一个笔记。这里还是要和大家强调一下,最好的学习资料就是官网网站。我这里只是一个笔记,一方面整理了思路,另一方面方便以后我再次用到的时候可以快速回忆起来。

百度语音识别服务是什么?

百度语音识别服务能将语音文件(指定格式,不是所有格式都可以)识别成文本。语音识别我们都接触过,手机输入法里就有语音识别服务。

什么是百度语音识别的 REST API?

按照官网的说法

行业率先推出语音识别REST API,采用HTTP方式请求,可适用于任何平台的语音识别,给你最大的自由度!

简单说来,就是无须在开发者的项目中写入代码,或引入 jar 包 。 REST API 就是将音频文件转换成某种特定格式,通过 http 请求发送给百度语音识别的服务器,由百度语音识别的服务器进行语音识别,最后返回识别出的文本。

在我看来,她好在可以很方便的调用,我们自己不用去维护语音识别部分的代码,接入也十分简单,关键是她是免费的

使用的方式简单说来是

1、根据百度语音识别官方网站提供的

App ID 和 API Key 获取 accessToken。

2、根据上一步的 accessToken 连同其它请求参数一起向百度语音识别网关发出请求,获得识别的文本。

是不是觉得和微信公众平台的开发有点像?的确是这样的。微信公众平台的开发的确也是先获取 token,再通过 token 去请求其它数据。

集成步骤

本集成步骤参考了《Baidu_Voice_REST_API_Manual》。建议各位朋友先下载该资料学习。

第 1 步:注册成为百度开发者,创建应用,得到 API KeySecret Key

这一步非常简单,在官网上也有操作提示,这里就不多介绍了。

第 2 步:开通语音识别服务

开通语音识别服务的步骤也非常简单,大家可以自行操作或者参考官方文档。这里要注意:“ 语音识别” 服务初次开通成功后即可获得 50000 次/ 日 的在线调用次数配额。

如果我们每天调用的次数大于 50000 次,可以向百度申请提高次数,据说也是免费的,大赞。

以下的步骤就很关键了,因为我们要开始写代码了。

第 3 步:获取 Access Token

简而言之,就是向百度 OAuth2.0 授权服务的网关发出请求,将返回的数据(一般是字符串)进行解析,解析出我们想要的 Access Token。下面的图片截取自官网文档,写得非常详细了。

说明:其实就是使用 API KeySecret Key 和一个固定值的参数向百度 OAuth2.0 授权服务的网关发出 POST 请求,如果请求成功,则解析返回的字符串,从中解析出 access token 待用。

为了方便说明问题,下面代码没有应用良好格式,仅仅只是测试的方法,不建议直接应用于生产环境。

本例使用 HttpClient 框架发送 post 请求,HttpClient 的 Gradle 依赖为:

compile "org.apache.httpcomponents:httpclient:4.5.2"

示例代码:

/**
 * 获取 token,推荐用 POST 方法
 */
@Test
public void test01(){
    try {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost("https://openapi.baidu.com/oauth/2.0/token");
        List<NameValuePair> nvps = new ArrayList<>();
        nvps.add(new BasicNameValuePair("grant_type","client_credentials"));
        nvps.add(new BasicNameValuePair("client_id",apiKey));
        nvps.add(new BasicNameValuePair("client_secret",secretKey));
        httpPost.setEntity(new UrlEncodedFormEntity(nvps));
        ResponseHandler<String> responseHandler = new ResponseHandler(){

            @Override
            public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
                int status = response.getStatusLine().getStatusCode();
                if (status >= 200 && status < 300) {
                    HttpEntity entity = response.getEntity();
                    try {
                        return entity != null ? EntityUtils.toString(entity) : null;
                    } catch (ParseException ex) {
                        throw new ClientProtocolException(ex);
                    }
                } else {
                    throw new ClientProtocolException("Unexpected response status: " + status);
                }
            }
        };
        String responseBody = httpClient.execute(httpPost,responseHandler);
        System.out.println(responseBody);
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (ClientProtocolException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

返回数据:

{"access_token":"24.463f2a9f7ce6721fe4d15568f812c086.2592000.1469627568.282335-7038695","session_key":"9mzdDxLM148MA1QmcNNrxGfLYBU9VokNbUY\/8WsJ1r4rUSev1bjP9GTKP6L6SVDnjx4BZxE5ZpJoqztA2K7O0MM9l0Z4","scope":"public audio_voice_assistant_get wise_adapt lebo_resource_base lightservice_public hetu_basic lightcms_map_poi kaidian_kaidian","refresh_token":"25.f77abdb8f638404747dd969615c7b557.315360000.1782395568.282335-7038695","session_secret":"3efb3872a362beacab28879eed85497b","expires_in":2592000}

格式化以后:

我们须要从中解析出 access_token , json 字符串解析的框架有很多 fastjson、Jackson、json-lib、gson,这里就不多做介绍了。

另外还是要说明一下,因为 access_token 的有效时间是 2592000 (秒),即 30 × 24 × 60 × 60 (秒), 30 天,所以没有必要每一次请求都去获取 access_token ,建议把 access_token 放在应用的缓存里,如果失效了,再去获取,可以提高应用的效率,这一点和微信公众平台开发是一样的。

第 4 步:根据 Access Token 调用语音识别接口(隐式发送)

说明:我们这里采用的是隐式发送,即不发送真实的音频文件,而是发送音频文件转换而成的字节数组。这里一定要看官网说明,严格调用,才会识别出理想的结果。难点是音频格式的转换。

我是看了官网说明文档和示例代码,经过反复调试才得以调用成功的。鉴于这里篇幅的限制,请大家先看官网说明文档,我这里就不复制了。

这里为了方便说明,先上示例代码,同样地该代码只是为了便于说明问题,不建议在生产环境中直接使用:

/**
 * 识别英文
 */
@Test
public void test02(){
    recognize("voice_en.wav","en");
}

上面的测试方法调用了语音识别的方法,该方法传递两个参数,一个是文件的全路径,另一个是中文或者英文的参数。

下面

/**
  * 请求语音识别的时候使用
  */
 private static final String speech_recognition_url = "http://vop.baidu.com/server_api";
private void recognize(String wavName,String language){
    File wavFile = new File(wavName);
    HttpPost httpPost = null;
    CloseableHttpResponse response = null;
    CloseableHttpClient httpClient = HttpClients.createDefault();
    httpPost = new HttpPost(speech_recognition_url);
    SpeechRecognitionRequestEntity requestEntity = new SpeechRecognitionRequestEntity();
    // 语音压缩的格式:请按照官网文档填写 pcm(不压缩)、wav、opus、speex、amr、x-flac 之一,不区分大小写
    requestEntity.setFormat("wav");
    // 声道数,仅支持单声道,请填写 1
    requestEntity.setChannel("1");
    // 采样率,支持 8000 或者 16000 (这个类型是 int ,不能设置为 String 类型,关于采样率如何转换,请见下文)
    requestEntity.setRate(16000);
    // todo 这里应判断 AccessToken 是否过期,处理异常,如果过期了,应该重新获取 accessToken
    requestEntity.setToken("24.463f2a9f7ce6721fe4d15568f812c086.2592000.1469627568.282335-7038695");
    // Cuid 貌似可以随意填写
    requestEntity.setCuid("goodluck");

    requestEntity.setLen(wavFile.length());
    // 官网说: speech 要传递真实的语音数据,需要进行 base64 编码
    // 重点关注:请见后面封装的方法,就是把一个文件转换成为指定格式的字节数组
    requestEntity.setSpeech(handlerWavFile(wavFile));
    // 语种选择,中文=zh、粤语=ct、英文=en,不区分大小写,默认中文
    requestEntity.setLan(language);
    // 关键点 1 :将请求参数转换为 json 格式
    String requestEntityJson = JSON.toJSONString(requestEntity);
    // 关键点 2 :封装 StringEntity ,为解决中文乱码问题,应该设置编码
    StringEntity entity = new StringEntity(requestEntityJson.toString(), "UTF-8");
    entity.setContentEncoding("UTF-8");
    // 关键点 3 :设置 StringEntity 的 ContentType
    entity.setContentType("application/json");
    httpPost.setEntity(entity);
    ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
        @Override
        public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
            String resData = null;
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode >= 200 && statusCode < 300) {
                HttpEntity httpEntity = response.getEntity();
                resData = EntityUtils.toString(httpEntity,"utf-8");
                EntityUtils.consume(httpEntity);
            }
            return resData;
        }
    };
    try {
        String responseStr = httpClient.execute(httpPost,responseHandler);
        System.out.println(responseStr);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

SpeechRecognitionRequestEntity 类(省略了 get 和 set 方法):

public class SpeechRecognitionRequestEntity {

    // 语音压缩的格式
    private String format;

    /**
     * 注意,采样率的数据类型一定是 int,不能是 String
     */
    // 采样率,支持 8000 或者 16000,在我们的项目中,写 16000
    private int rate;
    // 声道数,仅支持单声道,请填写 1
    private String channel;
    // 开发者身份验证密钥
    private String token;
    // 用户 ID,推荐使用设备 mac 地址 手机 IMEI 等设备唯一性参数
    // todo 貌似可以随意填写,唯一即可
    private String cuid;

    /**
     * 注意:这里填写的是原始语音的长度,不是使用 base64 编码的语音长度
     */
    // 原始语音长度,单位字节
    private long len;
    // 真实的语音数据,需要进行 base64 编码
    private String speech;
    // 语种选择,中文=zh、粤语=ct、英文=en,不区分大小写,默认中文
    private String lan;
}

这部分代码摘抄自官网示例代码:

private byte[] loadFile(File file) throws IOException {
    InputStream is = new FileInputStream(file);
    long length = file.length();
    byte[] bytes = new byte[(int) length];
    int offset = 0;
    int numRead = 0;
    while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
        offset += numRead;
    }
    if (offset < bytes.length) {
        is.close();
        throw new IOException("Could not completely read file " + file.getName());
    }
    is.close();
    return bytes;
}

返回结果:

{"corpus_no":"6300874524819907792","err_msg":"success.","err_no":0,"result":["one day in the cage club got bad news, ","one day in the case club got bad news, ","one day in that case club got bad news, ","one day in the cage club got the bad news, ","one day in the case club got the bad news, "],"sn":"843115237281467036671"}

格式化以后:

以上介绍了代码如何编写。但是在开发中,我遇到了一个难题,要将音频文件转换成百度语音识别能够识别的格式。请看官方文档说明。

于是,为了测试,我使用格式工厂软件进行格式转化。以下是格式转换的参数。

很高兴的是,经过格式工厂软件转换以后的音频文件能够被百度语音识别 REST 服务识别,识别度还不错,这是令人兴奋的。

但是,我又遇到了另一个问题,在服务器上总不能每个从客户端传来的音频文件都用格式工厂转换吧。于是,我找到了 Linux 平台上一款很好用的软件 sox。使用 sox 命令进行格式转换的命令格式:

sox 原始文件名全路径 -r 16000 -c1 生成的文件名全路径

接下来,我又继续查找资料,在 Linux 上调用 Linux 平台上的服务可以使用Java 中的 Runtime 和 Process 类运行外部程序。

参考代码:

String[] cmdStrings = new String[] { "/usr/bin/sox", tempWavFileName, "-r", "16000", "-c1", soundFileName_16000 };
Process psProcess = Runtime.getRuntime().exec(cmdStrings);
psProcess.waitFor();

到这里,语音识别开发的难点都攻克了。现在总结下来,真的是收获了不少。在这里先做个记录,有些知识点的掌握我还不是很透彻,后续还要再完善一下。

时间: 2024-08-10 11:17:41

百度语音识别服务 —— 语音识别 REST API 开发笔记的相关文章

【转】Android开发笔记(序)写在前面的目录

原文:http://blog.csdn.net/aqi00/article/details/50012511 知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面希望通过分享自己的经验教训,与网友互相切磋,从而去芜存菁进一步提升自己的水平.因此博主就想,入门的东西咱就不写了,人不能老停留在入门上:其次是想拾缺补漏,写写虽然小众却又用得着的东西:另外就是想以实用为主,不求大而全,但求小而精:还有就是有的知识点是java的,只是Android开发也会经常遇上,所以蛮记下来.个人的经

搜狗语音云开发入门(二)——使用离线语音识别服务

1 简介 之前在<搜狗语音云开发入门--移动端轻松添加高大上的语音识别>中介绍了使用搜狗语音云为客户端程序添加在线语音识别服务.在线语音服务需要联网使用,但是你不能指望用户拥有完美的环境,事实上大多数情况下用户的外围环境都会有所限制.有的时候没有Wi-Fi.没有流量,还想使用语音识别,如果你给用户一个提示"您没开流量..."只能说你的程序弱爆了.有条件情况下给用户提供完美的服务,没有条件创造条件服务质量依然完美,并且清风徐来了无痕迹,这才是完美应用的体现.你不是用户的亲戚朋

[Openwrt 项目开发笔记]:Samba服务&amp;vsFTP服务(四)

在上一节中,我们讲述了如何在路由器上挂载U盘,以及如何通过改造U盘提升路由器的存储和数据处理能力.通过增加USB挂载,为我们后续进行智能家居构建提供了保障. 在本节中,我将简单介绍如何在路由器上建立网络文件共享服务. 一.为何要建立文件共享服务: 在智能家居网络中,一个很重要的需求就是安全.当一个智能家居方案部署之后,用户往往最先考虑的也是安全问题.那么怎样进行安全管理呢?一种常用的解决方法是实时的监控传感器以及家电的状态,一旦发生异常,通过邮件.短信甚至打电话的方式通知用户.当用户接收到异常之

深入浅出百度地图API开发系列(1):前言

百度地图API目前在地图API领域越来越受到众多开发者的关注,许多应用都使用到了百度地图API服务,包括博主me,我自己使用做的是Javascript API,根据经验,我想整理出一份系列教程,如果能给想使用百度地图API的开发者带来一点帮助的话,那就太好了,也希望大家能够互相交流,共同学习. 写在前面的话:如果各位真的想深入了解地图的JSAPI,首先一些基础的GIS概念必不可少,GIS就是地理信息系统,可能很多程序猿没听过,简单地说,GIS就是通过计算机软件技术将地理信息进行可视化管理,地图就

实现Android语音识别服务接口 RecognitionService的方法

之前的一篇文章介绍过SpeechRecognizer类,该类可以作为对外的一个接口,并通过Intent传递一个ComponentName获取可支持语音识别的功能的服务,一般的用户手机中如果安装了语音识别的一些软件,就会拥有这样的能力,但是如果开发者希望自己通过某公司的sdk集成自己的语音识别服务,那么就需要实现RecognitionService这个类. 这个类是个抽象类,需要开发者完成其中的几个抽象方法.下面的代码注释中对每个方法进行了说明. <pre name="code"

android 基于百度地图api开发定位以及获取详细地址

一:百度地图开发必须要到百度开发平台android开发api下载相应的库,已经申请百度地图开发key. 二:新建项目baidumaplocation.设计main.xml文件这里注意的是MapView控件必须使用来自百度库封装好的com.baidu.mapapi.MapView .设计代码如下: Xml代码   <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&q

API开发第一篇:关于session的APP服务端API开发

第一次做app的API开发,遇到的第一个问题就是:我的sessionid哪儿去了? 实现的一个功能是:短信验证功能,大体流程图如下: 问题的产生就发生在提交验证的时候,客户端并未通过header头带过来sessionid.那么这个时候,服务端就不知道该从哪一个session会话中取出值来进行判断.所以问题的解决核心点就是这个sessionid哪儿去了?以前只做PC端的时候,从来不怎么关心这个问题,因为浏览器自己就帮我们把这些事情搞完了. 解决办法一: 首先声明这个错误并不是由于服务端的错误,服务

IOS开发笔记-百度地图(第三方库)

最近做了百度地图,在导入SDK后遇到了一些问题 编译错误: linker command failed with exit code 1 (use -v to see invocation) 想了很多办法,查了很多资料,最后终于解决. 可能原因: 1. 有重复的.m文件,或者未导入 解决办法:有重复的删除即可 在工作左边导航栏Target-->Build Phases-->compile Sources中,第三库库的所有.m文件都添加到里面 2.Valid Architectures 的值 在

[开发笔记]-使用bat命令来快速安装和卸载Service服务

一般我们在编写完Service服务程序后,都是通过cmd命令提示窗口来安装或卸载服务,但频繁的在cmd窗口中去"拼"文件的路径着实让人"不能忍".所以,我们需要一钟"更快捷"的方式来进行安装或者卸载操作. 直接上代码: 安装服务: @echo off c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\installutil.exe E:\abc\abc.exe net start 服务名称 echo --