通过JS与重叠播放音频

Play Audio through JS with Overlapping

本文关键字:播放 音频 重叠 JS 通过      更新时间:2023-09-26

我为一款基于画布的游戏编写了以下JS代码。

var EXPLOSION = "sounds/explosion.wav";
function playSound(str, vol) {
  var snd = new Audio();
  snd.src = str;
  snd.volume = vol;
  snd.play();
}
function createExplosion() {
  playSound(EXPLOSION, 0.5);
}

这个可以工作,但是每次调用它时,它都会发送一个服务器请求来下载声音文件。或者,如果我事先声明Audio对象:

var snd = new Audio();
snd.src = EXPLOSION;
snd.volume = 0.5;
function createExplosion() {
  snd.play();
}

这是有效的,但是如果createExplosion函数在声音完成播放之前被调用,它根本不播放声音。这意味着一次只允许播放一个声音文件——在多个爆炸发生的场景中,它根本不起作用。

是否有办法正确播放音频文件多次重叠与自己?

我在一款俄罗斯方块游戏中寻找了很长时间,我认为这个解决方案是最好的。

function playSoundMove() {
  var sound = document.getElementById("move");
  sound.load();     
  sound.play();
}

只要把它加载好就可以了

您可以使用复制节点的cloneNode()play()来复制节点。

我的audio元素看起来像这样:

<audio id="knight-audio" src="knight.ogg" preload="auto"></audio>

,我有一个onClick监听器,它就是这样做的:

function click() {
    const origAudio = document.getElementById("knight-audio");
    const newAudio = origAudio.cloneNode()
    newAudio.play()
}

由于不显示audio元素,因此实际上不必将节点附加到任何东西上。

我验证了客户端和服务器端Chrome只尝试下载一次音频文件。

警告:我不确定对性能的影响,因为在我的网站上,这个剪辑的播放次数最多不超过40倍。如果你做的事情比这个大得多,你可能需要清理音频节点?

试试这个:

(function() {
    var snds = {};
    window.playSound(str,vol) {
        if( !snds[str]) (snds[str] = new Audio()).src = str;
        snds[str].volume = vol;
        snds[str].play();
    }
})();

那么你第一次调用它时,它将获取声音,但之后每次它都会重复使用相同的声音对象。


编辑:你也可以预加载重复,让声音播放不止一次:

(function() {
    var snds = {}
    window.playSound = function(str,vol) {
        if( !snds[str]) {
            snds[str] = [new Audio()];
            snds[str][0].src = str;
        }
        var snd = snds[str], pointer = 0;
        while( snd[pointer].playing) {
            pointer++;
            if( pointer >= snd.length) {
                snd.push(new Audio());
                snd[pointer].src = str;
            }
        }
        snd[pointer].volume = vol;
        snd[pointer].play();
    };
})();

请注意,如果你播放的声音重叠太多,它会发送多个请求,但它应该很快返回未修改,只有当你播放的次数比以前多时才会这样做。

在我的游戏中,我使用预加载,但在声音启动后(不预加载或在页面加载时预加载所有内容并不明智,有些声音根本没有在某些游戏玩法中播放,为什么要加载它们)

const audio {};
audio.dataload = {'entity':false,'entityes':[],'n':0};
audio.dataload.ordernum = function() {
  audio.dataload.n = (audio.dataload.n + 1)%10;
  return audio.dataload.n;
}
audio.dataload.play =  function() {
 
    audio.dataload.entity = new Audio('/some.mp3');
    for (let i = 0; i<10;i++) {
      audio.dataload.entityes.push(audio.dataload.entity.cloneNode());
    }
   
    audio.dataload.entityes[audio.dataload.ordernum()].play();
  
}
audio.dataload.play() // plays sound and preload sounds to memory when it isn't

我创建了一个允许分层音频的类。这与其他答案非常相似,它使用相同的src创建另一个节点,但该类只在必要时才会执行。如果它已经创建了一个已经完成的节点,它将重播该现有节点。

另一个调整是最初获取音频并使用blob的URL。我这样做是为了效率;因此,SRC不必在每次创建新节点时都从外部获取。

class LayeredAudio {
    url;
    samples = [];
    constructor(src){
        fetch(src)
            .then(response => response.blob())
            .then((blob) => {
                this.url = URL.createObjectURL(blob);
                this.samples[0] = new Audio(this.url);
            });
    }
    play(){
        if(!this.samples.find(e => e.paused)?.play()){
            this.samples.push(new Audio(this.url))
            this.samples[this.samples.length - 1].play()
        }
    }
}
  
const aud = new LayeredAudio("URL");
aud.play()

更多地依赖于内存而不是处理时间,我们可以制作多个音频克隆的数组,然后按顺序播放它们:

function gameSnd() {
    tick_wav = new Audio('sounds/tick.wav');
    victory_wav = new Audio('sounds/victory.wav');
    counter = 0;
    ticks = [];
    for (var i = 0; i<10;i++)
        ticks.push(tick_wav.cloneNode());
    tick = function(){
        counter = (counter + 1)%10;
        ticks[counter].play();
    }
    victory = function(){
        victory_wav.play();
    }
}

当我尝试其他一些解决方案时,出现了一些延迟,但我可能已经找到了更好的替代方案。如果您将音频数组的长度设置得很高,这将消耗大量内存。我怀疑你是否需要在同一时间播放相同的音频超过10次,但如果你只是把数组长度变长。

  var audio = new Array(10); 
  // The length of the audio array is how many times
  // the audio can overlap
  for (var i = 0; i < audio.length; i++) {
    audio[i] = new Audio("your audio");
  }
  function PlayAudio() {
    // Whenever you want to play it call this function
    audio[audioIndex].play();
    audioIndex++;
    if(audioIndex > audio.length - 1) {
      audioIndex = 0;
    }
  }

我发现这是一个简单的方法来重叠相同的音频本身

<button id="btn" onclick="clickMe()">ding</button>
<script>
function clickMe() {
    const newAudio = new Audio("./ding.mp3")
    newAudio.play()

}