先说一下我的需求:类似一个钢琴键盘,摁键实时播放对应的音频,要求就是延迟够低,播放流畅。目前我试了大概三种方案。其中一种方案可能对大多数人不适用,当然最后也没有采用
第一种方案: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两个平台,使用第二种方案目前效果是最好的
这里的总结仅个人见解,如果有大佬在这方面有研究或者有更好的替代方案欢迎讨论