A helping hand for bedroom coders throughout the land.
Sound in XNA 3.1, Part II.

In Part I, we went through the basics of how to add a simple sound effect to a game using XACT. In this part, we’re going to look at some of the benefits that XACT gives us over the pure code solution.

Instance Limiting

If you play a sound effect that is likely to repeated quickly enough to overlap with itself, you will very quickly end up with a horrible sounding mess. In fact, create enough overlapping instances and the odds are that the XBox 360 will struggle to play them all. To solve this problem, XACT allows us to set a limit on the number of concurrent instances of a sound effect.

Click on the Cue in the SoundBank window, and scroll to the bottom of the Properties window, where you will see the following section:

 

Instance_Limiting

 

Check the Limit Instances box, and set the Maximum number of instances you want playing at a time. The key part is that you then specify what you want the audio engine to do when that limit is reached. You can tell it to ignore the new requests, queue the requests up so that they all get played eventually, or to replace one of the existing instances. If you choose this last option, you additionally get to specify what criteria to use when deciding which old instance to replace, and how to cross fade between the two instances. I usually just stick with Oldest, and, I have to be honest, I’ve not really noticed any difference between the cross fade types, but that could be related to the types of sound effects I’ve used this on.

You should find that your sound effect will now be sounding a lot better. The individual instances no longer overlap, and it doesn’t degenerate into the horrible sounding mess we started with. However, it still doesn’t sound great, as it very obviously sounds like the same sound being played over and over again.

Adding variation to a sound effect

To create a more dynamic sound effect, you really need to introduce some variation into the Sound that is played. One way that you can do that using XACT is to associate more than one wave file to a sound effect, and get XACT to play a different one each time you play the sound effect.

Open the Wave Bank and the Sound Bank windows, and select the Sound you want to work with. Drag the wave file to the Play Wave node in the track pane of the Sound Bank window. This node represents an XACT Event. Be careful not to drop it on the existing wave file under that node, as that will replace the existing file.

 

Random_Play

 

When I first tried this, I thought that playing the sound effect now would result in both wave files playing at the same time. However, that’s not actually how Tracks and Events work. To have multiple wave files playing at the same time for the same sound effect, you need to have multiple Tracks in a Sound, with a wave file attached to each one. With multiple wave files attached to a Play Wave Event, only one of them will ever play at a time, and you can specify the algorithm the XACT engine uses to select which one it plays. To do that, click on the Play Wave node and scroll to the bottom of the Properties window, to the section marked Variation Playlist.

 

Variation_Playlist

 

There is a drop-down menu that lets you specify the playlist type, and it defaults to Random (no immediate repeats), which randomly picks a wave to play, but never plays the same one twice in a row. Every wave file in that Event is given a percentage chance of being played, and this is initially automatically calculated to give all wave files the same chance of being played. The Wave Entry section lets you weight the wave files individually, so that you can change that percentage. The other playlist types available are:

  • Random, so waves can be played twice in a row
  • Ordered, where the waves are played in the order you specify (the Wave Entry section lets you drag the wave files into an order)
  • Ordered from Random, as above, but the starting wave is selected randomly
  • Shuffle, where the waves are played at random, but all waves are played before one is repeated.

So if we now play our sound effect, the two wave files will play alternately, giving us our variation.

Pitch Variation

Another way that you can add some variation to a Sound is by getting XACT to randomly alter the pitch of the wave file every time it plays it.

Click on the Sound in the SoundBank window, and scroll halfway down the Properties window, until you see the following section:

 

Random_Pitch_Variation

 

Here you can randomly vary the Sound in four different ways. We’re just going to choose one here, the Pitch Variation. By enabling this function, you get to specify the upper and lower limits of the pitch variation, up to +/- twelve semitones (one octave). Now, each time you play the Cue, the audio engine will vary the pitch of the original audio file. You’ll need to experiment with values to get the best results, as you don’t want to add too much variation, otherwise you’ll spoil the effect.

One final thing to note is that this variation is applied to the Sound and not the individual wave files, so if you do have multiple wave files attached to the Sound, this pitch variation will be applied to them all, giving your sound effect even more variation.

3D Audio

As you saw in the previous section, you can get XACT to randomly change the panning of a sound effect, i.e. where in the game space the sound effect appears to be coming from, however we would be better off utilising panning to produce true 3D audio. With this, the sound effects will appear to move around the game space to match the position of the object that is making them. All you need to implement it are two classes, the AudioListener and the AudioEmitter. You give both these objects a Vector3 position in your game space, with the AudioEmitter going where you want a sound to come from, and the AudioListener going where you want the game player to hear the noise from.

    _audioListener.Position = new Vector3(_player.Position.X, 0, _player.Position.Y);

    _explosionEmitter.Position = explosionPosition;

    _soundEffectsSoundBank.PlayCue("Explosion", _audioListener, _explosionEmitter);

And that’s all you need to do! This isn’t, however, purely a feature of XACT, as you can do the same thing in a pure code solution, by passing an AudioListener and an AudioEmitter into the Apply3D method on your SoundEffectInstance instance before playing it.

Audio Attenuation

Now that we've got our sounds moving around in the game space, it would sound even more impressive if we could add in some volume attenuation, i.e. make the volume of a sound effect vary with distance. Again, this is something that is relatively easy to set up in XACT, without having to write a line of code to do it. To do this, we need to utilise another part of XACT: Variables. If you look at the Variables node in the project tree, you can see that there are two sections:

  1. Global. You can think of these as single instance static variables in your game
  2. Cue instance. Every instance of a cue gets it's own copy.

There are a number of default variables provided in every XACT project. These are maintained and updated by the XACT audio engine, so you don’t need to do anything with them in your code. The one that we are interested in is the Distance variable. I assume that this is calculated as the distance between the position of the AudioListener and AudioEmitter that you pass in to the PlayCue method.

By default, it’s maximum value is set to 1,000,000. I haven’t seen anything explaining what the scale of the variable is in relation to game space, but if you start by assuming a one to one relation between your game level in pixels and the maximum distance value, you won’t be too far off.

 

Edit_Variables

 

Now we have set up the Distance variable, we can use it. To do that, we need to create an RPC, or Runtime Parameter Control, which allow you to control a sound programmatically. Right-click on the RPC node in the project tree, and select “New RPC Preset”. A new window will open up. At the top of the window, there is a line of drop-down and text boxes:

  • In Variable, you choose the variable that you want to associate with the RPC; we want Distance.
  • Value contains the initial value of the variable.
  • Object specifies what the RPC acts on. In our case, it is the Sound, rather than any individual tracks in that sound
  • Parameter is the output parameter that we want to change; we want Volume

Having set those up, we now need to specify how the volume changes in relation to the Distance variable, and we do that by setting up a curve in the graph part of the window. By filling in the settings, two points are added to the graph, and a linear line drawn between them. We want a logarithmic curve for the drop off in volume with distance, so either put one point in the top left-hand corner of the graph and the other in the bottom right-hand corner, or else in the datagrid marked “Points”, change the Distance values to 0.00 and 50,000, and the Volume values to 0.00 and –96.00. That’s moved the points, but it is still a straight line, so double-click the Curvature cell for the first point in that datagrid. A small menu will pop up, and you want to select Fast. You will now have your curve!

 

Create_RPC

 

Close down the RPC window and drag one of the sound effect or the RPC tree node to the other, and the two are now linked. If you look at the property window for the RPC, you’ll see the Sounds that it is attached to:

 

Attached_RPC

 

Similarly, if you look at the bottom of the property pane of the Sound that you have attached, you’ll see the RPC is listed there too.

If you now play the sound, you’ll find that the further away the explosion is, the quieter the sound effect is.

Again, whilst you can do something similar using pure code, you don’t get the same flexibility as with XACT. In pure code, you can set the value of the static DistanceScale property on SoundEffect, however this means that all your sound effects will have the same attenuation curve. With XACT, you can apply different curves and drop-off rates to different sound effects.

Summary

If you know nothing about adding sound to an XNA game, it is fair to say that you will get up and running quicker using the new classes rather then using XACT. However, despite that, I have to say that I'd still recommend that you bite the bullet and get stuck in to XACT. Sure, there's that learning curve, but you get a lot of functionality in XACT for a lot less effort than you'd have to put in to reproduce it yourself in code. And writing less code has got to be a good thing!


Posted Fri, Mar 26 2010 3:15 PM by manzanotti

Comments

manzanotti wrote re: Sound in XNA 3.1, Part II.
on Fri, Mar 26 2010 3:09 PM

Sorry for the delay in posting this; life kept getting in the way!

GameDevKicks.com wrote Sound in XNA 3.1, Part II
on Fri, Mar 26 2010 3:13 PM

You've been kicked (a good thing) - Trackback from GameDevKicks.com

Zoon wrote re: Sound in XNA 3.1, Part II.
on Fri, Mar 26 2010 11:00 PM

I have been fighting with XACT for a while now... with no real progress, but this post makes me want to give it another go!

Thanks for the post!

Sound in XNA, Part II « Sgt. Conker wrote Sound in XNA, Part II « Sgt. Conker
on Sat, Mar 27 2010 12:15 AM

Pingback from  Sound in XNA, Part II « Sgt. Conker

ben wrote re: Sound in XNA 3.1, Part II.
on Thu, Apr 15 2010 3:38 AM

Thank-you very much for this, I think XACT is pretty dam good but more advanced tutorials for it are sorely lacking.

This is the best one I have found so far, I look forward to more :)