Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems preloading audio in Javascript

I'm trying to make a cross-device/browser image and audio preloading scheme for a GameAPI I'm working on. An audio file will preload, and issue a callback once it completes.

The problem is, audio will not start to load on slow page loads, but will usually work on the second try, probably because it cached it and knows it exists.

I've narrowed it down to the audio.load() function. Getting rid of it solves the problem, but interestingly, my motorola droid needs that function.

What are some experiences you've had with HTML5 audio preloading?

Here's my code. Yes, I know loading images in a separate function could cause a race condition :)

var resourcesLoading = 0;

function loadImage(imgSrc) {
    //alert("Starting to load an image");
    resourcesLoading++;

    var image = new Image();
    image.src = imgSrc;

    image.onload = function() {
        //CODE GOES HERE
        //alert("A image has been loaded");
        resourcesLoading--;
        onResourceLoad();
    }
}

function loadSound(soundSrc) {
    //alert("Starting to load a sound");
    resourcesLoading++;

    var loaded = false;

    //var soundFile = document.createElement("audio");
    var soundFile = document.createElement("audio");
    console.log(soundFile);
    soundFile.autoplay = false;
    soundFile.preload = false;

    var src = document.createElement("source");
    src.src = soundSrc + ".mp3";
    soundFile.appendChild(src);

    function onLoad() {
        loaded = true;

        soundFile.removeEventListener("canplaythrough", onLoad, true);
        soundFile.removeEventListener("error", onError, true);

        //CODE GOES HERE
        //alert("A sound has been loaded");
        resourcesLoading--;
        onResourceLoad();
    }

    //Attempt to reload the resource 5 times
    var retrys = 4;

    function onError(e) {
        retrys--;

        if(retrys > 0) {
            soundFile.load();
        } else {
            loaded = true;

            soundFile.removeEventListener("canplaythrough", onLoad, true);
            soundFile.removeEventListener("error", onError, true);

            alert("A sound has failed to loaded");
            resourcesLoading--;
            onResourceLoad();
        }
    }

    soundFile.addEventListener("canplaythrough", onLoad, true);
    soundFile.addEventListener("error", onError, true);
}

function onResourceLoad() {
    if(resourcesLoading == 0)
        onLoaded();
}

It's hard to diagnose the problem because it shows no errors and only fails occasionally.

like image 656
Jeffrey Sweeney Avatar asked Dec 04 '11 14:12

Jeffrey Sweeney


2 Answers

I got it working. The solution was fairly simple actually:

Basically, it works like this:

channel.load();
channel.volume = 0.00000001;
channel.play();

If it isn't obvious, the load function tells browsers and devices that support it to start loading, and then the sound immediately tries to play with the volume virtually at zero. So, if the load function isn't enough, the fact that the sound 'needs' to be played is enough to trigger a load on all the devices I tested.

The load function may actually be redundant now, but based off the inconsistiency with audio implementation, it probably doesn't hurt to have it.

Edit: After testing this on Opera, Safari, Firefox, and Chrome, it looks like setting the volume to 0 will still preload the resource.

like image 163
Jeffrey Sweeney Avatar answered Sep 23 '22 10:09

Jeffrey Sweeney


canplaythrough fires when enough data has buffered that it probably could play non-stop to the end if you started playing on that event. The HTML Audio element is designed for streaming, so the file may not have completely finished downloading by the time this event fires.

Contrast this to images which only fire their event once they are completely downloaded.

If you navigate away from the page and the audio has not finished completely downloading, the browser probably doesn't cache it at all. However, if it has finished completely downloading, it probably gets cached, which explains the behavior you've seen.

I'd recommend the HTML5 AppCache to make sure the images and audio are certainly cached.

like image 24
AshleysBrain Avatar answered Sep 23 '22 10:09

AshleysBrain