HTML5 音频 - 单击时重复播放声音,无论先前的迭代是否已完成

发布于 2024-11-27 00:32:02 字数 562 浏览 2 评论 0原文

我正在构建一个游戏,其中点击时播放 wav 文件 - 在本例中,它是枪声“砰”。

问题是,如果我快速单击,它不会为每次单击播放一次声音 - 就好像在播放声音时忽略单击一样,一旦声音完成,它就会再次开始监听单击。延迟似乎大约为一秒长,所以你算一下,如果有人每秒点击 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();
}); 

想法?

I'm building a game in which a wav file plays on click - in this case it's a gun sound "bang".

The problem is if I rapid click, it won't play the sound once for each click - it's as if the clicks are ignored while the sound is playing, and once the sound is finished, it starts listening for clicks again. The delay seems to be about one second long, so you figure if someone clicks 4 or 5 times per second, I want 5 bangs, not 1.

Here's my HTML:

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

Here's my JS:

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

Ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

瞳孔里扚悲伤 2024-12-04 00:32:02

我知道这是一个很晚的答案,我只是想,但是在寻找解决方案时,我发现就我而言,最好的解决方案是预加载声音,然后在每次我想播放它时克隆节点,这使我可以播放即使前一个还没有结束:

var sound = new Audio("click.mp3");
sound.preload = 'auto';
sound.load();

function playSound(volume) {
  var click=sound.cloneNode();
  click.volume=volume;
  click.play();
}

I know it's a very late answer, I just wanted to , but seeking for a solution I found that in my case the best one was to preload the sound and then clone the node each time I want to play it, this allows me to play it even if the previous has not ended:

var sound = new Audio("click.mp3");
sound.preload = 'auto';
sound.load();

function playSound(volume) {
  var click=sound.cloneNode();
  click.volume=volume;
  click.play();
}
清秋悲枫 2024-12-04 00:32:02

预加载 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();
});

Once the gun_bang.wav is preloaded, you can dynamically make new elements with that sound attached to it, play the audio, and then remove them when the audio has finished.

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();
});
花海 2024-12-04 00:32:02

您可以使用自己的特定于程序的点击逻辑来包装以下内容,但以下内容会同时演示 3 个枪声。如果你想要任何类型的延迟,你可以引入一个小的 sleep() 函数(很容易找到许多针对此的 kludge hacks),或者你可以使用 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>

You can wrap the following with your own program-specific click logic, but the following demonstrates 3 gun sounds at the same time. If you want any kind of delay, you can introduce a minor sleep() function (many kludge hacks for this are easily found), or you can use setTimeout() to call subsequent ones.

<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>
一身仙ぐ女味 2024-12-04 00:32:02

HTML5 音频元素有很多限制,它不太适合游戏等复杂的音频用例。

相反,请考虑使用 WebAudio API。您必须:

  • 使用 XMLHttpRequest< /code>将音频文件加载到缓冲区中


  • 将该缓冲区分配给 AudioBufferSourceNode 实例。

  • 将该节点连接到适当的目标或中间节点(例如<代码>GainNode)。

  • 如果您还需要停止声音,则需要跟踪已创建且尚未完成播放的每个源节点,这可以通过监听源节点的 onending

下面是一个类,它封装了从 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();
})

The HTML5 audio element has many limitations and it's not well suited for a complex audio use-case like a game.

Instead, consider using the WebAudio API. You'll have to:

  • Use a XMLHttpRequest to load your audio file in a buffer

  • Assign that buffer to a AudioBufferSourceNode instance.

  • Connect that node to the appropriate destination or intermediate nodes (e.g. GainNode).

  • If you also need to stop sounds, you'll need to keep track of each source node that is created and that hasn't finished playing yet, which can be done by listen to the source node's onended.

Here's a class that encapsulates the logic to load a sound from a URL and play/stop it as many times as you want, keeping track of all currently playing sources and cleaning them up as needed:

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 = [];
    }

}

You can then do something like this:

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();
})
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文