We are about to switch to a new forum software. Until then we have removed the registration on this forum.
I'm writing a game that has lot and lots of agent sprites that asynchronously generate short sounds, often overapping when they do things like run into walls.
problem: in chrome, this works great. in safari there's a noticeable lag before I hear the sound. In firefox some of the audio files produce no sound at all. I'd not care about lag if this were just playing some 2 minute song. but these are short 0.2 second beeps and dings that need to happen with the action.
I've been using html5 audio tags I have a javacript function to play. I bind the javascript into my processing pde using Pomax's pattern for binding to create a function I can call to play a requested sound.
I've done this two ways and both ways work, but as I said are laggy in safari.
1) boneheaded: I've created an new audio instance for every sound type, and then play this when requested. The virtue of this is that I'm not reloading the sound .src every time. it's loaded into the audio instance and I just need to tell it to .play().
The limitation of that approach is that I can't overlap same sound. one agent can't play the same sound as another if that sound is already playing.
2) In order to play more than one sound of the same type at the same time, I maintain an array of audio instances. When the function gets a request to play a sound, it iterates through the list of audio instances till it finds an idle one, loads the desired sound .src and hits .play().
Both approaches work great in chrome and are laggy in safari. Sometimes sounds don't play at all in firefox.
is there a better way to do this?
examples of code that goes in the .html page and is then bound into the pde.
boneheaded method:
<audio id="ouch" src="audio2/ouch.m4a" preload="auto"></audio>
<audio id="beep" src="audio2/beep.m4a" preload="auto"></audio>
function play_sound(s) {
if (audiochannels[s] === undefined ) {
audiochannels[s] = new Audio();
audiochannels[s].src = document.getElementById(s).src;
audiochannels[s].load();
};
audiochannels[s].play();
}
multiple sounds of the same type at the same time:
var channel_max = 12; // number of channels
audiochannels = new Array();
for (a=0;a<channel_max;a++) { // prepare the channels
audiochannels[a] = new Array(); // this is just to create an object with attributes.
audiochannels[a]['channel'] = new Audio(); // create a new audio object
audiochannels[a]['finished'] = -1; // expected end time for this channel
}
var ac_last = 0;
function play_multi_sound(s) {
var a = ac_last;
for (var t=0;t<audiochannels.length;t++) {
a = (a+t)%audiochannels.length;
var thistime = new Date();
if (audiochannels[a]['finished'] < thistime.getTime()) { // is this channel finished?
audiochannels[a]['finished'] = thistime.getTime() +document.getElementById(s).duration*1000;
audiochannels[a]['channel'].src = document.getElementById(s).src;
audiochannels[a]['channel'].load();
audiochannels[a]['channel'].play();
ac_last = a+1;
break;
}
}
}
Answers
I think one reason for the lag in safari is that it does not heed the preload command. thus it does a server GET command everytime it plays the audio. Yuck.
in fact the safari browser is a lazy loader, not actually loading them at .load(), but waiting for .play(). conversely, chrome looks ahead at loads everything it can.
Is there a way to force the safari browser to load the files?
I haven't figured out how to get it to cache the sounds. I tried messing with the html5 .manifest file and html tag, but then I had problems getting it to expire my html and pde file as I edited them.
firefox doesn't like .m4a or .aiff files apparently.
The issue with laggy sound in Safari is that watching the server I see that safari does two gets per sound. I've figured out how to cache the sounds after the first time I play them so now I only get the lag the first time it plays.