This tutorial will teach you how to initialize, release, and utilize DirectX Audio. You will learn to load and play WAVs and MIDIs. This tutorial does not cover 3D sound, and does not assume any knowledge of sound or sound programming.
Remember to link dsound.lib and include the headers dmusici.h and dsound.h.
[size="5"]Initializing DirectX Audio
Before we can actually play sounds with DirectX audio, though, we must initialize it. There are three steps to initializing DirectX Audio (as written in OpenGL Game Programming):
- Initialize COM
- Create and initialize the performance object
- Create the loader
Unlike other components of DirectX, DirectAudio is pure COM. This means that you have to create the com objects yourself, mostly using the function CoCreateInstance(). Initializing the actual COM system, however, is very easy:
CoInitialize(NULL);
[size="3"]2. Create and initialize the performance object
First of all, let's talk about the performance object. The performance object handles all data flow from the sound segments to the synthesizer that plays the sounds. Basically, it is the master interface of DirectX Audio. The performance object can be made by creating an IDirectMusicPerformance8 interface object. Then, you must create the object by calling CoCreateInstance.
IDirectMusicPerformacne8* g_performance = NULL;
CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC,
IID_IDirectMusicPerformance8, (void**) &g_performance);
HRESULT IDirectMusicPerformance8::InitAudio(
IDirectMusic** ppDirectMusic,
IDirectSound** ppDirectSound,
HWND hWnd,
DWORD dwDefaultPathType,
DWORD wPChannelCount,
DWORD dwFlags,
DMUS_AUDIOPARAMS pParams);
g_performance->InitAudio(NULL, // no interface needed
NULL, // no interface needed
g_hWND, // handle to the window
DMUS_APATH_SHARED_STEREOPLUSREVERB, // default audiopath
64, // 64 channels allocated to the audiopath
DMUS_AUDIOF_ALL, // allow all synthesizer features
NULL); // default audio parameters
The loader object, as its name implies, loads all audio content, such as MIDIs and WAVs, into segments that hold the data. The loader is represented in the IDirectMusicLoader8 interface. Creating the object requires a simple call to, you guessed it, CoCreateInstance.
IDirectMusicLoader8* g_loader = NULL;
CoCreateInstance(CLSID_IDirectMusicLoader, NULL, CLSCTX_INPROC,
IID_IDirectMusicLoader8, (void**) &g_loader);
[size="5"]Playing Audio
Now that we've gotten DirectX Audio ready to go, we need something to play. Next item on the list: creating a segment. Let's see what we have to do:
- Create a segment
- Load the segment
- Download the band
- Play the segment
You might be wondering what a segment is. A segment is an object that encapsulates the sound data that is loaded from a sound file. Any sound that is played in DirectX Audio is in the form of a segment, just about. If you've been following along, you probably already know that the segment object is the form of the IDirectMusicSegment8 interface and is initialized through a COM call.
IDirectMusicSegment8* g_segment = NULL;
CoCreateInstance(CLSID_IDirectMusicSegment, NULL, CLSCTX_INPROC,
IID_IDirectMusicSegment8, (void**) &g_segment);
Now that you have a segment, you must load it with sound content. This is where the loader comes in. first, you want to set the search directory for the loader. This can be accomplished using the IDirectMusicLoader8::SetSearchDirectory function.
HRESULT IDirectMusicLoader8::SetSearchDirectory(
REFGUID rguidClass,
WCHAR* pwszPath,
BOOL fClear);
char searchPath[MAX_PATH];
WCHAR wSearchPath[MAX_PATH];
GetCurrentDirectory(MAX_PATH, searchPath);
MultiByteToWideChar(CP_ACP, 0, searchPath, -1, wSearchPath, MAX_PATH);
g_loader->SetSearchDirectory(GUID_DirectMusicAllTypes, wSearchPath, FALSE);
HRESULT IDirectMusicLoader::LoadObjectFromFile(
REFGUID rguidClassID,
REFIID iidInterfaceID,
WCHAR *pwzFilePath,
void **ppObject);
char filename[MAX_PATH] = "test.wav";
WCHAR wFilename[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, filename, -1, wFilename, MAX_PATH);
g_loader->LoadObjectFromFile(CLSID_DirectMusicSegment,
IID_IDirectMusicSegment8,
wFilename,
(void**) &g_segment);
In order to be able to play the sound, you have to download the sound's band into the synthesizer. This can be done with the IDirectMusicSegment8::Download function.
HRESULT IDirectMusicSegment8::Download(Iunknown *pAudioPath);
g_segment->Download(g_performance);
Finally, the real stuff. Playing the sound, like most operations in DirectX Audio, only takes one function to complete. You can either use PlaySegment or PlaySegmentEx. Since PlaySegmentEx offers more functionality, we will be using it. Here is the prototype:
HRESULT IDirectMusicPerformance8::PlaySegmentEx(
IUnknown* pSource,
WCHAR* pwzSegmentName,
IUnknown* pTransition,
DWORD dwFlags,
__int64 i64StartTime,
IDirectMusicSegmentState** ppSegmentState,
IUnknown* pFrom,
IUnknown* pAudioPath);
g_performance->PlaySegmentEx(g_segment, NULL, NULL, 0, 0, NULL, NULL, NULL);
[size="5"]Other functions
Now you can load and play songs with DirectX Audio. Now you need some more control over your music. The only thing as important as starting your music is stopping it. That's simple enough:
HRESULT IDirectMusicPerformance8::Stop(
IDirectMusicSegment* pSegment,
IDirectMusicSegmentState* pSegmentState,
MUSIC_TIME mtTime,
DWORD dwFlags);
HRESULT IDirectMusicPerformance8::StopEx(
IUnknown* pObjectTostop,
__int64 i64StopTime,
DWORD dwFlags);
g_performance->StopEx(g_segment, 0, 0);
g_performance->Stop(NULL, NULL, 0, 0);
Another point: What if we want to check and see whether a song is playing or not? Use the IsPlaying function:
HRESULT IDirectMusicPerformance8::IsPlaying(
IDirectMusicSegment* pSegment,
IDirectMusicSegmentState* pSegState);
if (g_performance->IsPlaying(g_segment) == S_OK)
{
// do something because the song is still playing
}
HRESULT IDirectMusicSegment8::SetRepeats(
DWORD dwRepeats);
[size="5"]Shutting Down
We've had our fun, but now we need to shut down the program. First thing we do is stop all the sounds that are playing with the Stop function mentioned above. Then we call IDirectMusicPerformance8::CloseDown, which takes no parameters. Next, we release all objects with their Release function, which also takes no parameters. Lastly, we shut down COM with CoUninitialize, which takes no paramerters.
g_performance->CloseDown();
g_loader->Release();
g_performance->Release();
g_segment->Release();
CoUninitialize();
[size="5"]References:
[bquote]Hawkins, Kevin; Astle, Dave, OpenGL Game Programming
DirectX Documentation[/bquote]
If you found this tutorial helpful, or you encounter any bugs or questions, please email me at [email="masonium@yahoo.com"]masonium@yahoo.com[/email].
DirectX Documentation[/bquote]