微信小程序语音转文字功能实战:从踩坑到上线
已完成开篇
最近产品经理又双叒叕提需求了:"咱们小程序加个语音输入功能呗,你看竞品都有!" 这种能提升用户体验的功能当然是很有必要的。但真做起来才发现,微信小程序的语音识别功能虽然封装得不错,但坑也不少。今天就把我趟过的坑和最终解决方案分享给大家。
准备工作:
别急着写代码
对于语音识别 ,微信小程序其实已经封装好了一个插件,那就是同声传译的插件。不过使用这个插件之前我们需要做一些准备工作:
1、插件授权
我们首先需要登录微信公众平台,在账号设置-第三方设置-插件管理中点击《添加插件》,搜索“同声传译”或者搜索插件AppId:wx069ba97219f66d99 并点击添加。(如果是个人小程序则无法使用这个插件,我们可以选择腾讯云语音识别能力,也是比较好用的,能满足基本使用)

2、麦克风功能隐私协议添加
语音识别首先要获取录音权限,那我们需要在公众平台-基本设置-服务内容声明-用户隐私保护指引中添加录音功能,这个非常重要,需要微信官方审核,如果不开通的话,我们就不能使用录音功能了。

等待审核通过之后,我们就可以着手代码工作了。
代码配置
1. 插件配置
在app.json里加插件配置时,我一开始忘记加版本号,结果调试半天才发现问题:
// 正确姿势{"plugins": {"WechatSI": {"version": "0.3.4", // 这个千万不能少!"provider": "wx069ba97219f66d99"}}}2. 权限处理
用户拒绝授权时不能直接摆烂,得引导他们去设置页。这里有个细节:wx.openSetting现在必须由用户点击触发,不能直接在fail回调里调用了,得用弹窗中转:
getAuth() {wx.authorize({scope: 'scope.record',fail: () => {// 这个modal必须要有,不然openSetting不生效wx.showModal({title: '提示',content: '需要麦克风权限才能语音输入',success(res) {if (res.confirm) {// 用户点击确定后才能跳转设置wx.openSetting()}}})}})}核心代码
1. 识别管理器要全局单例
const manager = speekplugin.getRecordRecognitionManager()// 这个manager一定要放在Page外面!// 放里面会导致重复创建,识别结果会错乱
2. 三个回调必须全监听
| initRecord() { |
| // 实时结果回调(会触发多次) |
| manager.onRecognize = (res) => { |
| // 这里有个坑:res.result可能包含上次结果 |
| this.setData({ resultText: res.result }) |
| } |
| // 最终结果回调 |
| manager.onStop = (res) => { |
| // 实测有时候res.result比onRecognize的最后一次结果更准确 |
| this.setData({ |
| resultText: res.result, |
| recognizing: false |
| }) |
| } |
| // 错误回调(网络问题、说话太短等) |
| manager.onError = (res) => { |
| // 常见错误码: |
| // -10006:录音时间太短 |
| // -10007:录音时间太长(超过60s) |
| console.error('识别出错', res) |
| } |
| } |
3. 开始/结束录音
| // 开始录音 |
| startRecord() { |
| this.setData({ listening: true }) |
| manager.start({ |
| lang: 'zh_CN', |
| // 实测这个参数可以提升短语音识别准确率 |
| detectInterrupt: true |
| }).catch(err => { |
| // 这里要catch,不然用户拒绝授权时会崩 |
| console.error('启动录音失败', err) |
| }) |
| } |
| // 结束录音 |
| stopRecord() { |
| manager.stop() // 这个stop()没有回调,结果在onStop里拿 |
| // 注意:这里不能立即setData,要等onStop触发 |
| } |
过程中踩了一些坑记录一下
坑1:onRecognize结果重复
现象:实时回调返回的结果会包含之前识别的内容
解决方案:直接使用res.result而不是累加
| // 错误写法(结果会重复) |
| this.setData({ resultText: this.data.resultText + res.result }) |
| // 正确写法 |
| this.setData({ resultText: res.result }) |
坑2:安卓机权限问题
现象:安卓机上有时授权成功了但还是无法录音
解决方案:加个延迟再启动录音
wx.authorize({scope: 'scope.record',success: () => {// 安卓机需要稍微等一等setTimeout(() => {manager.start({/*...*/})}, 300)}})坑3:长时间录音卡顿
现象:录音超过30秒后识别变慢
解决方案:建议分段识别,每60秒自动分段(微信限制最长60s)
| // 录音计时 |
| let recordTimer = null |
| startRecord() { |
| this.setData({ listening: true }) |
| manager.start({/*...*/}) |
| // 60秒自动停止 |
| recordTimer = setTimeout(() => { |
| this.stopRecord() |
| }, 60000) |
| } |
| stopRecord() { |
| clearTimeout(recordTimer) |
| // ...原有逻辑 |
| } |
完整代码优化版
| const speekplugin = requirePlugin("WechatSI") |
| const manager = speekplugin.getRecordRecognitionManager() |
| let recordTimer = null |
| Page({ |
| data: { |
| resultText: '', |
| listening: false, |
| recognizing: false, |
| errorMsg: '' |
| }, |
| onLoad() { |
| this.getAuth() |
| this.initRecord() |
| }, |
| // 权限处理(优化版) |
| getAuth() { |
| wx.getSetting({ |
| success: (res) => { |
| if (!res.authSetting['scope.record']) { |
| wx.authorize({ |
| scope: 'scope.record', |
| fail: () => this.showAuthModal() |
| }) |
| } |
| } |
| }) |
| }, |
| showAuthModal() { |
| wx.showModal({ |
| title: '需要麦克风权限', |
| content: '用于语音输入功能', |
| success: (res) => { |
| if (res.confirm) wx.openSetting() |
| } |
| }) |
| }, |
| // 识别初始化(防抖版) |
| initRecord() { |
| manager.onRecognize = (res) => { |
| this.setData({ resultText: res.result }) |
| } |
| manager.onStop = (res) => { |
| clearTimeout(recordTimer) |
| this.setData({ |
| resultText: res.result || this.data.resultText, |
| listening: false, |
| recognizing: false |
| }) |
| } |
| manager.onError = (res) => { |
| clearTimeout(recordTimer) |
| this.setData({ |
| errorMsg: this.getErrorMsg(res.retcode), |
| listening: false, |
| recognizing: false |
| }) |
| } |
| }, |
| // 开始录音(安全版) |
| startRecord() { |
| this.setData({ |
| listening: true, |
| errorMsg: '', |
| resultText: '' |
| }) |
| setTimeout(() => { |
| manager.start({ |
| lang: 'zh_CN', |
| detectInterrupt: true |
| }).catch(err => { |
| this.setData({ |
| listening: false, |
| errorMsg: '启动录音失败' |
| }) |
| }) |
| recordTimer = setTimeout(() => { |
| this.stopRecord() |
| }, 60000) |
| }, 300) |
| }, |
| // 结束录音 |
| stopRecord() { |
| manager.stop() |
| this.setData({ recognizing: true }) |
| }, |
| // 错误码转文字 |
| getErrorMsg(code) { |
| const map = { |
| '-10006': '说话时间太短', |
| '-10007': '说话时间超过60秒', |
| '-10001': '网络错误', |
| 'default': '识别失败,请重试' |
| } |
| return map[code] || map.default |
| } |
| }) |
最后总结
这个功能上线后用户反馈还不错,但有几个小建议:
一定要加个"正在聆听"的动画反馈,不然用户不知道麦克风开了没
识别结果可以加个编辑功能,毕竟语音识别不可能100%准确
安卓机的兼容性测试要重点做,特别是各种国产机型


回帖 ( 0 )