Sample Code

Windows Driver Samples/ DirectMusic Software Synthesizer Sample/ C++/ instr.cpp/

//      Copyright (c) 1996-2000 Microsoft Corporation.  All rights reserved.
//      Instrument.cpp

#include "common.h"

#define STR_MODULENAME "DDKSynth.sys:Instr: "

#include "math.h"

#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)
{
    KFLOATING_SAVE FloatSave; 
    DWORD flTempcopy = 0;
    if (STATUS_SUCCESS == KeSaveFloatingPointState(&FloatSave))
    {
        double flTemp;
        if (tcTime ==  0x80000000) 
        {
            KeRestoreFloatingPointState(&FloatSave);
            return (0);
        }
        flTemp = tcTime;
        flTemp /= (65536 * 1200);
        flTemp = pow(2.0,flTemp);
        flTemp *= dwSampleRate;
        flTempcopy = (DWORD) flTemp;
        KeRestoreFloatingPointState(&FloatSave);
    }
    return flTempcopy;
}

/*****************************************************************************
 * PitchCents2PitchFract()
 *****************************************************************************
 * Translate from pitch cents to fractional pitch.
 */
DWORD PitchCents2PitchFract(long pcRate,DWORD dwSampleRate)
{
    KFLOATING_SAVE FloatSave;
    DWORD fTempcopy = 0;
    if (STATUS_SUCCESS == KeSaveFloatingPointState(&FloatSave))
    {
        double fTemp;
        fTemp = pcRate;
        fTemp /= 65536;
        fTemp -= 6900;
        fTemp /= 1200;
        fTemp = pow(2.0,fTemp);
        fTemp *= 7381975040.0;  // (440*256*16*4096);
        fTemp /= dwSampleRate;
        fTempcopy = (DWORD) (fTemp);
        KeRestoreFloatingPointState(&FloatSave);
    }
    return fTempcopy;    
}

/*****************************************************************************
 * 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)((int)pData[dwX] - 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;
}

Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS