小程序语音播报
背景:项目中有一个功能是小程序扫码之后进行语音播报,使用uni.createInnerAudioContext
方法,并不是一帆风顺的,所以记录一下。
需要注意的点
- 全局使用一个音频实例(
uni
官方建议这种方法,因为频繁切换src
,可能会出现-99
的错误) - 语音播放过程中,调用
uni.scanCode
会中断语音 - 据测试,
ios
直接播放,会出现延迟播放的情况,项目里面使用uni.downloadFile
将资源下载下来,再播放可以有效缓解该问题 ios
直接进行播放,可能是由于音频资源没准备好,可能会直接进入到onError
,这时根据错误码重新调用播放即可。 碰到的两种错误信息如下:
-
{"errMsg": "operateAudio:fail jsapi has no permission, event=operateAudio, runningState=background, permissionMsg=permission got, detail=jsapi has no permission, appId=wxd161895220febc6f", "errCode": -1}
-
{"errMsg": "operateAudio:fail audioInstance is not set", "errCode": -1}
代码示例
ts
import { onUnload } from "@dcloudio/uni-app";
let innerAudioContext: any = null;
// 销毁音频
const destroyAudio = () => {
if (innerAudioContext) {
innerAudioContext.stop();
innerAudioContext.destroy();
innerAudioContext = null;
}
};
// 创建音频
const createAudio = (audioSrc: string) => {
destroyAudio(); // 每次创建将上一个实例销毁
innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.src = audioSrc;
// 播放事件
innerAudioContext.onPlay(() => {
console.log("play audio");
});
// 播放结束事件
innerAudioContext.onEnded(() => {
console.log("ended audio");
});
// 播放错误事件
innerAudioContext.onError((error: any) => {
console.log("play audio error", error);
// 兼容ios(估计是官方bug),可能是ios资源没准备好时,播放会发生错误返回errCode为-1,这时需要重新播放,直至播放成功
if (error.errCode === -1) {
createAudio(audioSrc);
} else {
// 比如文件不存在等情况,会进入到这个提示
uni.showToast({
title: "播放失败:" + error.errMsg,
icon: "none",
});
}
});
innerAudioContext.play();
};
const downloadAudio = (fileName: string) => {
if (!fileName) {
return;
}
// 扫码失败时接口返回一个音频文件名,通过文件名拼接成完整的音频地址
const audioUrl = "https://xxx.com/static/" + fileName;
// 先将资源下载下来,在ios下播放速度会更快(经本人测试,不下载,直接播放,ios机型会有明显的延迟播放行为)
uni.downloadFile({
url: audioUrl,
success: ({ tempFilePath }) => {
createAudio(tempFilePath);
},
});
};
const onScan = () => {
uni.scanCode({
success(res) {
// 扫码结果
scanCheck(res.result);
},
});
};
const scanCheck = (code: string) => {
// 发送扫码请求
scanRequest(code)
.then(() => {
// 扫码成功之后,每隔1s进行一次扫码
setTimeout(() => onScan(), 1000);
uni.showToast({
title:"扫码成功",
icon:'none',
mask:true
}
);
})
.catch((res) => {
// 扫码失败之后,利用接口返回的文件名创建语音实例,播报音频
downloadAudio(res.fileName);
uni.showToast({
title:res.msg|| "扫码失败",
icon:'none',
mask:true,
duration:2000
}
});
};
onUnload(() => {
// 页面卸载时销毁音频
destroyAudio();
});