Like haptic feedback, using sound effectively
can help you create a great application. As with any technique, though,
some finesse is required to ensure optimal use. On Windows Phone, you
have two options for playing sound: You can use the MediaElement
or use XNA. In addition, the phone enables you to record sound. This
section will cover all three of these aspects of using sound in your
application.
Playing Sounds with MediaElement
Because sound is just another type of media, the easiest way for most people to play sound is to just use the MediaElement
:
<Grid x:Name="ContentPanel">
<MediaElement x:Name="soundElement"
Source="/Assets/Foo.wav"
AutoPlay="False" />
</Grid>
For playing sounds, the MediaElement
is just an invisible part of the XAML and has no user interface. To use the MediaElement
, you typically specify a source file (as a URL to the sound). By setting AutoPlay
to false,
you can play the sound with code:
private void playButton_Click(object sender, RoutedEventArgs e)
{
soundElement.Play();
}
Although using a MediaElement
is a straightforward way to play a sound, it has some drawbacks.
• You must tie the playing of the sound directly to the user interface (the MediaElement
must exist in the XAML).
• Only one MediaElement
can be playing a sound at a time. So playing different sounds means swapping the Source
property as you use it.
When using the MediaElement
you can use one of a number of formats, including the following:
• PCM WAV file (.wav)
• Microsoft Windows Media Audio (.wma)
• ISO MPEG-1 Layer III (.mp3)
• Unprotected ISO Advanced Audio Coding (.aac)
By using the MediaElement
to play
your audio files, you have full media control (for example, volume,
stereo control, play/pause/stop, position). In most cases, if you want
to play longer sounds like songs or albums, you should probably use the built-in media player functionality, discussed next.
Using XNA Libraries
At the center of a standard XNA library is a
game loop that runs for the length of the program and handles the task
of updating the UI and accepting input. It is not event-based, like XAML
applications. The game loop’s job is to give up control so that each
frame of the game can be shown (that is, it updates the screen). This
doesn’t happen in XAML applications, so we must use the FrameworkDispatcher
class’s Update
method to accomplish this for us. In some cases we can just call it
after when we need to accomplish something with the XNA libraries (such
as playing a sound effect); other times we need to mimic the updating.
We can do this by using a DispatcherTimer
to update the game loop periodically:
DispatcherTimer _updateTimer = new DispatcherTimer();
// Constructor
public MainPage()
{
InitializeComponent();
// Set up timer to call the XNA Dispatcher (for example, Game Loop)
_updateTimer.Interval = TimeSpan.FromMilliseconds(50);
_updateTimer.Tick += (s, e) =>
{
FrameworkDispatcher.Update();
};
}
When we do this, we are periodically ceding to the XNA framework to do the work it normally does during the game loop.
Playing Sounds with XNA
Because you’re using XAML to build your
applications for the phone, you also can use some of the XNA libraries
in your application. The key class is the SoundEffect
class, which lets you play a short, fire-and-forget sound. This class
supports only PCM WAV (.wav) files, but it will give you a
high-performance sound effect when you need it (it is “high-performance”
in that it does not require nearly the amount of system resources to
play a sound as do formats that require
decompression [for instance, .mp3 and .wma]). In addition, playing
sounds with XNA will enable you to play multiple sounds at the same
time.
To use the SoundEffect
class you’ll need a reference to the Microsoft.Xna.Framework.dll
assembly and will have to import the Microsoft.Xna.Framework.Audio
namespace. After you do that, you can create a sound effect by calling the SoundEffect.FromStream
method, like so:
using Microsoft.Xna.Framework.Audio;
...
private void xnaPlayButton_Click(object sender, RoutedEventArgs e)
{
// Get the sound from the XAP file
var info = App.GetResourceStream(
new Uri("assets/chord.wav", UriKind.Relative));
// Load the SoundEffect
SoundEffect effect = SoundEffect.FromStream(info.Stream);
// Tell the XNA Libraries to continue to run
FrameworkDispatcher.Update();
// Play the Sound
effect.Play();
}
After you load the file from the XAP (using the GetResourceStream
method of the Application
class), you can create the sound effect directly from the stream of the
.wav file. Before you can play the effect, you must tell XNA to let
updates happen to the rest of the application using the FrameworkDispatcher.Update
method. Finally, you can play the effect. Playing the effect is not a blocking call (for example, Play
returns immediately).
Adjusting Playback
You can also use the SoundEffect
class to control the volume, pitch, and looping of a sound effect. To do
this, you need to create an instance of the sound effect you’ve loaded,
like so:
private void loopPitchButton_Click(object sender, RoutedEventArgs e)
{
// Get the sound from the XAP file
var info =
App.GetResourceStream(new Uri("alert.wav", UriKind.Relative));
// Load the SoundEffect
SoundEffect effect = SoundEffect.FromStream(info.Stream);
// Get an instance so we can affect the sound effect
SoundEffectInstance instance = effect.CreateInstance();
// Change the pitch and volume
instance.Pitch = -.35f; // Slow it down
instance.Volume = .95f; // Make it a little quiet
// Loop the sound
instance.IsLooped = true;
// Tell the XNA Libraries to continue to run
FrameworkDispatcher.Update();
// Play the Sound
instance.Play();
}
After you create the instance, you can specify
the pitch (zero is normal speed) and volume (1.0 is normal volume). You
can also specify whether the sound is to be looped. After you’ve made
these changes, you can play the sound with the changes (using the Play
method). Note that you’re playing the instance and not the sound effect, which is different from earlier examples.