Export (0) Print
Expand All

Tutorial 3: Making Sounds with XNA Game Studio

Details how to use the XNA Framework Audio API to play them.
Bb203895.note(en-us,XNAGameStudio.31).gifNote
This tutorial builds on code you have written during the previous tutorial: Tutorial 2: Making Your Model Move Using Input. Follow the steps in the previous tutorial before you start this tutorial.

The Complete Sample

The code in this tutorial illustrates the technique described in the text. A complete code sample for this tutorial is available for you to download, including full source code and any additional supporting files required by the sample.

Step 1: Get Some Wave Files

Audio in XNA Game Studio is wave based. There are two methods for using wave files in your game: using XACT to create and play your sound effects or using the SoundEffect class. We'll use the second method because it lets you quickly get some cool sound effects into your game.

Bb203895.note(en-us,XNAGameStudio.31).gifNote
For more information on using XACT, see Audio Overview.

The first thing to do is get some wave files. The files used in this tutorial are included in the (GoingBeyond3_Tutorial_Sample.zip). Download this file now and extract it to its own directory.

  • Make sure your project from Tutorial 2: Making Your Model Move Using Input is open. If it is not, open it by clicking the File menu, and then click Open Project and browse to your project.
  • In Solution Explorer, right-click the Content node, click Add, and then click New Folder. Name this folder Audio.
  • Right-click the Audio folder you just created, click Add, and then click New Folder. Name this folder Waves.
  • Open Windows Explorer. Browse to the folder containing the content from your recently extracted sample file and from there to the Content\Audio\Waves folder. Inside that folder, you will see two audio files: engine_2.wav and hyperspace_activate.wav.
  • Copy those files to the Content\Audio\Waves folder in Windows Explorer.
  • Right-click the Waves folder, click Add, and then click Existing Item. Using the dialog box that appears, browse back to the location of the extracted tutorial sample files and find the Contents\Audio\Waves folder. Select both wave files, and click OK.

Step 2: Loading the Wave Files by Using the Content Pipeline

You probably remember that you have already used the content pipeline to load content into your game. In this step, you will use that same technique to load sound effects into your game. With a few simple lines of code, you can play those sound effects at the appropriate time.

Open your Game1.cs file, and take a look at the LoadContent method. This is where the ship model is loaded by the content pipeline. You will add new code that declares two SoundEffect members and one SoundEffectInstance, and loads the recently added sound effect files into those new member variables.

  • After the code that declares the myModel class member, add the following code:
    //Set the sound effects to use
    SoundEffect soundEngine;
    SoundEffectInstance soundEngineInstance;
    SoundEffect soundHyperspaceActivation;
    
  • Modify the LoadContent code (including adding the lines shown above the method) to look like this:
    // The aspect ratio determines how to scale 3d to 2d projection.
    float aspectRatio;
    
    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);
    
        myModel = Content.Load<Model>("Models\\p1_wedge");
        soundEngine = Content.Load<SoundEffect>("Audio\\Waves\\engine_2");
        soundEngineInstance = soundEngine.CreateInstance();
        soundHyperspaceActivation = 
            Content.Load<SoundEffect>("Audio\\Waves\\hyperspace_activate");
    
        aspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio;
    }
    

In these steps, you declared two member variables, and told the content pipeline to load two wave files into your game when LoadContent is called at the beginning of your game. You also assigned a SoundEffectInstance to one of those files, so you can monitor the playback later.

The code now loads the wave files. Your next step is to play them at the appropriate times.

Step 3: Play Sounds Using the Audio API

You can access the sounds you wish to play in your game through a SoundEffect object, or play directly by calling Play.

For your looping engine sound, call SoundEffectInstance.Play to begin looping the engine sound effect. Use the SoundEffectInstance to pause and resume the sound effect as your engines turn on and off when the user holds the trigger. When the player presses the A button to warp, play the hyperspace sound by calling SoundEffect.Play. Calling SoundEffect.Play plays the sound, but it doesn't allow us to pause it (which is okay for the hyperspace sound, but not for the engine sound).

  • Find the UpdateInput method. Modify it to look like this:

    protected void UpdateInput()
    {
        // Get the game pad state.
        GamePadState currentState = GamePad.GetState(PlayerIndex.One);
        if (currentState.IsConnected)
        {
            // Rotate the model using the left thumbstick, and scale it down
            modelRotation -= currentState.ThumbSticks.Left.X * 0.10f;
    
            // Create some velocity if the right trigger is down.
            Vector3 modelVelocityAdd = Vector3.Zero;
    
            // Find out what direction we should be thrusting, 
            // using rotation.
            modelVelocityAdd.X = -(float)Math.Sin(modelRotation);
            modelVelocityAdd.Z = -(float)Math.Cos(modelRotation);
    
            // Now scale our direction by how hard the trigger is down.
            modelVelocityAdd *= currentState.Triggers.Right;
    
            // Finally, add this vector to our velocity.
            modelVelocity += modelVelocityAdd;
    
            GamePad.SetVibration(PlayerIndex.One, 
                currentState.Triggers.Right,
                currentState.Triggers.Right);
    
            //Play engine sound only when the engine is on.
            if (currentState.Triggers.Right > 0)
            {
    
                if (soundEngineInstance.State == SoundState.Stopped)
                {
                    soundEngineInstance.Volume = 0.75f;
                    soundEngineInstance.IsLooped = true;
                    soundEngineInstance.Play();
                }
                else
                    soundEngineInstance.Resume();
            }
            else if (currentState.Triggers.Right == 0)
            {
                if (soundEngineInstance.State == SoundState.Playing)
                    soundEngineInstance.Pause();
            }
    
            // In case you get lost, press A to warp back to the center.
            if (currentState.Buttons.A == ButtonState.Pressed)
            {
                modelPosition = Vector3.Zero;
                modelVelocity = Vector3.Zero;
                modelRotation = 0.0f;
                soundHyperspaceActivation.Play();
            }
        }
    }
    

Many things are happening here. Here is a breakdown of what you are doing.

SoundEffectInstance soundEngineInstance;

The SoundEffectInstance represents an instance of a sound. In this case, soundEngineInstance will represent the sound of your engines when you hold the right trigger.

//Play engine sound only when the engine is on.
if (currentState.Triggers.Right > 0)
{

    if (soundEngineInstance.State == SoundState.Stopped)
    {
        soundEngineInstance.Volume = 0.75f;
        soundEngineInstance.IsLooped = true;
        soundEngineInstance.Play();
    }
    else
        soundEngineInstance.Resume();
}
else if (currentState.Triggers.Right == 0)
{
    if (soundEngineInstance.State == SoundState.Playing)
        soundEngineInstance.Pause();
}

This code manages the engine sound. Since you enter this code once each frame, you have to make sure you do not continually try to play the same sound. You only want to modify the state of the SoundEffectInstance if there is a change, such as the trigger being released after being held. If the sound is stopped (which it will be at the start of the game), we call Play to start it up.

From that point forward, each release of the trigger will call Pause and halt playback of the SoundEffectInstance. Subsequently, holding the trigger again will call Resume, and playback will continue.

// In case you get lost, press A to warp back to the center.
if (currentState.Buttons.A == ButtonState.Pressed)
{
    modelPosition = Vector3.Zero;
    modelVelocity = Vector3.Zero;
    modelRotation = 0.0f;
    soundHyperspaceActivation.Play();
}

Finally, pressing the A button warps, gets, and plays a sound all at once using Play. Since you do not need to stop or pause this sound, but can just let it play, there is no reason to hold on to the sound in a SoundEffectInstance object.

Congratulations!

At this point, you have a spaceship floating in 3D space. This spaceship moves around when you use your Xbox 360 Controller, it makes sounds, and it gives you feedback in your controller. You have created the very beginnings of a 3D game using XNA Game Studio, and you are only just getting started. There is so much more to explore!

Next...

Ideas to Expand

If you are ready to go further with this sample, why not try a few of these ideas?

  • Use some of the advanced features of the Play to change the volume and pitch of your engines as you change pressure on your right trigger.
  • Add some background music and try setting different volumes for sound effects and background music.
  • Get more ideas and resources at XNA Creators Club Online.

Community Additions

ADD
Show:
© 2014 Microsoft