Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Play Audio through JS with Overlapping

I have the following JS code for a canvas based game.

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);
}

This works, however it sends a server request to download the sound file every time it is called. Alternatively, if I declare the Audio object beforehand:

var snd = new Audio();
snd.src = EXPLOSION;
snd.volume = 0.5;

function createExplosion() {
  snd.play();
}

This works, however if the createExplosion function is called before the sound is finished playing, it does not play the sound at all. This means that only a single playthrough of the sound file is allowed at a time - and in scenarios that multiple explosions are taking place it doesn't work at all.

Is there any way to properly play an audio file multiple times overlapping with itself?

like image 695
Lucas Penney Avatar asked Feb 28 '13 00:02

Lucas Penney


2 Answers

You could just duplicate the node with cloneNode() and play() that duplicate node.

My audio element looks like this:

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

and I have an onClick listener that does just that:

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

And since the audio element isn't going to be displayed, you don't actually have to attach the node to anything.

I verified client-side and server-side that Chrome only tries to download the audio file once.

Caveats: I'm not sure about performance impacts, since this on my site this clip doesn't get played more than ~40x maximum for a page. You might have to clean up the audio nodes if you're doing something much larger than that?

like image 68
Roger Filmyer Avatar answered Oct 01 '22 14:10

Roger Filmyer


Try this:

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

Then the first time you call it it will fetch the sound, but every time after that it will reuse the same sound object.


EDIT: You can also preload with duplicates to allow the sound to play more than once at a time:

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

Note that this will send multiple requests if you play the sound overlapping itself too much, but it should return Not Modified very quickly and will only do so if you play it more times than you have previously.

like image 32
Niet the Dark Absol Avatar answered Oct 01 '22 15:10

Niet the Dark Absol