一、使用uniapp的Speech模块(使用 plus.speech.startRecognize api)
1.在Speech.js文件中存放语音识别监听的逻辑。
let nowText = '';
class Speech {
setSpeech() {
plus.speech.addEventListener('start', this.onStart, false);
plus.speech.addEventListener('recognition', this.onRecognition, false);
plus.speech.addEventListener('end', this.onEnd, false);
}
onStart() {
nowText = '';
}
onRecognition(e) {
// console.log('识别完成');
nowText = e.result;
uni.$emit('SPEECH', {
result: nowText
})
}
onEnd() {
console.log('Event: end', '结束录制');
console.log('结束语音录制....');
if(!nowText) {
plus.nativeUI.toast('没有识别到内容');
}
plus.speech.stopRecognize();
}
checkMicrophone() {
// 检查麦克风权限
const info = uni.getAppAuthorizeSetting();
if(info.microphoneAuthorized !== 'authorized') {
plus.nativeUI.toast('请打开麦克风权限,再重启应用');
return false;
}
return true;
}
}
export default new Speech();
2.在App.vue中引入Speech.js文件。
<script>
import Speech from '@/utils/Speech.js';
export default {
onLaunch: () {
// #ifdef APP-PLUS
// 初始化语音识别功能
Speech.setSpeech();
// #endif
}
}
</script>
3.现在就可以在你想要的地方使用了,例如:
// 在自己的语音识别组件中加入下面这段
let options = {
engine: 'baidu',
timeout: 10 * 1000, //超时时间
punctuation: true, //是否需要标点符号
continue: true, //语音识别是否采用持续模式
userInterface: true, //安卓手机为true时会影响@touchend触摸结束事件,暂未发现好的解决方法
};
// console.log('开始语音识别:');
plus.speech.startRecognize(options, () => {}, (errMsg) => {
console.log('语音识别失败:' + errMsg);
});
uni.$on('SPEECH', (e: any) => {
console.log('识别结果:', e.result);
tips.value = e.result;
emits('micInput', e.result);
})
// 在组件的beforeDestroy生命周期或语音识别组件的关闭方法中加入移除全局监听
uni.$off('SPEECH');
这种方式有个问题,配置options中的userInterface必须是true(为false会识别不了语音内容),为true时又无法自定义识别样式,只能使用其自带的样式,就导致无法实现长按语音识别效果,因此更推荐使用第二种方式。
二、使用百度短语音识别的接口上传录音文件,获取返回的语音识别信息
1.百度语音识别接口需要携带百度的access_token参数,因此建议在登录的时候调用该接口获取access_token,鉴权相关文档: ai.baidu.com/ai-doc/REFE… 。
uni.request({
url: 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=你的百度API Key&client_secret=你的百度Secret Key&',
method: 'POST',
header: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
success: (res) => {
console.log('百度token:', res.data.access_token);
uni.setStorageSync('BAIDU_ACCESS_TOKEN', res.data.access_token);
},
fail: (err) => {
resolve('fail:', err);
}
})
2.然后在你的语音识别组件中录音,录音结束后调用百度短语音识别接口携带base64格式的录音文件参数等信息,就可以获取语音识别结果了,这里是用的是json方式上传音频,接口参数的格式请自行前往百度短语音识别开发文档查看( cloud.baidu.com/doc/SPEECH/… )。
<script>
import { pathToBase64 } from '@/utils/pathToBase64';
import dayjs from 'dayjs';
export default {
data() {
...,
voiceTextValue: '', // 记录识别结果
recorderManager: null,
audioContext: null,
openTimer: 0,
...
},
mounted() {
// 获取全局唯一的录音管理器
this.recorderManager = uni.getRecorderManager();
// 创建并返回内部 audio 对象
this.audioContext = uni.createInnerAudioContext();
this.recorderManager.onStop((res) => {
const closeTimer = dayjs().valueOf();
console.log('recorder stop:', closeTimer - this.openTimer);
// 判断录音时长是否大于1s,大于1s才能识别
if(closeTimer - this.openTimer > 1000) {
uni.showLoading({
title: '正在识别中...'
})
// 获取录音文件的临时地址
this.audioContext.src = res.tempFilePath;
} else {
uni.showToast({
title: '说话时间太短,请重新说一遍',
icon: 'none'
})
}
});
this.recorderManager.onError((err) => {
console.log('错误信息', err);
})
// 当audio获取到文件地址时,会触发onCanplay事件
this.audioContext.onCanplay(() => {
this.getSpeechRecognition(this.audioContext.src);
})
},
methods: {
getSpeechRecognition(path) {
// 获取录音文件的大小
uni.getFileInfo({
filePath: path,
success: file => {
console.log('文件大小:', file.size);
// 根据所传地址获取base64格式的文件
pathToBase64(path).then(res => {
// 只需要文件的内容
const data = res.split(',')[1];
this.requestRecordData(data, file.size);
})
},
fail: () => {
uni.hideLoading();
uni.showToast({
title: '未找到文件',
icon: 'none'
})
}
})
},
// 请求百度语音识别接口,获取语音识别结果
requestRecordData(data, size) {
uni.request({
url: 'http://vop.baidu.com/server_api',
method: 'POST',
header: {
'content-type': 'application/json;charset=UTF-8'
},
data: {
format: 'm4a',
rate: 16000,
channel: 1,
token, // 之前获取的百度access_token
cuid, // 设备识别码,建议使用通过 plus.device.getInfo()获取的设备uuid
speech: data,
len: size
},
success: speech => {
uni.hideLoading();
if(+speech.data.err_no === 0 && speech.data.result[0]) {
this.voiceTextValue = speech.data.result[0];
} else {
uni.showToast({
title: '未识别到内容',
icon: 'none'
})
}
},
fail: () => {
uni.hideLoading();
uni.showToast({
title: '获取语音识别结果失败',
icon: 'none'
})
}
})
},
// 开始语音识别
startSpeek() {
// #ifdef APP-PLUS
// 记录开始语音识别的时间
this.openTimer = dayjs().valueOf();
this.recorderManager.start({
duration: 60000,
sampleRate: 16000,
format: 'mp3',
hideTips: true
});
// #endif
},
// 结束语音识别
endSpeek() {
// #ifdef APP-PLUS
// 停止录音
this.recorderManager.stop();
// #endif
}
}
}
</script>
其中引用的pathToBase64.js文件内容:
export function pathToBase64(path) {
return new Promise(function(resolve, reject) {
// #ifdef H5
if (typeof FileReader === 'function') {
var xhr = new XMLHttpRequest()
xhr.open('GET', path, true)
xhr.responseType = 'blob'
xhr.onload = function() {
if (this.status === 200) {
let fileReader = new FileReader()
fileReader.onload = function(e) {
resolve(e.target.result)
}
fileReader.onerror = reject
fileReader.readAsDataURL(this.response)
}
}
xhr.onerror = reject
xhr.send()
return
}
var canvas = document.createElement('canvas')
var c2x = canvas.getContext('2d')
var img = new Image
img.onload = function() {
canvas.width = img.width
canvas.height = img.height
c2x.drawImage(img, 0, 0)
resolve(canvas.toDataURL())
canvas.height = canvas.width = 0
}
img.onerror = reject
img.src = path
// #endif
// #ifdef APP-PLUS
plus.io.resolveLocalFileSystemURL(path, function(entry) {
entry.file(function(file) {
var fileReader = new plus.io.FileReader()
fileReader.onload = function(data) {
resolve(data.target.result)
}
fileReader.onerror = function(error) {
reject(error)
}
fileReader.readAsDataURL(file)
}, function(error) {
reject(error)
})
}, function(error) {
reject(error)
})
// #endif
})
}