Microsoft DirectX 9.0

Creating DLS Instruments Programmatically

The following example code shows how to create a DLS instrument from a WAV file and make it available to the performance.

The entry point for the example is the PlayDLSFromWAV function, which creates and downloads the instrument and plays two notes on it by sending performance messages. This function uses two classes defined in the DirectX sample framework. The pWaveFile parameter is an instance of CWaveFile representing a WAV file that has already been opened. An instance of CMusicManager is used to create and initialize the performance. For more information on these classes, see CWaveFile Sample Class and CMusicManager Sample Class.

// Declare data structures for download.
 
#pragma pack(2)
struct INSTRUMENT_DOWNLOAD
{
    DMUS_DOWNLOADINFO   dlInfo;
    ULONG               ulOffsetTable[4];
    DMUS_INSTRUMENT     dmInstrument;
    DMUS_REGION         dmRegion;
    DMUS_ARTICULATION   dmArticulation;
    DMUS_ARTICPARAMS    dmArticParams;
};
 
struct WAVE_DOWNLOAD
{
    DMUS_DOWNLOADINFO   dlInfo;
    ULONG               ulOffsetTable[2];
    DMUS_WAVE           dmWave;
    DMUS_WAVEDATA       dmWaveData;
};

#pragma pack()

// Define some values for instrument parameters.

#define FIVE_HERTZ (0xFCACAE9C)
#define ZERO_SECONDS (0x80000000)
#define ONE_MILLISECOND (0xD1490F12)
#define ONE_HUNDRED_PERCENT (0x03E80000)

void InitializeInstDownload(INSTRUMENT_DOWNLOAD *pInstDownload, DWORD dwDLId, DWORD dwPatch, DWORD dwDLIdWave)
{
    ZeroMemory(pInstDownload, sizeof(INSTRUMENT_DOWNLOAD));
    pInstDownload->dlInfo.dwDLType = DMUS_DOWNLOADINFO_INSTRUMENT;
    pInstDownload->dlInfo.cbSize = sizeof(INSTRUMENT_DOWNLOAD);
    pInstDownload->dlInfo.dwDLId = dwDLId;
    pInstDownload->dlInfo.dwNumOffsetTableEntries = 4;
    pInstDownload->ulOffsetTable[0] = offsetof(INSTRUMENT_DOWNLOAD,dmInstrument);
    pInstDownload->ulOffsetTable[1] = offsetof(INSTRUMENT_DOWNLOAD,dmRegion);
    pInstDownload->ulOffsetTable[2] = offsetof(INSTRUMENT_DOWNLOAD,dmArticulation);
    pInstDownload->ulOffsetTable[3] = offsetof(INSTRUMENT_DOWNLOAD,dmArticParams);
    
    pInstDownload->dmInstrument.ulFirstRegionIdx = 1;
    pInstDownload->dmInstrument.ulGlobalArtIdx = 2;
    pInstDownload->dmInstrument.ulPatch = dwPatch;

    pInstDownload->dmRegion.RangeKey.usHigh = 127;
    pInstDownload->dmRegion.RangeVelocity.usHigh = 127;
    pInstDownload->dmRegion.fusOptions = F_RGN_OPTION_SELFNONEXCLUSIVE;
    pInstDownload->dmRegion.WaveLink.ulChannel = 1;
    pInstDownload->dmRegion.WaveLink.ulTableIndex = dwDLIdWave;
    pInstDownload->dmRegion.WaveLink.usPhaseGroup = 0;
    pInstDownload->dmRegion.WSMP.cbSize = sizeof(WSMPL);
    pInstDownload->dmRegion.WSMP.fulOptions = F_WSMP_NO_TRUNCATION;
    pInstDownload->dmRegion.WSMP.usUnityNote = 60; // Middle C
    pInstDownload->dmRegion.WLOOP[0].cbSize = sizeof(WLOOP);
    pInstDownload->dmRegion.WLOOP[0].ulType = WLOOP_TYPE_FORWARD;

    pInstDownload->dmArticulation.ulArt1Idx = 3;

    pInstDownload->dmArticParams.LFO.tcDelay = ZERO_SECONDS;
    pInstDownload->dmArticParams.LFO.pcFrequency = FIVE_HERTZ;
    pInstDownload->dmArticParams.PitchEG.tcAttack = ZERO_SECONDS;
    pInstDownload->dmArticParams.PitchEG.tcDecay = ZERO_SECONDS;
    pInstDownload->dmArticParams.PitchEG.ptSustain = ONE_HUNDRED_PERCENT;
    pInstDownload->dmArticParams.PitchEG.tcRelease = ONE_MILLISECOND;

    pInstDownload->dmArticParams.VolEG.tcAttack = ZERO_SECONDS;
    pInstDownload->dmArticParams.VolEG.tcDecay = ZERO_SECONDS;
    pInstDownload->dmArticParams.VolEG.ptSustain = ONE_HUNDRED_PERCENT;
    pInstDownload->dmArticParams.VolEG.tcRelease = ONE_MILLISECOND;
}

void InitializeWaveDownload(WAVE_DOWNLOAD *pWaveDownload, DWORD dwDLId, WAVEFORMATEX *pwfex, DWORD dwWaveSize, DWORD dwOverallSize)
{
    ZeroMemory(pWaveDownload,sizeof(WAVE_DOWNLOAD));
    pWaveDownload->dlInfo.dwDLType = DMUS_DOWNLOADINFO_WAVE;
    pWaveDownload->dlInfo.cbSize = dwOverallSize;
    pWaveDownload->dlInfo.dwDLId = dwDLId;
    pWaveDownload->dlInfo.dwNumOffsetTableEntries = 2;

    pWaveDownload->ulOffsetTable[0] = offsetof(WAVE_DOWNLOAD,dmWave);
    pWaveDownload->ulOffsetTable[1] = offsetof(WAVE_DOWNLOAD,dmWaveData);

    pWaveDownload->dmWave.ulWaveDataIdx = 1;

    memcpy(&pWaveDownload->dmWave.WaveformatEx, pwfex, sizeof(WAVEFORMATEX));

    pWaveDownload->dmWaveData.cbSize = dwWaveSize;
}

void PlayDLSFromWAV(HWND hWndMain, CWaveFile *pWaveFile)
{
    const DWORD dwPatch = 0x00123456;

    // Create and initialize performance. 

    CMusicManager musicManager;
    IDirectMusicPerformance8* pPerf;
    HRESULT hr = musicManager.Initialize(hWndMain);
    pPerf = musicManager.GetPerformance();

    // Get interfaces to the port.

    IDirectMusicPort* pIDirectMusicPort = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pPerf->PChannelInfo(0, &pIDirectMusicPort, NULL, NULL);
    }

    IDirectMusicPortDownload8* pIDirectMusicPortDownload8 = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pIDirectMusicPort->QueryInterface(IID_IDirectMusicPortDownload8, (void **)&pIDirectMusicPortDownload8);
    }

    // Reserve two download IDs, and retrieve the first.

    DWORD dwDLId = 0;
    if (SUCCEEDED(hr))
    {
        hr = pIDirectMusicPortDownload8->GetDLId(&dwDLId, 2);
    }

    // Allocate a buffer for the instrument data (regions, articulations, and so on.)
    
    IDirectMusicDownload8* pIDirectMusicDownloadArticulation = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pIDirectMusicPortDownload8->AllocateBuffer( sizeof(INSTRUMENT_DOWNLOAD), &pIDirectMusicDownloadArticulation);
    }

    // Allocate a buffer for the WAV data.

    IDirectMusicDownload8* pIDirectMusicDownloadWave = NULL;
    DWORD dwAppend = 0;
    if (SUCCEEDED(hr))
    {
        hr = pIDirectMusicPortDownload8->GetAppend(&dwAppend);
        if (SUCCEEDED(hr))
        {
            hr = pIDirectMusicPortDownload8->AllocateBuffer(sizeof(WAVE_DOWNLOAD) + 
                    dwAppend * pWaveFile->GetFormat()->nBlockAlign + pWaveFile->GetSize(),
                    &pIDirectMusicDownloadWave);
        }
    }

    // Read format data from the WAV file into the buffer, and download it to the port.

    void *pvData = NULL;
    DWORD dwSize = 0;
    if (SUCCEEDED(hr))
    {
        hr = pIDirectMusicDownloadWave->GetBuffer( &pvData, &dwSize );
        if (SUCCEEDED(hr))
        {
            InitializeWaveDownload((WAVE_DOWNLOAD*)pvData, dwDLId, pWaveFile->GetFormat(),
                    pWaveFile->GetSize(), dwSize);

            DWORD dwRead = 0;
            hr = pWaveFile->Read(((WAVE_DOWNLOAD*)pvData)->dmWaveData.byData, 
                    pWaveFile->GetSize(), &dwRead);
            if (SUCCEEDED(hr) && pWaveFile->GetSize() == dwRead)
            {
                hr = pIDirectMusicPortDownload8->Download(pIDirectMusicDownloadWave);
                if (hr == DMUS_E_NOTMONO) 
                {
                  MessageBox(hWndMain, "WAV must be mono.", "Error", 0);
                }
            }
        }
    }

    // Put instrument data into the buffer and download to the port.

    if (SUCCEEDED(hr))
    {
        hr = pIDirectMusicDownloadArticulation->GetBuffer( &pvData, &dwSize );
        if (SUCCEEDED(hr))
        {
            InitializeInstDownload((INSTRUMENT_DOWNLOAD *)pvData, dwDLId + 1, dwPatch, dwDLId);
            hr = pIDirectMusicPortDownload8->Download(pIDirectMusicDownloadArticulation);
        }
    }

    // Get the performance toolgraph so messages can be stamped.

    IDirectMusicGraph* pIDirectMusicGraph = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pPerf->QueryInterface(IID_IDirectMusicGraph, (void **)&pIDirectMusicGraph);
    }

    // Create and send a message to put the instrument on channel 0.

    DMUS_PATCH_PMSG *pDMUS_PATCH_PMSG = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pPerf->AllocPMsg( sizeof(DMUS_PATCH_PMSG), (DMUS_PMSG **)&pDMUS_PATCH_PMSG);
    }
    if (SUCCEEDED(hr))
    {
        pDMUS_PATCH_PMSG->dwType = DMUS_PMSGT_PATCH;
        pDMUS_PATCH_PMSG->dwPChannel = 0;
        pDMUS_PATCH_PMSG->dwFlags = DMUS_PMSGF_REFTIME ;
        pDMUS_PATCH_PMSG->byInstrument = dwPatch & 0x7F;
        pDMUS_PATCH_PMSG->byLSB = (dwPatch & 0x7f00) >> 8;
        pDMUS_PATCH_PMSG->byMSB = (dwPatch & 0x7f0000) >> 16;
        hr = pIDirectMusicGraph->StampPMsg((DMUS_PMSG *)pDMUS_PATCH_PMSG);
    }
    if (SUCCEEDED(hr))
    {
        hr = pPerf->SendPMsg((DMUS_PMSG *)pDMUS_PATCH_PMSG);
        if (FAILED(hr))
        {
            pPerf->FreePMsg((DMUS_PMSG *)pDMUS_PATCH_PMSG);
        }
    }

    // The instrument is now available to be played. The following code
    // plays two "notes" at different pitches and durations.

    DMUS_NOTE_PMSG *pDMUS_NOTE_PMSG = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pPerf->AllocPMsg( sizeof(DMUS_NOTE_PMSG), (DMUS_PMSG **)&pDMUS_NOTE_PMSG);
    }
    if (SUCCEEDED(hr))
    {
        pDMUS_NOTE_PMSG->dwType = DMUS_PMSGT_NOTE;
        pDMUS_NOTE_PMSG->dwPChannel = 0;
        pDMUS_NOTE_PMSG->dwFlags = DMUS_PMSGF_REFTIME;
        pDMUS_NOTE_PMSG->bFlags = DMUS_NOTEF_NOTEON;
        pDMUS_NOTE_PMSG->bVelocity = 127;
        pDMUS_NOTE_PMSG->bMidiValue = 60;
        pDMUS_NOTE_PMSG->mtDuration = DMUS_PPQ * 4; // Whole note
        hr = pIDirectMusicGraph->StampPMsg((DMUS_PMSG *)pDMUS_NOTE_PMSG);
    }
    if (SUCCEEDED(hr))
    {
        hr = pPerf->SendPMsg((DMUS_PMSG *)pDMUS_NOTE_PMSG);
        if (FAILED(hr))
        {
            pPerf->FreePMsg((DMUS_PMSG *)pDMUS_NOTE_PMSG);
        }
    }

    Sleep(1000);

    DMUS_NOTE_PMSG *pDMUS_NOTE_PMSG2 = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pPerf->AllocPMsg( sizeof(DMUS_NOTE_PMSG), (DMUS_PMSG **)&pDMUS_NOTE_PMSG2);
    }
    if (SUCCEEDED(hr))
    {
        pDMUS_NOTE_PMSG2->dwType = DMUS_PMSGT_NOTE;
        pDMUS_NOTE_PMSG2->dwPChannel = 0;
        pDMUS_NOTE_PMSG2->dwFlags = DMUS_PMSGF_REFTIME;
        pDMUS_NOTE_PMSG2->bFlags = DMUS_NOTEF_NOTEON;
        pDMUS_NOTE_PMSG2->bMidiValue = 70;
        pDMUS_NOTE_PMSG2->bVelocity = 127;
        pDMUS_NOTE_PMSG2->mtDuration = DMUS_PPQ * 2; // Half note
        hr = pIDirectMusicGraph->StampPMsg((DMUS_PMSG *)pDMUS_NOTE_PMSG2);
    }
    if (SUCCEEDED(hr))
    {
        hr = pPerf->SendPMsg((DMUS_PMSG *)pDMUS_NOTE_PMSG2);
        if (FAILED(hr))
        {
            pPerf->FreePMsg((DMUS_PMSG *)pDMUS_NOTE_PMSG2);
        }
    }

    // Allow time for second note to play before music manager goes out of scope
    // and shuts down performance.

    Sleep(4000);

    // Clean up. SAFE_RELEASE is defined in dmutil.h.

    SAFE_RELEASE(pIDirectMusicPort);
    SAFE_RELEASE(pIDirectMusicPortDownload8);   
    SAFE_RELEASE(pIDirectMusicDownloadArticulation);
    SAFE_RELEASE(pIDirectMusicDownloadWave);
    SAFE_RELEASE(pIDirectMusicGraph);

}