可能是目前小程序实时交互音频的最优方案

2,549 阅读2分钟

先说一下我的需求:类似一个钢琴键盘,摁键实时播放对应的音频,要求就是延迟够低,播放流畅。目前我试了大概三种方案。其中一种方案可能对大多数人不适用,当然最后也没有采用

第一种方案:wx.createInnerAudioContext

const innerAudioContext = wx.createInnerAudioContext({
  useWebAudioImplement: false // 是否使用 WebAudio 作为底层音频驱动,默认关闭。对于短音频、播放频繁的音频建议开启此选项,开启后将获得更优的性能表现。由于开启此选项后也会带来一定的内存增长,因此对于长音频建议关闭此选项
})
innerAudioContext.src = 'http://ws.stream.qqmusic.qq.com/M500001VfvsJ21xFqb.mp3?guid=ffffffff82def4af4b12b3cd9337d5e7&uin=346897220&vkey=6292F51E1E384E061FF02C31F716658E5C81F5594D561F2E88B854E81CAAB7806D5E4F103E55D33C16F3FAC506D1AB172DE8600B37E43FAD&fromtag=46'

innerAudioContext.play() // 播放

innerAudioContext.pause() // 暂停

innerAudioContext.stop() // 停止

这种方案其实是原来的audio标签播放音频替代版。

从基础库 1.6.0 开始,audio标签已经停止维护,请使用 wx.createInnerAudioContext 代替

如果正常播放一个音频用这种方案还是可以的,不过我这里需要延迟低,而且会有多个音频,显然不太合适,所以就考虑第二种方案

第二种方案:WebAudioContext.createBufferSource

基础库 2.19.0 开始支持

官方示例:developers.weixin.qq.com/miniprogram…

核心代码片段:

// 初始化数据
const bufferMap = {};
wx.request({
  audiourl,
  responseType: 'arraybuffer',
  success: res => {
    audioCtx.decodeAudioData(res.data, buffer => {
      bufferMap[note] = buffer;
    });
  }
});

// 播放
const source = audioCtx.createBufferSource();
source.buffer = bufferMap[note];
source.connect(audioCtx.destination);
source.start();
    

前提是要把所需要的音频资源提前加载,放到内存中

这里做测试的时候延迟还算低,能够达到我们的要求,ios效果肯定比android的效果要好很多,因为他们的底层音频处理器用的不同

但是这个时候弹键盘的时候会有随机出现卡段的现象,后来查找问题原因应该是在每次创建source没有消除,内存溢出,然后对上面的代码进行更改

// 播放
let source = audioCtx.createBufferSource();
source.buffer = bufferMap[note];
source.connect(audioCtx.destination);
source.onended = () => {
  source.disconnect(); // 断开连接
  source = null; // 将 source 变量置为 null
};
source.start();

这样每次播放完都会消除对应的sourceNode,避免内存泄露

再次测试的效果流畅度会好很多

第三种方案:WebAudioContext.createScriptProcessor

采用这种方案是因为我们内部有基于rust的wasm,可以通过这个这种方式用wasm处理音频,前端只需要接口对应的outputbuffer,但是这种方案,在ios可以,对于安卓负载量太大,且处理性能不好,就pass掉了

总结:个人目前觉得无论是在流程度还是延迟,在ios跟android两个平台,使用第二种方案目前效果是最好的

这里的总结仅个人见解,如果有大佬在这方面有研究或者有更好的替代方案欢迎讨论