h1

my “50cents” tips for a “working” cross-browser Audio implementation

January 27, 2011

Well, while working on Frank Jr. I got several headaches on the Audio Implementation, and more specifically in getting it working on all browsers (I mean Safari, Chrome and Opera, as they are the only I really care about), and I wanted here to give some  feedback on how I finally achieved it. These tips are based on both my own experience/investigation on this matter, and the information I collected from the internet (based on other people experience). Also, this article does not cover the basics of loading sounds, lots of website are already largely covering the subject (see the links at the end of this article) and also does not cover mobile browser specific “issues” (like iOS, Android).

So, let’s get started :

Tip #1 : the loading part

- use the ‘canplaythrough‘ event to ensure you sound is properly loaded (as this event is fired only when the browser determines that it can play through the whole audio without stopping) and don’t forget as well to make a call to “removeEventListener” to remove the notification (it might be not needed, but seems to be cleaner this way).

- use the ‘error‘ event to be notified on any problem when loading your sound (see next tip).

Tip #2 : don’t trust your hosting services

- As I was saying, I use the ‘error‘ event to be notified when a sound cannot be loaded. This can be very useful in case you have a shitty hosting service for you stuff (like me), giving you sometimes some timeout when loading resources. So in my case each time a error event pop-up, from the callback called by the ‘error‘ eventListener, I just call again the load() function of the corresponding Audio Element, giving me a very simple infinite & automatic retry mechanism :

soundLoadError: function(sound_id) {
this._audio_channels[sound_id][0].load();
},

The “audio_channels” being here an array containing a reference to all the Audio Object I created.

Tip #3: cloneNode is your friend

If you already played with the Audio Element, you should probably know that actually the best(?) way to overcome the single-channel limitation of the audio tag is to actually create copy of the same sounds, allowing to simultaneously play them when needed, which gives for a 4 channels sound somethings like this (in a simplified way) :

{
  _audio_channels[my_sound_id][1].src = "/sounds/bangbang.mp3";
  _audio_channels[my_sound_id][2].load();
  _audio_channels[my_sound_id][2].src = "/sounds/bangbang.mp3";
  _audio_channels[my_sound_id][2].load();  
  _audio_channels[my_sound_id][3].src = "/sounds/bangbang.mp3";   
  _audio_channels[my_sound_id][3].load();
  _audio_channels[my_sound_id][4].src = "/sounds/bangbang.mp3";
  _audio_channels[my_sound_id][4].load();
}

As you can see, this is for sure not bad (once used properly and using some function to avoid duplicating some code), but why do you have to manually do 4 times the same stuff when the browser can do it for you, and most of all why not wait for the first mp3 to be loaded before duplicating it ?  CloneNode is very usefull function that creates an exact copy of a specified node, in our case an Audio Element, so each time the sound I want to be on multiple channel is loaded I just need to do :

for(var channel=1;channel<sound_channel;channel++)
{
   audio_channels[sound_id][channel] = audio_channels[sound_id][0].cloneNode(true);
}

Isn’t life beautiful ? First I know that my mp3 is loaded, and then I don’t have to care again about the path, and the event to manage… CloneNode just take care of duplicating the sound on channel 0, and it’s just easier that way and working perfectly !

Tips 4 : don’t trust your browser

Some browser don’t really do stuff correctly, so here some adjustment to be done :

- On some browser when you reach the end of a sound, it’s better to force the value of the CurrentTime property to 0. And to give another hint, on Chrome, it’s even better to force the value to something different than 0 (like 0.01) as else sometimes the sound is not played.

  audio_channels[sound_id][channel].pause();
  audio_channels[sound_id][channel].currentTime = 0.01;

Then there is two ways of doing this :

1. just before playing a sound

2. or using a callback, once the “ended” event is fired. I personally prefer the second one, as anyway I use this event in my code to notify me when a sound is fully played.

That’s it !, I hope you’ll find this useful :)

And to finish, I’d like also to share with a very basic drumbox of course fully implemented in HMTL5 using the Audio Element. The used samples are not so terrific, and the loop algorithm (to play them) is not correct, but it was not the point here.


And to finish, some external links that I found useful when implementing all this :

- Native Audio in the browser

- Stories in Flight : HTML5 Audio and Javascript control

- Everything you need to know about HTML5 Audio & Video

4 comments

  1. Hi Olive,

    I got some issues with DrumBox:
    – Mouse click shifted 2 circle belows on Chrome
    – Not displayed at all under Firefox and IE

    Please help doctor !! :)


    • Forget about IE, and be sure you have both last version of Chrome & FF.


  2. Nice technique. For all of the audio techniques thus far, this looks like the cleanest.

    Still, I can’t shake the feeling that [good] browsers will either cripple the current implementation, or make a better one soon enough…

    Should I just wait, or live for the moment?


    • I think you should live the moment ! but it mostly depends with what you want your game API to be compatible with ? Future browser version? or do you also want the existing ones to be working ?

      Also, I think my implementation is quite future proof, as I don’t see any reason why it should not work with any browser enhancement on the audio.

      Cheers !



Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: