吟秋知忆

V1

2022/08/10阅读:22主题:自定义主题1

小程序-语音播报功能

功能需求

实现通过传入“语音文案自动播报”,如:“测试播报”,语音自动播报“测试播报”

方案

  1. 使用wx.playVoice播放语音文件

优点:使用简单,直接播放录制好的音频文件

缺点:只能播放录制好的语音文件

  1. 第三方语音服务 + wx.createInnerAudioContext

优点:功能强大,可定制,可以满足复杂的场景;

缺点:需要服务端对接,付费

  1. 小程序插件 + wx.createInnerAudioContext

优点:简单快捷,适用于简单场景;

缺点:功能单一,无法定制,大写英文无法识别,部分小写英文发音不准

第三方语音平台

语音平台

  1. 腾讯Ai
  2. 科大讯飞
  3. 百度Ai
  4. 阿里云

服务端

对接第三方平台,将转换后的语音数据交给小程序。

小程序

1.添加请求

wx.request({
  url: 'https://xxxx.com',
  data: { data: "待合成的语音数据"},
  method: "get",
  header: {
    'content-type''application/json' // 默认值
  },
  dataType: "json",
  success: function (res{
    const innerAudioContext = wx.createInnerAudioContext();
    innerAudioContext.src = res.filename;
    innerAudioContext.play();
  }
})

微信插件

  1. 添加微信同声传译插件:在微信公众平台配置,找到设置–第三方设置–插件管理–点击添加插件 微信同声传译
  2. 搜索微信同声传译,点击添加 微信同声传译
  3. 引入插件:在项目根目录app.json文件中配置
"plugins": {   
    // 引入插件
    "WechatSI": {     //   自定义的名字
      "version""0.3.5",   // 引入插件的版本号
      "provider""wx069ba97219f66d99"    // 引入插件的appID
    }
  }
  1. 使用
const plugin = requirePlugin('WechatSI');

onReady() {
  this.innerAudioContext = wx.createInnerAudioContext();
  const that = this;
  plugin.textToSpeech({
    // 调用插件的方法
    lang: 'zh_CN',
    content: '测试语音播报',
    success: function (res) {
      that.playAudio(res.filename);
    }
  });
},

playAudio(e) {
  this.innerAudioContext.src = e;
  this.innerAudioContext.play();
},
  1. 封装优化
const plugin = requirePlugin('WechatSI');
import { trim, isLetter } from '@/shared/index';

class PluginService {
  innerAudioContext;

  audioBroadcast(content: string): Promise<any> {
    return new Promise((resolve, reject) => {
      this.innerAudioContext = wx.createInnerAudioContext();
      plugin.textToSpeech({
        // 调用插件的方法
        lang: 'zh_CN',
        content,
        success: res => {
          this.playAudio(res.filename, resolve, reject);
        },
        fail: reject
      });
    });
  }

  playAudio(e, resolve, reject) {
    this.innerAudioContext.src = e;
    this.innerAudioContext.play();

    this.innerAudioContext.onStop(() => {
      console.log('[ onStop ] >');
      resolve({
        type'stop'
      });
    });

    this.innerAudioContext.onEnded(() => {
      //播放结束,销毁该实例
      this.innerAudioContext.destroy();

      console.log('[ onEnded ] >');

      resolve({
        type'end'
      });
    });

    this.innerAudioContext.onError(error => {
      console.log('[ onError ] >', error);

      this.innerAudioContext.destroy();
      reject(error);
    });
  }

  stopAudio() {
    this.innerAudioContext.stop();
    this.innerAudioContext.destroy();
  }
}

export default new PluginService();

在shared/index中工具函数:

// 去掉字符串中所有空格(包括中间空格,需要设置第2个参数为:true)
export function trim(str: string, isGlobal = true{
  if (!str) {
    return str;
  }
  const result = str.replace(/(^\s+)|(\s+$)/g'');
  return isGlobal ? result.replace(/\s/g'') : result;
}

// 判断是否为字母
export function isLetter(str{
  const reg = /^[A-Za-z]+$/;
  return reg.test(str);
}

// 字母转中文
export function translateAlphabet(letters{
  if (!letters || !(letters.length > 0)) {
    return letters;
  }

  let tLetters = '';

  for (const key of letters) {
    tLetters += alphabetComparison(key);
  }

  return trim(tLetters);
}

// 字母中文读法
export function alphabetComparison(letter{
  if (!letter || !isLetter(letter)) {
    return letter;
  }

  const letterCase = letter.toUpperCase();
  const letterMap = {
    A: '诶',
    B: 'b',
    C: '西',
    D: '帝',
    E: '伊',
    F: 'f',
    G: 'g',
    H: 'h',
    I: 'i',
    J: 'j',
    K: 'k',
    L: 'l',
    M: 'm',
    N: 'n',
    O: '欧',
    P: 'p',
    Q: 'q',
    R: 'r',
    S: '爱死',
    T: 't',
    U: '优',
    V: 'v',
    W: 'w',
    X: 'x',
    Y: '歪',
    Z: 'z'
  };
  return letterMap[letterCase];
}

在页面中使用

import { pluginService, translateAlphabet } from '@util';
import { formatCabinetNo, getCacheCabinet } from '@/shared/index';

Page({

  ...
  
  onLoad(e) {
    // 获取订单结果类型
    this.autoPlay();
  },
  
  onUnload() {
    pluginService.stopAudio();
  },

  // 轮询播放多次
  autoPlay(timerCount = 0, maxCount = 100) {
    const { address, lockerName } = this.data;

    console.log('timerCount:', timerCount);

    if (timerCount > maxCount) {
      return;
    }

    const tAddress = translateAlphabet(address);

    console.log('[ translateAlphabet ] >', tAddress);

    // '认准柜门号再存放,不要存错门'
    const message = `您的柜门号是${tAddress}${lockerName},请认准柜门号存放,不要存错门`;

    pluginService
      .audioBroadcast(message)
      .then(res => {
        if (res.type !== 'stop') {
          this.autoPlay(++timerCount);
        }
        console.log('audioBroadcast.complete', res);
      })
      .catch(err => {
        console.log('[ audioBroadcast.err ] >', err);
        this.autoPlay(timerCount);
      });
  }
});

注意:

  1. 语音输入配额:每个小程序250条/分钟,3w条/天。
  2. 文本翻译配额:每个小程序500次/分钟,10w次/天。
  3. 语音合成配额:每个小程序100次/分钟,2w次/天。

问题归档

  1. 在小程序onHide,onUnload函数里调用stop方法无效;需要同时调用destroy方法
this.innerAudioContext.stop();
this.innerAudioContext.destroy();
  1. 经测试发现中文中的大写英文字母无法识别,部分小写英文字母发音有误

参考

关注我

利用空闲时间免费兼职(无门槛,长期有效),请联系我(PS:关注公众号后,点击“联系我”获取联系方式)~ 吟秋知忆

分类:

前端

标签:

前端

作者介绍

吟秋知忆
V1