Design

Your Own MP3 Duration Calculator

By Gigi Sayfan, January 26, 2010

The TrackDurations Class

The TrackDurations class is the workhorse that does the heavy lifting of accurately measuring the duration of each track. It utilizes the useful MediaElement class, which wraps the Windows Media Player engine (At least the Microsoft Windows Media Player 10 OCX for media playback).

Before diving into the TrackDurations class, let me introduce the TrackInfo class. This is basically a pair of properties: Name (a string) and Duration. A collection of TrackInfo object is the main data structure that travels from the TrackDurations class to the MainWindow class. The fact that Name and Duration are properties allows the UI to bind to them directly. More about it later:

The TrackDurations class uses a delegate to notify the UI whenever it discovers the duration of another track. After processing all the tracks it sends a null object to let the UI know it's done. Here is the signature:

delegate void TrackDurationsDelegate(TrackInfo ti);

A delegate is really a callback function. In this case a TrackInfo object is sent with each call.

TrackDurations implements the IDisposable interface, which allows explicitly disposing of resources by calling its Dispose() method:

class TrackDurations : IDisposable
{
...
}

The class keeps references to a media element and the delegate and stores a list of files in a stack:

The media element is not managed by TrackDurations (it doesn't create or own it). It is passed in the constructor. The reason for this design is that a TrackDurations object is instantiated every time a new folder needs to be scanned, but a single media element can be used because it is relatively expansive to instantiate one. Also, there is never more than one scan going on at the same time, so there is no need to worry about conflicts.

The constructor accepts a media element, a delegate and a list of file names. It stores them (the list of files in a stack that can be popped). It sets the LoadedBehavior of the media element to "Manual", which means programmatic control on play/pause/stop, attaches two event handlers for MediaOpened and MediaFailed, sets the Source property to the first file in the list, and tells the media element to play it:

The result of all these operations is that the media element will open the first file in order to play it. If the open operation succeeds the _onMediaOpened() event handler will be called; otherwise _onMediaFailed() will be called.

The _getNextTrack() method is used by both event handlers to move on after the current track has been handled. It reads the next file from the stack (processed files are removed by the event handlers) and tries to play it. If there are no more files the delegate is called with a null object to signal end of processing:

When the media element opens successfully the current file as a result of a Play() call the MediaOpened event is fired. That causes the _onMediaOpened() event handler to be called. At this point the correct duration is available as the NaturalDuration property of the media element. The event handler pauses the media element to avoid accidental playing, creates a new TrackInfo object, stops the media element completely, invokes the delegate and finally gets the next track.

If for some reason, the media element failed to open the track the MediaFailed event is fired and the _onMediaFailed() event handler is called. All it does, is pop the bad file from the files list and get the next track:

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!