📜 ⬆️ ⬇️

HTML5 Audio and Game Development: browser bugs, problems and solutions, ideas

In the topic, I will talk about the nuances of using the <audio> tag in different browsers when developing games, about the problems I encountered and how to solve them. The explanation will go in parallel with writing the wrapper for convenient work.


The Audio element has a very beautiful interface, but we need to expand it, so we will write a wrapper:
var LibCanvasAudio = function ( file ) {
this . audio = new Audio ;
this . audio . src = file ;
};

LibCanvasAudio . prototype = {};


.ogg vs .mp3


To begin with - dregs with supported codecs. Most browsers support ogg vorbis ( for Opera in Linux, do not forget to install gstreamer base and good plugins), but, for example, Apple decided to fuck up. Let's for sane browsers will give in .ogg, all the rest - in .mp3. All stars in the file name will be replaced with ogg or mp3, respectively:
')
var LibCanvasAudio = function ( file ) {
this . audio = new Audio ;
this . src ( file );
};

LibCanvasAudio . prototype = {
src : function ( file ) {
var
codec = this . getSupport ();
if (!
codec ) throw 'AudioNotSupported' ;
this . audio . src = file . replace (/\*/ g , this . getSupport ());
this . audio . load ();
return
this ;
},
getSupport : function () {
return !
this . audio . canPlayType ? false :
this . audio . canPlayType ( 'audio/ogg;' ) ? 'ogg' :
this . audio . canPlayType ( 'audio/mpeg;' ) ? 'mp3' : false ;
}
}



Gatling scheme


Good. We come to the main topic. Let's say we are developing an action. We have a huge number of explosions, shots, ets. Suppose one explosion with an echo lasts 5 seconds, and the interval between the explosions can be 0.5 seconds. If you just run the file first, the previous explosion will abruptly end. We could clone the Audio element each time before launch, but this way we will produce a bunch of DOM elements. After 5 minutes of play, all browsers go crazy. Therefore, I propose to use the Gatling scheme . We enter a certain number of elements into an array and call them in turn. Until the last element plays, the first one will have time to end. The main thing - to put enough number of "trunks" for each of the sounds.

LibCanvasAudio . prototype = {
// ...
cloneAudio : function () {
audioClone = this . audio . cloneNode ( true );
audioClone . load ();
return
audioClone ;
},
gatling : function ( count ) {
this . barrels = [];
this . gatIndex = 0 ;
while (
count --) {
this . barrels . push ( this . cloneAudio ());
}
return
this ;
},
getNext : function () {
var
elem = this . barrels [ this . gatIndex ];
++
this . gatIndex >= this . barrels . length && ( this . gatIndex = 0 );
return
elem ;
},
playNext : function () {
var
elem = this . getNext ();
elem . pause ();
elem . currentTime = 0 ;
elem . play ();
return
this ;
}
};



The interface we get is approximately as follows:
var shotSound = new LibCanvasAudio ( 'explosion.*' ). gatling ( 6 );

window . addEventListener ( 'keydown' , function ( e ) {
(
e . keyCode == keys . SPACE ) && shotSound . playNext ();
},
false );


Bummer at the Opera


In the Opera we are waited by a bummer. Having studied this question in detail, I found a bug that I wrote with the code DSK-309302. The cloned Audio element in Opera does not work:

// var audioOrig = document.createElement('audio'); // :
var audioOrig = new Audio ();
audioOrig . src = 'shot.ogg' ;
audioOrig . controls = 'controls' ;

var audioClone = audioOrig . cloneNode ( true );

function
appendToBody ( node ) {
document . getElementsByTagName ( 'body' )[ 0 ]. appendChild ( node );
}

audioOrig . play (); //
audioClone . play (); //

appendToBody ( audioOrig ); //
appendToBody ( audioClone ); //


Let's write a small fix for the Opera:
LibCanvasAudio . prototype = {
// ..
cloneAudio : function () {
if (
window . opera ) { // Reported Opera bug DSK-309302
var audioClone = new Audio ;
audioClone . src = this . audio . src ;
} else {
audioClone = this . audio . cloneNode ( true );
}
audioClone . load ();
return
audioClone ;
},
// ..
};


Fox Bug


We can see another bug in firefox 3.5 (in 3.6 and 4 already not) - when you repeat playback of the audio track, the first second ± is duplicated. Apparently, not only for me: “ the second time you hit the button” it plays the “badumm” twice in Firefox . ( video with the recording of the bug ). Let's add a small fix - we will wind off the audio not at the beginning, but for 25 milliseconds (the minimum value is set experimentally and is approximately 0.021-0.022 seconds). If you wish, you can add a version check and return everything except 3.5 Fox to the beginning (but the difference between 25 milliseconds and zero is not felt, as a last resort, knowing this nuance, you can move all audio in your favorite audio editor 25 milliseconds to the left):

LibCanvasAudio . prototype = {
// ..
playNext : function () {
var
elem = this . getNext ();
elem . pause ();
elem . currentTime = 0.025 ;
elem . play ();
return
this ;
}
};


Bonus


In ie9 preview 4, new Audio() does not work, but it is very easy to solve by replacing it with document.createElement('audio');

What happened in the end (poke into the gap)


The resulting source : pastebin.com/xG4mhX3w

Source: https://habr.com/ru/post/101145/


All Articles