• 02/06/02 03:51 PM
    Sign in to follow this  
    Followers 0

    Using DirectX Audio 8

    Graphics and GPU Programming

    Myopic Rhino
    So you have this wonderful game. It has everything a game could need: amazing graphics, lightning-quick input response, the works. But there's only one thing missing: SOUND. Quality sound can make or break a game, so you want to have sound system that's booming. So how do you implement it? DirectX Audio!!!

    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):
    1. Initialize COM
    2. Create and initialize the performance object
    3. Create the loader
    [size="3"]1. Initialize COM

    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:

    What, you were expecting something harder? This simple function call allows you to use COM to create the DirectAudio objects.

    [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);
    The previous code declares an IDirectMusicPerformance8 object and creates it. Now we have to initialize it. The IDirectMusicPerformance8 interface uses the member function InitAudio to initialize itself. Let's take a look at the function:

    HRESULT IDirectMusicPerformance8::InitAudio(
    IDirectMusic** ppDirectMusic,
    IDirectSound** ppDirectSound,
    HWND hWnd,
    DWORD dwDefaultPathType,
    DWORD wPChannelCount,
    DWORD dwFlags,
    Now let's show a working example of this function.

    g_performance->InitAudio(NULL, // no interface needed
    NULL, // no interface needed
    g_hWND, // handle to the window
    64, // 64 channels allocated to the audiopath
    DMUS_AUDIOF_ALL, // allow all synthesizer features
    NULL); // default audio parameters
    [size="3"]3. Create the loader

    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:
    1. Create a segment
    2. Load the segment
    3. Download the band
    4. Play the segment
    [size="3"]1. Create a 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);
    [size="3"]2. Load the 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);
    Chances are, you just want to set the search directory to the directory your program is in. This shows how you would do that

    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);
    The previous code snippet first declares a normal and a wide character array. The GetCurrentDirectory function retrieves the directory which the program is in. The MultiByteToWideChar function converts the regular character array into a WCHAR array. If you use dxutil.h | cpp, the DXUtil_ConvertGenericStringToWide function just uses MultiByteToWideChar, only it looks better. Finally, we call SetSearchDirectory with the predefined GUID that enables all music types to be read. Now that we've set the search path, we want to actually load a file. This can be accomplished with one function call:

    HRESULT IDirectMusicLoader::LoadObjectFromFile(
    REFGUID rguidClassID,
    REFIID iidInterfaceID,
    WCHAR *pwzFilePath,
    void **ppObject);
    Here's how you would load a file named test.wav:

    char filename[MAX_PATH] = "test.wav";
    WCHAR wFilename[MAX_PATH];

    MultiByteToWideChar(CP_ACP, 0, filename, -1, wFilename, MAX_PATH);

    (void**) &g_segment);
    [size="3"]3. Download the band

    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);
    That wasn't very exciting, was it? All you have to do is download the band to the performance object, like so:

    [size="3"]4. Play the segment

    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);
    Even though it looks a little complicated, it is actually very simple to use. Only a couple of the parameters are of importance to us. pSource is the sound data that you are going to play. In this case, you will be playing g_segment. i64StartTime is the performance time at which the song will start playing. Normally, you sill set this to zero so that the song will play immediately. Notice that this is a function of the IDirectMusicPerformance8 interface. This is because IDirectMusicSegment only handles sound data and playing information. IDirectMusicPerformance8 handles all manipulation of the segment, including playing and stopping. Now let's finally play some music.

    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);
    You can use either one to stop a sound. For the last two parameters, both have the same functionality. The second to last parameter indicates when to stop the sound. If you want to stop the sound immediately, that value should be 0. dwFlags is when the stop should occur in the segment, which should also be set to 0. But, StopEx offers extra functionality because it can stop a segment or an audiopath. For stopping one sound, we'll use that.

    g_performance->StopEx(g_segment, 0, 0);
    Simple. What if you want to stop all sounds that are playing? You don't have to keep track of which song is playing. Just use the Stop function.

    g_performance->Stop(NULL, NULL, 0, 0);
    When you pass NULL as the segment to stop, the function stops all playing sounds. I tried it with StopEx, but the program crashes.

    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);
    The function returns S_OK if the segment is still playing.

    if (g_performance->IsPlaying(g_segment) == S_OK)
    // do something because the song is still playing
    The last function I want to talk about before we conclude is SetRepeats.

    HRESULT IDirectMusicSegment8::SetRepeats(
    DWORD dwRepeats);
    This function, as the name implies, sets the number of times that the sound repeats. If you're playing background music, for example, and you want it to loop forever, pass DMUS_SEG_REPEAT_INFINITE as the parameter and the sound will loop until you explicitly stop it.

    [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.


    [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].

    Sign in to follow this  
    Followers 0

    User Feedback

    Create an account or sign in to leave a review

    You need to be a member in order to leave a review

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

    There are no reviews to display.