windows-nt/Source/XPSP1/NT/drivers/ddk/wdmaudio/ddksynth/instr.cpp

1572 lines
58 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
// Copyright (c) 1996-2000 Microsoft Corporation. All rights reserved.
// Instrument.cpp
#include "common.h"
#define STR_MODULENAME "DDKSynth.sys:Instr: "
#include "math.h"
void MemDump(char * prompt);
#pragma code_seg()
/*****************************************************************************
* CSourceLFO::CSourceLFO()
*****************************************************************************
* Constructor for CSourceLFO.
*/
CSourceLFO::CSourceLFO()
{
m_pfFrequency = 3804; // f = (256*4096*16*5hz)/(samplerate)
m_stDelay = 0;
m_prMWPitchScale = 0;
m_vrMWVolumeScale = 0;
m_vrVolumeScale = 0;
m_prPitchScale = 0;
}
/*****************************************************************************
* CSourceLFO::Init()
*****************************************************************************
* Initialize the CSourceLFO object.
*/
void CSourceLFO::Init(DWORD dwSampleRate)
{
m_pfFrequency = (256 * 4096 * 16 * 5) / dwSampleRate;
m_stDelay = 0;
m_prMWPitchScale = 0;
m_vrMWVolumeScale = 0;
m_vrVolumeScale = 0;
m_prPitchScale = 0;
}
/*****************************************************************************
* CSourceLFO::SetSampleRate()
*****************************************************************************
* Set the sample rate delta.
*/
void CSourceLFO::SetSampleRate(long lChange)
{
if (lChange > 0)
{
m_stDelay <<= lChange;
m_pfFrequency <<= lChange;
}
else
{
m_stDelay >>= -lChange;
m_pfFrequency >>= -lChange;
}
}
/*****************************************************************************
* CSourceLFO::Verify()
*****************************************************************************
* Sanity check on the object.
*/
void CSourceLFO::Verify()
{
FORCEBOUNDS(m_pfFrequency,64,7600);
FORCEBOUNDS(m_stDelay,0,441000);
FORCEBOUNDS(m_vrVolumeScale,-1200,1200);
FORCEBOUNDS(m_vrMWVolumeScale,-1200,1200);
FORCEBOUNDS(m_prPitchScale,-1200,1200);
FORCEBOUNDS(m_prMWPitchScale,-1200,1200);
}
/*****************************************************************************
* CSourceEG::CSourceEG()
*****************************************************************************
* Constructor for this object.
*/
CSourceEG::CSourceEG()
{
Init();
}
/*****************************************************************************
* CSourceEG::Init()
*****************************************************************************
* Initialize the CSourceEG object.
*/
void CSourceEG::Init()
{
m_stAttack = 0;
m_stDecay = 0;
m_pcSustain = 1000;
m_stRelease = 0;
m_trVelAttackScale = 0;
m_trKeyDecayScale = 0;
m_sScale = 0;
}
/*****************************************************************************
* CSourceEG::SetSampleRate()
*****************************************************************************
* Set the sample rate delta.
*/
void CSourceEG::SetSampleRate(long lChange)
{
if (lChange > 0)
{
m_stAttack <<= lChange;
m_stDecay <<= lChange;
m_stRelease <<= lChange;
}
else
{
m_stAttack >>= -lChange;
m_stDecay >>= -lChange;
m_stRelease >>= -lChange;
}
}
/*****************************************************************************
* CSourceEG::Verify()
*****************************************************************************
* Sanity check on the object.
*/
void CSourceEG::Verify()
{
FORCEBOUNDS(m_stAttack,0,1764000);
FORCEBOUNDS(m_stDecay,0,1764000);
FORCEBOUNDS(m_pcSustain,0,1000);
FORCEBOUNDS(m_stRelease,0,1764000);
FORCEBOUNDS(m_sScale,-1200,1200);
FORCEBOUNDS(m_trKeyDecayScale,-12000,12000);
FORCEBOUNDS(m_trVelAttackScale,-12000,12000);
}
/*****************************************************************************
* CSourceArticulation::CSourceArticulation()
*****************************************************************************
* Constructor for this object.
*/
CSourceArticulation::CSourceArticulation()
{
m_wUsageCount = 0;
m_sDefaultPan = 0;
m_dwSampleRate = 22050;
m_PitchEG.m_sScale = 0; // pitch envelope defaults to off
}
/*****************************************************************************
* CSourceArticulation::Init()
*****************************************************************************
* Initialize the CSourceArticulation object.
*/
void CSourceArticulation::Init(DWORD dwSampleRate)
{
m_dwSampleRate = dwSampleRate;
m_LFO.Init(dwSampleRate); // Set to default values.
m_PitchEG.Init();
m_VolumeEG.Init();
}
/*****************************************************************************
* CSourceArticulation::SetSampleRate()
*****************************************************************************
* Set the sample rate for this articulation.
*/
void CSourceArticulation::SetSampleRate(DWORD dwSampleRate)
{
if (dwSampleRate != m_dwSampleRate)
{
long lChange;
if (dwSampleRate > (m_dwSampleRate * 2))
{
lChange = 2; // going from 11 to 44.
}
else if (dwSampleRate > m_dwSampleRate)
{
lChange = 1; // must be doubling
}
else if ((dwSampleRate * 2) < m_dwSampleRate)
{
lChange = -2; // going from 44 to 11
}
else
{
lChange = -1; // that leaves halving.
}
m_dwSampleRate = dwSampleRate;
m_LFO.SetSampleRate(lChange);
m_PitchEG.SetSampleRate(lChange);
m_VolumeEG.SetSampleRate(lChange);
}
}
/*****************************************************************************
* CSourceArticulation::Verify()
*****************************************************************************
* Sanity check on the object.
*/
void CSourceArticulation::Verify()
{
m_LFO.Verify();
m_PitchEG.Verify();
m_VolumeEG.Verify();
}
/*****************************************************************************
* CSourceArticulation::AddRef()
*****************************************************************************
* Implementation of standard COM interface.
*/
void CSourceArticulation::AddRef()
{
m_wUsageCount++;
}
/*****************************************************************************
* CSourceArticulation::Release()
*****************************************************************************
* Implementation of standard COM interface.
*/
void CSourceArticulation::Release()
{
m_wUsageCount--;
if (m_wUsageCount == 0)
{
delete this;
}
}
/*****************************************************************************
* CSourceSample::CSourceSample()
*****************************************************************************
* Constructor for this object.
*/
CSourceSample::CSourceSample()
{
m_pWave = NULL;
m_dwLoopStart = 0;
m_dwLoopEnd = 1;
m_dwSampleLength = 0;
m_prFineTune = 0;
m_dwSampleRate = 22050;
m_bMIDIRootKey = 60;
m_bOneShot = TRUE;
m_bSampleType = 0;
}
/*****************************************************************************
* CSourceSample::~CSourceSample()
*****************************************************************************
* Destructor for this object.
*/
CSourceSample::~CSourceSample()
{
if (m_pWave != NULL)
{
m_pWave->Release();
}
}
/*****************************************************************************
* CSourceSample::Verify()
*****************************************************************************
* Sanity check on the object.
*/
void CSourceSample::Verify()
{
if (m_pWave != NULL)
{
FORCEUPPERBOUNDS(m_dwSampleLength,m_pWave->m_dwSampleLength);
FORCEBOUNDS(m_dwLoopEnd,1,m_dwSampleLength);
FORCEUPPERBOUNDS(m_dwLoopStart,m_dwLoopEnd);
if ((m_dwLoopEnd - m_dwLoopStart) < 6)
{
m_bOneShot = TRUE;
}
}
FORCEBOUNDS(m_dwSampleRate,3000,80000);
FORCEBOUNDS(m_bMIDIRootKey,0,127);
FORCEBOUNDS(m_prFineTune,-1200,1200);
}
/*****************************************************************************
* CSourceSample::CopyFromWave()
*****************************************************************************
* Duplicate a wave that is already referenced elsewhere.
*/
BOOL CSourceSample::CopyFromWave()
{
if (m_pWave == NULL)
{
return FALSE;
}
m_dwSampleLength = m_pWave->m_dwSampleLength;
m_dwSampleRate = m_pWave->m_dwSampleRate;
m_bSampleType = m_pWave->m_bSampleType;
if (m_bOneShot)
{
m_dwSampleLength--;
if (m_pWave->m_bSampleType & SFORMAT_16)
{
m_pWave->m_pnWave[m_dwSampleLength] = 0;
}
else
{
char *pBuffer = (char *) m_pWave->m_pnWave;
pBuffer[m_dwSampleLength] = 0;
}
}
else
{
if (m_dwLoopStart >= m_dwSampleLength)
{
m_dwLoopStart = 0;
}
if (m_pWave->m_bSampleType & SFORMAT_16)
{
m_pWave->m_pnWave[m_dwSampleLength-1] =
m_pWave->m_pnWave[m_dwLoopStart];
}
else
{
char *pBuffer = (char *) m_pWave->m_pnWave;
pBuffer[m_dwSampleLength-1] =
pBuffer[m_dwLoopStart];
}
}
Verify();
return (TRUE);
}
/*****************************************************************************
* CWave::CWave()
*****************************************************************************
* Constructor for this object.
*/
CWave::CWave()
{
m_hUserData = NULL;
m_lpFreeHandle = NULL;
m_pnWave = NULL;
m_dwSampleRate = 22050;
m_bSampleType = SFORMAT_16;
m_dwSampleLength = 0;
m_wUsageCount = 0;
m_dwID = 0;
m_wPlayCount = 0;
m_pWaveMem = NULL;
}
/*****************************************************************************
* CWave::~CWave()
*****************************************************************************
* Destructor for this object.
*/
CWave::~CWave()
{
if (m_pWaveMem)
{
if (m_lpFreeHandle)
{
m_lpFreeHandle((HANDLE) this,m_hUserData);
}
else
{
delete m_pWaveMem;
}
m_pWaveMem = NULL;
}
m_pnWave = NULL;
}
/*****************************************************************************
* CWave::Verify()
*****************************************************************************
* Sanity check on the object.
*/
void CWave::Verify()
{
FORCEBOUNDS(m_dwSampleRate,3000,80000);
}
/*****************************************************************************
* CWave::PlayOn()
*****************************************************************************
* Increment the play count.
*/
void CWave::PlayOn()
{
m_wPlayCount++;
AddRef();
}
/*****************************************************************************
* CWave::PlayOff()
*****************************************************************************
* Decrement the play count.
*/
void CWave::PlayOff()
{
m_wPlayCount--;
Release();
}
/*****************************************************************************
* CWave::IsPlaying()
*****************************************************************************
* Return whether the wave is currently playing.
*/
BOOL CWave::IsPlaying()
{
return (m_wPlayCount);
}
/*****************************************************************************
* CWave::AddRef()
*****************************************************************************
* Implementation of standard COM interface.
*/
void CWave::AddRef()
{
m_wUsageCount++;
}
/*****************************************************************************
* CWave::Release()
*****************************************************************************
* Implementation of standard COM interface.
*/
void CWave::Release()
{
m_wUsageCount--;
if (m_wUsageCount == 0)
{
delete this;
}
}
/*****************************************************************************
* CSourceRegion::CSourceRegion()
*****************************************************************************
* Constructor for this object.
*/
CSourceRegion::CSourceRegion()
{
m_pArticulation = NULL;
m_vrAttenuation = 0;
m_prTuning = 0;
m_bKeyHigh = 127;
m_bKeyLow = 0;
m_bGroup = 0;
m_bAllowOverlap = FALSE;
}
/*****************************************************************************
* CSourceRegion::~CSourceRegion()
*****************************************************************************
* Destructor for this object.
*/
CSourceRegion::~CSourceRegion()
{
if (m_pArticulation)
{
m_pArticulation->Release();
}
}
/*****************************************************************************
* CSourceRegion::SetSampleRate()
*****************************************************************************
* Set the sample rate for this region. Forward this to the articulation.
*/
void CSourceRegion::SetSampleRate(DWORD dwSampleRate)
{
if (m_pArticulation != NULL)
{
m_pArticulation->SetSampleRate(dwSampleRate);
}
}
/*****************************************************************************
* CSourceRegion::Verify()
*****************************************************************************
* Sanity check on the object.
*/
void CSourceRegion::Verify()
{
FORCEBOUNDS(m_bKeyHigh,0,127);
FORCEBOUNDS(m_bKeyLow,0,127);
FORCEBOUNDS(m_prTuning,-12000,12000);
FORCEBOUNDS(m_vrAttenuation,-9600,0);
m_Sample.Verify();
if (m_pArticulation != NULL)
{
m_pArticulation->Verify();
}
}
/*****************************************************************************
* CInstrument::CInstrument()
*****************************************************************************
* Constructor for this object.
*/
CInstrument::CInstrument()
{
m_dwProgram = 0;
}
/*****************************************************************************
* CInstrument::~CInstrument()
*****************************************************************************
* Destructor for this object.
*/
CInstrument::~CInstrument()
{
while (!m_RegionList.IsEmpty())
{
CSourceRegion *pRegion = m_RegionList.RemoveHead();
delete pRegion;
}
}
/*****************************************************************************
* CInstrument::Verify()
*****************************************************************************
* Sanity check on the object.
*/
void CInstrument::Verify()
{
CSourceRegion *pRegion = m_RegionList.GetHead();
CSourceArticulation *pArticulation = NULL;
for (;pRegion != NULL;pRegion = pRegion->GetNext())
{
if (pRegion->m_pArticulation != NULL)
{
pArticulation = pRegion->m_pArticulation;
}
pRegion->Verify();
}
if (pArticulation)
{
pRegion = m_RegionList.GetHead();
for (;pRegion != NULL;pRegion = pRegion->GetNext())
{
if (pRegion->m_pArticulation == NULL)
{
pRegion->m_pArticulation = pArticulation;
pArticulation->AddRef();
}
}
}
}
/*****************************************************************************
* CInstrument::SetSampleRate()
*****************************************************************************
* Set the sample rate for this instrument (forward to region).
*/
void CInstrument::SetSampleRate(DWORD dwSampleRate)
{
CSourceRegion *pRegion = m_RegionList.GetHead();
for (;pRegion;pRegion = pRegion->GetNext())
{
pRegion->SetSampleRate(dwSampleRate);
}
}
/*****************************************************************************
* CInstrument::ScanForRegion()
*****************************************************************************
* Retrieve the region with the given note value from the list.
*/
CSourceRegion * CInstrument::ScanForRegion(DWORD dwNoteValue)
{
CSourceRegion *pRegion = m_RegionList.GetHead();
for (;pRegion;pRegion = pRegion->GetNext())
{
if (dwNoteValue >= pRegion->m_bKeyLow &&
dwNoteValue <= pRegion->m_bKeyHigh)
{
break ;
}
}
return pRegion;
}
/*****************************************************************************
* CInstManager::SetSampleRate()
*****************************************************************************
* Set the sample rate for this instrument manager (forward to instruments).
*/
void CInstManager::SetSampleRate(DWORD dwSampleRate)
{
DWORD dwIndex;
m_dwSampleRate = dwSampleRate;
EnterCriticalSection(&m_CriticalSection);
for (dwIndex = 0; dwIndex < INSTRUMENT_HASH_SIZE; dwIndex++)
{
CInstrument *pInstrument = m_InstrumentList[dwIndex].GetHead();
for (;pInstrument != NULL; pInstrument = pInstrument->GetNext())
{
pInstrument->SetSampleRate(dwSampleRate);
}
}
LeaveCriticalSection(&m_CriticalSection);
}
/*****************************************************************************
* CInstManager::CInstManager()
*****************************************************************************
* Constructor for this object.
*/
CInstManager::CInstManager()
{
m_fCSInitialized = FALSE;
m_dwSampleRate = 22050;
InitializeCriticalSection(&m_CriticalSection);
m_fCSInitialized = TRUE;
}
/*****************************************************************************
* CInstManager::~CInstManager()
*****************************************************************************
* Destructor for this object.
*/
CInstManager::~CInstManager()
{
if (m_fCSInitialized)
{
DWORD dwIndex;
for (dwIndex = 0; dwIndex < INSTRUMENT_HASH_SIZE; dwIndex++)
{
while (!m_InstrumentList[dwIndex].IsEmpty())
{
CInstrument *pInstrument = m_InstrumentList[dwIndex].RemoveHead();
delete pInstrument;
}
}
for (dwIndex = 0; dwIndex < WAVE_HASH_SIZE; dwIndex++)
{
while (!m_WavePool[dwIndex].IsEmpty())
{
CWave *pWave = m_WavePool[dwIndex].RemoveHead();
pWave->Release();
}
}
while (!m_FreeWavePool.IsEmpty())
{
CWave *pWave = m_FreeWavePool.RemoveHead();
pWave->Release();
}
DeleteCriticalSection(&m_CriticalSection);
}
}
/*****************************************************************************
* CInstManager::Verify()
*****************************************************************************
* Sanity check on the object.
*/
void CInstManager::Verify()
{
DWORD dwIndex;
EnterCriticalSection(&m_CriticalSection);
for (dwIndex = 0; dwIndex < INSTRUMENT_HASH_SIZE; dwIndex++)
{
CInstrument *pInstrument = m_InstrumentList[dwIndex].GetHead();
for (;pInstrument != NULL;pInstrument = pInstrument->GetNext())
{
pInstrument->Verify();
}
}
LeaveCriticalSection(&m_CriticalSection);
}
/*****************************************************************************
* CInstManager::GetInstrument()
*****************************************************************************
* Get the instrument that matches this program/key.
*/
CInstrument * CInstManager::GetInstrument(DWORD dwProgram,DWORD dwKey)
{
EnterCriticalSection(&m_CriticalSection);
CInstrument *pInstrument = m_InstrumentList[dwProgram % INSTRUMENT_HASH_SIZE].GetHead();
for (;pInstrument != NULL; pInstrument = pInstrument->GetNext())
{
if (pInstrument->m_dwProgram == dwProgram)
{
if (pInstrument->ScanForRegion(dwKey) != NULL)
{
break;
}
else
{
Trace(1,"No region was found in instrument 0x%lx that matched note 0x%lx\n",
dwProgram, dwKey);
}
}
}
LeaveCriticalSection(&m_CriticalSection);
return (pInstrument);
}
/*****************************************************************************
* TimeCents2Samples()
*****************************************************************************
* Translate from time cents to samples.
*/
DWORD TimeCents2Samples(long tcTime, DWORD dwSampleRate)
{
if (tcTime == 0x80000000) return (0);
double flTemp = tcTime;
flTemp /= (65536 * 1200);
flTemp = pow(2.0,flTemp);
flTemp *= dwSampleRate;
return (DWORD) flTemp;
}
/*****************************************************************************
* PitchCents2PitchFract()
*****************************************************************************
* Translate from pitch cents to fractional pitch.
*/
DWORD PitchCents2PitchFract(long pcRate,DWORD dwSampleRate)
{
double fTemp = pcRate;
fTemp /= 65536;
fTemp -= 6900;
fTemp /= 1200;
fTemp = pow(2.0,fTemp);
fTemp *= 7381975040.0; // (440*256*16*4096);
fTemp /= dwSampleRate;
return (DWORD) (fTemp);
}
/*****************************************************************************
* CSourceArticulation::Download()
*****************************************************************************
* Download an articulation to this object.
*/
HRESULT
CSourceArticulation::Download(DMUS_DOWNLOADINFO * pInfo,void * pvOffsetTable[],
DWORD dwIndex, DWORD dwSampleRate,
BOOL fNewFormat)
{
// Depending on whether this is the new DX7 format, we either are reading
// a fixed set of parameters or parsing an articulation chunk directly
// copied from the DLS file. The latter is obviously more flexible and it
// turns out to make much more sense once we get to DLS2.
if (fNewFormat)
{
DMUS_ARTICULATION2 * pdmArtic =
(DMUS_ARTICULATION2 *) pvOffsetTable[dwIndex];
while (pdmArtic)
{
if (pdmArtic->ulArtIdx)
{
if (pdmArtic->ulArtIdx >= pInfo->dwNumOffsetTableEntries)
{
return DMUS_E_BADARTICULATION;
}
DWORD dwPosition;
void *pData = pvOffsetTable[pdmArtic->ulArtIdx];
CONNECTIONLIST * pConnectionList =
(CONNECTIONLIST *) pData;
CONNECTION *pConnection;
dwPosition = sizeof(CONNECTIONLIST);
for (dwIndex = 0; dwIndex < pConnectionList->cConnections; dwIndex++)
{
pConnection = (CONNECTION *) ((BYTE *)pData + dwPosition);
dwPosition += sizeof(CONNECTION);
switch (pConnection->usSource)
{
case CONN_SRC_NONE :
switch (pConnection->usDestination)
{
case CONN_DST_LFO_FREQUENCY :
m_LFO.m_pfFrequency = PitchCents2PitchFract(
pConnection->lScale,dwSampleRate);
break;
case CONN_DST_LFO_STARTDELAY :
m_LFO.m_stDelay = TimeCents2Samples(
(TCENT) pConnection->lScale,dwSampleRate);
break;
case CONN_DST_EG1_ATTACKTIME :
m_VolumeEG.m_stAttack = TimeCents2Samples(
(TCENT) pConnection->lScale,dwSampleRate);
break;
case CONN_DST_EG1_DECAYTIME :
m_VolumeEG.m_stDecay = TimeCents2Samples(
(TCENT) pConnection->lScale,dwSampleRate);
break;
case CONN_DST_EG1_SUSTAINLEVEL :
m_VolumeEG.m_pcSustain =
(SPERCENT) ((long) (pConnection->lScale >> 16));
break;
case CONN_DST_EG1_RELEASETIME :
m_VolumeEG.m_stRelease = TimeCents2Samples(
(TCENT) pConnection->lScale,dwSampleRate);
break;
case CONN_DST_EG2_ATTACKTIME :
m_PitchEG.m_stAttack = TimeCents2Samples(
(TCENT) pConnection->lScale,dwSampleRate);
break;
case CONN_DST_EG2_DECAYTIME :
m_PitchEG.m_stDecay = TimeCents2Samples(
(TCENT) pConnection->lScale,dwSampleRate);
break;
case CONN_DST_EG2_SUSTAINLEVEL :
m_PitchEG.m_pcSustain =
(SPERCENT) ((long) (pConnection->lScale >> 16));
break;
case CONN_DST_EG2_RELEASETIME :
m_PitchEG.m_stRelease = TimeCents2Samples(
(TCENT) pConnection->lScale,dwSampleRate);
break;
case CONN_DST_PAN :
m_sDefaultPan = (short)
((long) ((long) pConnection->lScale >> 12) / 125);
break;
}
break;
case CONN_SRC_LFO :
switch (pConnection->usControl)
{
case CONN_SRC_NONE :
switch (pConnection->usDestination)
{
case CONN_DST_ATTENUATION :
m_LFO.m_vrVolumeScale = (VRELS)
((long) ((pConnection->lScale * 10) >> 16));
break;
case CONN_DST_PITCH :
m_LFO.m_prPitchScale = (PRELS)
((long) (pConnection->lScale >> 16));
break;
}
break;
case CONN_SRC_CC1 :
switch (pConnection->usDestination)
{
case CONN_DST_ATTENUATION :
m_LFO.m_vrMWVolumeScale = (VRELS)
((long) ((pConnection->lScale * 10) >> 16));
break;
case CONN_DST_PITCH :
m_LFO.m_prMWPitchScale = (PRELS)
((long) (pConnection->lScale >> 16));
break;
}
break;
}
break;
case CONN_SRC_KEYONVELOCITY :
switch (pConnection->usDestination)
{
case CONN_DST_EG1_ATTACKTIME :
m_VolumeEG.m_trVelAttackScale = (TRELS)
((long) (pConnection->lScale >> 16));
break;
case CONN_DST_EG2_ATTACKTIME :
m_PitchEG.m_trVelAttackScale = (TRELS)
((long) (pConnection->lScale >> 16));
break;
case CONN_DST_ATTENUATION :
break;
}
break;
case CONN_SRC_KEYNUMBER :
switch (pConnection->usDestination)
{
case CONN_DST_EG1_DECAYTIME :
m_VolumeEG.m_trKeyDecayScale = (TRELS)
((long) (pConnection->lScale >> 16));
break;
case CONN_DST_EG2_DECAYTIME :
m_PitchEG.m_trKeyDecayScale = (TRELS)
((long) (pConnection->lScale >> 16));
break;
}
break;
case CONN_SRC_EG2 :
switch (pConnection->usDestination)
{
case CONN_DST_PITCH :
m_PitchEG.m_sScale = (short)
((long) (pConnection->lScale >> 16));
break;
}
break;
}
}
}
if (pdmArtic->ulNextArtIdx)
{
if (pdmArtic->ulNextArtIdx >= pInfo->dwNumOffsetTableEntries)
{
return DMUS_E_BADARTICULATION;
}
pdmArtic = (DMUS_ARTICULATION2 *) pvOffsetTable[pdmArtic->ulNextArtIdx];
}
else
{
pdmArtic = NULL;
}
}
}
else
{
DMUS_ARTICULATION * pdmArtic =
(DMUS_ARTICULATION *) pvOffsetTable[dwIndex];
if (pdmArtic->ulArt1Idx)
{
if (pdmArtic->ulArt1Idx >= pInfo->dwNumOffsetTableEntries)
{
return DMUS_E_BADARTICULATION;
}
DMUS_ARTICPARAMS * pdmArticParams =
(DMUS_ARTICPARAMS *) pvOffsetTable[pdmArtic->ulArt1Idx];
m_LFO.m_pfFrequency = PitchCents2PitchFract(
pdmArticParams->LFO.pcFrequency,dwSampleRate);
m_LFO.m_stDelay = TimeCents2Samples(
(TCENT) pdmArticParams->LFO.tcDelay,dwSampleRate);
m_LFO.m_vrVolumeScale = (VRELS)
((long) ((pdmArticParams->LFO.gcVolumeScale * 10) >> 16));
m_LFO.m_prPitchScale = (PRELS)
((long) (pdmArticParams->LFO.pcPitchScale >> 16));
m_LFO.m_vrMWVolumeScale = (VRELS)
((long) ((pdmArticParams->LFO.gcMWToVolume * 10) >> 16));
m_LFO.m_prMWPitchScale = (PRELS)
((long) (pdmArticParams->LFO.pcMWToPitch >> 16));
m_VolumeEG.m_stAttack = TimeCents2Samples(
(TCENT) pdmArticParams->VolEG.tcAttack,dwSampleRate);
m_VolumeEG.m_stDecay = TimeCents2Samples(
(TCENT) pdmArticParams->VolEG.tcDecay,dwSampleRate);
m_VolumeEG.m_pcSustain =
(SPERCENT) ((long) (pdmArticParams->VolEG.ptSustain >> 16));
m_VolumeEG.m_stRelease = TimeCents2Samples(
(TCENT) pdmArticParams->VolEG.tcRelease,dwSampleRate);
m_VolumeEG.m_trVelAttackScale = (TRELS)
((long) (pdmArticParams->VolEG.tcVel2Attack >> 16));
m_VolumeEG.m_trKeyDecayScale = (TRELS)
((long) (pdmArticParams->VolEG.tcKey2Decay >> 16));
m_PitchEG.m_trKeyDecayScale = (TRELS)
((long) (pdmArticParams->PitchEG.tcKey2Decay >> 16));
m_PitchEG.m_sScale = (short)
((long) (pdmArticParams->PitchEG.pcRange >> 16));
m_PitchEG.m_trVelAttackScale = (TRELS)
((long) (pdmArticParams->PitchEG.tcVel2Attack >> 16));
m_PitchEG.m_stAttack = TimeCents2Samples(
(TCENT) pdmArticParams->PitchEG.tcAttack,dwSampleRate);
m_PitchEG.m_stDecay = TimeCents2Samples(
(TCENT) pdmArticParams->PitchEG.tcDecay,dwSampleRate);
m_PitchEG.m_pcSustain =
(SPERCENT) ((long) (pdmArticParams->PitchEG.ptSustain >> 16));
m_PitchEG.m_stRelease = TimeCents2Samples(
(TCENT) pdmArticParams->PitchEG.tcRelease,dwSampleRate);
m_sDefaultPan = (short)
((long) ((long) pdmArticParams->Misc.ptDefaultPan >> 12) / 125);
}
}
Verify(); // Make sure all parameters are legal.
return S_OK;
}
/*****************************************************************************
* CSourceRegion::Download()
*****************************************************************************
* Download a region to this object.
* Parse through the region chunks, including any embedded articulation.
*/
HRESULT CSourceRegion::Download(DMUS_DOWNLOADINFO * pInfo, // DMUS_DOWNLOADINFO header struct.
void * pvOffsetTable[], // Offset table with embedded struct addresses.
DWORD *pdwRegionIX, // Region index for first region.
DWORD dwSampleRate, // Sample rate, used to convert time parameters.
BOOL fNewFormat) // DMUS_DOWNLOADINFO_INSTRUMENT2 format?
{
DMUS_REGION * pdmRegion = (DMUS_REGION *) pvOffsetTable[*pdwRegionIX];
*pdwRegionIX = pdmRegion->ulNextRegionIdx; // Clear to avoid loops.
pdmRegion->ulNextRegionIdx = 0;
// Read the Region chunk...
m_bKeyHigh = (BYTE) pdmRegion->RangeKey.usHigh;
m_bKeyLow = (BYTE) pdmRegion->RangeKey.usLow;
if (pdmRegion->fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE)
{
m_bAllowOverlap = TRUE;
}
else
{
m_bAllowOverlap = FALSE;
}
m_bGroup = (BYTE) pdmRegion->usKeyGroup;
// Now, the WSMP and WLOOP chunks...
m_vrAttenuation = (short) ((long) ((pdmRegion->WSMP.lAttenuation) * 10) >> 16);
m_Sample.m_prFineTune = pdmRegion->WSMP.sFineTune;
m_Sample.m_bMIDIRootKey = (BYTE) pdmRegion->WSMP.usUnityNote;
if (pdmRegion->WSMP.cSampleLoops == 0)
{
m_Sample.m_bOneShot = TRUE;
}
else
{
m_Sample.m_dwLoopStart = pdmRegion->WLOOP[0].ulStart;
m_Sample.m_dwLoopEnd = m_Sample.m_dwLoopStart + pdmRegion->WLOOP[0].ulLength;
m_Sample.m_bOneShot = FALSE;
}
m_Sample.m_dwSampleRate = dwSampleRate;
// Then the WAVELINK...
if (pdmRegion->WaveLink.ulChannel != WAVELINK_CHANNEL_LEFT)
{
return DMUS_E_NOTMONO;
}
m_Sample.m_dwID = (DWORD) pdmRegion->WaveLink.ulTableIndex;
// Does it have its own articulation?
if (pdmRegion->ulRegionArtIdx )
{
if (pdmRegion->ulRegionArtIdx >= pInfo->dwNumOffsetTableEntries)
{
return DMUS_E_BADARTICULATION;
}
CSourceArticulation *pArticulation = new CSourceArticulation;
if (pArticulation)
{
pArticulation->Init(dwSampleRate);
HRESULT hr = pArticulation->Download( pInfo, pvOffsetTable,
pdmRegion->ulRegionArtIdx, dwSampleRate, fNewFormat);
if (FAILED(hr))
{
delete pArticulation;
return hr;
}
m_pArticulation = pArticulation;
m_pArticulation->AddRef();
}
else
{
return E_OUTOFMEMORY;
}
}
return S_OK;
}
/*****************************************************************************
* CInstManager::DownloadInstrument()
*****************************************************************************
* Download an instrument to this instrument manager. This is dispatched
* to the various appropriate objects.
*
* This is called by Download() when an instrument chunk is encountered.
* Using the offset table to resolve linkages, it scans through the instrument,
* parsing out regions and articulations, then finally linking the regions in the
* instrument to the waves, which should have been previously downloaded.
*/
HRESULT CInstManager::DownloadInstrument(
LPHANDLE phDownload, // Pointer to download handle, to be created by synth.
DMUS_DOWNLOADINFO *pInfo, // DMUS_DOWNLOADINFO structure from the download chunk's head.
// This provides the total size of data, among other things.
void *pvOffsetTable[], // Offset table with addresses of all embedded structures.
void *pvData, // Pointer to start of download data.
BOOL fNewFormat) // Is this DMUS_DOWNLOADINFO_INSTRUMENT2 format download?
{
HRESULT hr = E_FAIL;
// The download data must start with the DMUS_INSTRUMENT chunk, so cast to that.
DMUS_INSTRUMENT *pdmInstrument = (DMUS_INSTRUMENT *) pvData;
CSourceArticulation *pArticulation = NULL;
// Create a new CInstrument structure. This stores everything that describes an instrument, including
// the articulations and regions.
CInstrument *pInstrument = new CInstrument;
if (pInstrument)
{
hr = S_OK;
// For debugging purposes, print a trace statement to show that the instrument has actually been downloaded.
// This only occurs in debug builds.
Trace(1,"Downloading instrument %lx\n",pdmInstrument->ulPatch);
pInstrument->m_dwProgram = pdmInstrument->ulPatch;
// Start by scanning through the regions.
DWORD dwRegionIX = pdmInstrument->ulFirstRegionIdx;
pdmInstrument->ulFirstRegionIdx = 0; // Clear to avoid loops.
while (dwRegionIX)
{
// For each region, verify that the index number is actually legal.
if (dwRegionIX >= pInfo->dwNumOffsetTableEntries)
{
hr = DMUS_E_BADINSTRUMENT;
goto ExitError;
}
CSourceRegion *pRegion = new CSourceRegion;
if (!pRegion)
{
hr = E_OUTOFMEMORY;
goto ExitError;
}
pInstrument->m_RegionList.AddHead(pRegion);
// Call the region's Download method to parse the region structure and optional embedded articulation.
hr = pRegion->Download(pInfo, pvOffsetTable, &dwRegionIX, m_dwSampleRate, fNewFormat);
if (FAILED(hr))
{
goto ExitError;
}
EnterCriticalSection(&m_CriticalSection);
// Once the region is parsed, we need to connect it to the wave, that should have been
// previously downloaded into the wavepool.
// Because of the hash table, the linked list is never very long, so the search is quick.
CWave *pWave = m_WavePool[pRegion->m_Sample.m_dwID % WAVE_HASH_SIZE].GetHead();
for (;pWave;pWave = pWave->GetNext())
{
// Each wave has a unique ID, which the regions match up with.
if (pRegion->m_Sample.m_dwID == pWave->m_dwID)
{
pRegion->m_Sample.m_pWave = pWave;
pWave->AddRef();
pRegion->m_Sample.CopyFromWave();
break;
}
}
LeaveCriticalSection(&m_CriticalSection);
}
// Once all the regions are loaded, see if we have a global articulation.
if (pdmInstrument->ulGlobalArtIdx)
{
// If so load it. First check that it's a valid index.
if (pdmInstrument->ulGlobalArtIdx >= pInfo->dwNumOffsetTableEntries)
{
hr = DMUS_E_BADARTICULATION;
goto ExitError;
}
// Create an articulation and have it parse the data.
pArticulation = new CSourceArticulation;
if (pArticulation)
{
// The articulation will convert all time parameters into sample times, so it needs to know the sample rate.
pArticulation->Init(m_dwSampleRate);
// Parse the articulation data. Note that the fNewFormat flag indicates whether this is in the DX6 fixed
// format articulation or the DX7 dynamic format (which is actually the same as the file format.)
hr = pArticulation->Download( pInfo, pvOffsetTable,
pdmInstrument->ulGlobalArtIdx, m_dwSampleRate, fNewFormat);
if (FAILED(hr))
{
goto ExitError;
}
// Once the global articulation is read, scan all regions and assign the articulation to all
// regions that don't have articulations yet.
for (CSourceRegion *pr = pInstrument->m_RegionList.GetHead();
pr != NULL;
pr = pr->GetNext())
{
if (pr->m_pArticulation == NULL)
{
pr->m_pArticulation = pArticulation;
pArticulation->AddRef();
}
}
if (!pArticulation->m_wUsageCount)
{
delete pArticulation;
pArticulation = NULL;
}
}
else
{
hr = E_OUTOFMEMORY;
goto ExitError;
}
}
else
{
for (CSourceRegion *pr = pInstrument->m_RegionList.GetHead();
pr != NULL;
pr = pr->GetNext())
{
if (pr->m_pArticulation == NULL)
{
hr = DMUS_E_NOARTICULATION;
goto ExitError;
}
}
}
if (SUCCEEDED(hr))
{
EnterCriticalSection(&m_CriticalSection);
// If this is a GM instrument, make sure that it will be searched for last by placing it at
// the end of the list. The DLS spec states that
// a DLS collection with the same patch as a GM instrument will always override the GM instrument.
if (pdmInstrument->ulFlags & DMUS_INSTRUMENT_GM_INSTRUMENT)
{
pInstrument->SetNext(NULL);
m_InstrumentList[pInstrument->m_dwProgram % INSTRUMENT_HASH_SIZE].AddTail(pInstrument);
}
else
{
m_InstrumentList[pInstrument->m_dwProgram % INSTRUMENT_HASH_SIZE].AddHead(pInstrument);
}
LeaveCriticalSection(&m_CriticalSection);
*phDownload = (HANDLE) pInstrument;
}
}
ExitError:
// Clean-up code.
if (FAILED(hr))
{
if (pArticulation)
{
delete pArticulation;
}
if (pInstrument)
{
delete pInstrument;
}
}
return hr;
}
/*****************************************************************************
* CInstManager::DownloadWave()
*****************************************************************************
* Download a wave to this instrument manager. It is put in the pool.
*
* This is called by Download when it receives a wave download chunk.
* DownloadWave parses the wave, converts the wave data if necessary, and
* places the wave in the wave pool, where it can subsequentley be
* connected to instruments. (All the waves referenced by an instrument
* are downloaded prior to downloading the instrument itself. This makes the
* whole process simpler and more reliable. Conversely, on unload, all
* waves are unloaded after the instruments that reference them.)
*/
HRESULT CInstManager::DownloadWave(
LPHANDLE phDownload, // Download handle, to be returned. This will be
// used by a later Unload call to reference the wave.
DMUS_DOWNLOADINFO *pInfo, // DMUS_DOWNLOADINFO structure from the download chunk's head.
// This provides the total size of data, among other things.
void *pvOffsetTable[], // The table of offsets in the download data.
void *pvData) // Finally, the data itself.
{
// The start of the data should align with a DMUS_WAVE header.
DMUS_WAVE *pdmWave = (DMUS_WAVE *) pvData;
// Make sure that the wave data is properly uncompressed PCM data.
if (pdmWave->WaveformatEx.wFormatTag != WAVE_FORMAT_PCM)
{
return DMUS_E_NOTPCM;
}
// The data can only be mono format.
if (pdmWave->WaveformatEx.nChannels != 1)
{
return DMUS_E_NOTMONO;
}
// The data can be only 8 or 16 bit.
if (pdmWave->WaveformatEx.wBitsPerSample != 8 &&
pdmWave->WaveformatEx.wBitsPerSample != 16)
{
return DMUS_E_BADWAVE;
}
// Ensure that the index to the wave data is a legal value in the offset table.
if (pdmWave->ulWaveDataIdx >= pInfo->dwNumOffsetTableEntries)
{
return DMUS_E_BADWAVE;
}
// Create a wave object and parse the data into it.
CWave *pWave = new CWave;
if (pWave)
{
// We've already verified that the wave data index is a valid index, so go ahead
// and use the offset table to convert that into a valid DMUS_WAVEDATA structure pointer.
DMUS_WAVEDATA *pdmWaveData= (DMUS_WAVEDATA *)
pvOffsetTable[pdmWave->ulWaveDataIdx];
Trace(3,"Downloading wave %ld\n",pInfo->dwDLId);
// Now initialize the CWave structure.
pWave->m_dwID = pInfo->dwDLId;
pWave->m_pWaveMem = pInfo;
pWave->m_hUserData = NULL;
pWave->m_lpFreeHandle = NULL;
pWave->m_dwSampleLength = pdmWaveData->cbSize;
pWave->m_pnWave = (short *) &pdmWaveData->byData[0];
pWave->m_dwSampleRate = pdmWave->WaveformatEx.nSamplesPerSec;
// If the wave data is 8 bit, the data needs to be converted to
// two's complement representation.
if (pdmWave->WaveformatEx.wBitsPerSample == 8)
{
pWave->m_bSampleType = SFORMAT_8;
DWORD dwX;
char *pData = (char *) &pdmWaveData->byData[0];
for (dwX = 0; dwX < pWave->m_dwSampleLength; dwX++)
{
pData[dwX] -= (char) 128;
}
}
else if (pdmWave->WaveformatEx.wBitsPerSample == 16)
{
pWave->m_dwSampleLength >>= 1;
pWave->m_bSampleType = SFORMAT_16;
}
pWave->m_dwSampleLength++; // We always add one sample to the end for interpolation.
EnterCriticalSection(&m_CriticalSection);
// Place the wave in a hash table of wave lists to increase access speed.
m_WavePool[pWave->m_dwID % WAVE_HASH_SIZE].AddHead(pWave);
LeaveCriticalSection(&m_CriticalSection);
// Return the pointer to the internal CWave object as the handle. This will
// be used in a subsequant call to unload the wave object.
*phDownload = (HANDLE) pWave;
pWave->AddRef();
return S_OK;
}
return E_OUTOFMEMORY;
}
/*****************************************************************************
* CInstManager::Download()
*****************************************************************************
* Download to this instrument manager.
*
* This is the heart of the DLS download mechanism, and is called from CSynth::Download().
* It verifies the offset table and converts it into physical addresses. Then,
* depending on whether this is a wave or instrument download, it calls the
* appropriate method.
*
* The data is stored in a continuous chunk of memory,
* pointed to by pvData. However, at the head of the chunk are two data
* structures, which define the nature of the data to follow. These are
* the DMUS_DOWNLOADINFO and DMUS_OFFSETTABLE structures. DMUS_DOWNLOADINFO
* is a header which describes how to parse the data, including
* its size and intention (wave or instrument.) DMUS_OFFSETTABLE provides
* a set of indexes into the data segment which follows. All parsing through the data
* is managed through this table. Whenever a structure in the data references
* another structure, it describes it by an index into the offset table.
* The offset table then converts it into a physical address in the memory.
* This allows the synthesizer to do bounds checking on all
* references, making the implementation more robust. In kernel mode
* implementations, the driver can make its own private copy of the offset
* table, and so ensure that an application in user mode can not mess with
* its referencing and cause a crash. This implementation also makes a unique copy.
*
* Looking closer at DMUS_DOWNLOADINFO, DMUS_DOWNLOADINFO.dwDLType
* determines the type of data being downloaded. It is set to
* DMUS_DOWNLOADINFO_INSTRUMENT or DMUS_DOWNLOADINFO_INSTRUMENT2
* for an instrument, DMUS_DOWNLOADINFO_WAVE for a wave. As new data types emerge,
* identifiers will be allocated for them.
* DMUS_DOWNLOADINFO.dwDLId holds a unique 32 bit identifier for the object.
* This identifier is used to connect objects together. For example, it is used
* to connect waves to instruments.
* DMUS_DOWNLOADINFO.dwNumOffsetTableEntries describes the number of entries in
* the DMUS_OFFSETTABLE structure, which follows.
* Finally, DMUS_DOWNLOADINFO.cbSizeData states the total size of the
* memory chunk, which follows the offset table.
*
* Depending on the synthesizer implementation, it may decide to use the memory
* in the download chunk. This reduces memory allocation and freeing, since, if enough
* memory has been allocated to store a wave, that same memory can be used by
* the synthesizer to store it for playback. So, the synthesizer has the option
* of hanging on to the memory, returning its decision in the pbFree parameter.
* If it does keep the memory, then the caller must not free it. Later, the
* CSynth::Unload command has a callback mechanism to handle asynchronous
* freeing of the memory once the unload request has been made.
*/
HRESULT CInstManager::Download(LPHANDLE phDownload, // Download handle, to be returned.
void * pvData, // Pointer to download data chunk.
LPBOOL pbFree) // Pointer to boolean whether data can
// be freed now or held until unload.
{
V_INAME(IDirectMusicSynth::Download);
V_BUFPTR_READ(pvData,sizeof(DMUS_DOWNLOADINFO));
HRESULT hr = DMUS_E_UNKNOWNDOWNLOAD;
// We need an array of pointers to reproduce the offset table, which is used to link to
// specific structures in the download chunk.
void ** ppvOffsetTable; // Array of pointers to chunks in data.
// At the head of the download chunk is the download header, in the form of a DMUS_DOWNLOADINFO structure.
DMUS_DOWNLOADINFO * pInfo = (DMUS_DOWNLOADINFO *) pvData;
// It is immediately followed by the offset table, so we cast a pointer to that.
DMUS_OFFSETTABLE* pOffsetTable = (DMUS_OFFSETTABLE *)(((BYTE*)pvData) + sizeof(DMUS_DOWNLOADINFO));
char *pcData = (char *) pvData;
V_BUFPTR_READ(pvData,pInfo->cbSize);
// Return the error code immediately.
if (0 == pInfo->dwNumOffsetTableEntries)
{
return hr;
}
// Create a copy of the offset table.
ppvOffsetTable = new void *[pInfo->dwNumOffsetTableEntries];
if (ppvOffsetTable) // Create the pointer array and validate.
{
// Each index in the offset table is an offset from the beginning of the download chunk to
// a position in the memory where a specific structure resides.
// Scan through the table and convert these offsets into actual memory
// addresses, and store these in the ppvOfsetTable array. These
// will be used by the parsing code to resolve indexes to actual
// memory addresses.
DWORD dwIndex;
for (dwIndex = 0; dwIndex < pInfo->dwNumOffsetTableEntries; dwIndex++)
{
// First, make sure the offset is not out of bounds.
if (pOffsetTable->ulOffsetTable[dwIndex] >= pInfo->cbSize)
{
delete [] ppvOffsetTable;
return DMUS_E_BADOFFSETTABLE; // Bad!
}
// Go ahead and calculate the actual memory address and store it.
ppvOffsetTable[dwIndex] = (void *) &pcData[pOffsetTable->ulOffsetTable[dwIndex]];
}
// Once the offset table is constructed, we can pass it to the appropriate parsing routine.
// There are three types of download chunks: DMUS_DOWNLOADINFO_INSTRUMENT,
// DMUS_DOWNLOADINFO_INSTRUMENT2, and DMUS_DOWNLOADINFO_WAVE.
// The two instrument formats exist because DMUS_DOWNLOADINFO_INSTRUMENT was changed to
// support a variable articulation to support DLS2 in the DX7 timeframe. In truth,
// only DMUS_DOWNLOADINFO_INSTRUMENT2 and DMUS_DOWNLOADINFO_WAVE need be supported,
// but we continue with DMUS_DOWNLOADINFO_INSTRUMENT support in this example.
// Depending on the type of download, we call the appropriate method, which then
// parses the data.
// To let dmusic understand that it does support the DMUS_DOWNLOADINFO_INSTRUMENT2,
// the synth must respond positively to the KsProperty Query GUID_DMUS_PROP_INSTRUMENT2,
// request (please see CUserModeSynth::KsProperty() for implementation details.)
if (pInfo->dwDLType == DMUS_DOWNLOADINFO_INSTRUMENT) // Instrument.
{
// Instrument does not need to keep the download chunk allocated, so indicate that
// the caller can free it.
*pbFree = TRUE;
// Call the download instrument method, indicating that this is a DX6 format download.
hr = DownloadInstrument(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0],FALSE);
}
if (pInfo->dwDLType == DMUS_DOWNLOADINFO_INSTRUMENT2) // New instrument format.
{
*pbFree = TRUE;
// Call the download instrument method, indicating that this is a DX7 or up format download.
hr = DownloadInstrument(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0],TRUE);
}
else if (pInfo->dwDLType == DMUS_DOWNLOADINFO_WAVE) // Wave.
{
// Wave does need to keep the download chunk allocated, because it includes the
// wave data buffer, which it will play directly out of, so indicate that
// the caller must not free it until it is unloaded.
*pbFree = FALSE;
hr = DownloadWave(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0]);
}
delete [] ppvOffsetTable;
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}
/*****************************************************************************
* CInstManager::Unload()
*****************************************************************************
* Unload the given previous download. Delete the instruments and waves.
*
* The unload method is called when it is unloading a previously downloaded
* instrument or wave chunk. This instructs the synthesizer to find the object that
* was downloaded (identified by the handle, hDownload, that was generated by the
* call to Download) and remove it.
*
* If the object was using the original download chunk, it needs to notify the caller
* when it is done using it so the memory can then be freed. This is not necessarily
* at the time of the download call because wave data may currently be in use by a
* performing note. So, a pointer to a callback function is also provided and the
* synthesizer must call this function at the time the memory is no longer in use.
*/
HRESULT CInstManager::Unload(
HANDLE hDownload, // Handle of previously downloaded
// wave or instrument.
HRESULT ( CALLBACK *lpFreeHandle)(HANDLE,HANDLE), // Callback function for releasing
// downloaded memory
HANDLE hUserData) // Parameter to pass to callback,
// to indicate which download is freed.
{
DWORD dwIndex;
EnterCriticalSection(&m_CriticalSection);
// First, check to see if this is an instrument.
// We keep all the instruments in a hash table to speed up access.
for (dwIndex = 0; dwIndex < INSTRUMENT_HASH_SIZE; dwIndex++)
{
CInstrument *pInstrument = m_InstrumentList[dwIndex].GetHead();
for (;pInstrument != NULL; pInstrument = pInstrument->GetNext())
{
// If the instrument matches the download handle, remove it from the list
// and delete it. There is no need to callback for releasing the memory because
// the synthesizer did not hang on to the original downloaded instrument memory.
if (pInstrument == (CInstrument *) hDownload)
{
// To help debug, print the patch number of the unloaded instrument.
Trace(1,"Unloading instrument %lx\n",pInstrument->m_dwProgram);
m_InstrumentList[dwIndex].Remove(pInstrument);
delete pInstrument;
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
}
}
// If it wasn't an instrument, try the wave list.
// Again, they are arranged in a hash table to increase access speed.
for (dwIndex = 0; dwIndex < WAVE_HASH_SIZE; dwIndex++)
{
CWave *pWave = m_WavePool[dwIndex].GetHead();
for (;pWave != NULL;pWave = pWave->GetNext())
{
// If the wave matches the download handle, remove it from the list.
// Also, store the callback function, lpFreeHandle, and associated instance
// parameter, hUserData, in the wave. Remove the wave from the wave pool, so
// it can no longer be connected with another instrument.
//
// When the last instance of a voice that is playing the wave finishes,
// lpFreeHandle will be called and the caller will be able to free the memory.
// Usually, the wave is not currently being played and this happens instantly.
if (pWave == (CWave *) hDownload)
{
Trace(3,"Unloading wave %ld\n",pWave->m_dwID);
m_WavePool[dwIndex].Remove(pWave);
pWave->m_hUserData = hUserData;
pWave->m_lpFreeHandle = lpFreeHandle;
pWave->Release();
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
}
}
LeaveCriticalSection(&m_CriticalSection);
return E_FAIL;
}