HTML5音频-重复播放声音点击,不管之前的迭代是否已经完成

HTML5 audio - play sound repeatedly on click, regardless if previous iteration has finished

本文关键字:迭代 是否 不管 -重 音频 播放声音 HTML5      更新时间:2023-09-26

我正在创建一个游戏,其中wav文件在点击时播放-在这种情况下,它是一声枪响"bang"。

问题是,如果我快速点击,它不会为每次点击播放一次声音-就好像在播放声音时忽略了点击,一旦声音完成,它又开始听点击。延迟似乎是1秒长,所以你认为如果有人每秒点击4或5次,我想要5次刘海,而不是1次。

这是我的HTML:

<audio id="gun_sound" preload>
    <source src="http://www.seancannon.com/_test/audio/gun_bang.wav" />
</audio>

这是我的JS:

$('#' + CANVAS_ID).bind(_click, function() {
    document.getElementById('gun_sound').play();
    adj_game_data(GAME_DATA_AMMO_ID, -1);
    check_ammo();
}); 

想法?

我知道这是一个很晚的答案,我只是想,但寻求一个解决方案,我发现,在我的情况下,最好的一个是预加载声音,然后克隆节点每次我想播放它,这允许我播放它,即使前一个还没有结束:

var sound = new Audio("click.mp3");
sound.preload = 'auto';
sound.load();
function playSound(volume) {
  var click=sound.cloneNode();
  click.volume=volume;
  click.play();
}

一旦预加载了gun_bang.wav,您就可以动态地制作带有该声音的新元素,播放音频,然后在音频完成时删除它们。

function gun_bang() {
  var audio = document.createElement("audio");
  audio.src = "http://www.seancannon.com/_test/audio/gun_bang.wav";
  audio.addEventListener("ended", function() {
    document.removeChild(this);
  }, false);
  audio.play();
}
$('#' + CANVAS_ID).bind(_click, function() {
  gun_bang();
  adj_game_data(GAME_DATA_AMMO_ID, -1);
  check_ammo();
});

你可以用你自己的程序特定的点击逻辑来包装下面的代码,但是下面的代码同时演示了3种枪响。如果您想要任何类型的延迟,您可以引入一个较小的sleep()函数(很容易找到许多用于此的工具包),或者您可以使用setTimeout()来调用后续的函数。

<audio id="gun_sound" preload="preload">
    <source src="http://www.seancannon.com/_test/audio/gun_bang.wav" />
</audio>
<script type="text/javascript">
jQuery(document).ready(function($){
  $('#gun_sound').clone()[0].play();
  $('#gun_sound').clone()[0].play();
  $('#gun_sound').clone()[0].play();
});
</script>

HTML5音频元素有许多限制,它并不适合像游戏这样复杂的音频用例。

可以考虑使用WebAudio API。你必须:

  • 使用XMLHttpRequest在缓冲区中加载音频文件

  • 将缓冲区分配给AudioBufferSourceNode实例

  • 将该节点连接到适当的目的地或中间节点(例如GainNode)。

  • 如果你还需要停止声音,你需要跟踪每一个创建的源节点,还没有完成播放,这可以通过听源节点的onended来完成。

下面是一个封装了逻辑的类,它可以从URL加载声音,并根据需要多次播放/停止它,跟踪所有当前播放的源并在需要时清理它们:

window.AudioContext = window.AudioContext || window.webkitAudioContext;
const context = new AudioContext();
export class Sound {
    url = '';
    buffer = null;
    sources = [];
    constructor(url) {
        this.url = url;
    }
    load() {
        if (!this.url) return Promise.reject(new Error('Missing or invalid URL: ', this.url));
        if (this.buffer) return Promise.resolve(this.buffer);
        return new Promise((resolve, reject) => {
            const request = new XMLHttpRequest();
            request.open('GET', this.url, true);
            request.responseType = 'arraybuffer';
            // Decode asynchronously:
            request.onload = () => {
                context.decodeAudioData(request.response, (buffer) => {
                    if (!buffer) {
                        console.log(`Sound decoding error: ${ this.url }`);
                        reject(new Error(`Sound decoding error: ${ this.url }`));
                        return;
                    }
                    this.buffer = buffer;
                    resolve(buffer);
                });
            };
            request.onerror = (err) => {
                console.log('Sound XMLHttpRequest error:', err);
                reject(err);
            };
            request.send();
        });
    }
    play(volume = 1, time = 0) {
        if (!this.buffer) return;
        // Create a new sound source and assign it the loaded sound's buffer:
        const source = context.createBufferSource();
        source.buffer = this.buffer;
        // Keep track of all sources created, and stop tracking them once they finish playing:
        const insertedAt = this.sources.push(source) - 1;
        source.onended = () => {
            source.stop(0);
            this.sources.splice(insertedAt, 1);
        };
        // Create a gain node with the desired volume:
        const gainNode = context.createGain();
        gainNode.gain.value = volume;
        // Connect nodes:
        source.connect(gainNode).connect(context.destination);
        // Start playing at the desired time:
        source.start(time);
    }
    stop() {
        // Stop any sources still playing:
        this.sources.forEach((source) => {
            source.stop(0);
        });
        this.sources = [];
    }
}

你可以这样做:

const soundOne = new Sound('./sounds/sound-one.mp3')
const soundTwo = new Sound('./sounds/sound-two.mp3')
Promises.all([
  soundOne.load(),
  soundTwo.load(),
]).then(() => {
  buttonOne.onclick = () => soundOne.play();
  buttonTwo.onclick = () => soundOne.play();
})