HelloWorld


HelloWorld!

No introduction to any API is complete without that ancient but useful ritual known as the "Hello World" application. Our first application loads a wave file and plays it. Before we do anything, we need to hook up the correct include files. DirectX Audio only requires dmusici.h, as it has all the interfaces, data structures, and GUIDs for the Performance Layer. In turn, it pulls in the dsound.h and dmusic.h lower-level header files, so we get everything we need. Here is what we need to put at the top of the application:

#include <dmusici.h>


 

Note 

You need to link with the GUID library that comes with DirectX. Be sure to include dxguid.lib in your project's linker settings or you will get a ton of errors for each IID or CLSID used in your code but not found by the linker. In VC 6, open the Project Settings dialog, select the Link tab, and add dxguid.lib to the Object/Library Modules edit box. Of course, this has already been set up properly with the projects included on the companion CD.

Now we can get to the program. Start by declaring the objects we use. Notice that the objects are all interface pointers. They do not yet represent real objects.

// The performance manages all audio playback.
IDirectMusicPerformance8* pPerformance = NULL;
// The loader manages all file I/O.
IDirectMusicLoader8* pLoader = NULL;
// Each audio file is represented by a Segment.
IDirectMusicSegment8* pSegment = NULL;
 

Before we start, initialize the COM system by calling CoInitialize(). This initializes the COM system so that CoCreateInstance() can function.

// Initialize COM
CoInitialize(NULL);

 

Note 

If you are working with DirectPlay and using the same thread, you should instead call CoInitializeEx(NULL,COINIT_MULTITHREADED), which is what DirectPlay requires. DirectMusic is completely thread-safe, so it works well with either.

Now, use COM to create the loader object. CoCreateInstance() is passed the class ID of the loader as well as the interface ID for the IDirectMusicLoader interface. CoCreateInstance() uses this information to query the system registry, find the corresponding dynamic-link library (DLL), which in this case is dmloader.dll, load it, and tell the DLL to create one Loader object with the IDirectMusicLoader interface.

// Create the loader. This is used to load any DirectX
// Audio object that is read from a file. We use
// it to read a wave file as a Segment.
// The three parameters that really matter are the
// class ID, interface ID, and returned pointer. The
// two other parameters assume more sophisticated COM
// programming, which is not required for DirectX Audio.
CoCreateInstance(
    CLSID_DirectMusicLoader, // Class ID of the DirectMusic loader.
    NULL,                    // Ignore COM aggregation.
    CLSCTX_INPROC,           // Must run within this application.
    IID_IDirectMusicLoader8, // Request the IDirectMusicLoader8 interface.
    (void**)&pLoader);       // Return pointer to interface here.
 

Likewise, create a Performance.

// Create the Performance. This manages the playback of sound and music.
CoCreateInstance(
    CLSID_DirectMusicPerformance, // Class ID of the DirectMusic Performance.
    NULL,                         // Ignore.
    CLSCTX_INPROC,                // Yada yada fancy COM stuff.
    IID_IDirectMusicPerformance8, // Interface we want to get back.
    (void**)&pPerformance);       // Returned IDirectMusicPerformance8 pointer.
 

You must initialize the Performance for playback. A call to InitAudio() archives this. At minimum, you must specify which AudioPath type you want to use by default and how many channels you need on that default path. An AudioPath represents a signal path through which the sounds (Segments) play. AudioPaths can include real-time effects, such as reverb or compression. You usually create AudioPaths from configuration files that define their layout, but you can also create predefined AudioPaths directly.

In our example, we create one stereo AudioPath with no effects, using the DMUS_APATH_DYNAMIC_STEREO standard AudioPath identifier.

// Initialize the Performance.
pPerformance->InitAudio(NULL,NULL,NULL,
    DMUS_APATH_DYNAMIC_STEREO,  // Default AudioPath type.
    2,                          // Only two pchannels needed for this wave.
    DMUS_AUDIOF_ALL,NULL);
 

Now it is time to use the Loader to read the wave file from disk. This creates a Segment. Note that the Loader's LoadObjectFromFile() method has a lot in common with the system CoCreateInstance() command. Again, you provide a class ID for the type of object you wish to load, and you provide an interface ID to indicate which interface you expect to use. Not surprisingly, the Loader turns around and calls CoCreateInstance() to create the object you requested.

Provide the name of the file to the Loader. In our HelloWorld application, it is, naturally, a wave file titled  Hello.wav.

// Load the demo wave. We are making the assumption that it is in the
// working directory.
// In VC6, this is set in the Project Settings/Debug/General/Working directory: field.
if (SUCCEEDED(pLoader->LoadObjectFromFile(
    CLSID_DirectMusicSegment,    // Class identifier.
    IID_IDirectMusicSegment8,    // ID of desired interface.
    L"Hello.wav",                // Filename.
    (LPVOID*) &pSegment          // Pointer that receives interface.
    )))
{
 

Once the Loader reads the wave into a Segment, we still need to prep it for play. The Download() command moves any audio data (waves and DLS instruments) required by the Segment into the synthesizer, priming it so it can play instantly.

   // Install the wave data in the synth
   pSegment->Download(pPerformance);
 

Now we are ready to rock. The Performance has a method, PlaySegmentEx(), which can play a Segment in all kinds of neat ways. We just use it to play our Segment in the simplest way for now. I cover the neat bells and whistles in upcoming chapters.

   // Play the wave.
   pPerformance->PlaySegmentEx(
       pSegment,  // Segment to play.
       NULL,NULL,0,0,NULL,NULL,NULL);
 

Wow! Sound!

PlaySegmentEx() returns immediately as the sound starts playing. Normally, this would be no problem, since the application would continue running. However, HelloWorld does not do anything after playing the sound, so it needs to wait patiently for eight seconds (roughly the duration of the sound) before cleaning up and closing down.

   // Wait eight seconds to let it play.
   Sleep(8000);
 

Okay, it should be done by now, so unload the Segment and release all objects.

   // Unload wave data from the synth.
   pSegment->Unload(pPerformance);
   // Release the Segment from memory.
   pSegment->Release();
}

// Done with the performance. Close it down, and then release it.
pPerformance->CloseDown();
pPerformance->Release();

// Same with the loader.
pLoader->Release();

// Bye bye COM.
CoUninitialize();
 

If you compile this and have trouble getting it to make any sound, the problem is almost certainly that it cannot find the wave file. Make sure that your project's working directory is set to the Media directory, where  Hello.wav resides. Refer to your compiler's user manual for information on how to set your working directory. Double-click on the version in the bin directory; it successfully plays because I placed a copy of  Hello.wav there.