阿里云语音识别官方文档
https://help.aliyun.com/zh/isi/developer-reference/api-reference-1
项目环境
uniapp + vite + vue3 + typescript
因为业务需要,小程序要添加语音识别功能,根据需求和费用,最终决定使用阿里云的一句话识别接口。具体的收费标准如下,需要注意的是:每个账号有 30 天试用期,可一旦你开通了商业模式,试用期就会被清空。
首先下载官方的小程序 Demo (下载地址:alibabacloud-nls-wx-sdk-master.zip),解压后文件如下,
然后我们将 utils 文件夹下的 js 文件拷贝到 uniapp 项目的 static 文件夹下。我在 static 文件夹下新建了一个 SR 文件夹,然后将拷贝的 js 文件放入了里面。
因为项目中多个地方要用到录音功能,所以就将其封装成了一个 hook 方便逻辑复用,完整的 hook 代码如下:
useSR.ts
const SpeechRecognition = require('../static/SR/sr')
const sleep = require('../static/SR/util').sleep
import { ref } from 'vue'
export default function () {
const srStart = ref(false)
const srResult = ref<any>()
const sr = ref<any>(null)
const initSR = async (token: string) => {
sr.value = new SpeechRecognition({
url : 'wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1', // 此地址是固定的,官方提供的
appkey: '', // 此处填写你自己申请的appkey
token
})
bindSREvent()
bindRMEvent()
}
// 绑定录音事件
const bindRMEvent = () => {
// 录音时实时传输录音数据给阿里云接口
uni.getRecorderManager().onFrameRecorded((res) => {
if (sr.value && srStart.value) {
sr.value.sendAudio(res.frameBuffer)
}
})
// 录音停止时删除临时文件
uni.getRecorderManager().onStop((res) => {
if (res.tempFilePath) {
uni.getFileSystemManager().removeSavedFile({filePath: res.tempFilePath})
}
})
uni.getRecorderManager().onError((res) => {
console.log("recording failed:" + res)
})
}
// 绑定识别事件
const bindSREvent = () => {
// 识别完成
sr.value.on("completed", (msg: string)=>{
srResult.value = msg
})
// 失败
sr.value.on("failed", (msg: string)=> {
console.log("Client recv failed:", msg)
})
}
// 开始
const onStart = async () => {
// 尚未初始化
if (!sr.value) {
return
}
// 识别已经开始
if (srStart.value) {
return
}
try {
await sr.value.start(sr.value.defaultStartParams())
srStart.value = true
} catch (e) {
console.log("start failed:" + e)
return
}
// 录音开始
uni.getRecorderManager().start({
duration: 60000,
numberOfChannels: 1,
sampleRate : 16000,
format: "PCM", // 注意:format 必须与识别接口设置为一致
frameSize: 4
})
}
// 结束
const onStop = async () => {
uni.getRecorderManager().stop()
await sleep(500)
if (srStart.value && sr.value) {
try {
await sr.value.close()
srStart.value = false
} catch(e) {
console.log("close sr failed:" + e)
}
}
}
// 关闭
const onClose = () => {
srStart.value = false
uni.getRecorderManager().stop()
if (sr.value) {
sr.value.shutdown()
} else {
console.log("sr is null")
}
}
return { initSR, onStart, onStop, onClose, srResult }
}
在页面或组建中使用,具体举例如下:
HTML 模板部分
<template>
<view class="script">
<view class="script-title">新增参考话术</view>
<view class="script-content">
<textarea
class="record-text"
v-model="script.content"
maxlength="500"
placeholder="在此输入,最多500字,您也可以语音识别录入,单次录音时长为 1 分钟,1分钟后自动停止"
placeholder-class="textarea-placeholder"
/>
</view>
<view class="script-buttons">
<st-button height="60" fontSize="28" width="700" @click="submitScript">提交</st-button>
</view>
</view>
<view class="record-button-box">
<view class="record-button" @touchstart="checkPermission" @touchend="srEnd">
按住 说话
</view>
</view>
</template>
JavaScript 部分
import { ref, watch } from 'vue'
import $apiLogin from '@/apis/login'
import useSR from '@/hooks/useSR'
import { onLoad, onUnload } from '@dcloudio/uni-app'
const { initSR, onStart, onStop, onClose, srResult } = useSR()
// 调用后端接口,获取语音识别接口 token
// 其实我们也可以在前端直接调用阿里云的 API 获取 token, 但是这样的话会在前端代码中暴露 AKID(AccessKey ID)和AKKEY(AccessKey Secret)
// AKID 和 AKKEY 是重要参数,暴露在前端不安全,如果被别人拿了去,可以直接刷你语音识别接口次数
// 所以还是放在后端,让后端封装一个接口去调用阿里云的 API,然后直接给我们返回 token 比较好
const getSRToken = async () => {
const result = await $apiLogin.getSRToken()
initSR(result.token)
}
// 页面加载时获取语音识别接口 token
onLoad((options: any) => {
getSRToken()
})
// 页面销毁时关闭语音识别
onUnload(() => { onClose() })
// 检查小程序是否开启了录音权限,没有开启的话提示用户开启,开启之后才可以进行语音识别
const checkPermission = () => {
uni.getSetting({
success (res) {
if (!res.authSetting['scope.record']) {
uni.authorize({
scope: 'scope.record',
success() {
srBegin()
},
fail() {
uni.openSetting({
success(settingData) {
if (settingData.authSetting['scope.record']) {
srBegin()
} else {
console.log('用户仍然没有开启录音权限');
}
}
});
}
});
} else {
srBegin()
}
}
})
}
// 计时器,当用户录音识别超过 1 分钟(60秒)时,自动停止
const timer = ref<any>(null)
const timerCount = ref(60)
// 开始识别
const srBegin = () => {
onStart().then(() => {
uni.showLoading({ title: '录音中', mask: false })
timer.value = setInterval(() => {
timerCount.value--
if (timerCount.value === 0) {
onStop().then(() => {
clearInterval(timer.value)
})
}
}, 1000)
})
}
// 停止识别
const srEnd = () => {
uni.hideLoading()
onStop().then(() => {
uni.hideLoading()
if (timer.value) {
clearInterval(timer.value)
timerCount.value = 60
}
})
}
watch(() => srResult.value, (newVal, oldVal) => {
if (newVal) {
try {
const r = JSON.parse(newVal)
if (r.payload) {
script.value.content += r.payload.result
}
} catch {}
}
})
如果你在集成过程中有解决不了的问题,你还可以加入阿里的官方技术支持群,找群内工作人员寻求帮助。不过目前他们的技术支持群只有钉钉群,所以为了寻求帮助,你必须得先下载安装一个钉钉才行。入群的二维码在阿里官网里有。我之前遇到问题,在群里提问,他们回复的还是很快的。
最后记得在小程序后台配置业务域名,不然真机测试的时候是获取不到语音识别数据的。而且域名配置还得注意,他们官方有两个地址,不要配错了,之前就踩了坑,花了冤枉时间。还有就是在微信开发者工具里面做不了语音识别测试,需要用手机才行。