当前位置:   article > 正文

uniapp实现微信小程序websocket+背景音频语音播报_uniapp 微信小程序订单语音播报

uniapp 微信小程序订单语音播报

业务需求:根据后台返回消息播报语音,要求后台运行可继续播报

实现步骤:

  1. 建立socket连接监听消息,并建立心跳检测机制,防止socket意外断连
  2. 将接收到的文字信息转化为音频文件
  3. 使用uni.getBackgroundAudioManager(),实现后台运行可以持续播报音频
  4. 解决并发问题,同时接收多个文件按顺序播报

1. 建立socket连接

  1. onShow() {
  2. const SocketTask = getApp().globalData.SocketTask;
  3. if (!SocketTask) {
  4. this.linkSocket();
  5. }
  6. }
  7. async linkSocket() {
  8. // 连接Socket服务器
  9. let SocketTask;
  10. const TOKEN = store.getters['base/_token'];
  11. await new Promise((resolve, reject) => {
  12. SocketTask = uni.connectSocket({
  13. url: wsUrl,
  14. header: {
  15. 'content-type': 'application/json',
  16. Authorization: TOKEN
  17. },
  18. success: () => {
  19. console.log(`WebSocket connect成功`);
  20. resolve();
  21. },
  22. fail: () => {
  23. console.log('WebSocket connect失败');
  24. reject();
  25. }
  26. });
  27. });
  28. SocketTask.id = this.randomInt(1000, 9999);
  29. // 初始化心跳,心跳用于检测连接是否正常
  30. SocketTask.heartCheck = { ...heartCheck };
  31. SocketTask.reconnectObj = { ...reconnectObj };
  32. // 赋值全局变量,用于判断是否已连接
  33. getApp().globalData.SocketTask = this.SocketTask = SocketTask;
  34. // 开始监听
  35. this.initEventHandle(this.SocketTask);
  36. }
  37. randomInt(min, max) {
  38. return Math.floor(Math.random() * (max - min + 1) + min);
  39. }

定义心跳对象

  1. // ws 心跳对象
  2. let heartCheck = {
  3. timeout: 5000, // 收到pong后再次发ping的间隔时间;也是ws连接不上的最大等待时间 之后关掉连接并重连
  4. timeoutObj: null,
  5. serverTimeoutObj: null,
  6. reset: function() {
  7. clearTimeout(this.timeoutObj);
  8. clearTimeout(this.serverTimeoutObj);
  9. return this;
  10. },
  11. start: function(SocketTask) {
  12. this.timeoutObj = setTimeout(() => {
  13. SocketTask.send({ data: 'ping' });
  14. this.serverTimeoutObj = setTimeout(() => {
  15. SocketTask.close();
  16. }, this.timeout);
  17. }, this.timeout);
  18. }
  19. };

消息监听

  1. initEventHandle(SocketTask) {
  2. // 监听消息
  3. SocketTask.onMessage(res => {
  4. // 心跳检测,后台返回pong表示连接正常
  5. if (res.data === 'pong') {
  6. SocketTask.heartCheck.reset().start(SocketTask);
  7. } else {
  8. this.toAudioText = res.data;
  9. // 接收到文字消息转语音
  10. this.getMp3();
  11. }
  12. });
  13. SocketTask.onOpen(() => {
  14. console.log(`${SocketTask.id} WebSocket onOpen`);
  15. SocketTask.heartCheck.reset().start(SocketTask);
  16. SocketTask.heartCheck.isAlive = true;
  17. });
  18. SocketTask.onError(res => {
  19. console.log(`${SocketTask.id} WebSocket onError`, res);
  20. this.reconnect(SocketTask);
  21. });
  22. SocketTask.onClose(res => {
  23. console.log(`${SocketTask.id} WebSocket onClose`, res);
  24. SocketTask.heartCheck.isAlive = false;
  25. if (res.code === 4000 || res.code === 4001) {
  26. console.log(`${SocketTask.id} WebSocket ${res.code}关闭`);
  27. } else {
  28. // 非正常关闭 重连
  29. this.reconnect(SocketTask);
  30. }
  31. });
  32. }

重连机制

  1. let reconnectObj = {
  2. timeout: 5000, // 重连间隔时间
  3. timer: null, // 重连计时器
  4. lock: false, // 重连锁
  5. limit: 0 // 重连最多次数
  6. };
  7. ...
  8. reconnect(SocketTask) {
  9. if (SocketTask.reconnectObj.lock) return;
  10. SocketTask.reconnectObj.lock = true;
  11. clearTimeout(SocketTask.reconnectObj.timer);
  12. if (SocketTask.reconnectObj.limit < 24) {
  13. SocketTask.reconnectObj.timer = setTimeout(() => {
  14. this.linkSocket();
  15. SocketTask.reconnectObj.lock = false;
  16. }, SocketTask.reconnectObj.timeout);
  17. SocketTask.reconnectObj.limit++;
  18. } else {
  19. console.log(`${SocketTask.id}WebSocket reach limit 24!!!!!!!`);
  20. uni.showToast({
  21. title: '很抱歉,您与服务器失去连接,请重启小程序',
  22. icon: 'none'
  23. });
  24. }
  25. }

2.使用零七生活API完成文字转语音,api返回二进制音频流,直接在小程序无法播放,需要使用writeFile写入文件后可播放,注意播放完成后使用fs.unlink删除本地文件

  1. const fs = uni.getFileSystemManager(); // 文件管理器API
  2. ...
  3. getMp3() {
  4. const target = `${wx.env.USER_DATA_PATH}/${new Date().getTime()}.mp3`;
  5. return new Promise((resolve, reject) => {
  6. uni.request({
  7. url: `https://api.oick.cn/txt/apiz.php?text=${this.toAudioText}&spd=5`,
  8. method: 'GET',
  9. responseType: 'arraybuffer', // 注意此处,否则返回音频格式不可用
  10. success: (res: any) => {
  11. fs.writeFile({
  12. filePath: target,
  13. data: res.data,
  14. encoding: 'binary',
  15. success: res => {
  16. // 将接收到的消息推进消息队列
  17. this.playAudio.push(target);
  18. },
  19. fail: err => {
  20. console.log(err);
  21. }
  22. });
  23. },
  24. fail: (err: any) => {
  25. reject(err);
  26. }
  27. });
  28. });
  29. }

3. 播放背景音频:注意需要在 app.json 中配置 requiredBackgroundModes 属性

为了实现锁屏也可接收消息,进入页面后开始循环播放空音频,保证音频播放不中断,接收到消息后播放消息队列;

  1. const backgroundAudioManager = uni.getBackgroundAudioManager();
  2. ...
  3. onShow() {
  4. //循环播放音频
  5. this.playInit();
  6. }
  7. playInit() {
  8. backgroundAudioManager.title = '语音播报';
  9. backgroundAudioManager.src = kong;
  10. backgroundAudioManager.onEnded(() => {
  11. if (this.index < this.playAudio.length) {
  12. console.log('播放列表:', this.playAudio);
  13. backgroundAudioManager.src = this.playAudio[this.index];
  14. // backgroundAudioManager.src = ding;
  15. this.index++;
  16. } else {
  17. backgroundAudioManager.src = kong;
  18. // 清空播放列表
  19. if (this.index > 0) {
  20. this.clearFile();
  21. this.playAudio = [];
  22. this.index = 0;
  23. }
  24. }
  25. });
  26. }

4.解决并发问题,定义playAudio数组用于存放音频队列,当接收多个消息时,将消息推入数组,当播放完一个音频后,检测数组是否为空,如果不为空,播放消息音频,否则继续播放空音频。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/381518
推荐阅读
相关标签
  

闽ICP备14008679号