O'Reilly    
 Published on O'Reilly (http://oreilly.com/)
 See this if you're having trouble printing code examples


Download tutorial files (.zip file)

I've finally cracked the code. In "Build a Simple MP3 Player for Your Site," I discovered how to create links that would magically open an MP3 player in a compact window (see Figure 1). No longer would visitors be booted onto a blank page or bamboozled by mysterious plugin errors when clicking an MP3 link. Creating the links was simple, too, thanks to a Dreamweaver extension I included with the article. Now web designers could create pop-up music links with a few clicks and visitors could play the music with a single click. Or so I thought.

Encouraged by the response (that article became one of our most popular), I followed with "Build an Enhanced MP3 Player for Your Site," which showed how to add a photo, caption, and textured background to the pop-up player (see Figure 2).

Where to go from there? Readers asked how to add custom text formatting, photographic backgrounds, audio streaming, and playlist support. Several wondered how to get the playback controller bar without using a pop-up window.

Fig. 1. Mac Player

Figure 1. The original audio pop-up audio player as it appears in Firefox on the Mac. Alas, some Windows browsers didn't fare so well.

And a handful asked why the player didn't work.

In most cases, I could trace the problem to a typo the reader had made while entering the code. Omitting the link to the external JavaScript file was another common problem. (When you click a link, the script is supposed to intercept the click and generate the pop-up player by writing HTML to a new window.) Several readers found that upgrading Windows Media Player to the latest version got the audio playing. But still, there were a few reports of errors on the Windows side.

Fig. 2: Enhanced MP3 Player

Figure 2. Version 2 of the pop-up player added spiffy visuals to the audio playback. But problems still lurked inside.

JavaScript: The Definitive Guide

Related Reading

JavaScript: The Definitive Guide
By David Flanagan

The Phantom Plugin

Fig. 3. Firefox Windows Fails

Figure 3. Windows users will likely see this show-stopper if they aren't running Internet Explorer or QuickTime. Annoyingly, non-IE browsers usually do have the necessary plugin to play this audio file, but must be told where to find it.

The problem turned out to be the way Windows browsers handle audio. If QuickTime is installed, the pop-up audio player seems to work fine. If QuickTime is absent, Internet Explorer still works, because it can automatically embed the Windows Media Player. But other Windows browsers, like Firefox and Opera, must use the ancient Windows Media plugin instead. Because my original script didn't specify the plugin, Firefox and Opera would throw an error like the one in Figure 3.

So, to make the player work, I had to tell Macs to use QuickTime and Windows PCs to use the Windows Media plugin. To do that, I first make the script detect the visitor's operating system. Next, it determines the audio file's MIME type so the player will know how to handle it. Windows players will always get the MIME type application/x-mplayer2, which calls the Windows Media plugin. Macs (and Linux) will get a MIME type derived from the file's extension. For MP3 files, that's audio/mpeg.

Here's an example of how it works, using an audio file from Jim Aikin's recent vocoder tutorial:

First, there's a link to the external script:

<script src="BatmoAudioPop.js" type="text/javascript"></script>

Then there's the link itself, which looks like this:

<a href="/examples/oreilly/digitalmedia/2006/05/better-player-vocoder.mp3" 
target="_blank" onclick="javascript:BatmoAudioPop('Vocoder Example',this.href,'1'); 
return false">Vocoder Example</a>

As described in "Build a Simple MP3 Player," clicking the link passes three bits of information to the external script: the window title, the URL of the audio file, and an ID that will prevent subsequent clicks on the same link from spawning new windows. If JavaScript is disabled, the file will open normally in a new window or external player.

As before, all of the magic happens in the external script, which I've commented below. It's very similar to the code in the previous articles, with the addition of the browser and file-type detection. I also updated the embedding parameters based on some new information. Namely, most Boolean values in the object tag need to be written as 0 or 1, not false or true. I also discovered that the Windows Media plugin is three pixels taller than I'd thought, so the object height is now 45 instead of 42. Finally, I discovered that the plugin will display a handy status bar in Windows if you set the showstatusbar parameter to 1. So I increased the object height by 24 pixels to accommodate that.

// Pop-Up Embedder Script by David Battino, www.batmosphere.com
// Version 2006-05-31 
// OK to use if this notice is included
   
function BatmoAudioPop(filedesc,filepath,WindowNumber) {
   // Get Operating System 
   var isWin = navigator.userAgent.toLowerCase().indexOf("windows") !=-1
   if (isWin) { // Use MIME type = "application/x-mplayer2"
      visitorOS="Windows";
   } 
   else { // Use MIME type = "audio/mpeg"; // or audio/x-wav or audio/x-ms-wma, etc.
      visitorOS="Other";
   }

   // Get the MIME type of the audio file from its extension (for non-Windows browsers)
   var mimeType = "audio/mpeg"; // assume MP3/M3U
   var objTypeTag = "application/x-mplayer2"; // The Windows MIME type to load the WMP plug-in in Firefox, etc.
   var theExtension = filepath.substr(filepath.lastIndexOf('.')+1, 3); // truncates .aiff to aif
   if (theExtension.toLowerCase() == "wav") { mimeType = "audio/x-wav"};
   if (theExtension.toLowerCase() == "aif") { mimeType = "audio/x-aiff"};    
   if (theExtension.toLowerCase() == "wma") { mimeType = "audio/x-ms-wma"};
   if (theExtension.toLowerCase() == "mid") { mimeType = "audio/mid"};
   // Add additional MIME types as desired
   
   if (visitorOS != "Windows") { objTypeTag = mimeType; // audio/mpeg, audio/x-wav, audio/x-ms-wma, etc.};
      PlayerWin = window.open('',WindowNumber,'width=320,height=217,top=0,left=0,screenX=0,screenY=0,resizable=0,scrollbars=0,titlebar=0,toolbar=0,menubar=0,status=0,directories=0');
      PlayerWin.focus();
      PlayerWin.document.writeln("<html><head><title>" + filedesc + "</title></head>");
      PlayerWin.document.writeln("<body bgcolor='#9999ff'>"); // specify background img if desired
      PlayerWin.document.writeln("<div align='center'>");
      PlayerWin.document.writeln("<b style ='font-size:18px;font-family:Lucida,sans-serif;line-height:1.6'>" + filedesc + "</b><br />");
      PlayerWin.document.writeln("<object width='280' height='69'>");
      PlayerWin.document.writeln("<param name='src' value='" + filepath + "'>");
      PlayerWin.document.writeln("<param name='type' value='" + objTypeTag + "'>");
      PlayerWin.document.writeln("<param name='autostart' value='1'>");
      PlayerWin.document.writeln("<param name='showcontrols' value='1'>");    
      PlayerWin.document.writeln("<param name='showstatusbar' value='1'>");
      PlayerWin.document.writeln("<embed src ='" + filepath + "' type='" + objTypeTag + "' autoplay='true' width='280' height='69' controller='1' showstatusbar='1' bgcolor='#9999ff' kioskmode='true'>");
      PlayerWin.document.writeln("</embed></object></div>");
      PlayerWin.document.writeln("<p style='font-size:12px;font-family:Lucida,sans-serif;text-align:center'><a href='" + filepath +"'>Download this file</a> <span style='font-size:10px'>(right-click    or Control-click)</span></p>");
      PlayerWin.document.writeln("<form><div align='center'><input type='button' value='Close this window' onclick='javascript:window.close();'></div></form>");
      PlayerWin.document.writeln("</body></html>");
      PlayerWin.document.close(); // "Finalizes" new window
}

Not Just for Singles

With MIME detection in place, we can now handle MP3 playlists, which didn't work in the previous implementation. To create a playlist, you simply type the URL of each audio file on a separate line in a text document, and then save the document with a .m3u extension. The following link will open a sequence of three MP3s from Spencer Critchley's article "Country Music's Digital Surprise," which walked through the surprisingly high-tech process of recording a song in Nashville. (The piano in part three was actually software running on a laptop, as you can see in Figure 4.)

Laptop Piano

Figure 4. Gene Rabbai tickles the virtual ivories in Nashville for Bo Billy's hit "When I See You Smile."

In-Place Embedding

I implemented the original player in a pop-up window so visitors could read multi-page articles without cutting off a sound that was playing on the current page. But several readers asked about embedding sounds directly. Here's how to do that, using an example from our article "The Synful Orchestra: Better Music Through Database Splicing:"

As before, we start with the link to the external JavaScript file:

<script src="BatmosphereAudioEmbed.js" type="text/javascript"></script>

The audio file gets embedded with its own script:

<script language="JavaScript" type="text/javascript">
   <!-- Call external JavaScript file to embed player
   embedPlayer("Fanfare","/examples/oreilly/digitalmedia/2006/05/better-player-copland.mp3"); // title, filename
   // -->
</script>

Of course, if visitors have JavaScript disabled, they won't see anything. So for safety, you can add a plain MP3 link after in a <noscript> tag, like this:

<noscript><a href="/examples/oreilly/digitalmedia/2006/05/better-player-copland.mp3" 
target="_blank">Fanfare</a></noscript>

Finally, the external file looks like this:

// Batmosphere Embedded Media Player, version 2006-05-31 
// Written by David Battino, www.batmosphere.com
// OK to use if this notice is included
// This function reads an MP3 URL and title from the referring page and generates embedding code to play back the audio file.
// Windows browsers (except for Internet Explorer) will play back the file with the Windows Media Player *plugin.* Internet Explorer will use Windows Media Player.
// Non-Windows browsers will play back the file with their standard audio handler for the MIME type audio/mpeg. On Macs, that handler will usually be QuickTime.

var audioFolder = ""; // If you have a default audio directory, e.g., http://www.your-media-hosting-site.com/sounds/, you can put it here to make links on the referring page shorter.

function embedPlayer(MP3title,MP3URL) { 
   // Get Operating System 
   var isWin = navigator.userAgent.toLowerCase().indexOf("windows") != -1;
   if (isWin) { // Use MIME type application/x-mplayer2
      visitorOS="Windows";
   } else { // Use MIME type audio/mpeg, audio/x-wav, etc.
      visitorOS="Other";
   }

   var audioURL = audioFolder + MP3URL;
   var objTypeTag = "application/x-mplayer2"; // The MIME type to load the WMP plugin in non-IE browsers on Windows
   if (visitorOS != "Windows") { objTypeTag = "audio/mpeg"}; // The MIME type for Macs and Linux 
  
   document.writeln("<div>");
   document.writeln("<strong style='font-size:18px; position:relative; top:-28px'>" + MP3title + "&nbsp;</strong>");  // Adjust font style to taste
   document.writeln("<object width='280' height='69'>"); // Width is the WMP minimum. Height = 45 (WMP controls) + 24 (WMP status bar) 
   document.writeln("<param name='type' value='" + objTypeTag + "'>");
   document.writeln("<param name='src' value='" + audioURL + "'>");
   document.writeln("<param name='autostart' value='0'>");
   document.writeln("<param name='showcontrols' value='1'>");
   document.writeln("<param name='showstatusbar' value='1'>");
   document.writeln("<embed src ='" + audioURL + "' type='" + objTypeTag + "' autoplay='false' autostart='0' width='280' height='69' controller='1' showstatusbar='1' bgcolor='#ffffff'></embed>"); 
   
   // Firefox and Opera Win require both autostart and autoplay
   document.writeln("</object>");
   document.writeln("</div>");
   document.close(); // Finalizes the document
}

Play On

After an enormous amount of research and experimentation, I think I've finally found a near-universal way to embed audio in web pages without using Flash. I now have MP3s, M3Us, MIDs, AIFFs, WMAs, and WAVs playing on Windows (Internet Explorer 6, Internet Explorer 7, Firefox, and Opera) and the Mac (Safari, Firefox, Camino, and Opera). Better, the Windows browsers work both with and without QuickTime, and the JavaScript method is the recommended way to deal with Internet Explorer 7's aversion to embedded media. The only stubborn browser is Opera on the Mac, which tries to load the whole file before starting playback. I haven't been able to test this technique on Linux, so I'm curious to hear your experiences; please leave comments below.

My next goal is to develop an easy way to "skin" the player, probably by adding a few more variables to the launch links. I've also been experimenting with launching movie clips, though the multitude of codecs out there makes that onerous.

Eventually, I'd like to implement this technique in a non-Flash version of the Del.icio.us Play Tagger, a bookmarklet that rewrites any page you visit, replacing generic MP3 links with an embedded audio player. Play Tagger currently handles only MP3s at standard bitrates, and it seems to need to load them completely before starting playback, so there's room for improvement. And after all, that's is what got me interested in web audio in the first place. Download the tutorial files and let us know what you think.

We're All Embed Together

Here are some sites that helped me develop the audio embedding code. Several contradict each other, so experimentation was essential.

David Battino is the audio editor for O’Reilly’s Digital Media site, the co-author of The Art of Digital Music, and on the steering committee for the Interactive Audio Special Interest Group (IASIG). He also writes, publishes, and performs Japanese kamishibai storycards.


Return to the digitalmedia.oreilly.com

Copyright © 2009 O'Reilly Media, Inc.