Friday, March 1, 2013

A minor detour

While working on audio visualization and processing, I thought it might be fun to try to learn HTML5's support for drag-and-drop, and see how well I could tie it into <audio> to make a jukebox of sorts. Call this version 0.1 - demo.

Just drag in any music file (.wav, .mp3, .m4a). Double-click a track to start playing. The player should rotate through the playlist and keep playing songs.

Disclaimer: I write my Javascript for Google Chrome. If this works on a Mozilla or IE browser, great. (If it works on an IE browser, I'll be really surprised... IE is pretty horrible) I'm not investing the time to make everything cross-compatible.
Disclaimer: You're on your own for sound files. I cannot and will not provide any music to use.
Disclaimer: This is a demo. If you're an enterprising person, or you're really lazy, you'll choose to copy and paste my code into your own application. Use of this code comes with no guarantee of support. If you choose to use it, learn from it, do something with it, at least do me the favor of dropping a comment in the box below and let me know.

    UPDATE: The Jukebox demo has been relocated here

The Interesting Stuff
Drag-and-drop support in HTML5 is really nice, and rather painless. Quick notes on how to use it:
1. Add the 'drop' and 'dragover' event handlers to the element that is going to be dropped into. In my demo, I use a <div>. I'm not sure if all elements can be dropped into - my brief research didn't suggest that there were any restrictions on particular tags.
2. The drop handlers need to stop the browser from handling any dragged content natively. This is done through function calls to e.preventDefault() and/or e.stopPropogation() in the dragover and drop handlers.
3. When dropping files, you'll get a file list returned from e.dataTransfer.files. You'll need to handle it like you'd handle any file list in Javascript. Iterate through the array of File objects and handle each file iteratively.

I really like the File->Blob URL conversion that can be done rather painlessly through the native File API. Want a way to handle your File blobs in a way that native HTML tags and the browser will understand? Convert it to a Blob, then generate a blob:// URL for the Blob. You can treat that file as a URL for the lifetime of your program. Here's a snippet showing how I generate and store my blob URLs in an indexed hash:
  fileBlobs[f.name] = window.URL.createObjectURL(f.slice(0, f.size, "image"));

Despite storing local files in a Blob, there is still some latency between when the source URL is assigned to the blob in the audio tag, and when the meta data from the file is parsed so things like the track duration can be retrieved. If you try to read the duration attribute right after assigning the src attribute, you'll likely get 'NaN' returned. The workaround - add an event listener for the 'loadedmetadata' event to the audio tag. Once the meta data has loaded, you can read the duration attribute and get a correct value (in seconds). Here's an example of how I handled support for calculating the duration:
  audio.addEventListener('loadedmetadata', function(e) {
    var durationDiv = document.getElementById("duration");
    var minutes = Math.floor(audio.duration / 60);
    var seconds = Math.floor(audio.duration) % 60;
    if (seconds < 10)
      durationDiv.innerHTML = "<b>Length:</b> " + minutes + ":0" + seconds;
    else
      durationDiv.innerHTML = "<b>Length:</b> " + minutes + ":" + seconds;
  }, false);

I'll revisit this demo in the future, adding some visualizations rendered from AudioContext post-processing, a track randomizer, custom play controls, support for track seeking, maybe even real-time, controllable filtering.

No comments:

Post a Comment