DCSIMG
Windows Phone: Playing Sound Effects - Pavel's Blog
Sign in | Join | Help

Pavel's Blog

Pavel is a software guy that is interested in almost everything
software related... way too much for too little time

Windows Phone: Playing Sound Effects

I wanted to create a simple timer application for Windows Phone. The timer would count backwards from a user configured value and when it reached zero, it would play some sound effect to indicate expiration. Sounds simple enough…

This is how the application looks like:

image

The problem turned out to be playing that sound effect. I had a WAV file I wanted to play. Silverlight has a MediaElement object that is capable of playing video and/or audio, so it seemed to be a good candidate for the job. MediaElement is an element, so must be placed somewhere in the visual tree. it can be placed anywhere, because it won’t be visible anyway. Here’s a simple setup for that kind of MediaElement:

 

<MediaElement Source="sound.wav" AutoPlay="False" x:Name="_media" />

This looks simple enough. All that’s left is to call the Play method and we’re done. Unfortunately, this kind of setup won’t pass marketplace certification. Why?

The problem is that MediaElement is too aggressive in nature. If a song is playing (because the user played something through the Music hub) at the moment the page loads, that music immediately stops. This is considered impolite, and will fail certification. What can we do about that?

One thing we may consider is not to create the MediaElement upfront, but to create at only when the sound actually needs to play – insert it into the visual tree and play. This is not good enough, because that background music will stop at play time, again failing certification.

The next thing we may try is to warn the user that this may happen if a song is currently playing in the background. Looking at the Silverlight APIs, we find the MediaHistory singleton class, that seems to be able to provide (through the NowPlaying property) information about the currently playing song (if any). Unfortunately, this doesn’t work. This class returns the currently playing item if started from this application only – not globally. This class is used if the app is integrating with the Music hub.

Surprisingly enough, the solution is found in an XNA class called MediaPlayer (in the Microsoft.XNA.Framework.Media namespace). This does look at the global queue of playing items. We can use code similar to this:

 

var song = MediaPlayer.Queue.ActiveSong;
if (song != null) {
    var result = MessageBox.Show("When the timer expires, the music will stop. Continue?", "Simple Timer (Free)",
        MessageBoxButton.OKCancel);
    if (result == MessageBoxResult.Cancel)
        NavigationService.GoBack();
}

Will this approach pass marketplace certification? Maybe. But this seems overkill – stop all music to play some measly sound?

The best approach for this is to abandon MediaElement. It is a heavy duty element, that’s good for video playback. For simple sounds, the best approach is to go with functionality provided by XNA. Games built with XNA use the SoundEffect class (and SoundEffectInstance) to play sounds in a low-latency way, with no disruption to any background music that may be playing.

All we need to do is create a SoundEffect object, load it with the WAV file bits, and call Play when appropriate. Here’s a way to set it up:

_effect = SoundEffect.FromStream(Application.GetResourceStream(new Uri("sound.wav", UriKind.Relative)).Stream);

SoundEffect has a factory method that creates an instance based on a stream, which is handy in this case. Are we done? Almost.

Another thing that needs to be done is make sure XNA is being updated every frame. In a typical XNA game, a game loop is executed repeatedly, and this is part of what makes SoundEffect work correctly. Since this is a Silverlight app (and not XNA), we need to run that loop artificially, by calling FrameworkDispatcher.Update every rendering frame like so:

CompositionTarget.Rendering += (sender, args) => FrameworkDispatcher.Update();

The update is done by registering for the static CompositionTarget.Rendering event.

That’s it. Playing simple sounds on Windows Phone can be done with XNA even if the app is built with Silverlight. Hopefully, in Windows Phone 8, things will be somewhat more integrated and would not require straddling between frameworks.

Comments List

No Comments

Leave a Comment

(required) 
(
required
)
 
(optional)
(required) 

Enter the numbers above: