前言
最近使用 uniapp 做小程序的时候,需要实现一个语音输入转换为文本的功能,功能的实现并不复杂,但还是踩了一个坑,因此记录了一下整体的实现过程以及提供了可以直接复制使用的代码,希望可以帮助到你。
引用插件
微信的同声传译插件有三个功能,分别是 语音输入,文本翻译和语音合成(文字转语音),我用到的就是语音输入功能。
首先微信服务市场中登录并添加同声传译插件,我是个人账号同样可以申请,链接在这里:fuwu.weixin.qq.com/service/det…
添加完成后在小程序后台 mp.weixin.qq.com/ 点击左下角的账号设置
顶部 Tab 栏选择第三方设置,在插件管理中找到同声传译插件,点击详情
在详情中可以查看插件的 APPID,提前复制好
接下来在小程序项目的 manifest.json
文件中添加一个插件引用:
"mp-weixin": {
"appid": "wxff30426f8850b0",
"setting": {
"urlCheck": false
},
"usingComponents": true,
+ "plugins": {
+ "WechatSI": {
+ "provider": "上一步复制的 APPID",
+ "version": "0.3.6"
}
}
},
如此操作,插件就算是在项目中成功引用了。
功能使用
这里我直接写了一个 hook,如果有需要可以自己复制使用,插件的初始化,转换成功后文本的插入都在这个 hook 中实现了>
import { ref, reactive } from 'vue'
export function useVoiceInput() {
// 初始化语音识别插件
const plugin = requirePlugin('WechatSI')
const manager = plugin.getRecordRecognitionManager()
// 记录光标位置
const cursorPosition = ref(0)
// 语音输入状态
const voiceState = reactive({
isRecording: false, // 是否正在录音
recordLoading: false, // 录音加载状态
})
// 处理文本框失焦事件,保存光标位置
const handleTextareaBlur = (e: any) => {
cursorPosition.value = e.target.cursor || 0
}
// 初始化语音识别事件监听
const initVoiceListeners = (updateText: (text: string) => void) => {
// 录音结束事件
manager.onStop = (res) => {
if (res.result) {
updateText(res.result)
}
voiceState.isRecording = false
}
// 录音错误事件
manager.onError = (res) => {
console.error('语音识别错误:', res.msg)
voiceState.isRecording = false
voiceState.recordLoading = false
}
// 录音开始事件
manager.onStart = () => {
voiceState.recordLoading = false
}
}
// 处理语音输入按钮点击
const handleVoiceInput = (vibrate: () => void) => {
vibrate()
if (voiceState.isRecording) {
// 如果正在录音,则停止
voiceState.isRecording = false
manager.stop()
} else {
// 开始新的录音
voiceState.isRecording = true
voiceState.recordLoading = true
manager.start({
duration: 30000, // 最长录音时间:30秒
lang: 'zh_CN' // 识别语言:中文
})
}
}
// 在文本指定位置插入新内容
const insertTextAtCursor = (currentText: string, newText: string): string => {
const position = cursorPosition.value
return (
currentText.slice(0, position) +
(position > 0 && !currentText.endsWith(' ') ? ' ' : '') +
newText +
currentText.slice(position)
)
}
// 更新光标位置
const updateCursorPosition = (currentText: string, insertedText: string) => {
cursorPosition.value =
cursorPosition.value +
insertedText.length +
(cursorPosition.value > 0 && !currentText.endsWith(' ') ? 1 : 0)
}
return {
voiceState,
cursorPosition,
handleTextareaBlur,
handleVoiceInput,
initVoiceListeners,
insertTextAtCursor,
updateCursorPosition
}
}
功能的使用其实很简单,我原本以为插件只是实现了转换了逻辑,关于麦克风权限和语音文件处理都需要自己来做的,结果插件中都封装好了,自己只需要维护一些录音相关的状态就好了。
代码中包含一些光标位置相关的处理,是因为用户可能并不是在一个空的文本输入框中进行语音输入,为了在正确的位置插入文本,我们就需要维护一下用户当前输入框的光标位置,如果你的语音输入逻辑默认想要插入在当前文本的最后面,那也可以将这段逻辑给删除。
逻辑实现后,在页面中渲染一个语音输入的按钮就好了:
<!-- 语音输入按钮 -->
<view
class="absolute -right-2 -top-6 w-8 h-8 rounded-full flex items-center justify-center transition-colors transition-all"
:class="[
voiceState.isRecording
? 'bg-indigo-500 recording-button'
: 'bg-indigo-50',
]"
:hoverClass="voiceState.isRecording ? '' : 'scale-95 origin-center'"
:hoverStartTime="0"
:hoverStayTime="200"
@tap="() => handleVoiceInput(vibrate)"
>
<wd-loading v-if="voiceState.recordLoading" />
<text
v-else
:class="[
voiceState.isRecording
? 'i-tabler-player-stop-filled'
: 'i-tabler-microphone',
voiceState.isRecording ? 'text-white' : 'text-indigo-500',
]"
class="text-base"
/>
</view>
最终实现的效果如下图所示:
录音状态 | 默认状态 |
---|---|
💡注意点!!
发布小程序的时候,要选择采集用户隐私,否则线上版本是不会弹出采集麦克风权限的提示的,而是会直接调用失败。个人小程序也可以申请采集用户隐私的,实测可以审核通过。
总结
实现语音转文字的效果通过插件来实现还是很简单的,就是要记得看上面提到的注意点。不然我们在发布之后才发现无法语音输入去排查,是很难看出问题的。
如果你想看看实际实现效果也可以进入学习卡盒小程序,在卡盒中点击顶部制定学习计划,然后使用复述模式进行复习,就可以看到语音输入的按钮了。如果文章对你有帮助,欢迎点个赞,respect~