Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Content Obsolete

This content is no longer current.

Beginning Game Development Part X –Direct Sound Part III

  Welcome to the tenth article on beginning game development. In the last article we manipulated sounds by adding sound effects. Together with the use of 3D Buffers to make the sounds positionaly accurate we now have a fully featured sound system in the game. But, while we're are able to play and manipulate short sounds and sound effects we cannot play longer WMA or MP3 files with the DirectSound namespace. To accomplish that we need to turn to the AudioVideoPlayback namespace.
3Leaf Development

Difficulty: Intermediate
Time Required: 1-3 hours
Cost: Free
Software: Visual Studio Express 2005, Microsoft DirectX SDK
Hardware: Any PC capable of running DirectX 9.0
Download: Download

Previous Articles:

  1. Beginning Game Development Part 1 - Introduction
  2. Beginning Game Development Part II - Introduction to DirectX
  3. Beginning Game Development: Part III - DirectX II
  4. Beginning Game Development: Part IV - DirectInput
  5. Beginning Game Development: Part V - Adding Units
  6. Beginning Game Development: Part VI - Lights, Materials and Terrain
  7. Beginning Game Development: Part VII –Terrain and Collision Detection
  8. Beginning Game Development: Part VIII - DirectSound
  9. Beginning Game Development: Part VIII - DirectSound II
  10. Beginning Game Development: Part VIII - DirectSound III

Why more sound

You may be wondering why we even need to add longer sound files to the game. As I mentioned in article 8, most games include fully featured soundtracks in addition to the regular game sounds. These sound tracks are often produced specifically for the game much like a movie soundtrack is produced for a movie. A well made sound track can add additional drama to the game and provide a memorable experience. Sometimes these game tracks are also available as separate music CDs or downloads to bring more players to the game. There is really no way that anyone is going to be able to produce a nice sounding soundtrack as a WAV file. Most music uses the MP3 codec or can be converted to this codec.

Audio Playback

Using the DirectSound namespace we were only able to play WAV files. Longer music is normally encoded in more efficient formats such as MP3 or WMA. To play these files we have to access the API in the AudioVideoPlayback namespace. This namespace is the smallest of the namespaces and contains a single class called Audio for playing audio files. (It also contains a Video class which I will not cover)

Using the Audio class to play an audio file is very easy.

1. Create a new Audio class and pass the path to the Audio file to the constructor.

Audio audio = new Audio("AudioFileName");
Or instantiate a new Audio class and then call the Open method.
 
Audio audio = new Audio();
audio.Open("AudioFileName");
2. Call the Play method.
 
audio.Play();
3. While playing an audio file you can pause it and then resume it by calling pause and then play.
 
audio.Pause();
4. When you are done playing the file just call the Stop method.
 
audio.Stop();
5. Finally remember to dispose the Audio Object.
 
audio.Dispose();
There is no looping built into the Audio class, but the class exposes an Ending Event that we can hook into the loop the audio.
 
private void _audio_Ending(object sender, EventArgs e)
{
    audio.Play();
}
Another neat feature of the Audio class is the ability to open an audio file from an URI rather than a file name. This allows you play audio files from the Internet or URL addressable location.
 
audio.Open("//www.audiofile.com/music.mp3");

While playing the audio file you can control the volume and balance of the file.

Volume: Volume is expressed as an integer. Somewhat counterintuitive is that a setting of 0 is full volume. Smaller values decrease the volume up to a setting of -10,000 which is silent.

audio.Volume = 0; // Maximum
audio.Volume = -10000; // Silent
Balance: Balance is expressed as an integer in the range of 10,000 to -10,000. A value of zero means that the sound is perfectly balanced, while -10,000 plays only the left channel and, you guessed it, 10,000 plays on the right.
 
audio.Balance = -10000; // Only Left channel
audio.Balance = 10000; // only Right channel
audio.Balance = 0; // balanced
For each of the methods representing the major states of the class the Audio class raises events to indicate these states and exposes properties to check what state the class is in. You already saw the Ending event. This matrix shows the major states, methods to get the class into the state, event raised when entering the state and properties that can be used to detect the state.

State Method Event Property (Boolean)
Started Play Starting Playing*
Paused Pause Pausing Paused
Stopped Stop and StopWhenReady Stopping Stopped**
Ended N/A Ending

* While playing the audio class indicates the current position of the stream with the CurrentPosition property.

** The audio class also provides a property, StopPosition, which indicates the position in the audio stream where the file is stopped

You can also use the State property of the Audio class to determine its state. This property returns a StateFlags enumeration with the possible values of: Running, Paused and Stopped.
Also of interest is the SeekingCaps property of the Audio class. This read only property indicates the seeking capabilities of the playback stream. It is represented in the form of the SeekingCaps structure. This structure indicates whether the stream supports the following seek actions:

Action SeekingCaps property (Boolean)
Current position CanGetCurrentPostion
Duration CanGetDuration
Stop position CanGetStopPostition
Absolute CanSeekAbsolute
Seek Forwards CanSeekForward
Seek Backward CanSeekBackward

You should check the capabilities of the stream before calling SeekCurrentPosition, SeekStopPosition or checking the Duration property.

Ok, let's package this simple class into a Jukebox. I want the Jukebox class to pull files from a predetermined location so I can just point it at a media folder within the games directory structure. I also want the Jukebox to either play all the files it finds in order or play a single file in a looping fashion.

Finally I want this class to play a specific file on demand, for example, when I am on the splash screen I want to play the intro tune, while on the main game screen I want some other background music.

NOTE: I am not providing any audio files with the source code. By default the SoundTrack class reads from C:\Documents and Settings\All Users\Documents\My Music\Sample Music where you should have two WMA files on a standard XP machine. If you do not have those files you need to change the directory and point it at a location that has sound files.

Creating the SoundTrack class

The first step is to add a reference to the Microsoft.DirectX.AudioVideoPlayback assembly.
Next we add a new class and call it Soundtrack. At the top of the class we need add the using statement for the AudioVideoPlayback namespace.

using Microsoft.DirectX.AudioVideoPlayback;
Like all the other classes we are going to implement the IDisposable interface to ensure proper resource management.
 
public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}
 
protected virtual void Dispose(bool disposing)
{
    if (!this._disposed)
    {
        if (disposing)
        {
            if (_audio != null)
            _audio.Dispose();
        }
    }
    _disposed = true;
}
 
// Use C# destructor syntax for finalization code.
~SoundTrack()
{
    // Simply call Dispose(false).
    Dispose(false);
}
 
private bool _disposed;
The constructor of the class will receive an array of file names and an indicator if we want to play the files in a loop.
 
public SoundTrack ( string[] songCollection, bool repeat )
{
    _songList = songCollection;
    _isRepeat = repeat;
    
    if ( _songList.Length == 0 )
        _enabled = false;
    else
        _audio = new Audio ( _songList[ _songCounter ].ToString ( ) );
}
Next we declare a number of private properties for the class to hold the songs, and counter information.
 
private Audio _audio;
private string[] _songList;
private int _songCounter = 0;
private bool _isRepeat;
private bool _enabled = true;
Now we add the Play method to the class.
 
public void Play()
{
    if ( !_enabled )
        return;
    
    _audio.Play();
    _audio.Ending += new EventHandler(_audio_Ending);
}
All we have to do is to call the Play method of the Audio class. Finally we need to hook the Ending event to implement the looping capabilities.
 
private void _audio_Ending(object sender, EventArgs e)
{
    if ( _isRepeat )
    {
        Stop ( );
        Play ( );
    }
    else
        NextSong ( );
}
This method simply calls the NextSong method if we are not repeating the song, or stops and plays the same song again.
 
public void NextSong ( )
{
    if ( !_enabled )
        return;
    
    _songCounter++;
    
    if ( _songCounter == _songList.Length )
        _songCounter = 0;
    
    if ( _audio.Playing == true )
        _audio.Stop ( );
    
    _audio.Open ( _songList[ _songCounter ].ToString ( ) );
    _audio.Play ( );
}
Here we simply increment the counter to the next song, pass that song to the Audio class and play the song.

The only remaining method we now need is to stop the player

public void Stop ( )
{
    if ( !_enabled )
        return;
    
    if ( _audio != null )
        _audio.Stop ( );
}

To allow us to control the balance and volume we simply add two properties to the SoundTrack class that expose the identical properties of the Audio class. Since we know the upper and lower bounds of the values, we add a simple check to the property setter to ensure we only pass valid values. This is more efficient than passing an invalid value and having to check for exceptions. In general you should enforce boundaries whenever possible.

   1: public int Volume
   2: {
   3:     get { return _audio.Volume; }
   4:     set 
   5:     {
   6:         if ( value > 0 | value < -10000 )
   7:             return;
   8:         
   9:         _audio.Volume = value; 
  10:     }
  11: }
  12:  
  13: public int Balance
  14: {
  15:     get { return _audio.Balance; }
  16:     set 
  17:     {
  18:         if ( value > 10000 | value < -10000 )
  19:             return;
  20:         
  21:         _audio.Balance = value; 
  22:     }
  23: }

Now we can integrate the new class into the game. First we add a reference to the new class to the GameEngine class.

private SoundTrack _mainSoundTrack;
Now we initialize the new class in the ConfigureSounds method of the GameEngine class.
 
_mainSoundTrack = new SoundTrack ( Directory.GetFiles ( @"C:\Documents" + 
    "and Settings\All Users\Documents\My Music\Sample Music" ), true );
To control the sounds for experimental purpose only, we are going to associate a number of keyboard keys with the Play, Stop and Next methods of the class. In a real game we would only expose a mute function, while the sound settings for volume and balance would be part of the game options screen. The actual playing of the various sounds would be controlled by the state of the game.
 
// Control the Sound
if ( _keyboard.State[ Key.P ] )
    _mainSoundTrack.Play ( );
 
if ( _keyboard.State[ Key.M ] )
    _mainSoundTrack.Stop ( );
 
if ( _keyboard.State[ Key.N ] )
    _mainSoundTrack.NextSong ( );
 
if ( _keyboard.State[ Key.UpArrow ] )
    _mainSoundTrack.Volume += 10;
 
if ( _keyboard.State[ Key.DownArrow ] )
    _mainSoundTrack.Volume -= 10;
 
if ( _keyboard.State[ Key.RightArrow ] )
    _mainSoundTrack.Balance += 10;
 
if ( _keyboard.State[ Key.LeftArrow ] )
    _mainSoundTrack.Balance -= 10; 
That's it. Start the game and press P to hear the first tune, you can use the cursor keys to change the volume (up/down) and the balance (left/right). To skip to the next song just press N, and to stop the soundtrack press M.

Summary

Now we have pretty much completed the pass through the basic functionality of the all the DirectX namespaces. There are many more advanced topics that could cover additional graphics classes such as HLSL (High Level Shader Language), but in the next articles we are going to focus on two non DirectX parts of game development - Artificial Intelligence (AI) and Physics.

Without good AI and Physics, even the best designed games are no fun to play. Nothing shatters the illusion of reality more than predictable enemies or rocks floating in mid air.

Together with game Physics, AI represents a major portion of the game that has no support in DirectX and limited support in other (free) libraries. We are not going to be able to fill that gap and write a fully featured AI engine, but we can cover most of the principles and techniques behind some of the more basic AI problems, such as chasing and evading and path finding.

Until then: Happy coding.

Derek Pierson is a software developer with 12 years experience designing and developing enterprise applications using a variety of programming languages. He is an enthusiastic teacher of the art of programming and believes that elegance and simplicity are defining features of good software.

Filed under: arcade, gaming

Follow the Discussion

  • Migel AnjelMigel Anjel

    Thanks man!

    This was very useful.

  • TheOneWhoLovesThisTutorialTheOneWho​LovesThis​Tutorial

    This is a very nice tutorial! Nice job! It would be better thought if you recorded a video showing us how to do all those things. Very nice though. Thanks!

  • Quagmire990Quagmire990

    This has been a huge help to getting me started on 3d 1st person game programming. I have two questions:

    1: where you don't specify VB or C#, does that mean the code is neutral as it can be put in VB and C#? because im programming in VB express not C#

    2: Are there more parts to this tutorial? or was this the last one? are there more tutorials like this here?

  • Clint RutkasClint I'm a "developer"

    @Quagmire990 Managed DirectX is no longer supported.  XNA has replaced it for .NET game development.

    The download ZIP file does have a VB example with it but looks like the article only is C#.

    There are more parts and are at the top of the article.  But once again, I suggest you try XNA as it is supported for .NET development.

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.