Playing short sound clips with XHTML and Javascript
Thursday, December 29, 2005
How often do you need to play a short sound clip on a webpage? I recently come across the problem. It sounds like easy isn't it? In the old days we have the <bgsound> tag, and <embed>, now we have some choices of popular player plugins like WMP, Quicktime, Real Player and even Flash player can do it. So why bother? Well, sure it's not hard to acheive the effect , I mean playing sound clips, but how can I make my markup neat and clean? It's essential because I need to insert lots of clips on a webpage, and I want the user could play it at once as well as download it later. To begin, you may want to see the final example at once, or download the zip pakage.
The best thing that XHTML taught me is that I need to be meaningful(or semantical), that is, the attributes should really mean something instead of telling the browser how to render the content (as we all known, that's Mr. CSS's business). The [class] attribute is what I'm going to use. I can't imagine why this cute attribute is just used by css rules only, isn't it intended for classification? Actually, lots of great minds have already thought about it. The use of classification is limitless (some guyz even make animations out of it!) .
Here's the markup I'm going to deal with:
<a href="names/Acanthogobius_flavimanus.mp3" class="latin sound_clip">Acanthogobius flavimanus</a>
Fairly simple, isn't it? Any human could understand its meaning. Now if you load it in a web browser, a normal link will appear and when clicked on it, the browser will show a download dialog to the user.
Ouch, don't throw eggs at me, stop... not now..;(. I haven't begin yet. OK, next, I need to prepare an audio player to handle the mp3 files I'm going play. Since Flash is said to be "installed on nearly 98% of the world's computers", let's make an audio player out of Flash. Though I call it a "player", it just have two most simple functions: play and stop, remember we are playing short clips ?
Since Flash 5 (or earlier?), Macromedia introduces an actionscript object called "Sound" , which allows the player to load external mp3 files and play it. That's just what I need! You can load and play a file like this (example in Actionscript 2):
var sndTrack:Sound = new Sound();
//Load sound:
sndTrack.onLoad = function(bSuccess:Boolean) { //When the file loaded
if (!bSuccess) {
// Failed to load sound
} else { // Success!
this.start();//Play sound
}
};
sndTrack.loadSound("SomeSound.mp3" , false); // begin loading the sound
But wait, you may say, how will I know which file is going to play? That would be easy to use the url parameter! For example, if I "query" the swf file like this: player.swf?file=abc.mp3, then actionscript will know a variable named "file" is "abc.mp3", and this "query string" can be generated by javascript later. OK, problem solved! Now I'm getting to create this player, first, drag two buttons (Pause and Play) on to the stage(size 16 x 16 px), then give them instance names "btn_Play" and "btn_Pause" so that I can control them using AS. Then create another layer named "as" which the scripts goes. Here's the actionscript code for the layer:
var sFile:String = (file == undefined) ? null : file; // Detect the parameter passed by external js
var sndTrack:Sound = new Sound(); // Create the sound object
//Set the onLoad handler
sndTrack.onLoad = function(bSuccess:Boolean) {
if (!bSuccess) {
// Failed to load sound
SetUIState("failed");
} else {
SetUIState("stopped");
//this.setVolume(60);
}
};
sndTrack.onSoundComplete = function() {
SetUIState("stopped"); // When sound ends, reset the buttons
};
btn_Play.onRelease = function() { // When "Play" button clicked
SetUIState("playing"); // Set button states
sndTrack.start(0, 1); // And play the file
};
btn_Stop.onRelease = function() { // When "Stop" button clicked
sndTrack.stop(); // Stop playing
SetUIState("stopped"); // Reset the buttons
};
function SetUIState(sState:String) { // The function use to handle the buttons states
if (sState == "loading") {
btn_Play._visible = false;
btn_Stop._visible = false;
}
if (sState == "failed") {
btn_Play._visible = true;
btn_Stop._visible = false;
btn_Play.enabled = false;
}
if (sState == "playing") {
btn_Play._visible = false;
btn_Stop._visible = true;
}
if (sState == "stopped") {
btn_Play._visible = true;
btn_Stop._visible = false;
trace("set state: stopped, btn:"+btn_Play.onRelease);
}
}
//End of preperation!
//Begin Load sound
SetUIState("loading");
if (sFile != null) {
sndTrack.loadSound(sFile, false);
// Try to load sound;
} else {
SetUIState("failed");
// No sound file specified;
}
Publish the SWF file and the player's done! So far, the XHTML page looks the same, because we haven't "connect" the player with the markup yet. Now that's what I'm going to do. All I need to do is to enumerate all the <a> tags with the class name "sound_clip", and insert this player before the link. Note that I haven't used the standard DOM methods since it's more complex, instead innerHTML is used for manipulating the markup. Here's the Javascript code:
var SND_CLIP_PLAYER = "player.swf"; // Define the player's name
var SND_CLIP_LINK_TITLE = "Click the arrow to play clip."; // Title text
function SndClip_Init() // Init the sound clip links
{
var arrLinks = document.getElementsByTagName("a"); // Find all anchors
var arrTargetLinks = new Array(); // The array for anchors with the className "sound_clip"
for (var i=0;i<=arrLinks.length-1;i++) {
var oLink = arrLinks[i];
if ((new String(oLink.className)).toLowerCase().indexOf("sound_clip")=="-1") continue;
arrTargetLinks[arrTargetLinks.length] = oLink;
}
for (var i=0;i<=arrTargetLinks.length-1;i++) { // Manipulate the link one after another
var oLink = arrTargetLinks[i];
//document.write(SndClip_GenCode(oLink.href));
oLink.innerHTML = SndClip_GenCode(oLink.href) + oLink.innerHTML;
oLink.title += "\n" + SND_CLIP_LINK_TITLE;
}
}
function SndClip_GenCode(sSoundFile) { //Helper function to generate html code
var sRet = '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
sRet += 'codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0"'
sRet += ' width="16" height="16" class="sndclip_obj" align="absmiddle">';
sRet += '<param name="allowScriptAccess" value="sameDomain" />';
sRet += '<param name="movie" value="'+ SND_CLIP_PLAYER +'?file='+ escape(sSoundFile) +'" />';
//Pass the parameter here, use escape to avoid encoding problems
sRet += '<param name="loop" value="false" />';
sRet += '<param name="menu" value="false" />';
sRet += '<param name="quality" value="high" />';
sRet += '<param name="scale" value="exactfit" />';
sRet += '<param name="bgcolor" value="#ffffff" />';
sRet += '<embed src="'+ SND_CLIP_PLAYER + '?file=' + escape(sSoundFile) + '" ';
//Pass the parameter here, use escape to avoid encoding problems
sRet += 'loop="false" menu="false" quality="high" scale="exactfit" ';
sRet += 'bgcolor="#ffffff" width="16" height="16" name="player" align="absmiddle" ';
sRet += 'allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />';
sRet += '</object>';
return sRet;
}
function fnAddEventListener(obj, name, handler)
{
//alert(typeof(obj.attachEvent));
if (typeof(obj.attachEvent)=="object") {
obj.attachEvent("on" + name, handler);
} else {
obj.addEventListener(name, handler, false);
}
};
fnAddEventListener( window , "load", SndClip_Init);
fnAddEventListener() is used to attach the SndClip_Init function to the window.onload handler, so that when the page finishs loading, all links on that page will be changed into playable.
The benefit for this method is that the markup is totally standard and compatible with any user agents, hence assesible. And advanced browsers would allow the user to preview as well as download the sound files. To enhance the method, you can pass more parameters to the player such as begin point, loop times etc.



1 Comments:
初到此Blog,comment一下先……原来大家都是用blogger + sitesled的。我的Blogger在:
http://cathsfz.sitesled.com
我在CSDN也见别人问过如何在页面里需要的时候播放一下声音提示,第一反应就是如果允许用Flash那就最好了,调用一个专门放声音的Flash,给它一个URL让它播放。
如果环境不允许用Flash,那就没办法咯。HTML本身的名称就和media没有半点联系。
Post a Comment
Links to this post:
Create a Link
<< Home