jQuery

Drumbeat Demo – HTML5 Audio Text Sync

Last month I had the pleasure of travelling to Barcelona to participate in Mozilla’s Drumbeat festival (of which more details are to come).

I very much wanted to demo the capabilities of HTML5 audio and so set about creating a demo in keeping with the theme of the festival – ‘Learning, Freedom and the Web’. I ended up with a very rough prototype of a web app that synchronised audio to text, word for word, more accurately it provided an interface that allowed a person to synchronise the audio to text and then demonstrated a couple of things that were made possible once this synchronisation had taken place.

So what was possible?

The first thing I found was that once I had the timings I could easily create a mechanism to control the audio from the text. Now by clicking on individual words I could jump to the corresponding part of the audio, useful for navigating audio and also potentially as an aid to learning language. I took this a little further by allowing the user to highlight areas of the text and having just that part of the text played back, which was um, an interesting exercise*.

*This feature is very very experimental and needs some love.

Finally I added a bit of razzmatazz, tacking on ‘Image Overlay Mode’, which is really a text overlayed on image mode, but that was a bit too wordy. To achieve image overlay mode in my limited time, I used canvas, however I’m aware that CSS3 is probably a better fit for this type of ‘animation’.

The code is very crude – it really was just flung together in a desperate rush to get it working for the Drumbeat Science Fair, so I hesitate to say feel free to take it and do with it what you will. But please do consider all demos posted on our blog open source and dual licensed under the MIT and GPL licenses.

Once again the jPlayer library came in handy, providing a useful abstraction and ensuring that the solution works on various platforms.

The demo.

Instructions :

1. In Sync Mode – press play on the player and use the space bar or sync button to synchronise the words with the audio.

2. Switch to Playback Mode to see the words synched to the audio. Click on the words to play the audio from that point. Try selecting areas of text.

3. Hit Image Overlay Mode if you are so inclined and have a canvas enabled browser.

4. Try Hack mode if you want to adjust any part of the timings and/or words.

Alternatively watch a screen capture of the demo :

mp4 | ogv

(Flash version coming soon, honest.)

Feedback and ideas on this demo are particularly appreciated. I’m also interested in possible uses (apart from karaoke ;) ) and perhaps other projects that I could collaborate with to make something genuinely useful.

Thanks to @elmook, @f1lt3r, @sroux, @aulentina and others who gave feedback and encouragement. Special thanks to @bluetezza for the original idea.

Mark B

Sunday, December 5th, 2010 Audio, HTML5, javascript, jPlayer, jQuery 26 Comments

Add a Stylish Audio Player to your Blog Without using Plugins

Mark Boas

Admittedly, it’s a little crude, but it’s simple and it works.

If you’ve ever wanted to add an audio player to your blog post and didn’t want to go to the trouble of installing a specific plugin to do so, you can always embed jPlayer within an iFrame. The one-liner you need to achieve this would probably look a lot like this:


which could give you something like this :

Sure, it’s not that efficient, you may end up loading jQuery twice if it’s already included as part of your blog, but there’s the added advantage that the page will render progressively and you have a nice little sandbox to play in should you just want to alter the player.

I used to hate iFrames and shudder at their very mention, but I must admit I’m coming around to them.

Mark B

A Simple and Robust jQuery 1.4 CDN Failover in One Line

Mark Boas

Google, Yahoo, Microsoft and others host a variety of JavaScript libraries on their Content Delivery Networks (CDN) – the most popular of these libraries being jQuery. But it’s not until the release of jQuery 1.4 that we were able to create a robust failover solution should the CDN not be available.

First off, lets look into why you might want to use a CDN for your JavaScript libraries:

1. Caching – The chances are the person already has the resource cached from another website that linked to it.

2. Reduced Latency – For the vast majority the CDN is very likely to be much faster and closer than your own server.

3. Parallelism – There is a limit to the number of simultaneously connections you can make to the same server. By offloading resource to the CDN you free up a connection.

4. Cleanliness – Serving your static content from another domain can help ensure that you don’t get unnecessary cookies coming back with the request.

All these aspects are likely to add up to better performance for your website – something we should all be striving for.

For more discussion of this issue I highly recommend this article from Billy Hoffman : Should You Use JavaScript Library CDNs? which includes arguments for and against.

The fly in the ointment is that we are introducing another point of failure.

Major CDNs do occasionally experience outages and when that happens this means that potentially all the sites relying on that CDN go down too. So far this has happened fairly infrequently but it is always good practice to keep your points of failure to a minimum or at least provide a failover. A failover being a backup method you use if your primary method fails.

I started looking at a failover solution for loading jQuery1.3.2 from a CDN some months ago. Unfortunately jQuery 1.3.2 doesn’t recognise that the DOM is ready if it is added to a page AFTER the page has loaded. This made making a simple but robust JavaScript failover solution more difficult as the possibility existed for a race condition to occur with jQuery loading after the page is ready. Alternative methods that relied on blocking the page load and retrying an alternative source if the primary returned a 404 (page not found) error code were complicated by the fact that the Opera browser didn’t fire the "load" event in this situation, so when creating a cross-browser solution it was difficult to move on to the backup resource.

In short, writing a robust failover solution wasn’t easy and would consume significant resource. It was doubtful that the benefit would justify the expense.

The good news is that jQuery1.4 now checks for the dom-ready state and doesn’t just rely on the dom-ready event to detect when the DOM is ready, meaning that we can now use a simpler solution with more confidence.

Note that importantly the dom-ready state is now implemented in Firefox 3.6, bringing it in line with Chrome, Safari, Opera and Internet Explorer. This then gives us great browser coverage.

So now in order to provide an acceptably robust failover solution all you need do is include your link to the CDN hosted version of jQuery as usual :

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>

and add some JS inline, similar to this:

  <script type="text/javascript">
  if (typeof jQuery === 'undefined') {
     var e = document.createElement('script');
     e.src = '/local/jquery-1.4.min.js';
     e.type='text/javascript';
     document.getElementsByTagName("head")[0].appendChild(e);
  }
  </script>

You are then free to load your own JavaScript as usual:

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

It should be well noted however that the above approach does not "defer" execution of other script loading (such as jQuery plugins) or script-execution (such as inline JavaScript) until after jQuery has fully loaded. Because the fallbacks rely on appending script tags, further script tags after it in the markup would load/execute immediately and not wait for the local jQuery to load, which could cause dependency errors.

One such solution is:

<script type="text/javascript">
  if (typeof jQuery === 'undefined') 
     document.write('script type="text/javascript" src="/local/jquery-1.4.min.js"><\/script>');
</script>

Not quite as neat perhaps, but crucially if you use a document.write() you will block other JavaScript included lower in the page from loading and executing.

Alternatively you can use Getify‘s excellent JavaScript Loading and Blocking library LABjs to create a more solid solution, something like this :

  <script type="text/javascript" src="LAB.js">
  <script type="text/javascript">
  function loadDependencies(){
  $LAB.script("jquery-ui.js").script("jquery.myplugin.js").wait(...);
  }

  $LAB
  .script("http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js").wait(function(){
  if (typeof window.jQuery === "undefined") 
     // first load failed, load local fallback, then dependencies
     $LAB.script("/local/jquery-1.4.min.js").wait(loadDependencies);
  else 
     // first load was a success, proceed to loading dependencies.     
     loadDependencies();  
  });
  </script>

Note that since the dom-ready state is only available in Firefox 3.6 + <script> only solutions without something like LABjs are still not guaranteed to work well with versions of Firefox 3.5 and below and even with LABjs only with jQuery 1.4 (and above).

This is because LABjs contains a patch to add the missing "document.readyState" to those older Firefox browsers, so that when jQuery comes in, it sees the property with the correct value and dom-ready works as expected.

If you don’t want to use LABjs you could implement a similar patch like so:

  <script type="text/javascript">
  (function(d,r,c,addEvent,domLoaded,handler){
     if (d[r] == null && d[addEvent]){
        d[r] = "loading";
        d[addEvent](domLoaded,handler = function(){
        d.removeEventListener(domLoaded,handler,false);
        d[r] = c;
     },false);
   }
})(window.document,"readyState","complete","addEventListener","DOMContentLoaded");
</script>

(Adapted from a hack suggested by Andrea Giammarchi.)

So far we have talked about CDN failure but what happens if the CDN is simply taking a long time to respond? The page-load will be delayed for that whole time before proceeding. To avoid this situation some sort of time based solution is required.

Using LABjs, you could construct a (somewhat more elaborate) solution that would also deal with the timeout issue:

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

  <script type="text/javascript">
  function test_jQuery() { jQuery(""); }
  function success_jQuery() { alert("jQuery is loaded!"); 

  var successfully_loaded = false;
  function loadOrFallback(scripts,idx) {
     function testAndFallback() {
        clearTimeout(fallback_timeout);
        if (successfully_loaded) return; // already loaded successfully, so just bail
        try {
           scripts[idx].test();
           successfully_loaded = true; // won't execute if the previous "test" fails
           scripts[idx].success();
        } catch(err) {
           if (idx < scripts.length-1) loadOrFallback(scripts,idx+1);
        }
     }
 
     if (idx == null) idx = 0;
     $LAB.script(scripts[idx].src).wait(testAndFallback);
     var fallback_timeout = setTimeout(testAndFallback,10*1000); // only wait 10 seconds
  }
  loadOrFallback([
     {src:"http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js", test:test_jQuery, success:success_jQuery),
     {src:"/local/jquery-1.4.min.js", test:test_jQuery, success:success_jQuery}
  ]);
  </script>

To sum up – creating a truly robust failover solution is not simple. We need to consider browser incompatabilities, document states and events, dependencies, deferred loading and time-outs! Thankfully tools like LABjs exist to help us ease the pain by giving us complete control over the loading of our JavaScript files. Having said that, you may find that in many cases the <script> only solutions may be good enough for your needs.

Either way my hope is that these methods and techniques provide you with the means to implement a robust and efficient failover mechanism in very few bytes.

A big thanks to Kyle Simpson, John Resig, Phil Dokas, Aaoran Saray and Andrea Giammarchi for the inspiration and information for this post.

Mark B

Related articles / resources:

1.http://LABjs.com

2.http://groups.google.com/group/jquery-dev/browse_thread/thread/87a675bd840ff070/ac60006335f9e23a?hl=en

3.http://aaronsaray.com/blog/2009/11/30/auto-failover-for-cdn-based-javascript/

4.http://snipt.net/pdokas/load-jquery-even-if-the-google-cdn-is-down/

5.http://stackoverflow.com/questions/1447184/microsoft-cdn-for-jquery-or-google-cdn

6.http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html

7.http://zoompf.com/blog/2010/01/should-you-use-javascript-library-cdns/

Tags: , , ,

Thursday, January 28th, 2010 AJAX, configuration, development, javascript, jQuery 10 Comments

jPlayer: the CSS styleable jQuery audio player plugin

[Edit: jPlayer 2.x now supports video. Released 20th December 2010.]

With the jPlayer plugin for jQuery it is possible to include a fully customizable mp3 player on webpages. jPlayer has been developed by Happyworm and features standard track and volume controls as well as the possibility to play a playlist.

jPlayer Demo

jPlayer Features:

  • play and control mp3 files in your webpage
  • create and style an mp3 player using just HTML and CSS
  • add sound effects to your jQuery projects
  • improved bandwidth in HTML5 with alternative ogg format support

What’s unique about this plugin is that the designer has full CSS control over the audio player on their webpage. Using standard HTML and CSS, they can style the audio player to be added to their site so that it fits with their overall site design.

The plugin checks for and uses HTML5 <audio> tag support for mp3 and ogg formats in complaint browsers, otherwise it uses a piece of Flash to play the mp3 files. This is hidden from view and the final look of the site is entirely up to the designer. As you’d expect, the operations of the plugin may be customised using bespoke javascript code in the webpages to perform whatever functions required.

For example, the following code automatically plays and then repeats ‘elvis.mp3′ or ‘elvis.ogg’ depending on browser support. Play, pause and stop controls are also made available.

$(document).ready(function(){
	$("#jquery_jplayer").jPlayer({ // Instantiate the plugin
		ready: function () { // Executed when the plugin is ready
			this.element.jPlayer("setFile", "elvis.mp3", "elvis.ogg").jPlayer("play"); // Auto-play the audio
		},
		oggSupport: true,
		customCssIds: true
	});

	$("#jquery_jplayer").jPlayer("cssId", "play", "play_button"); // Associates play
	$("#jquery_jplayer").jPlayer("cssId", "pause", "pause_button"); // Associates pause
	$("#jquery_jplayer").jPlayer("cssId", "stop", "stop_button"); // Associates stop

	$("#jquery_jplayer").jPlayer("onSoundComplete", function() { // Executed when the mp3 ends
		this.element.jPlayer("play"); // Auto-repeat
	});
});

<body> 
<div id="jquery_jplayer"> </div>
<ul>
	<li id="play_button"> play </li>
	<li id="pause_button"> pause </li>
	<li id="stop_button"> stop </li>
</ul>
</body>  
    The MP3 files used must be encoded according to the browser’s Flash Plugin limitations:

  • Constant Bitrate Encoded.
  • Sample Rate a multiple of 11,0025Hz. ie., 22,050Hz and 44,100Hz are valid sample rates.

Please use the jPlayer Google Group for support, bug reports and general discussion.

Mark P

NB: This post has been updated for jPlayer 1.x series. Please visit the main site for the latest documentation for jPlayer 2.x and above.

NB: This thread has been closed. Please use the jPlayer Google Group for support requests.

Tuesday, May 5th, 2009 CSS, flash, HTML, javascript, jPlayer, jQuery 147 Comments