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)
{
//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.
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.
//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.