Experiments In Game Programming
Main
 Home
XNA - C#
 Coming Soon!
 Level Editing - GTK
DirectX 9 - C++
 Downloads
 Disclaimer
 Introduction
Part 1 - DirectX
 1 - Breakout
 2 - Create DX
 3 - 2d Images
 4 - 3d Models
 5 - Cameras & Lights
 6 - Animation Timing
 7 - Keyboard/Mouse
 8 - Sound
Part 2 - Breakout
 1 - Art and Sounds
 2 - The Menu
 3 - Starting Breakout
 4 - The Level
 5 - The Paddle
 6 - The Ball
 7 - Finishing Touches

Untitled Document

Chapter 8

 

Sound

 

 

Alright, this chapter is going to address the 'annoying sound-effects' portion of the requirements list from chapter 1. We do this using the CSoundManager, and CSound utility classes provided with the directx SDK. So in addition to the classes we are writing I have added dsutil, and dxutil to the project.

Topics Covered in this Chapter:
  • Creating A SoundManager Class
  • Testing our SoundManager

To play sounds with directX we first need to create a DirectSound device. We are using the CSoundManager, it will handle creating the direct sound device for us. Because we are using the CSoundManager to handle loading of sound, our SoundManager is going to keep track of all the sounds we load and handle playing and stopping them. Our SoundManager will use a standard vector class to hold pointers to all of the sounds we load. When we load a sound the SoundManager will return a buffer number. The buffer number is the index in the sounds vector of the sound we have loaded. When we want to play a sound we just ask the SoundManager to play a buffer number.

Take a look at the SoundManager class. The first function of interest is init, init handles creating the Direct Sound device. When we Initialize the CSoundManager, it makes a call to DirectSoundCreate8, and sets the cooperative level of the device for us.

bool SoundManager::init(HWND wndHandle)
{
     //create the direct Sound Manager
     directSoundManager = new CSoundManager();
     HRESULT hr;
     //initialize the sound manager
     hr = directSoundManager->Initialize( wndHandle, DSSCL_PRIORITY );

     if FAILED (hr){
         return false;
     }

     return true;

    
}

loadFile is the next function, it handles loading a wav file, createing a direct sound buffer, and copying the wav data into the buffer. Well actually the CSoundManager class does most of that for us. CSound tempSound is our temporary sound buffer controller, CSound handles the directSound sound buffer details for us. Filling our buffer is fairly simple using the CSoundManager, we call SetPrimaryBufferFormat to set the format of our sound buffer. Then we call Create, passing it our buffer and filename, and it kindly fills it for us. Once we have a filled buffer we push that buffer onto the sounds vector. The position of the buffer in the sounds vector is then returned.

int SoundManager::loadFile(std::string filename)
{

     CSound* tempSound = NULL; //holds our sound buffer
     HRESULT hr; //result variable

     //make the filename usable by the sound manager
     char* file = (char*)filename.c_str();

     //set the buffer format
     directSoundManager->SetPrimaryBufferFormat( 2, 22050, 16 );

     //create the sound buffer from the file
     directSoundManager->Create( &tempSound, file, 0, GUID_NULL );

     //save our loaded buffer into the sounds vector
     sounds.push_back((CSound*) tempSound);

     //return the index number of the saved buffer.
     return (int)(sounds.size() - 1);

}

Now that we have loaded our sound file, we need functions to start and stop playback. The last 3 functions in the SoundManager are for starting the playback, stopping the playback and starting a looping playback.
void SoundManager::playSound(int bufferNumber)
{
     sounds[bufferNumber]->Play( 0, 0, 0);
}

void SoundManager::loopSound(int bufferNumber)
{
     sounds[bufferNumber]->Play( 0, 0, DSBPLAY_LOOPING);
}


void SoundManager::stopSound(int bufferNumber)
{
     sounds[bufferNumber]->Stop();
}


 Now that we have SoundManager, we test it by adding it to the GameMain as usual. Once the appropriate headers have been added we need to create the object in the init function of GameMain. I have loaded 2 of the annoying windows sounds using the SoundManager.

Changes to GameMain init:
bool GameMain::init(HWND wndHandle, HINSTANCE hInst)
{
     ...

     //setup and play some test sounds.
     sounds = new SoundManager();
     sounds->init(wndHandle);


     soundOne = sounds->loadFile("ir_end.wav");
     soundTwo = sounds->loadFile("ding.wav");
     //end test sounds


     ...

     return true;
}

Changes to GameMain update:

void GameMain::update(void)
{
     ...

    
     //play the anoying windows sounds
     sounds->playSound(soundOne);
     sounds->playSound(soundTwo);


     // begin rendering
     ...

}

 By adding the playSound to the update loop, the sounds are played over and over at update call. Be warned this is really terrible sounding...

Summing Up - Chapter 8

In this chapter we created a SoundManager class, it allows us to handle loading as many sound files as we like. The sounds are then played back by calling play or loop with the requested buffer number.

 
 ©2008 David Whittaker