Writing

Other Sites

Playing Multiple Simultaneous Sounds in WPF

Friday 25 January, 2008, 11:40 AM

WPF’s MediaElement makes simple media playback pretty straightforward, but moving beyond the simple scenarios can sometimes raise surprising challenges. For example, I recently saw someone tripped up by the MediaElement when attempting to play several sounds concurrently.

As you’ll see, one solution would have been to use MediaPlayer instead of MediaElement. The difference between these WPF classes is fairly straightforward. MediaPlayer is the class that knows how to play media files – both video and audio. MediaElement is a wrapper around MediaPlayer that provides a simple way to connect it into a visual tree (i.e. a user interface), which in turn lets us hook it into things like the animation system or event triggers.

(Note: do not be misled by the class name. Although WPF and Windows Media Player depend on the same infrastructure for media decoding, the MediaPlayer class is not a wrapper around the Windows Media Player control. While they share codecs, the path by which decoded video gets onto the screen in WPF is significantly different from Windows Media Player.)

How would that get you into trouble when using MediaElement? If it’s a wrapper around MediaPlayer, surely you could use a MediaElement any place a MediaPlayer would work? In fact it’s not always that simple. To see why, we’ll start with a simple example.

One MediaElement

The simplest way to use MediaElement is to add it to a UI and point it at a media file:

This approach is also often sufficient for playing multiple different sounds. you can change the Source property and call Play again. However, if you want to play multiple sounds simultaneously, this approach doesn’t work – setting the Source will stop playback if it is in progress. A single MediaElement or MediaPlayer can only play one thing at a time.

That’s OK, because we can always create multiple MediaElements.

Multiple MediaElements

Modifying the example above simply by adding multiple MediaElements to the Window will stop the Xaml from compiling, because Window can have only a single direct descendant. So we need to find something to hold the MediaElements. And this is where the example I saw tripped up: the developer put them into the UI’s Resources section.

On the face of it, this was a perfectly reasonable thing to do – the elements are all playing audio, so it doesn’t seem like they should need to be part of the visual tree, so why not make them resources? After all, WPF’s resource mechanism is designed to hold useful objects, right?

Well this is where the difference between MediaPlayer and MediaElement becomes important. Remember, the distinction is that MediaElement connects media playback into a visual tree. And it turns out that until it makes that connection, MediaElement won’t play the media. That makes sense for video – you don’t want that to start playing before you can see it. But while you might think a connection with the visual tree would be optional for audio, MediaElement sees it differently. (And there are reasons for that. For example, MediaElement can synchronize media playback with timelines of animations in the visual tree.)

So in this case, the extra functionality provided by the wrapper has worked against us.

One solution is simply to give the MediaElement what it wants. As long as we put it into the visual tree, it’s happy. So we can put the elements into a layout panel such as a Grid or Canvas:

The other approach is to go straight for the MediaPlayer – if we have no need for the visual tree integration features MediaElement offers, we may as well go straight to the underlying player. The only snag is that you can’t initialize MediaPlayer from Xaml – you must use the Open method to point it at the media file, and Xaml doesn’t do method calls. But it’s not a huge amount of effort: