可以重用缓冲区,这有点老套。
首先创建
const audioCtx = new AudioContext();
然后像往常一样获取资源:
let fire = await fetch('shotgun.mp3');
fire = await fire.arrayBuffer();
fire = await audioCtx.decodeAudioData(fire);
const shot = audioCtx.createBufferSource();
shot.buffer = fire;
shot.loopEnd = 0.00001; //some small value to make it unplayable
shot.start(0);
然后,在活动期间(在我的情况下是鼠标按下):
shot.loopEnd = 1; //that restarts sound and plays in a loop.
接下来,在演奏完毕后,再次设置
shot.loopEnd = 0.00001;
在我的例子中,我在requestAnimationFrame中停止它
<canvas width="1200" height="800" id="main"></canvas>
<script>
var Mouse = {x: 0, y: 0, fire: false};
(async function() {
"use strict";
const reload = 25;
var ireload = 0;
const audioCtx = new AudioContext();
let fire = await fetch('shotgun.mp3');
let bgMusic = await fetch('hard.mp3');
fire = await fire.arrayBuffer();
bgMusic = await bgMusic.arrayBuffer();
const bgMdecoded = await audioCtx.decodeAudioData(bgMusic);
const fireDecoded = await audioCtx.decodeAudioData(fire);
const bgM = audioCtx.createBufferSource();
bgM.buffer = bgMdecoded;
bgM.loop = true;
bgM.connect(audioCtx.destination)
bgM.start(0);
let shot = audioCtx.createBufferSource();
shot.buffer = fireDecoded;
shot.connect(audioCtx.destination);
shot.loopEnd = 0.00001; //some small value to make it unplayable
shot.start(0);
document.getElementById('main').onmousedown = function(e) {
Mouse.x = e.layerX;
Mouse.y = e.layerY;
Mouse.fire = true;
}
function main(tick) {
var dt = lastTick - tick;
lastTick = tick;
///take fire
//asuming 60fps, which is true in my case, I stop it after a second
if(reload < -35) {
shot.loopEnd = 0.00001;
}
if(--ireload < 0 && Mouse.fire) {
ireload = reload;
shot.loopEnd = 1; //that restarts sound and plays in a loop.
Mouse.fire = false;
}
/* moving objects, rendering on thread with offscreen canvas */
requestAnimationFrame(main);
}
let lastTick = performance.now();
main(lastTick);
})();
</script>
关于GC,它确实可以快速处理音频缓冲区,但我已经检查过,GC只在有分配和内存重新分配时才会触发。垃圾回收器会中断所有脚本执行,因此会出现jank、lag。
我使用内存池来配合这个技巧,在初始化时分配池,然后只重用对象,在第二次扫描后实际上没有GC,它在初始化后运行一次,在优化后第二次启动,减少了未使用的内存。在那之后,根本没有GC。使用类型数组和workers可以获得非常高的性能组合,帧率为60 fps,声音清脆,完全没有延迟。
您可能认为锁定GC是一个坏主意。也许你是对的,但毕竟,仅仅因为有GC而浪费资源似乎也不是一个好主意。
经过测试,AudioWorklets似乎按预期工作,但这些设备很重,难以维护,消耗了大量资源,简单地将输入复制到输出的写入处理器违背了它的目的。PostMessaging系统是一个非常繁重的过程,您必须以标准方式连接并重新创建缓冲区,或者将其复制到Worklet空间并通过共享数组和原子操作手动管理。
您可能还对以下内容感兴趣:
Writeup about WebAudio design
如果作者有共同的担忧,但遇到了完全相同的问题,请引用
我知道我在这里打一场艰苦的战斗,但GC不是我们想要的
实时音频播放时的需求。
尽管在我自己的测试中,保留一个AudioBuffers池似乎是有效的
随着时间的推移,在大型GC擦除之前,我仍然看到12MB的缓慢增长,
根据Chrome分析器。
还有
Writeup about GC
,其中描述了JavaScript中的内存泄漏。引用:
考虑以下情况:
-
执行了一组相当大的分配。
-
这些元素中的大多数(或全部)都被标记为不可达(假设我们将指向缓存的引用设为null,我们没有
更长的需求)。
-
没有进行进一步的分配。
在这种情况下,大多数GC将不会运行任何进一步的收集过程。
换句话说,即使有无法访问的引用可用
对于收藏,收藏家不会认领这些物品。这些不是
严格来说是泄漏,但仍然会导致比平时更高的内存使用率。