Export (0) Print
Expand All

Tutorial 3: Making Sounds with XNA Game Studio and XACT

This article details how to use the Microsoft Cross-Platform Audio Creation Tool (XACT) to make sounds for an XNA Game Studio game, and how to use the XNA Framework Audio API to play them.
Bb203895.note(en-US,XNAGameStudio.20).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 starting 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, at heart, wave-based. Wave files (.wav) are assembled into an XACT project and built into wave banks that are loaded into your game.

The first thing to do is get some wave files. This is where the Spacewar Windows Starter Kit (2.0) project you created in Tutorial 1: Displaying a 3D Model on the Screen will come in handy. Go get the wave files from that project.

  • 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 the 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 Spacewar Windows Starter Kit (2.0) project folder and from there to the Content\Audio\Waves folder. Inside that folder, you will see several subcategories of folders, containing many sounds for you to use.
  • Copy the following files from the Spacewar Waves folder to the Content\Audio\Waves folder in Windows Explorer: Ships\engine_2.wav, and Weapons\hyperspace_activate.wav.

Step 2: Create an XACT Project with Wave Files

Notice that you do not add wave files like other content files, through the Solution Explorer. The Content Pipeline processes XACT projects, which are compilations of wave files, and not just the raw wave files themselves. You must now create that compilation using the Microsoft Cross-Platform Audio Creation Tool (XACT).

Launch XACT.

  • From the Start menu, browse to All Programs, then Microsoft XNA Game Studio 2.0, then Tools.
  • Click Microsoft Cross-Platform Audio Creation Tool (XACT).
Bb203895.note(en-US,XNAGameStudio.20).gifNote
On Windows Vista, you may need to run XACT with adminstrator privileges. If you encounter an error when trying to run XACT, from the Start menu, right-click on Microsoft Cross-Platform Audio Creation Tool (XACT), and select Run as administrator.

XACT will launch. Once you see the XACT window, create and save a new project as follows:

  • From the XACT window, click the File menu, and then click New Project.
  • From the dialog box that appears, browse to your project folder, then to the Content\Audio folder.
  • Name your project MyGameAudio.
  • Click OK.

The project is saved in your game project folder under Contents\Audio. You will load this XACT project through the Content Pipeline later. First, put your wave files in the project so that when the XACT project loads, there will be sounds to play.

  • The screen contains an empty project. In the empty project, you must first create a wave bank. To do this, right-click Wave Banks, and then click New Wave Bank. A new wave bank appears in the tree view under Wave Banks with the default name "Wave Bank." Press ENTER to accept the default name.
  • Create a new sound bank. To do this, right-click Sound Banks, and then click New Sound Bank. A new sound bank appears in the tree view under Sound Banks with the default name "Sound Bank." Press ENTER to accept the default name.
  • At this point, two new windows have appeared: one for the wave bank and one for the sound bank. To arrange these windows for easier viewing, click Window, and then click Tile Horizontally. The windows should now look similar to the following:

    Bb203895.goingbeyond_xact1(en-US,XNAGameStudio.20).jpg
  • Add both of your wave files to the wave bank window. Make sure the wave bank window is active by clicking on it, then click Wave Banks, and click Insert Wave File(s). Browse to your game project folder, and into the Content\Audio\Waves folder. Select both wave files. If you successfully added them, they appear in the wave bank window, which will look similar to the following:

    Bb203895.goingbeyond_xact2(en-US,XNAGameStudio.20).jpg
  • For each wave listed in the wave bank window, drag the wave from the wave bank window to the sound bank window, and drop it on the Cue Name panel. XACT automatically creates a new cue that is linked to a new sound that plays this wave file. It should look similar to the following:

    Bb203895.goingbeyond_xact3(en-US,XNAGameStudio.20).jpg
  • If the lower-left corner of the sound bank window does not have any entries, you must add entries to it by dragging each sound from the upper-left panel of the sound bank to the lower-left panel of the sound bank. This action creates cues that correspond to the sounds.
  • The last thing to do before saving is make sure your engine_2 sound loops when you play it. You do not want it to just play once and stop. Click engine_2 in the upper-left panel of the sound bank. When you do this, a tree structure will appear in the upper-right panel of the sound bank.
  • In the upper-right panel of the sound bank, click the Play Wave item. Notice that the Property pane in the far lower-left panel of your XACT window will change and list a set of properties.
  • In this Property pane, find the Looping section. Inside that section, select the Infinite checkbox. It should look like this:

    Bb203895.goingbeyond_xact4(en-US,XNAGameStudio.20).jpg
  • Save the XACT project by clicking the File menu and then clicking Save Project.

Step 3: Load an XACT Project Through the Content Pipeline

You have just created and saved an XACT project that contains two wave files. The next step is to load this XACT project into the Content Pipeline.

  • Return to your supported version of Microsoft Visual Studio tools, and make sure your game project is loaded. If it is not, open it by clicking the File menu, and then click Open Project and browse to your project.
  • Right-click the Content\Audio folder in the Solution Explorer, click Add and then Existing Item. Using the dialog box that appears, browse to your game project's (not Spacewar's) Content\Audio folder, and select MyGameAudio.xap. If you cannot see any files, make sure you change the Files of type selection box to read Audio Files. Click OK.

The .xap file will be built automatically by the Content Pipeline as part of building your game. Now, all that is left is to load the output files from your XACT project into your game.

  • View the code by double-clicking Game1.cs in Solution Explorer.
  • Find the Initialize method. Modify it to look like this:

    AudioEngine audioEngine;
    WaveBank waveBank;
    SoundBank soundBank;
    
    protected override void Initialize()
    {
        audioEngine = new AudioEngine("Content\\Audio\\MyGameAudio.xgs");
        waveBank = new WaveBank(audioEngine, "Content\\Audio\\Wave Bank.xwb");
        soundBank = new SoundBank(audioEngine, "Content\\Audio\\Sound Bank.xsb");
        base.Initialize();
    }
    

Notice that you need to load three files for your audio.

  • An AudioEngine by passing in the name of the XACT project file with an .xgs extension.
  • A WaveBank by passing in your AudioEngine and the name of the wave bank you created in the XACT project with an .xwb extension.
  • A SoundBank by passing in your AudioEngine and the name of the sound bank you created in the XACT project with an .xsb extension.

Once you have done this, you are ready to add the code to play your sounds when game events happen.

Step 4: Play Sounds Using the Audio API

Sounds you wish to play in your game are accessed through a Cue object, which you can get by calling GetCue, or play directly by calling PlayCue.

In this tutorial, you do both. For your looping engine sound, call GetCue to get and hold the engine cue, and pause and resume the cue 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 PlayCue.

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

    // Cue so we can hang on to the sound of the engine.
    Cue engineSound = null;
    
    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);
    
            // Set some audio based on whether we're pressing a trigger.
            if (currentState.Triggers.Right > 0)
            {
                if (engineSound == null)
                {
                    engineSound = soundBank.GetCue("engine_2");
                    engineSound.Play();
                }
    
                else if (engineSound.IsPaused)
                {
                    engineSound.Resume();
                }
            }
            else
            {
                if (engineSound != null && engineSound.IsPlaying)
                {
                    engineSound.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;
    
                // Make a sound when we warp.
                soundBank.PlayCue("hyperspace_activate");
            }
        }
    }
    

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

// Cue so we can hang on to the sound of the engine.
Cue engineSound = null;

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

// Set some audio based on whether we're pressing a trigger.
if (currentState.Triggers.Right > 0)
{
    if (engineSound == null)
    {
        engineSound = soundBank.GetCue("engine_2");
        engineSound.Play();
    }

    else if (engineSound.IsPaused)
    {
        engineSound.Resume();
    }
}
else
{
    if (engineSound != null && engineSound.IsPlaying)
    {
        engineSound.Pause();
    }
}

This code manages the engine sound. Since you enter this code once every 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 Cue if there is a change, such as the trigger being released after being held. This code uses GetCue the very first time through the loop to ready the Cue to play, and play it if the trigger is down.

From that point forward, each release of the trigger will call Pause and halt playback of the Cue. 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;

    // Make a sound when we warp.
    soundBank.PlayCue("hyperspace_activate");
}

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

Congratulations!

At this point, you have a spaceship floating in 3D space, that moves around when you use your Xbox 360 Controller, and makes sounds and 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 runtime parameter control (RPC) functionality of XACT to change the volume and pitch of your engines as you change pressure on your right trigger. (For more information about how to do this, see XACT Audio Authoring.)
  • Add some background music and try setting volumes using categories. (See How To: Change Sound Volume Levels.)
  • Get more ideas and resources at XNA Creators Club Online.

Community Additions

ADD
Show:
© 2014 Microsoft