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.
// 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);
}