Playing Audio, Windows Phone Audio

0
304

Getting Started with Windows Phone Audio

The audio system in XNA makes it possible to reproduce sound effects and music in two different ways, but the WP7 platform supports only one of them. First, we can play audio clips directly from audio files loaded at runtime, with support for the most common audio file formats. Second, in XNA, we can use Microsoft’s Cross-Platform Audio Creation Tool, also known as XACT, which is more often used for complex games with many audio files. The first approach involves loading and managing audio objects in our own code. The second approach leaves the details largely up to classes provided for working with XACT resources built at compile time and then made available from a container of objects. But we can’t use XACT with WP7 projects, so we’ll learn about the first and only option instead. We’ll cover the audio system in the Microsoft.Xna.Framework.Audio namespace with an example of the audio system.

Simple Audio Playback

There is one very easy way to get audio to play in an XNA project: by using the SoundEffect class. There is a drawback to using SoundEffect assets that really can’t be avoided: the tendency for the content project to become cluttered with asset files. Even so, the SoundEffect class is convenient and easy to use. A helper class called SoundEffectInstance is also used in conjunction with SoundEffect for audio playback. SoundEffect itself has a Play() method, but it is rudimentary. SoundEffectInstance.Play() is more capable and versatile.

Adding Audio Content

Before we can play an audio clip, we have to add it to the content system.

  1. First, right-click the Content project in Solution Explorer, choose Add, and then choose Existing Item, as shown in Figure 18.1. This opens the Add Existing File dialog box.

    Adding an existing audio file to the content project.
    FIGURE 18.1 Adding an existing audio file to the content project.
  2. Locate the audio file you want to add to the project. Select the file in the file browser and click the OK button.

Loading an Audio Asset File

The following audio file types can be added to an XNA project for use with the SoundEffect class, but remember that WP7 does not support XACT:

  • XAP
  • WAV
  • WMA
  • MP3

You can create an instance of the SoundEffect class with a variable declared in the globals section at the top of the class as follows:

[code]
SoundEffect clip;
[/code]

The SoundEffect object is created in the usual LoadContent() function where all other asset files are loaded:

[code]
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“Arial”);
clip = Content.Load<SoundEffect>(“sound_clip”);
}
[/code]

Playing an Audio Clip

The audio clip can be played back using the SoundEffect.Play() method. There is a simple version of Play() and an overloaded version, which gives you control over the Volume, Pitch, and Pan properties. The audio clip is played directly from the object like so:

[code]
clip.Play();
[/code]

Audio Clip Length

There is really only one useful property in the SoundEffect class: Duration. This property gives you the length of the audio clip in seconds. In the Draw() function of your sample program (Simple Audio Demo), you can print out the length of the audio clip.

[code]
string text = “Clip length: “ + clip.Duration.ToString();
spriteBatch.DrawString(font, text, Vector2.Zero, Color.White);
[/code]

When it comes down to playing audio clips in gameplay code, this class is not very helpful at all. What we need is a class with more functionality.

SoundEffectInstance

The SoundEffect class works, but not very well on its own. For one thing, a SoundEffect clip played does not work well in a complex audio environment (with music and different sound effects playing at the same time). For that, we need a helper class that can repeat playback with mixing support. That class is called SoundEffectInstance. This is really what we want to use when playing audio clips.

The SoundEffectInstance class enhances the basic functionality of SoundEffect with additional properties and methods that make it more useful in a real-world game. After loading the SoundEffect in LoadContent(), we can create an instance of the SoundEffectInstance class from its CreateInstance() method:

[code]
SoundEffect clip = Content.Load<SoundEffect>(“sound_clip”);
SoundEffectInstance clipInst = clip.CreateInstance();
[/code]

This class makes available several useful methods: Play(), Stop(), Pause(), and Resume(). And the Volume, Pitch, and Pan properties have been moved from parameters into real class properties, which we can modify outside of the Play() method. In addition, we can cause an audio clip to loop during playback with the IsLooping property.

MySoundEffect Class

I have prepared a helper class with essentially the sole purpose of just combining a SoundEffect and SoundEffectInstance object with a Load() method. We will use this helper class in the example coming up next. The source code for the class is found in Listing 18.1.

LISTING 18.1 Source Code for the MySoundEffect Class

[code]
public class MySoundEffect
{
private ContentManager p_content;
public SoundEffect effect;
public SoundEffectInstance instance;
public MySoundEffect(ContentManager content)
{
p_content = content;
effect = null;
instance = null;
}
public void Load(string assetName)
{
effect = p_content.Load<SoundEffect>(assetName);
instance = effect.CreateInstance();
}
}
[/code]

Creating the Audio Demo Program

The Audio Demo program is a complete example of audio for WP7 with some user input features as well, as shown in Figure 18.2. Five buttons are located on the screen that can be touched to play the five audio clips loaded into the program.

The Audio Demo program.
FIGURE 18.2 The Audio Demo program.

Button Class

The audio sample project uses a helper class called Button to draw numbered buttons on the screen that respond to screen touch input events. The Button class requires a bitmap file called button.png that is just a transparent image with a border around its edges. Listing 18.2 contains the source code for the Button class.

LISTING 18.2 The Button Class

[code]
public class Button : Sprite
{
public string text;
private SpriteBatch p_spriteBatch;
private SpriteFont p_font;
public Button(ContentManager content, SpriteBatch spriteBatch,
SpriteFont font) : base(content, spriteBatch)
{
p_spriteBatch = spriteBatch;
p_font = font;
Load(“button”);
text = ““;
color = Color.LightGreen;
}
public void Draw()
{
base.Draw();
Vector2 size = p_font.MeasureString(text);
Vector2 pos = position;
pos.X -= size.X / 2;
pos.Y -= size.Y / 2;
p_spriteBatch.DrawString(p_font, text, pos, color);
}
public bool Tapped(Vector2 pos)
{
Rectangle rect = new Rectangle((int)pos.X, (int)pos.Y, 1, 1);
return Boundary().Intersects(rect);
}
}
[/code]

Audio Demo Source

The main source code for the Audio Demo project is found in Listing 18.3. The required assets are a button image and five audio files named clip1, clip2, clip3, clip4, and clip5.

LISTING 18.3 Source Code for the Audio Demo Program

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TouchLocation oldTouch;
SpriteFont font;
Button[] buttons;
MySoundEffect[] sounds;
int current = -1;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
oldTouch = new TouchLocation();
}
protected override void Initialize()
{
base.Initialize();
this.IsMouseVisible = true;
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
//create buttons
buttons = new Button[5];
for (int n = 0; n < 5; n++)
{
buttons[n] = new Button(Content, spriteBatch, font);
buttons[n].text = (n+1).ToString();
buttons[n].position = new Vector2(100 + 110 * n, 200);
}
//create sound clips
sounds = new MySoundEffect[5];
for (int n = 0; n < 5; n++)
{
sounds[n] = new MySoundEffect(Content);
sounds[n].Load(“Audio//clip” + (n+1).ToString());
}
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
TouchCollection touchInput = TouchPanel.GetState();
if (touchInput.Count > 0)
{
TouchLocation touch = touchInput[0];
if (touch.State == TouchLocationState.Pressed)
{
current = -1;
int n = 0;
foreach (Button b in buttons)
{
int x = (int)touch.Position.X;
int y = (int)touch.Position.Y;
if (b.Boundary().Contains(x,y))
{
current = n;
sounds[current].instance.Play();
break;
}
n++;
}
}
oldTouch = touch;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.FrontToBack,
BlendState.AlphaBlend);
spriteBatch.DrawString(font, “Audio Demo”, Vector2.Zero,
Color.White);
foreach (Button b in buttons)
{
b.Draw();
}
if (current > -1)
{
string text = “Clip length: “ +
sounds[current].effect.Duration.ToString();
spriteBatch.DrawString(font, text, new Vector2(0, 400),
Color.White);
}
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

We have now fully covered the XNA audio system with an example of how to load and play audio files. The helper class, MySoundEffect, can be dropped into any WP7 project and used to more easily handle the audio needs of a game.