Step 2: Initializing the Performance

The DirectMusic performance is set up in the InitializeGame function in Donuts.cpp.

First the application queries the registry to obtain the search path for the DirectX sample music files, by calling the GetSearchPath function. Then it creates COM objects for the composer and the performance, as follows:

CoInitialize(NULL);
 
if ( !SUCCEEDED(::CoCreateInstance(
        CLSID_DirectMusicComposer,
        NULL,
        CLSCTX_INPROC, 
        IID_IDirectMusicComposer,
        (void**)&gpComposer
)))
{
    return CleanupAndExit("Couldn't create a composer object");
}
 
if ( !SUCCEEDED( CoCreateInstance( CLSID_DirectMusicPerformance, 
        NULL, 
        CLSCTX_INPROC, 
        IID_IDirectMusicPerformance,
        (void**)&gpPerformance )))
{
    return CleanupAndExit("Couldn't create a performance object");
}
 

The application then initializes the performance by calling IDirectMusicPerformance::Init, which creates a DirectMusic object that can be used to create and activate its ports. Depending on whether or not DirectSound is being used for other sound effects, the call either attaches the existing DirectSound object to the performance by passing in lpDS or creates one by passing in NULL.

#ifdef USE_DSOUND
    if( !SUCCEEDED(gpPerformance->Init(&gpDirectMusic, 
           lpDS, hWndMain)))
    {
        return CleanupAndExit("Couldn't initialize the performance");
    }
#else
    if( !SUCCEEDED(gpPerformance->Init(&gpDirectMusic, 
            NULL, hWndMain)))
    {
        return CleanupAndExit("Couldn't initialize the performance");
    }
#endif
 

The application now gets the default port, creates an instance of it with one channel group, and retrieves its capabilities:

IDirectMusicPort* pPort = NULL;
DMUS_PORTPARAMS dmos;
DMUS_PORTCAPS dmpc;
GUID guidSynthGUID;
HRESULT hr = S_OK;

if ( !SUCCEEDED(gpDirectMusic->GetDefaultPort(&guidSynthGUID)))
{
    return CleanupAndExit("Could't GetDefaultPort on IDirectMusic");
}
 
ZeroMemory(&dmos, sizeof(dmos));
dmos.dwSize = sizeof(DMUS_PORTPARAMS);
dmos.dwChannelGroups = 1;
dmos.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS;
 
if( !SUCCEEDED(gpDirectMusic->CreatePort(guidSynthGUID,
            &dmos,
            &pPort,
            NULL)))
{
    return CleanupAndExit("Couldn't CreatePort on IDirectMusic");
}
 
ZeroMemory(&dmpc, sizeof(dmpc));
dmpc.dwSize = sizeof(DMUS_PORTCAPS);
 
if( !SUCCEEDED(pPort->GetCaps(&dmpc)))
{
    if (pPort) pPort->Release();
    return CleanupAndExit("Couldn't GetCaps on IDirectMusicPort");
}
 

The behavior of the application now varies depending on whether _SOFTWARE_SYNTH_ is defined. If it is, a synthesizer with DLS capabilities is wanted, so the application checks for the DMUS_PC_DLS capabilities flag on the default port. If it fails to find that flag, it goes on to free the default port and enumerate available ports until if finds an output port that has the DMUS_PC_DLS capability. Finally, it creates an instance of that port.

if ((dmpc.dwClass != DMUS_PC_OUTPUTCLASS) 
        || !(dmpc.dwFlags & DMUS_PC_DLS))
{
    pPort->Release();
    pPort = NULL;
}
 
if (!pPort)
{
    for (DWORD index = 0; hr == S_OK; index++)
    {
        ZeroMemory(&dmpc, sizeof(dmpc));
        dmpc.dwSize = sizeof(DMUS_PORTCAPS);
 
        hr = gpDirectMusic->EnumPort(index, &dmpc);
        if(hr == S_OK)
        {
            if ( (dmpc.dwClass == DMUS_PC_OUTPUTCLASS) && 
                 (dmpc.dwFlags & DMUS_PC_DLS) )
            {
                CopyMemory(&guidSynthGUID, &dmpc.guidPort,
                        sizeof(GUID));
 
                ZeroMemory(&dmos, sizeof(dmos));
                dmos.dwSize = sizeof(DMUS_PORTPARAMS);
                dmos.dwChannelGroups = 1;
                dmos.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS;
 
                hr = gpDirectMusic->CreatePort(guidSynthGUID, 
                        &dmos, &pPort, NULL);
                break;
            }
        }
    }
    if (hr != S_OK)
    {
        if (pPort) pPort->Release();
        return CleanupAndExit("Couldn't initialize the Synth port");
    }
}
 

If, on the other hand, _SOFTWARE_SYNTH_ is not defined, a legacy hardware port is wanted, and the application goes on to enumerate ports until it finds the MIDI mapper. (That code is omitted here.)

Now the port is activated and attached to the performance.

pPort->Activate(TRUE);
gpPerformance->AddPort(pPort);
 

The next call maps PChannels 0-15 to the first group of MIDI channels on the port. Note that this step is necessary because the application did not pass NULL to AddPort.

gpPerformance->AssignPChannelBlock(0, pPort, 1);
 

The original reference to the port can now be released. This call doesn't remove the port from the performance.

if (pPort) pPort->Release();