uniapp小程序语音播报功能

小程序语音播报

背景:项目中有一个功能是小程序扫码之后进行语音播报,使用uni.createInnerAudioContext方法,并不是一帆风顺的,所以记录一下。

需要注意的点

  • 全局使用一个音频实例(uni 官方建议这种方法,因为频繁切换 src,可能会出现-99 的错误)
  • 语音播放过程中,调用uni.scanCode会中断语音
  • 据测试,ios直接播放,会出现延迟播放的情况,项目里面使用uni.downloadFile将资源下载下来,再播放可以有效缓解该问题
  • ios直接进行播放,可能是由于音频资源没准备好,可能会直接进入到onError,这时根据错误码重新调用播放即可。 碰到的两种错误信息如下:
  1. {"errMsg": "operateAudio:fail jsapi has no permission, event=operateAudio, runningState=background, permissionMsg=permission got, detail=jsapi has no permission, appId=wxd161895220febc6f", "errCode": -1}

  2. {"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();
});
相关推荐
Htr_2 小时前
Cortex 使用指南:开源 API 知识层
前端·数据库·人工智能·重构·计算机外设
计算机魔术师2 小时前
Dario Amodei 发文呼吁 AI 行业放慢前沿速度并公布三步计划
前端
EatFan2 小时前
前后端分离项目中 Token 到底应该怎么设计?Access Token + Refresh Token 实战
java·前端·后端
dsyyyyy11012 小时前
Typescript装饰器
前端·javascript·typescript
一拳不是超人3 小时前
一个没测暗色模式的 Bug,吃掉了我一半 谷歌扩展用户
前端·javascript·程序员
liangshanbo12154 小时前
React useState 函数式更新面试题整理
前端·react.js·前端框架
涛涛ing4 小时前
为什么你的页面在 Safari 上总出问题?Interop 2027 正在解决这个 20 年老毛病
前端
不一样的少年_4 小时前
WebP 压缩到底在干嘛?小白也能看懂的原理拆解
前端·后端·图片资源
问心无愧05134 小时前
ctf show web 177
前端·笔记
不一样的少年_4 小时前
PNG/JPG 如何变成 WebP?真相不是改后缀!
前端·后端·图片资源