阿里云语音识别官方文档

https://help.aliyun.com/zh/isi/developer-reference/api-reference-1


项目环境

uniapp + vite + vue3 + typescript


因为业务需要,小程序要添加语音识别功能,根据需求和费用,最终决定使用阿里云的一句话识别接口。具体的收费标准如下,需要注意的是:每个账号有 30 天试用期,可一旦你开通了商业模式,试用期就会被清空。

收费标准.png


首先下载官方的小程序 Demo (下载地址:alibabacloud-nls-wx-sdk-master.zip),解压后文件如下,

解压后的文件夹.png


然后我们将 utils 文件夹下的 js 文件拷贝到 uniapp 项目的 static 文件夹下。我在 static 文件夹下新建了一个 SR 文件夹,然后将拷贝的 js 文件放入了里面。

目录结构.png


因为项目中多个地方要用到录音功能,所以就将其封装成了一个 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 {}
    }
})


如果你在集成过程中有解决不了的问题,你还可以加入阿里的官方技术支持群,找群内工作人员寻求帮助。不过目前他们的技术支持群只有钉钉群,所以为了寻求帮助,你必须得先下载安装一个钉钉才行。入群的二维码在阿里官网里有。我之前遇到问题,在群里提问,他们回复的还是很快的。

钉钉技术支持群.png


最后记得在小程序后台配置业务域名,不然真机测试的时候是获取不到语音识别数据的。而且域名配置还得注意,他们官方有两个地址,不要配错了,之前就踩了坑,花了冤枉时间。还有就是在微信开发者工具里面做不了语音识别测试,需要用手机才行。

域名问题.png

本文最后更新于 2025-02-12 18:20:24VUE
天生我材必有用,千金散尽还复来~~
作者:鄢云峰 YYF声明:转载请注明文章出处地址:https://yanyunfeng.com/article/70
评论
提交
来发第一个评论啦~