Controlling an audio file
Music has the ability to impact the emotions of human beings. Different styles of music produce different feelings. Fast tempo music makes people feel nervous or excited; when the tempo becomes slow, people feel relaxed and safe. In modern video games, music often plays an important role in creating the atmosphere. Game designers and composers work together to tailor the music to the plot. When a player goes into a beautiful scene, a euphonious and harmonious song will accompany it; when immersed in a dangerous situation, the music will sound oppressive. In this recipe, you will learn how to use the media technology to control music in your Windows Phone 7 game.
Getting ready
In Windows Phone 7 XNA, a song or music is encapsulated as a Song class. This class provides the metadata of a song and includes the song’s name, artist, album, and duration. The Name and Duration are the most direct properties for your song. We will use them in our example. As a class design consideration, the Song class does not have a Play() method to play a song. You should use the MediaPlayer class, which provides methods and properties for playing songs. To control song playback, we can use the following methods:
- Play(): The Play() method kicks off the song.
- Stop(): The Stop() method stops the song playing and sets the playing position to the song’s beginning.
- Pause(): The Pause() method also stops the song playing, the difference between the Pause() and the Stop() method is that the Pause() method will not reset the playing position to the start. Instead, it keeps the playing position at the place where the song is paused.
- Resume(): The Resume() method replays the song from the paused position.
Besides these essential methods, if you have more than one song in your playing queue, the MoveNext() and MovePrevious() methods will help you to do circular playing. The two methods move to the next or previous song in the queue. They operate as if playing the queue was circular. That is, when the last song is playing the MoveNext() method moves to the first song and when the first song is playing the MovePrevious() method moves to the last song. If the IsShuffled property is set, the MoveNext() and MovePrevious() methods will randomly choose a song in the playing queue to play. To use these methods, there is no need to instantiate the MediaPlayer class, which is actually a static class. You can directly call the method using the following pattern:
[code]
MediaPlayer.MethodName()
[/code]
In this example, we will play, pause, resume, and stop a song using the MediaPlayer and Song classes. Besides this, the song’s name, playing state, and position will be displayed on the Windows Phone 7 screen.
How to do it…
The following steps show you how to load, play, pause, and resume a song in the Windows Phone7 XNA application:
- Create a Windows Phone Game named PlayMusic, and change Game1.cs to PlayMusicGame.cs. Add the audio file music.wma and the sprite font file gameFont.spritefont to the content project.
- Declare the indispensable variable. Add the following lines to the field of the PlayMusicGame class:
[code]
// SpriteFont object for showing song information
SpriteFont font;
// Text presents the song playing position
string textPlayingPosition = “”;
// Song’s name
string textSongName;
// Playing state
MediaState PlayingState;
// Song object stores a song
Song song;
[/code] - Load the game font and the audio file. Then get the song’s name and play it. Add the following code to the LoadContent() method:
[code]
// Load the game font
font = Content.Load<SpriteFont>(“gameFont”);
// Load the song
song = Content.Load<Song>(“music”);
// Get the song’s name
textSongName = song.Name;
// Play the song
MediaPlayer.Play(song);
[/code] - Use the MediaPlayer class to play, pause, resume, and stop the song. Meanwhile, update the playing state and position. Insert the following code to the Update() method:
[code]
// Get the tapped position
TouchCollection touches = TouchPanel.GetState();
if (touches.Count > 0 && touches[0].State ==
TouchLocationState.Pressed)
{
Point point = new Point((int)touches[0].Position.X,
(int)touches[0].Position.Y);
// If the tap gesture is valid
if (GraphicsDevice.Viewport.Bounds.Contains(point))
{
// If the the media player is playing, pause the
// playing song
if (MediaPlayer.State == MediaState.Playing)
{
MediaPlayer.Pause();
PlayingState = MediaState.Paused;
}
else if (MediaPlayer.State == MediaState.Paused)
{
// If the song is paused and not stopped, resume
//it
MediaPlayer.Resume();
PlayingState = MediaState.Playing;
}
else if (MediaPlayer.State == MediaState.Stopped)
{
// If the song is stopped, replay it
MediaPlayer.Play(song);
PlayingState = MediaState.Playing;
}
}
}
if (MediaPlayer.State == MediaState.Playing)
{
// If the song’s playing position meets the end, stop
// playing
if (MediaPlayer.PlayPosition == song.Duration)
{
MediaPlayer.Stop();
PlayingState = MediaState.Stopped;
}
// Show the song’s playing position and the total duration
textPlayingPosition = MediaPlayer.PlayPosition.ToString()
+ ” / ” + song.Duration.ToString();
}
[/code] - Draw the song’s name, playing state, and playing position on the Windows Phone 7 screen. Add the following code to the Draw() method:
[code]
spriteBatch.Begin();
// Draw the instruction text
spriteBatch.DrawString(font, “Tap Screen to Play, Pause and ”
+”Resume the Song”, new Vector2(0, 0), Color.White);
// Draw the song’s name
spriteBatch.DrawString(font, “Song’s Name: ” + textSongName,
new Vector2(0,200 ), Color.White);
// Draw the song’s playing state
spriteBatch.DrawString(font, “State: ” +
PlayingState.ToString(),
new Vector2(0, 240), Color.White);
spriteBatch.DrawString(font, textPlayingPosition,
new Vector2(0, 280), Color.White);
spriteBatch.End();
[/code] - Now, build and run the application; it should run as shown in the following screenshot:
How it works…
In step 2, font will be used to show the instructions, song’s name, playing state, and position; the texPlayingPosition stores the song’s current playing position; textSongName saves the song’s name; PlayingState shows the song’s playing state: Playing, Paused, or Stopped; the song is the object of the Song class and will be used to process and load an audio file.
In step 4, the first part of the Update() method is to check whether the player taps on the Windows Phone 7 screen; if he/she does, it has three MediaState options for controlling the song. When the song is Playing, the tap gesture will pause it; if the song is Paused, the gesture will replay it from the paused position using the MediaPlayer.Resume() method; once the song’s current MediaState is Stopped, we will replay the song from the beginning when a valid tap gesture takes place. The second part is about updating the song’s current playing position with its total duration using the two properties of the Song class: PlayPosition and Duration. Besides these, when the current playing position equals the song’s duration, it means the song has ended. In this example, we will replay the song once. You can change it in another way by moving to the next song.
Adding sound effects to your game
“The sound effects are artificially created or enhanced sounds or sound processes used to emphasize artistic or other content of films, television shows, live performance, animation, video game, music, or other media.” – Wiki.
When you are playing games such as Counter Strike or Quake, the sound you hear while firing is the sound effect. Every weapon has its corresponding sound effect. In the early times, the sound effect came from the sound synthesis, a kind of midi rhythm. Today, the game studio can sample the sound from real instances. In making a racing game, the engine sound of every car is different. The game studio can record the sound from a real car, maybe a Lamborghini or a Panamera Turbo, to make the game more realistic. The Windows Phone 7 XNA framework simplifies the work needed to control sound effects. It is up to you to use the sound effects in your game. In this recipe, you will learn how to make your game more interesting by applying sound effects.
Getting ready
In XNA, a SoundEffect contains the audio data and metadata (such as wave data and loop information) loaded from a sound file. You can create multiple SoundEffectInstance objects, and play them from a single SoundEffect. These objects share the resources of that SoundEffect. The only limit to the number of loaded SoundEffect instances is memory. A loaded SoundEffect will continue to hold its memory resources throughout its lifetime. When a SoundEffect instance is destroyed, all SoundEffectInstance objects previously created by that SoundEffect will stop playing and become invalid. Unlike the Song class, SoundEffect has a Play() method; usually, the sound effect is fast and short, plays once and then stops. If you do not want to loop a sound, the Play() method is enough. Otherwise, you should create an instance of the sound effect using the SoundEffect. CreateInstance() method. As the basic metadata, the SoundEffect class also has Name and Duration properties, and it is easy to get the name of any sound effect and its duration. The DistanceScale and DopplerScale properties will help you to simulate a realistic 3D sound effect, especially when you use the SoundEffectInstance.Apply3D() method.
For DistanceScale, if sounds are attenuating too fast, which means that the sounds get quiet too quickly as they move away from the listener, you need to increase the DistanceScale. If sounds are not attenuating fast enough, decrease the DistanceScale. This property will also affect Doppler sound.
The DopplerScale changes the relative velocities of emitters and listeners. If sounds are shifting (pitch) too much for the given relative velocity of the emitter and listener, decrease the DopplerScale. If sounds are not shifting enough for the given relative velocity of the emitter and listener, increase the DopplerScale.
In this example, we will use the SoundEffect class to play two different weapons’ sounds.
How to do it…
The following steps present a complete guide for controlling a sound effect in a Windows Phone 7 XNA game using a .wav file:
- Create a Windows Phone Game named PlaySoundEffect, and change the Game1. cs to PlaySoundEffectGame.cs. Add the audio file Laser.wav, MachineGun. wav and the sprite font file gameFont.spritefont to the project content.
- Add the following code as the required variables to the field of PlaySoundEffectGame class:
[code]
// Sprite font for showing the name of current sound effect
SpriteFont font;
// Current weapon’s name
string CurrentWeapon;
// Sound effect variables
SoundEffect SoundEffectLaser;
SoundEffect soundEffectMachineGun;
// Current Sound effect
SoundEffect soundEffectPlaying;
[/code] - Enable the Hold gesture for Windows Phone 7 TouchPanel. Add the following line to the Initialize() method:
[code]
// Enable the hold gesture
TouchPanel.EnabledGestures = GestureType.Hold;
[/code] - Load the game font, sound effects of weapons, and set the current sound effect of a weapon for playing. Paste the following code into the LoadContent() method:
[code]
// Load the font
font = Content.Load<SpriteFont>(“gameFont”);
// Load the sound effect of laser gun
SoundEffectLaser = Content.Load<SoundEffect>(“Laser”);
// Load the sound effect of machine gun
soundEffectMachineGun = Content.Load<SoundEffect>(“MachineG
un”);
// Set the sound effect of laser to the current sound effect
// for playing
soundEffectPlaying = SoundEffectLaser;
// Set the name of current sound effect
CurrentWeapon = “Laser”;
[/code] - Play the sound effect and use the Hold gesture to switch the sound effects between different weapons. Add the following code to the Update() method:
[code]
// Play the current sound effect when tap on the screen
TouchCollection touches = TouchPanel.GetState();
if (touches.Count > 0 && touches[0].State ==
TouchLocationState.Pressed)
{
Point point = new Point((int)touches[0].Position.X,
(int)touches[0].Position.Y);
if (GraphicsDevice.Viewport.Bounds.Contains(point))
{
if (soundEffectPlaying != null)
{
soundEffectPlaying.Play();
}
}
}
// Using Hold gesture to change the sound effect of weapons
while(TouchPanel.IsGestureAvailable)
{
// Read the gesture
GestureSample gestures = TouchPanel.ReadGesture();
if (gestures.GestureType == GestureType.Hold)
{
// If the Hold gesture is taking place, change the
// sound effect.
if (soundEffectPlaying.Equals(soundEffectLaser))
{
soundEffectPlaying = soundEffectMachineGun;
CurrentWeapon = “Machine Gun”;
}
else if (soundEffectPlaying.Equals
(soundEffectMachineGun))
{
soundEffectPlaying = soundEffectLaser;
CurrentWeapon = “Laser”;
}
}
}
[/code] - Draw the instructions and the name of the current sound effect. Insert the following code to the Draw() method.
[code]
spriteBatch.Begin();
// Draw the instructions
spriteBatch.DrawString(font, “Tap and hold on for changing
your” + “weapon.nTap for firing”, new Vector2(0,0), Color.
White);
// Draw the current weapon’s name
spriteBatch.DrawString(font, “Current Weapon: ” +
CurrentWeapon, new Vector2(0, 70), Color.White);
spriteBatch.End();
[/code] - Build and run the application. It should run as shown in the screenshot to the left. When you tap the screen and hold it for a few seconds, the sound effect will be something similar to the screenshot on the right:
How it works…
In step 2, the font will be used to draw the name of the current sound effect and the controlling instructions; the CurrentWeapon indicates the name of the current weapon; soundEffectLaser and soundEffectMachineGun, the SoundEffect instances, individually represent the laser and machine gun sounds; soundEffectPlaying is the currently playing sound effect.
In step 3, we use the Hold gesture to switch the playing sound effect. It is required to enable the gesture type in TouchPanel.
In step 5, the first part is to check whether the user taps on the Windows Phone 7 screen. If so, then play the current sound effect if it is not null. The second part is to switch the sound effect for playing using the Hold gesture. If the on-going gesture is Hold, we will alternate the sound effects between the laser and the machine gun for playing.
Adding stereo sounds to your game
Sometimes, the music and simple sound effects are not enough for you, if you are pursuing the realistic feeling. You cannot determine the place where the sound comes from in your game world. If you have experience of playing Counter-Strike, it is easy to know how many enemies are near you when you stop moving, by listening to the sound. This technique is called Stereo Sound. It uses two or more independent audio channels through a symmetrical configuration of loudspeakers to create the impression of the sound heard from different directions, similar to natural hearing. For the stereo sound, Windows Phone 7 XNA simulates a sound emitter and listener, so that when the position of the emitter is changing, the listener will get a processed sound effect according to the distance between them. In this recipe, you will learn how to use the XNA framework to implement a stereo sound.
Getting ready
In this example, we will use the SoundEffectInstance class with its methods to simulate a 3D sound effect. SoundEffectInstance provides the single playing, paused, and stopped methods to control an instance of sound effect. You can create a SoundEffectInstance by calling the SoundEffect.CreateInstance() method. Initially, the SoundEffectInstance is created as stopped, but you can play it by calling the SoundEffectInstance.Play() method. The volume, panning, and pitch of SoundEffectInstance can be modified by setting the Volume, Pitch, and Pan properties. On Windows Phone 7, a game can have a maximum of 16 total playing SoundEffectInstance instances at one time, combined across all loaded SoundEffect objects. Attempts to play a SoundEffectInstance beyond this limit will fail.
The SoundEffectInstance.Apply3D() method simulates the 3D sound effect. It receives two parameters, the object of AudioEmitter and AudioListener classes. This method will calculate the 3D audio values between an AudioEmitter and an AudioListener object, and will apply the resulting values to the SoundEffectInstance instance. If you want to apply the 3D effect to a SoundEffectInstance, you must call the method before you call the SoundEffectInstance.Play()method. Calling this method automatically sets the Windows Phone 7 speaker mix for any sound played by this SoundEffectInstance to a value calculated by the difference in Position property values between the listener and the emitter. In preparation for the mix, the sound is converted to mono. Any stereo information in this sound is discarded.
How to do it…
The following steps give you a complete guide to implementing a stereo sound effect:
- Create a Windows Phone Game named PlayStereoSound and change the Game1. cs to PlayStereoSoundGame.cs. Add the audio file drums.wma and the model file BallLowPoly.fbx to the project content.
- Declare the essential variables to the field of the PlayStereoSoundGame class. Add the following code to the class:
[code]
// Sound effect object loads the sound effect file
SoundEffect soundEffect;
// Instance of a SoundEffect sound.
SoundEffectInstance soundEffectInstance;
// AudioEmitter and AudioListener simulate 3D audio effects
AudioEmitter emitter;
AudioListener listener;
// The world position represents the 3D position for
AudioEmitter
Vector3 objectPos;
// A ball for visually presenting the varying AudioEmitter
// world position.
Model modelBall;
Matrix worldBall = Matrix.Identity;
// Camera
Vector3 cameraPosition;
Matrix view;
Matrix projection;
[/code] - Initialize the camera, the audio emitter, and the audio listener. Add the following code to the Initialize() method:
[code]
// Initialize the camera
cameraPosition = new Vector3(0, 30, 50);
view = Matrix.CreateLookAt(cameraPosition, Vector3.Zero,
Vector3.Up);
projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio,
1.0f, 1000.0f);
// Initialize the AudioEmitter and AudioListener
emitter = new AudioEmitter();
listener = new AudioListener();
[/code] - Load the ball model and the drum sound effect. Then, create the instance of the drum sound effect and apply the audio emitter to the audio listener of the instance. Finally, play the sound effect. Add the following code to the LoadContent() method:
[code]
// Load the ball model
modelBall = Content.Load<Model>(“BallLowPoly”);
// Load the sound effect
soundEffect = Content.Load<SoundEffect>(“drums”);
// Create an instance of the sound effect
soundEffectInstance = soundEffect.CreateInstance();
// Apply 3D position to the sound effect instance
soundEffectInstance.Apply3D(listener, emitter);
soundEffectInstance.IsLooped = true;
// Play the sound
soundEffectInstance.Play();
[/code] - Rotate the audio emitter around the Y axis. Add the following lines to the Update() method:
[code]
// Rotate around axis Y
objectPos = new Vector3(
(float)Math.Cos(gameTime.TotalGameTime.TotalSeconds) / 2,
0,
(float)Math.Sin(gameTime.TotalGameTime.TotalSeconds) /
2);
// Update the position of the audio emitter
emitter.Position = objectPos;
// Apply the new position of the audio emitter to the audio
listener
soundEffectInstance.Apply3D(listener, emitter);
[/code] - Define the DrawModel() method. Add the following code to the PlayStereoSoundGame class:
[code]
// Draw the 3D model
public void DrawModel(Model model, Matrix world, Matrix view,
Matrix projection)
{
Matrix[] transforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.World = transforms[mesh.ParentBone.Index] *
world;
effect.View = view;
effect.Projection = projection;
}
mesh.Draw();
}
}
[/code] - Draw the ball model and rotate it around the Y axis to coincide with the position of the audio emitter. Add the following code to the Draw() method:
[code]
// Draw the rotating ball
DrawModel(modelBall, worldBall *
Matrix.CreateTranslation(objectPos * 30), view,
projection);
[/code] - Build and run the application, and it should run similar to the following screenshots:
How it works…
In step 2, the soundEffect will be used to load the sound effect file; the soundEffectInstance plays and applies the 3D sound effect; emitter and listener will combine with each other to simulate the 3D audio effects; objectPos represents the position changes around the Y axis, the latest value will be used to update the position value of the AudioEmitter object; modelBall loads the ball model; worldBall stores the world position of a ball model in 3D; the next three variables cameraPosition, view, and project depict the camera.
In step 4, after loading the ball model sound effect, we create a SoundEffectInstance object using the soundEffect.CreateInstance() method. Note that it is required to call the SoundEffectInstance.Apply3D() method with AudioListener and AudioEmitter objects before the Play() method. If you do not do so, the next time you call the Apply3D() method, it will throw an exception.
In step 5, we compute the objectPos for rotating around the Y axis. The X value comes from the Math.Cos() method; the Z value comes from the Math.Sin(). These two factors are equal to the value of a round in the XZ plain. After that, we use the newly created objectPos to update the position of the emitter and then call the SoundEffectInstance. Apply3D() method to re-calculate the playing 3D sound effect in the surroundings. In step 7, for the world parameter of the DrawModel() method, we use the latest objectPos to update the translation of the ball model in the 3D world. This will make the ball rotate around the Y axis along with the position of the sound effect emitter.