Playing Multiple Simultaneous Sounds in WPF
Also see: Bloggers in the Mavs Locker Room ?
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:
Also see: Natural Sorting in C#
Also see: Passing the Community Torch: In Search of a New Chief Executive in Redmond
Also see: Parallels adds “Express Windows Installation”
Also see: When Will Foreign Ownership of US Sports Teams Start ?
Also see: Why Yahoo should say Yes to MicroSoft
Also see: The Internet is Officially Dead & Boring – Its the economy stupid !
Also see: The 2 Technology Magazines You Should Read
<Window x:Class="MediaPlayback.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <MediaElement Source="file:///c:/windows/media/tada.wav" /> </Window>
This will play the file soon as the UI loads. If you want a bit more control, you can tell it to wait until you’re ready:
<MediaElement x:Name="audioPlayer" Source="file:///c:/windows/media/tada.wav" LoadedBehavior="Manual" />
Also see: Silverlight 2 Beta 1 Cross Domain Bug
Also see: Mix 08 Sessions Published
Also see: We Live in an “Open Book” World, the Lie of Information Overload
Also see: Single source code base for Silverlight and WPF solutions
Also see: Important changes to the BASE element for IE 7
Also see: REST2SQL in a Jiffy, with Tagspace for Spice
Also see: The 2 Technology Magazines You Should Read
Also see: A Quick Fix for the Validator SetFocusOnError Bug
Also see: Determining Whether a File Is an Assembly
Also see: On the Perils of Wikipedia
Also see: The PDC and Application Compatibility, but still no Hosting
Also see: A Couple of My Rules for Startups
Also see: DevWeek 2008 Cross Platform Silverlight Demos
Also see: Versioning/Deploying Unmanaged Files
Also see: Java Frameworks State of the (dis)Union.
Also see: Help John Baez and Mike Stay!
Also see: DevWeek 2008 Cross Platform Silverlight Demos
Also see: Is this the best NBA season ever ?
Also see: Hosting
Also see: Sometimes, it’s the small things..
Also see: Finally, the Killer App
Also see: A Couple of My Rules for Startups
Also see: From C# to Java: Part 4
Also see: On the Perils of Wikipedia
Also see: Java perfomance talk
Also see: Startup, Shutdown and related matters
It’ll now hold off until you call audioPlayer.Play().
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.)
Also see: JSR-294 Superpackages
Also see: REST2SQL in a Jiffy, with Tagspace for Spice
Also see: Big in Japan
Also see: Determining the Referencing Assembly
Also see: Finalization
Also see: Interested in Artificial Intelligence? What about Wiki’s? Well, now you can have both.
Also see: The PDC and Application Compatibility, but still no Hosting
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:
<Canvas> <MediaElement x:Name="mediaElem1" Source="file:///c:/windows/media/tada.wav" LoadedBehavior="Manual" /> <MediaElement x:Name="mediaElem2" Source="file:///c:/windows/media/Windows Logoff Sound.wav" LoadedBehavior="Manual" /> </Canvas>
Also see: Tagspace: Social Bookmarking for the Whole Web…from Microsoft
Also see: Data Types a la Carte
Also see: Alexbarn Leaves Microsoft…ARGH!
Also see: My Presidential Endorsement:
Also see: Eriskay: a Programming Language Based on Game Semantics
Also see: Apartments and Pumping in the CLR
Also see: Turning bitboards from potential moves into legal moves, pawn moves, and conditional rules.
Also see: My Presidential Endorsement:
Also see: Help John Baez and Mike Stay!
Also see: SIGPLAN Workshop on Undergraduate Programming Language Curriculum
Also see: C# 3.0 Lambdas and Type Inference
Also see: Versioning/Deploying Unmanaged Files
Also see: Why Yahoo should say Yes to MicroSoft
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:
MediaPlayer mp = new MediaPlayer(); mp.Open(new Uri(wavPath)); mp.Play();
That is all the code required; we don’t need anything at all in the Xaml. And to play multiple simultaneous sounds, you can simply create multiple MediaPlayers.
http://www.interact-sw.co.uk/iangblog/2008/01/25/wpf-concurrent-audio