项目需求,需要使用讯飞的语音识别接口,将微信小程序上传的录音文件识别成文字返回

首先去讯飞开放平台中申请开通语音识别功能

在这里面下载sdk,然后解压,注意appid与sdk是关联的,appid在初始化接口时候需要

由于是在Linux上开发,所以需要将.so文件和.dll文件上传到Linux服务器上安装的jdk/lib/amd64里面,要不会报引擎错误,window环境直接放在项目跟目录就行.

由于微信小程序上传的文件格式是silk的,而讯飞接口能识别wav 格式的文件,所以需要将小程序上传的silk文件转成wav的格式

由于小程序上传的silk文件是变异的silk(小程序上传的silk文件中在编码头多添加了一个字节)文件,所以需要将他处理成正常的silk文件

由于项目是运行在Linux上,所以写了一个简单的shell脚本以供java程序调用处理

这个脚本的作用是删除输入文件中#!SILK_V3所在行的第一个字节

好了,文件处理完了,现在就是格式转换了

经调研,发现一般是先将silk文件转换成pcm,这里使用的是Kronopath/SILKCodec,下载到linux服务器上,然后在SILK_SDK_SRC_ARM里执行

make lib
make decoder

执行之后会生成命令行工具decoder

使用方法:

./decoder  要转换文件.silk   要生成文件.pcm

执行完上面代码就会生成.pcm文件,然后就是将pcm转成wav格式了,这里使用的是ffmpeg,没有安装的可以参考一下

ubuntu14.04安装ffmpeg:http://blog.csdn.net/leezha/article/details/77849286

阿里云linux安装ffmpeg:http://blog.csdn.net/baijinwen/article/details/77235725

安装ffmpeg可能出现的问题:http://blog.51cto.com/zlyang/1709508

为了保证语音识别的准确性,使用一下代码识别生成的wav文件,讯飞接口识别结果最好

ffmpeg -f s16le -ar 12k -ac  -i /path/to/pcm -f wav -ar 16k -ac  /path/to/wav

下面就是java的讯飞语音接口开发了,直接贴代码

package com.example.utils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List; /**
* Created by songzs on 2017/12/12.
* 封装的转码工具类
*/
public class FFMPEGUtil {
public static String silk2Pcm(String inputfile,String outputfile){
List<String> commend = new ArrayList<String>();
commend.add("/usr/local/silk2pcm_tool/SILKCodec/SILK_SDK_SRC_ARM/./decoder");
commend.add(inputfile);
commend.add(outputfile);
StringBuffer test=new StringBuffer();
for(int i=0;i<commend.size();i++)
test.append(commend.get(i)+" ");
System.out.println("decoder命令:"+test+"");
exec(test);
return outputfile;
}
public static String pcm2Wav(String inputfile,String outputfile){
//ffmpeg -f s16le -ar 12k -ac 2 -i /path/to/pcm -f wav -ar 16k -ac 1 /path/to/wav
List<String> commend = new ArrayList<String>();
commend.add("ffmpeg");
commend.add("-f");
commend.add("s16le");
commend.add("-ar");
commend.add("12k");
commend.add("-ac");
commend.add("2");
commend.add("-i");
commend.add(inputfile);
commend.add("-f");
commend.add("wav");
commend.add("-ar");
commend.add("16k");
commend.add("-ac");
commend.add("1");
commend.add(outputfile);
StringBuffer test=new StringBuffer();
for(int i=0;i<commend.size();i++)
test.append(commend.get(i)+" ");
System.out.println("ffmpeg命令:"+test+"");
exec(test);
return outputfile;
} public static String silk_remove_word(String filepath){
List<String> commend = new ArrayList<String>();
commend.add("/home/workspace/./test.sh");
commend.add(filepath);
StringBuffer test=new StringBuffer();
for(int i=0;i<commend.size();i++)
test.append(commend.get(i)+" ");
System.out.println("test命令:"+test+"");
exec(test);
return filepath;
} private static void exec(StringBuffer test){
try {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(test.toString());
InputStream stderr = proc.getErrorStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ( (line = br.readLine()) != null) ; } catch (Exception e) {
e.printStackTrace();
}
}
}

语音结果处理工具类(代码简陋,见谅)

package com.example.utils;

import com.alibaba.fastjson.JSON;

import java.util.List;
import java.util.Map; /**
* Created by songzs on 2017/12/15.
*/
public class SR2Words { public static String sr2words(String jsonString){
StringBuffer sb = new StringBuffer();
String[] split = jsonString.split("}]}]}");
for (int i = 0; i < split.length; i++) {
String s = split[i] + "}]}]}";
System.out.println(s);
Map parse = (Map) JSON.parse(s);
List<Map> ws = (List<Map>) parse.get("ws");
for (int i1 = 0; i1 < ws.size(); i1++) {
List<Map> cw = (List<Map>)ws.get(i1).get("cw");
String w = cw.get(0).get("w").toString();
sb.append(w);
} }
return sb.toString();
}
}

小程序录音文件上传与讯飞语音识别

package com.example.service.impl;

import com.example.service.XunFeiService;
import com.example.utils.FFMPEGUtil;
import com.example.utils.SR2Words;
import com.example.utils.SRTool;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID; /**
* Created by songzs on 2017/12/12.
*/
@Service
public class XunFeiServiceImpl implements XunFeiService { @Override
public Map<String,String> speechRecognition(MultipartFile multi) {
Map<String,String> map =new HashMap<>();
UUID uuid = UUID.randomUUID();
String path = "/home/workspace/audio";
String fileName = uuid.toString()+".silk";
//临时silk文件
String tempFile = "/home/workspace/audio/"+uuid.toString()+".silk";
//中间过渡pcm文件
String pcmFile = "/home/workspace/audio/"+uuid.toString()+".pcm";
//可识别的wav文件
String wavFile = "/home/workspace/audio/"+uuid.toString()+".wav";
File file = new File(path,fileName);
try {
multi.transferTo(file);
} catch (IOException e) {
e.printStackTrace();
}
/*移除临时silk文件首字节start*/
//标准silk文件
String silkFile = FFMPEGUtil.silk_remove_word(tempFile);
/*移除临时silk文件首字节end*/
//silk文件转换成pcm文件
String silk2Pcm = FFMPEGUtil.silk2Pcm(silkFile, pcmFile);
//pcm文件转换成wav文件
String pcm2Wav = FFMPEGUtil.pcm2Wav(silk2Pcm, wavFile);
//讯飞语音识别接口识别wav音频文件,转成文字返回
SRTool sr = new SRTool();
String words = null;
try {
words = sr.voice2words(pcm2Wav);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("讯飞识别的语音结果:"+words);
if("".equals(words)){
System.out.println("讯飞识别的语音结果:null");
map.put("status","error");
map.put("content","对不起,请您在描述一遍!");
return map;
}
String result = SR2Words.sr2words(words);
System.out.println("讯飞识别的语音结果:"+result);
map.put("status","success");
map.put("content",result);
return map;
}
}

讯飞语音识别工具类

package com.example.utils;

import com.iflytek.cloud.speech.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList; /**
* Created by songzs on 2017/12/4.
*/
public class SRTool { private int perWaitTime = 100; private StringBuffer mResult = new StringBuffer(); static {
SpeechUtility.createUtility("appid=********");//申请的appid
} public String voice2words(String fileName) throws InterruptedException, IOException {
return to(fileName);
} public String to(String fileName) throws InterruptedException, IOException { File file = new File(fileName);
if(!file.exists()){
throw new RuntimeException("要读取的文件不存在");
}
FileInputStream fis = new FileInputStream(file);
int len = 0;
byte[] buf = new byte[fis.available()];
fis.read(buf);
fis.close(); //1.创建SpeechRecognizer对象
SpeechRecognizer mIat = SpeechRecognizer.createRecognizer();
//2.设置听写参数,详见《MSC Reference Manual》SpeechConstant类
mIat.setParameter(SpeechConstant.DOMAIN, "iat");
mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
mIat.setParameter(SpeechConstant.ACCENT, "mandarin ");
mIat.setParameter(SpeechConstant.AUDIO_SOURCE, "-1");
//3.开始听写
mIat.startListening(mRecoListener); //voiceBuffer为音频数据流,splitBuffer为自定义分割接口,将其以4.8k字节分割成数组
ArrayList<byte[]> buffers = splitBuffer(buf, buf.length, 4800);
for (int i = 0; i < buffers.size(); i++) {
// 每次写入msc数据4.8K,相当150ms录音数据
mIat.writeAudio(buffers.get(i), 0, buffers.get(i).length);
}
mIat.stopListening(); while (mIat.isListening()) {
Thread.sleep(perWaitTime);
}
return mResult+"";
} /**
* 将字节缓冲区按照固定大小进行分割成数组
*
* @param buffer 缓冲区
* @param length 缓冲区大小
* @param spsize 切割块大小
* @return
*/
private ArrayList<byte[]> splitBuffer(byte[] buffer, int length, int spsize) {
ArrayList<byte[]> array = new ArrayList<byte[]>();
if (spsize <= 0 || length <= 0 || buffer == null
|| buffer.length < length)
return array;
int size = 0;
while (size < length) {
int left = length - size;
if (spsize < left) {
byte[] sdata = new byte[spsize];
System.arraycopy(buffer, size, sdata, 0, spsize);
array.add(sdata);
size += spsize;
} else {
byte[] sdata = new byte[left];
System.arraycopy(buffer, size, sdata, 0, left);
array.add(sdata);
size += left;
}
}
return array;
} //听写监听器
private RecognizerListener mRecoListener = new RecognizerListener() {
public void onResult(RecognizerResult results, boolean isLast) {
System.out.println("Result:" + results.getResultString());
mResult.append(results.getResultString());
} //会话发生错误回调接口
public void onError(SpeechError error) {
System.out.println(error.getErrorCode()+"=========="+error.getErrorDesc());
System.out.println(error);
} //开始录音
public void onBeginOfSpeech() {
} //音量值0~30
public void onVolumeChange(int volume) {
} @Override
public void onVolumeChanged(int i) { } @Override
public void onEndOfSpeech() { } @Override
public void onEvent(int i, int i1, int i2, String s) { }
};
}

*小程序上传接口必须是https请求,所以可能需要搭建https,相关内容可以参考我上一篇文章

微信小程序语音与讯飞语音识别接口(Java)的更多相关文章

  1. 微信小程序语音与讯飞语音识别接口(Java),Kronopath/SILKCodec,ffmpeg处理silk,pcm,wav转换

    项目需求,需要使用讯飞的语音识别接口,将微信小程序上传的录音文件识别成文字返回 首先去讯飞开放平台中申请开通语音识别功能 在这里面下载sdk,然后解压,注意appid与sdk是关联的,appid在初始 ...

  2. 微信小程序集成腾讯云 IM SDK

    微信小程序集成腾讯云 IM SDK 1.背景 因业务功能需求需要接入IM(即时聊天)功能,一开始想到的是使用 WebSocket 来实现这个功能,然天意捉弄(哈哈)服务器版本太低不支持 wx 协议(也 ...

  3. 微信小程序内判断是否关注公众号(JAVA)

    微信小程序内判断是否关注公众号(JAVA) 思路来源(第二种): https://blog.csdn.net/Yanheeee/article/details/117295643 /** * 总体思路 ...

  4. 微信小程序语音同步智能识别的实现案例

    目录 一.背景 二.同声传译插件介绍 1. 微信小程序后台添加插件 2. 微信小程序启用插件 三.语音同步转换的前端实现 1.界面UI与操作 2.代码实现 四.后端SpringBoot实现语音文件上传 ...

  5. 如何开发一款堪比APP的微信小程序(腾讯内部团队分享)

    一夜之间,微信小程序刷爆了行业网站和朋友圈,小程序真的能如张小龙所说让用户"即用即走"吗? 其功能能和动辄几十兆安装文件的APP相比吗? 开发小程序,是不是意味着移动应用开发的一次 ...

  6. 微信小程序基于腾讯云对象存储的图片上传

    在使用腾讯云对象存储之前,公司一直使用的是传统的FTP的上传模式,而随着用户量的不断增加,FTP所暴露出来的问题也越来越多,1.传输效率低,上传速度慢.2.时常有上传其他文件来攻击服务器,安全上得不到 ...

  7. 微信小程序 使用腾讯地图SDK详解及实现步骤

    信小程序 使用腾讯地图SDK详解及实现步骤    微信小程序JavaScript SDK: 官方文档:http://lbs.qq.com/qqmap_wx_jssdk/index.html 步骤: 1 ...

  8. 微信小程序-使用腾讯Wxpage

    微信小程序想要更快的速度吗? 满足你 https://github.com/tvfe/wxpage#-c%E5%AE%9A%E4%B9%89 使用超简单(导入wxpage.js,最后使用对象名:P): ...

  9. 微信小程序语音(A)发给别人(B),也能播放,是需要先把语音上传到自己的服务器上才可以

    小程序语音(A)发给别人(B),也能播放,是需要先把语音上传到自己的服务器上才可以. https://developers.weixin.qq.com/miniprogram/dev/api/medi ...

随机推荐

  1. 18个超有趣的SVG绘制动画赏析

    SVG作为时下比较新颖的技术标准,已经建立了很多基于SVG的前端项目.由于SVG在绘制路径上非常灵活,我们将很多网页上的元素使用SVG来绘制而成,有各种人物.小图标.小动画等等.今天我们收集了18个非 ...

  2. html 自定义标签使用实现方法

    通过指定html命名空间的名字来定义自定义标签:默认的一些标签p div等都在html默认的命名空间下.而自定义的标签可以放在自定义的命名空间下,可通过xmlns:命名空间名 来指定,而自定义标签需要 ...

  3. js跨域问题解决方案

     跨域:当协议.域名.端口号任何一个不相同时,叫称为跨域.   HTML5  CORS(cross-origin-resource-sharing)跨域资源共享: 原理:当需要访问跨域的资源时,可以通 ...

  4. 高性能 AJAX

    请求数据的常用的五种方式 1.XMLHttpRequest (XHR) var url = '/data.php'; var params = [     'id=934875',     'limi ...

  5. Chartist.js-同时画柱状图和折线图

    最近几天都在研究chartist,因为echarts生成的图是位图,导成PDF的时候不够清晰.而chartist是搜到的免费插件中呼声较高的,基于SVG. 今天主要是想举一些代码例子给大家,介绍下如何 ...

  6. windows查看端口占用命令

    开始--运行--cmd 进入命令提示符 输入netstat -aon 即可看到所有连接的PID 之后在任务管理器中找到这个PID所对应的程序如果任务管理器中没有PID这一项,可以在任务管理器中选&qu ...

  7. layui + jfinal 实现上传下载

    1.需要把jfinal的环境配置好 2.导入相关的库文件 layui的库文件 就是这两个文件需要导入到自己的页面 注意:jfinal总会把路径拦截,所以需要静态文件处理.本人不太懂.就网上找了下,说w ...

  8. JAVA的免费天气api接口调用示例

    step1:选择本文所示例的接口"免费天气api" url:https://www.juhe.cn/docs/api/id/39/aid/87 step2:每个接口都需要传入一个参 ...

  9. C#2.0中使用yield关键字简化枚举器的实现

    我们知道要使用foreach语句从客户端代码中调用迭代器,必需实现IEnumerable接口来公开枚举器,IEnumerable是用来公开枚举器的,它并不实现枚举器,要实现枚举器必需实现IEnumer ...

  10. 《天书夜读:从汇编语言到windows内核编程》五 WDM驱动开发环境搭建

    (原书)所有内核空间共享,DriverEntery是内核程序入口,在内核程序被加载时,这个函数被调用,加载入的进程为system进程,xp下它的pid是4.内核程序的编写有一定的规则: 不能调用win ...