Introduction
Until HTML5, in order to play a sound it was needed to use a flash codec with "embed" or "object" tag and to define a flash player. Because flash is less and less supported by Smartphones this kind of implementation is starting to be out-of-date.
Today with HTML5 it seems to be easier to play a sound, seemingly you just need to use "audio" tag and it works. In this article we'll see that is not as easy as it seems. In a first part we'll see how to implement audio HTML5 tag, in the second part we'll define the problem that use of HTML5 can create and give you a way to solve it.
Audio tag
As I told you HTML5 make your development easier! If you go on W3C website you can find this example:
<audio controls="controls">
<source src="horse.ogg" type="audio/ogg">
<source src="horse.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
As you can see, it looks very easy to use! But if you look precisely, first you need to have two sound formats due to browsers which don't support the same format. Until now it still looks very easy to use.
Problems encountered & fix
What are these problems I'm telling you since the beginning? If you look at the audio documentation in W3C you can see HTML5 audio is supported only with the newest browsers. So what happened if you use only HTML5 and your visitor uses Internet Explorer 8 which doesn't support HTML5? When they try to play the sound they receive an alert "Your browser does not support the audio element.". Maybe you're thinking: "Anyway, Internet Explorer is an old version, we don't care!", If you look the market share browsers (still in W3C) you can see in September 2012 Internet Explorer 8 and lower is used by 58.5% of the Internet Explorer user, consequently 9.6% of the total market. So, if you have professional website you can put away 10% of your possible visitors.
What are we able to do? In IDM, I developed this solution. If your browser doesn't support HTML5, you use the old flash system. And if they both are not supported, you only redirect the user to the sound (for example old Android don't support HTML5 and don't have the flash player).
How do you detect if HTML5 is supported by your browser? I find this function:
function supportAudioHtml5(){
var audioTag = document.createElement('audio');
try{
return (
!!(audioTag.canPlayType) /* function canPlayType is defined */
&& ( ( "no" != audioTag.canPlayType("audio/mpeg") && "" != audioTag.canPlayType("audio/mpeg") ) /* test mp3 audio format */
|| ( "no" != audioTag.canPlayType("audio/ogg") && "" != audioTag.canPlayType("audio/ogg") ) /* test ogg audio format */
));
}catch(e){
return false;
}
}
If this function return true, you can use HTML5, if not you need to know if flash is installed with this function:
function flashAvailable(){
var flashinstalled = 0;
var flashversion = 0;
MSDetect = "false";
if (navigator.plugins && navigator.plugins.length){
x = navigator.plugins["Shockwave Flash"];
if (x){
flashinstalled = 2;
if (x.description){
y = x.description;
flashversion = y.charAt(y.indexOf('.')-1);
}
}
else
flashinstalled = 1;
if (navigator.plugins["Shockwave Flash 2.0"]){
flashinstalled = 2;
flashversion = 2;
}
}
else if (navigator.mimeTypes && navigator.mimeTypes.length){
x = navigator.mimeTypes['application/x-shockwave-flash'];
if (x && x.enabledPlugin)
flashinstalled = 2;
else
flashinstalled = 1;
}
else{// it's an MSVersion
for(var i=7; i>0; i--){
flashVersion = 0;
try{
var flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash." + i);
flashVersion = i;
return (flashVersion > 0)
}
catch(e){
}
}
} return (flashinstalled == 2);
}
If this function return true you can use flash. Or else you need to create a pop-up like that:
window.open(source_mp3,'Sound',"menubar=no, status=no, scrollbars=no, menubar=no, width=200, height=100");
We use the mp3 format in this case because mp3 is supported by all the OS players. But we can test if the OS player support the format and give him the best format.
Now can I play sound in all the browsers? No, not really! If you use jQuery to play you sound like use you need to fix some other bugs! If HTML5 is supported you can play the sound with:
yourAudioObject.play();
But there is a problem here! Because you need to use two types of sound formats your browser needs to get the best formats for him. The problem is, to play the sound the play() method use the "src" attribute of your object, and your browser set the "currentSrc" attribute with the good sound file for you. So you need to do : youAudioObject.src = yourAudioObject.currentSrc;
But here there is another problem in Android 4.0, the default browser doesn't set the currentSrc and any other attribute! As a consequence, you need to set the "src" attribute for him. But how do you detect the best format for the browser ?
There is a method on you sound objet which is call canPlayType("format type"). If you are on Firefox then:
youAudioObject.canPlay("audio/mpeg") is false
youAudioObject.canPlay("audio/ogg") is true
If all of your format are not supported, try the flash, and if it doesn't available to use a redirect.
Now imagine, you gave a source a file which is not available (wrong path for example).The sound will never play, and you need to inform your visitor. There is an HTML attribute "onerror" which can help us.
onerror="playSoundException()"
If there is an audio error call playSoundException() and for example display an alert "Sound are not available". But still once, there is a problem. In internet explorer 9 if you load the page and there is an audio error the alert will be show directly even if the visitor doesn't want to play the sound. To fix this you can for example add an attribute data-clicked and test on the playSoundException() if this attribute is set.
function playSoundException(div){
if($("."+div).attr("data-clicked") == "1"){ // if there is a click
alert("Apologize, the sound is not available."); // show alert
$("."+div).attr("data-clicked","0");// set the click to 0
}
}
But now, you need to had on click this line: $(this).attr("data-clicked","1");
And also when sound are finished to be played: $(this).attr("data-clicked","0");
To conclude, as you can see HTML5 can be really easy to use if you don't want to support old browsers. But if you need to support most of the old browsers version there is a lot of particularly case which can create problems.
Our code example :
<a class="sound play_sound_button speaker"
title="Your title"
data-audio_div_id='audio_wotd'>
</a>
<audio id="audio_wotd" onerror="playSoundException('play_sound_button')">
<source class="audio_file_source" src="yourmp3.mp3" type="audio/mpeg" />
<source class="audio_file_source" src=" yourOgg.ogg" type="audio/ogg" />
</audio>
Here, we don't click directly on the audio, but on link which in fact a speaker picture (speaker class).
jQuery(document).ready(function(){
$(".play_sound_button").click(function() {
$(this).attr("data-clicked","1");
$(this).removeClass("speaker");
$(this).addClass("spinner");
var audio_div_id = $(this).attr("data-audio_div_id");
playSound(audio_div_id,this);
});
});
Onclick on the « play_sound_button » we replace the speaker image by a spinner. We get the audio id to call which is on the link attribute "data-audio_div_id".And we call our function playSound.
function playSound(audio_id,div){
var defaultType = ".mp3"; // the default type to format to use if html5 doesn't work var classSources = ".audio_file_source"; // the class for all source (unique by audio)
var audio = $('#' + audio_id);
if (supportAudioHtml5() && audio.length == 1){
var sound=audio.get(0);
sound.addEventListener('canplaythrough', function() { // if we can play
$(div).removeClass("spinner"); // loading complete we remove the spinner
$(div).addClass("speaker"); // and put the speaker
}, false);
sound.addEventListener("ended", function() {
$(div).attr("data-clicked","0");
});
var sourceAvailable = true;
if(sound.currentSrc != ""){ // if there is a preferred sound browser
sound.src=sound.currentSrc; // set preferred sound by browser
}
else if(sound.src == ""){
sourceAvailable = false;
$(classSources).each(function(){// for each sources
if(sound.canPlayType($(this).attr("type"))){
sound.src = $(this).attr("src");
sound.currentSrc = $(this).attr("src");
sourceAvailable = true;
}
});
}
if(!sourceAvailable){ // if there is any sound available playSoundWithoutHTML5($(classSources+'[src$="'+defaultType+'"]').attr("src"),div);
}
sound.play();
}else{ playSoundWithoutHTML5($(classSources+'[src$="'+defaultType+'"]').attr("src"),div);
}
}
function playSoundWithoutHTML5(source_mp3,div){
if(flashAvailable()){// if flash is available (function describe in this article)
var mp3_file ="speaker.swf?song_url=" + source_mp3 + "&autoplay=true')";
var sPlayer;
if (navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) { // netscape plugin architecture
sPlayer = "<embed type='application/x-shockwave-flash' src='" + mp3_file + "' width='0' height='0'></embed>"
} else { // PC I
sPlayer = "<object type='application/x-shockwave-flash' width='0' height='0' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0' data='" + mp3_file + "'><param name='wmode' value='transparent'/><param name='movie' value='" + mp3_file + "'/><embed src='" + mp3_file + "' width='0' height='0' ></embed></object>"
}
}
else{// if flash is not available redirect
window.open(source_mp3,'Sound',"menubar=no, status=no, scrollbars=no, menubar=no, width=200, height=100");
}
$("body").append(sPlayer);
$(div).removeClass("spinner");// loading complete we remove the spinner
$(div).addClass("speaker");// set preferred sound by browser
$(div).attr("data-clicked","0");
}
Sébastien Ferry
Design & development engineer at IDM
