也许偶尔就会有这样的需求,我是说如果,就是需要在手机端录入语音,然后发往后端,转为文字,再做对应的逻辑处理。是不是一听就很想有copy的想法呢,是的,就是有可以copy的东西。
首先整体思路是用最小的代价,实现我们的功能。那么手机端的语音录入,我们可以借助于微信小程序去实现,小程序内部有api可以调用语音输入。然后将得到的语音发往后端,后端呢,去调用百度的语音接口,就完成了语音翻译。
好了,接下来开启我们的代码之旅吧。
首先需要明确一个概念,在微信的体系内,如何去标识一个用户呢,那就是openId,单个用户的openId在某个微信小程序下是唯一的,我们就用它去标识。那些微信小程序的注册和申请、域名配置啥、小程序开发工具下载啥的咱们就跳过吧。还有一个,如果我们没有https的备案域名,开发的时候也不要紧,只要我们在开发者工具里面将域名校验给关闭掉就可以。
第一步,获取openId,这个任务自然是交给后端去做了。首先在微信小程序端,会获得一个code,这个code只有5分钟的有效期,可以根据code去兑换openId,小程序端的逻辑如下:
App({
onLaunch() {
// 登录
wxUtil.login(code => {//code自动获取
console.log(code)
let paramJson = {code: code};
wxUtil是一个工具类,包装了微信的http请求,不用关注
wxUtil.post('wx/appletcode2openid', paramJson, wxUtil.audio, { isShowLoading: true }, (result) =>{
console.log(result);
wxUtil.setOpenIdByStorage(result.data.openID);
});
});
},
globalData: {
userInfo: null
}
})
对应的后端获取openId的逻辑:
/**
* 微信小程序根据code获取openId等信息
*
* @return
* @throws
* @throws IOException
*/
public static WxAppletAuthResultVO getOpenIdByAuthCodeOnApplet(String appId, String appSecret, String code) {
if(appId==null||appId.trim().isEmpty()){
throw new RuntimeException("appId为空");
}
if(appSecret==null||appSecret.trim().isEmpty()){
throw new RuntimeException("appSecret为空");
}
if(code==null||code.trim().isEmpty()){
throw new RuntimeException("code为空");
}
try {
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code".replace("APPID", appId).replace("SECRET",appSecret).replace("JSCODE", code);
String result = HttpUtil.doPost(url, null);
if(result==null||result.isEmpty()){
throw new RuntimeException("微信小程序根据code获取openId等信息返回为空");
}
return JSON.parseObject(result, WxAppletAuthResultVO.class);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
return null;
}
有了openId,下面我们就可以获取录入的语音了,然后将语音和openId一起发往后端。微信小程序端的js代码:
const wxUtil = require('../../utils/wxutil.js');
const recorderManager = wx.getRecorderManager();
const innerAudioContext = wx.createInnerAudioContext({"useWebAudioImplement": true});
recorderManager.onStop((res) => {//注意 给recorderManager注册了事件 后面会触发
var tempFilePath = res.tempFilePath;//音频文件地址
const fs = wx.getFileSystemManager();
fs.readFile({//读取文件并转为ArrayBuffer
filePath: tempFilePath,
success(res) {
wx.showLoading({
title: '正在语音识别中...',
});
const base64Data = wx.arrayBufferToBase64(res.data);
var fileSize = res.data.byteLength ;
var paramJson = {
format: 'pcm',
sampleRate: 16000,
encodeBitRate: 48000,
data: base64Data
};
wxUtil.post('wx/audioupload', paramJson, wxUtil.audio, { isShowLoading: true }, (result) =>{
console.log( result);
});
}
})
});
Page({
data: {},
onLoad() {},
//语音识别
handleTouchStart: function(e){
//录音参数
const options = {
sampleRate: 16000,
numberOfChannels: 1,
encodeBitRate: 48000,
format: 'pcm'
}
//开启录音
recorderManager.start(options);
wx.showLoading({
title: '正在录音中...',
});
},
handleTouchEnd: function(e){
recorderManager.stop();//stop之后就会触发onStop事件
}
})
这样,我们是直接把语音文件转为base64的字符串,发往后端。后端就去调用百度的接口转为文字。百度的接口虽然付费,但是有免费调用量,需要先注册账号,申请应用,然后调用。
调用之前需要先获取百度语音的accessToken:
public static BaiduAudioAccessTokenVO getAccessToken(String apiKey,String secretKey){
Map<String, String> parameters = new HashMap<>();
parameters.put("grant_type","client_credentials");
parameters.put("client_id",apiKey);
parameters.put("client_secret",secretKey);
String response = HttpUtil.sendPost("https://aip.baidubce.com/oauth/2.0/token",parameters);
log.info("调用百度语音accessToken返回:" + response);
BaiduAudioAccessTokenVO baiduAudioAccessTokenVO = JSON.parseObject(response, BaiduAudioAccessTokenVO.class);
ParamCheckUtil.objectNull(baiduAudioAccessTokenVO,"调用接口未获取到accessToken");
ParamCheckUtil.stringEmpty(baiduAudioAccessTokenVO.getAccess_token(),"调用接口未获取到accessToken");
return baiduAudioAccessTokenVO;
}
接着调用百度语音接口:
public String transToText(String base64Audio,Integer len,String format,Integer rate) {
//1参数校验
ParamCheckUtil.stringEmpty(base64Audio,"语音文件为空");
ParamCheckUtil.stringEmpty(format,"语音格式不能为空");
ParamCheckUtil.notTrue(
"pcm".equalsIgnoreCase(format)
|| "wav".equalsIgnoreCase(format)
|| "amr".equalsIgnoreCase(format),"语音文件格式不符合要求");
//2 获取百度语音accessToken
String accessToken = this.getAccessToken();
//3 语音转为文字
BaiduAudio2TextDTO audio2TextDTO = new BaiduAudio2TextDTO();
audio2TextDTO.setFormat(format);
audio2TextDTO.setRate(rate);
audio2TextDTO.setCuid(baiduAudioProperties.getAppID());
audio2TextDTO.setToken(accessToken);
audio2TextDTO.setLen(len);
audio2TextDTO.setBase64Audio(base64Audio);
String text = BaiduAudioUtil.audio2Text(audio2TextDTO);
log.info("翻译文字为:" + text);
return text;
}
//工具类
public static String audio2Text(BaiduAudio2TextDTO baiduAudio2TextDTO){
Map<String, Object> parameters = new HashMap<>();
parameters.put("format",baiduAudio2TextDTO.getFormat());
parameters.put("rate",baiduAudio2TextDTO.getRate());
parameters.put("channel",1);
parameters.put("cuid",baiduAudio2TextDTO.getCuid());
parameters.put("token",baiduAudio2TextDTO.getToken());
parameters.put("speech",baiduAudio2TextDTO.getBase64Audio());
parameters.put("len",baiduAudio2TextDTO.getLen());
String response = HttpUtil.sendPostJson("https://vop.baidu.com/server_api",JSON.toJSONString(parameters));
log.info("语音转换接口返回:" + response);
ParamCheckUtil.stringEmpty(response,"语音转换接口返回为空");
BaiduAudio2TextVO textVO = JSON.parseObject(response, BaiduAudio2TextVO.class);
return textVO.getResult().get(0);
}
就这样,就完成了。
网友评论