Step 4: Loading the MIDI File

In this step you will implement a function, LoadMIDISegment, that takes a pointer to the IDirectMusicLoader created in the last step and uses it to create a segment object encapsulating the data from a MIDI file.

IDirectMusicSegment* LoadMIDISegment(IDirectMusicLoader* pLoader, 
        WCHAR wszMidiFileName )
{
    DMUS_OBJECTDESC ObjDesc; 
    IDirectMusicSegment* pSegment = NULL;
 

Let's assume that all the MIDI files you want to play are in the current working directory. You need to let the loader know this, by setting the search directory. (If the search directory is not being changed elsewhere, in order to load objects from other directories, this actually has to be done only once, not each time you load a file.)

    char szDir[_MAX_PATH];
    WCHAR wszDir[_MAX_PATH];
 
    if(_getcwd( szDir, _MAX_PATH ) == NULL)
    {
        return NULL;
    }

/* 
Convert from multibyte format to Unicode using the following macro:
#define MULTI_TO_WIDE( x,y )  MultiByteToWideChar( CP_ACP, \
        MB_PRECOMPOSED, y, -1, x, _MAX_PATH );
*/

    MULTI_TO_WIDE(wszDir, szDir);
    HRESULT hr = pLoader->SetSearchDirectory(GUID_DirectMusicAllTypes,
        wszDir, FALSE);
    if (FAILED(hr)) 
    {
        return NULL;
    }
 

You then describe the object to be loaded, in a DMUS_OBJECTDESC structure:

    ObjDesc.guidClass = CLSID_DirectMusicSegment;
    ObjDesc.dwSize = sizeof(DMUS_OBJECTDESC);
    wcscpy( ObjDesc.wszFileName, wszMidiFileName );
    ObjDesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;
 

Now load the object and query it for the IDirectMusicSegment interface. This is done in a single call to IDirectMusicLoader::GetObject. Note that loading the object also initializes the tracks and does everything else necessary to get the MIDI data ready for playback.

    pLoader->GetObject(&ObjDesc,
            IID_IDirectMusicSegment, (void**) &pSegment);
 

To ensure that the segment plays as a standard MIDI file, you now need to set a parameter on the band track. Use the IDirectMusicSegment::SetParam method and let DirectMusic find the track, by passing -1 (or 0xFFFFFFFF) in the dwGroupBits method parameter.

    g_pMIDISeg->SetParam(GUID_StandardMIDIFile,
            -1, 0, 0, (void*)g_pPerf);
 

This step is necessary because DirectMusic handles program changes and bank selects differently for standard MIDI files than it does for MIDI content authored specifically for DirectMusic. The GUID_StandardMIDIFile parameter must be set before the instruments are downloaded.

The next step is to download the instruments. This is necessary even for playing a simple MIDI file, because the default software synthesizer needs the DLS data for the General MIDI instrument set. If you skip this step, the MIDI file will play silently. Again, you call SetParam on the segment, this time specifying the GUID_Download parameter:

    g_pMIDISeg->SetParam(GUID_Download, -1, 0, 0, (void*)g_pPerf);
 

Note that there's no harm in requesting the download even though this might already have been done in a previous call to the LoadMIDISegment function. A redundant request is simply ignored. Eventually you have to unload the instruments, but that can wait until you're ready to shut down DirectMusic.

The function now returns a pointer to the segment, which is ready to be played.

    return pSegment;
 
} // End of LoadSegment()
 

Before loading a new segment, clean up any existing one. Then pass a file name to the LoadMIDISegment function.

if (g_pMIDIseg)
{
    g_pMIDIseg->Release();
    g_pMIDIseg = NULL;
}
 
if (g_pLoader)
{
    IDirectMusicSegment* g_pMIDIseg = LoadMIDISegment(g_pLoader,
            L"tune.mid");
}