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();
});
相关推荐
2601_9637713733 分钟前
Hardening Enterprise WordPress Sites Against REST API Leaks and Bad Headers
前端·windows·word·php
蓝银草同学36 分钟前
Stream 实战:博客列表排序、过滤与分页(AI 辅助学习 Java 8)
java·前端·后端
2601_957190901 小时前
飞行影院安装施工指南:场地、动感系统与影片内容配套
大数据·前端·人工智能
爱折腾的小黑牛1 小时前
简记往来批量录入功能的实现:从文本到结构化数据
前端·算法
YHHLAI1 小时前
Agent 智能体开发实战 · 第六课:MCP 协议 —— 让 Agent 跨进程调用工具
前端·人工智能
SmartBoyW1 小时前
前端死磕:一文彻底搞懂 JS 事件循环 (Event Loop) 与宏微任务
前端·javascript
爱勇宝2 小时前
3位工程师靠“删AI代码”创业,一周收费1万美元:以后最贵的能力,可能不是写代码
前端·后端·架构
Curvatureflight2 小时前
客户端日志上报怎么设计?让线上问题不再只靠用户截图
前端·系统架构
不法2 小时前
uniapp map地图导航/地图路线
前端·vue.js·uni-app