微信小程序语音识别探索

微信小程序语音识别探索

背景:最近在做一个旅游相关的工程,其中有微信小程序模块,需求方希望我们在小程序加上语音识别景点的功能,可是小程序并没有支持这个功能,所以小程序的同学就扔给我这个安卓端的来做(笑)

参考资料:

1.http://www.jianshu.com/p/b092da81feb0 本文可以说是在这篇文章的基础上完成的,但一开始遇到了很大的困难(涉及到一个坑下文会提及),学过node.js的同学应该可以做到即插即用,但是本工程使用的是javaweb完成的,还是衷心感谢下作者,虽然不回我私信

2.https://github.com/kn007/silk-v3-decoder 将silk v3转化成wav使用的库,作者十分热心

为了帮助像我这样比较着急完成工程的同学,这次探索过程的苦水我就不倒了,简单粗暴上结果:

在使用微信web开发工具进行调试和真机上使用的时候,录音文件保存的格式是不一样的,本文以这个为分水岭进行介绍:

1.微信web开发者工具(0.18.182200)

总体处理流程(简述):

本地录音储存文件格式:webm类型的base64字符串

开头指明格式,随后是base64字符串

这里我的处理方法是:

1.忽略前面的类型说明,读入base64字符串

//将微信中的webm格式的base64字符提取出来
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
StringBuilder sb = new StringBuilder("");
String temp;
while((temp=reader.readLine())!=null)
{
    sb.append(temp);
}
reader.close();
temp = sb.toString();
int index = temp.indexOf("base64,");
temp = temp.substring(index+7);
System.out.println(temp.substring(0,3));
byte[] nfile = decodebase64(temp);
//以字节的方式写入新文件
FileOutputStream fos = new FileOutputStream(webm);
fos.write(nfile);
fos.close();

2.使用Base64.decodeBase64(code)进行转化,得到byte[]

public  byte[] decodebase64(String code)
    {
        return Base64.decodeBase64(code);
    }

3.写入新的文件,得到真正的webm文件

4.使用ffmpeg命令的帮助:

ffmpeg -i input.webm -ac 1 -ar 16000 wav

这里ar是采样率,ac是声道数,按照百度语音的推荐设置来的,还可以设成8000.实测效果一般

这里使用的是java的Runtime环境,实测对于ffmpeg命令应该写绝对路径,不然会失败

Runtime runtime = Runtime.getRuntime();
Process proce;
String cmd = "/usr/local/bin/ffmpeg -i "+webm+" -ac 1 -ar 16000 "+wav;
proce = runtime.exec(cmd);
proce.waitFor();
BufferedReader br = new BufferedReader(new InputStreamReader(proce.getInputStream()));  
StringBuffer msb = new StringBuffer();  
String line;  
while ((line = br.readLine()) != null) {  
     msb.append(line).append("\n");  
}  
String mresult = msb.toString();  
System.out.println("exec result = "+mresult);

5.使用百度语音识别client进行在线识别,并返回识别结果

// 初始化一个FaceClient
AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);
client.setConnectionTimeoutInMillis(2000);
client.setSocketTimeoutInMillis(60000);
 res = client.asr(wav, "wav", 16000, null);
System.out.println(res.toString(4));
response.setContentType("text/html; charset=UTF8");
response.getWriter().print("start handle");
JSONObject jb = new JSONObject(res.toString());
JSONArray result = jb.getJSONArray("result");
response.getWriter().print("get->"+result);

2.手机端语音保存格式:silk-v3

总体流程跟本地调试类似,只是文件格式不一样

手机保存的录音格式为silk v3,需要使用v3库https://github.com/kn007/silk-v3-decoder 转化

作者提供了一个脚本文件converter.shi进行转化,命令为:sh converter.sh inputName wav

为了适应百度语音识别的格式,我们还需要对脚本进行一定的修改,类似上面的调整采样率和声道数

ffmpeg -y -f s16le -ar 24000 -ac 1 -i "$1.pcm" "${1%.*}.$2" > /dev/null 2>&1

剩下的步骤和上文类似。

下面是踩过的坑,希望对别的同学能有帮助:

1.版本:服务器端一开始使用的是apt-get安装ffmpeg,版本是2.8.0(好像),结果转换的时候失败了,目前使用的版本为3.3.3,这一点上库的作者没有提及,目前我的版本可以成功,供大家参考

2.ffmpeg其实包含几个模块,有的需要单独编译,参考文章http://thierry-xing.iteye.com/blog/2017864 ,一开始调查的时候找到很多文章,对这一块没有说清楚,对于时间紧迫而且对视频编码这一块没有怎么了解的确实很难办,这里提供一个方法:使用brew安装,这一一开始是在mac上用的,后来发现ubuntu也可以,还能保持两边版本相同,何乐而不为(笑)。使用方法:敲brew,聪明的系统会提示你该怎么做的。

3.命令路径:由于本工程是在java平台上完成的,因此使用了javaRuntime运行命令,发现使用命令时需要写上命令绝对路径,不然会出现执行失败的情况。可以使用命令

which ffmpeg

确定你的命令具体位置

下面会讲到一些具体的操作步骤,属于具体细节的处理,可以给大家参考一下

1.微信语音上传

 wx.uploadFile({
            url: 'https://xxx.xxx/xxx/upload',
            filePath: res.tempFilePath,
            name: 'file',
            // header: {}, // 设置请求的 header
            formData: {
              'msg': 'voice'
            }, // HTTP 请求中其他额外的 form data
            success: function (res) {
            ...
            }
})

2.文件上传接收:

使用apache的fileupload模块

 // 1.创建一个DiskFileItemFactory工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中
factory.setSizeThreshold(1024 * 10);// 设置缓冲区的大小为100KB,如果不指定,那么默认缓冲区的大小是10KB
// 设置上传时生成的临时文件的保存目录
factory.setRepository(tmpFile);
// 2.创建一个文件上传解析器
ServletFileUpload upload = new ServletFileUpload(factory);
// 监听文件上传进度
upload.setProgressListener(new ProgressListener() {
                @Override
                public void update(long readedBytes, long totalBytes, int currentItem) {
                    // TODO Auto-generated method stub
                    System.out.println("current modify:" + readedBytes + "-----------file size:" + totalBytes + "--" + currentItem);
                }
            });
// 设置上传单个文件的最大值
upload.setFileSizeMax(1024 * 1024 * 10);// 10M
List items = upload.parseRequest(request);
Iterator itr = items.iterator();
while (itr.hasNext()) {//开始遍历
 FileItem item = (FileItem) itr.next();
  //保存文件
 File file = new File(savePath, saveFileName);
 item.write(file);
}

3.格式转换处理

Runtime runtime = Runtime.getRuntime();
Process proce;
String cmd = "/usr/local/ffmpeg -i "+webm+" -ac 1 -ar 16000 "+wav;
proce = runtime.exec(cmd);
proce.waitFor();
//监听命令执行结果
BufferedReader br = new BufferedReader(new InputStreamReader(proce.getInputStream()));  
StringBuffer msb = new StringBuffer();  
String line;  
while ((line = br.readLine()) != null) {  
     msb.append(line).append("\n");  
}  
String mresult = msb.toString();  
System.out.println("exec result = "+mresult);

4.返回结果

JSONObject jb = new JSONObject(res.toString());
JSONArray result = jb.getJSONArray("result");
response.getWriter().print("get->"+result);

https://github.com/SGZoom/WechatServer

你可能感兴趣的:(Android)