//
// Copyright (c) 1996-2001 Microsoft Corporation
// Instrument.cpp
//

#ifdef DMSYNTH_MINIPORT
#include "common.h"
#else
#include "simple.h"
#include <mmsystem.h>
#include <dmerror.h>
#include "synth.h"
#include "math.h"
#include "debug.h"
// @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample.  See ddkreadme.txt for more info.
#include "..\shared\validate.h"
#if 0 // The following section will only take affect in the DDK sample.
// @@END_DDKSPLIT
#include "validate.h"
// @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample.
#endif
// @@END_DDKSPLIT
#endif

void MemDump(char * prompt);

//#include <windowsx.h>

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;
    m_prCPPitchScale = 0;
    m_vrCPVolumeScale = 0;
    m_prCutoffScale = 0;
    m_prMWCutoffScale = 0;
    m_prCPCutoffScale = 0;
}

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;
    m_prCPPitchScale = 0;
    m_vrCPVolumeScale = 0;
    m_prCutoffScale = 0;
    m_prMWCutoffScale = 0;
    m_prCPCutoffScale = 0;
}

void CSourceLFO::SetSampleRate(long lChange)

{
    if (lChange > 0)
    {
        m_stDelay <<= lChange;
        m_pfFrequency <<= lChange;
    }
    else
    {
        m_stDelay >>= -lChange;
        m_pfFrequency >>= -lChange;
    }
}

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);
    FORCEBOUNDS(m_prCPPitchScale,-1200,1200);
    FORCEBOUNDS(m_vrCPVolumeScale,-1200,1200);
    FORCEBOUNDS(m_prCutoffScale, -12800, 12800);
    FORCEBOUNDS(m_prMWCutoffScale, -12800, 12800);
    FORCEBOUNDS(m_prCPCutoffScale, -12800, 12800);
}

CSourceEG::CSourceEG()

{
    Init();
}

void CSourceEG::Init()

{
    m_stAttack = 0;
    m_stDecay = 0;
    m_pcSustain = 1000;
    m_stRelease = 0;
    m_trVelAttackScale = 0;
    m_trKeyDecayScale = 0;
    m_sScale = 0;
    m_stDelay = 0;
    m_stHold = 0;
    m_prCutoffScale = 0;
}

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;
    }
}

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);
    FORCEBOUNDS(m_trKeyHoldScale,-12000,12000);
    FORCEBOUNDS(m_prCutoffScale,-12800,12800);
}

CSourceFilter::CSourceFilter()
{
    Init(22050);
}

void CSourceFilter::Init(DWORD dwSampleRate)
{
    // First, calculate the playback samplerate in pitch rels.
    // The reference frequency is a440, which is midi note 69.
    // So, calculate the ratio of the sample rate to 440 and
    // convert into prels (1200 per octave), then add the
    // offset of 6900.
    double fSampleRate = (double)dwSampleRate;

    fSampleRate /= 440.0;
    fSampleRate = log(fSampleRate) / log(2.0);
    fSampleRate *= 1200.0;
    fSampleRate += 6900.0;
    m_prSampleRate = (PRELS)fSampleRate;

    m_prCutoff = (PRELS)0x7FFF;
    m_vrQ = (VRELS)0;
    m_prVelScale = (PRELS)0;
    m_prCutoffSRAdjust = 0;
    m_iQIndex = 0;
}

void CSourceFilter::SetSampleRate(LONG lChange)
{
    // lChange ==  1 -> doubles -> add 1200 cents
    // lChange ==  2 -> quad    -> add 2400 cents
    // lChange == -1 -> halves  -> sub 1200 cents
    // lChange == -2 -> 1/4ths  -> sub 2400 cents
    //
    if (lChange > 0)
    {
        m_prSampleRate += (1200 << (lChange - 1));
    }
    else
    {
        m_prSampleRate -= (1200 << ((-lChange) - 1));
    }

    m_prCutoffSRAdjust = FILTER_FREQ_RANGE - m_prSampleRate + m_prCutoff;
}

void CSourceFilter::Verify()
{
    if ( m_prCutoff == 0x7FFF )
    {
        m_vrQ = 0;
        m_prVelScale = 0;
    }
    else
    {
        FORCEBOUNDS(m_prCutoff, 5535, 11921);
        FORCEBOUNDS(m_vrQ, 0, 225);
        FORCEBOUNDS(m_prVelScale, -12800, 12800);
    }
}

CSourceArticulation::CSourceArticulation()

{
//    m_sVelToVolScale = -9600;
    m_wUsageCount = 0;
    m_sDefaultPan = 0;
    m_dwSampleRate = 22050;
    m_PitchEG.m_sScale = 0; // pitch envelope defaults to off
}

void CSourceArticulation::Init(DWORD dwSampleRate)

{
    m_dwSampleRate = dwSampleRate;
    m_LFO.Init(dwSampleRate);       // Set to default values.
    m_PitchEG.Init();
    m_VolumeEG.Init();
    m_LFO2.Init(dwSampleRate);
    m_Filter.Init(dwSampleRate);
}

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);
        m_LFO2.SetSampleRate(lChange);
        m_Filter.SetSampleRate(lChange);
    }
}

void CSourceArticulation::Verify()

{
    m_LFO.Verify();
    m_PitchEG.Verify();
    m_VolumeEG.Verify();
    m_LFO2.Verify();
    m_Filter.Verify();
}

void CSourceArticulation::AddRef()

{
    m_wUsageCount++;
}

void CSourceArticulation::Release()

{
    m_wUsageCount--;
    if (m_wUsageCount == 0)
    {
        delete this;
    }
}

CSourceSample::CSourceSample()

{
    m_pWave = NULL;
    m_dwLoopStart = 0;
    m_dwLoopEnd = 1;
    m_dwLoopType = WLOOP_TYPE_FORWARD;
    m_dwSampleLength = 0;
    m_prFineTune = 0;
    m_dwSampleRate = 22050;
    m_bMIDIRootKey = 60;
    m_bOneShot = TRUE;
    m_bSampleType = 0;
}

CSourceSample::~CSourceSample()

{
    if (m_pWave != NULL)
    {
        m_pWave->Release();
    }
}

void CSourceSample::Verify()

{
    if (m_pWave != NULL)
    {
        FORCEBOUNDS(m_dwSampleLength,0,m_pWave->m_dwSampleLength);
        FORCEBOUNDS(m_dwLoopEnd,1,m_dwSampleLength);
        FORCEBOUNDS(m_dwLoopStart,0,m_dwLoopEnd);
        if ((m_dwLoopEnd - m_dwLoopStart) < 6)
        {
            m_bOneShot = TRUE;
        }
    }
    FORCEBOUNDS(m_dwSampleRate,3000,200000);
    FORCEBOUNDS(m_bMIDIRootKey,0,127);
    FORCEBOUNDS(m_prFineTune,-1200,1200);
}

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()
{
    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_bStream = FALSE;
    m_bActive = FALSE;
    m_bLastSampleInit = FALSE;
    m_bValid = FALSE;
}

CWave::~CWave()

{
    if (m_pnWave && m_lpFreeHandle)
    {
        m_lpFreeHandle((HANDLE) this,m_hUserData);
    }
}

void CWave::Verify()

{
    FORCEBOUNDS(m_dwSampleRate,3000,200000);
}

void CWave::PlayOn()

{
    m_wPlayCount++;
    AddRef();
}

void CWave::PlayOff()

{
    m_wPlayCount--;
    Release();
}

BOOL CWave::IsPlaying()

{
    return (m_wPlayCount);
}

void CWave::AddRef()

{
    m_wUsageCount++;
}

void CWave::Release()

{
    m_wUsageCount--;
    if (m_wUsageCount == 0)
    {
        delete this;
    }
}

CWaveArt::CWaveArt()
{
    m_wUsageCount = 1;
    m_dwID = 0;
    m_bSampleType = 0;
    m_bStream = FALSE;
    memset(&m_WaveArtDl,0,sizeof(DMUS_WAVEARTDL));
    memset(&m_WaveformatEx,0,sizeof(WAVEFORMATEX));
}

CWaveArt::~CWaveArt()
{
//>>>>>>>>>> clear list
    while(!m_pWaves.IsEmpty())
    {
        CWaveBuffer* pWaveBuffer = m_pWaves.RemoveHead();
        if(pWaveBuffer)
        {
            pWaveBuffer->m_pWave->Release();
            pWaveBuffer->m_pWave = NULL;
            delete pWaveBuffer;
        }
    }
}

void CWaveArt::AddRef()
{
    m_wUsageCount++;
}

void CWaveArt::Release()
{
    m_wUsageCount--;
    if (m_wUsageCount == 0)
    {
        delete this;
    }
}

void CWaveArt::Verify()
{
}

CSourceRegion::CSourceRegion()
{
    m_pArticulation = NULL;
    m_vrAttenuation = 0;
    m_prTuning = 0;
    m_bKeyHigh = 127;
    m_bKeyLow = 0;
    m_bGroup = 0;
    m_bAllowOverlap = FALSE;
    m_bVelocityHigh = 127;
    m_bVelocityLow  = 0;
    m_dwChannel = 0;
    m_sWaveLinkOptions = 0;
}

CSourceRegion::~CSourceRegion()
{
    if (m_pArticulation)
    {
        m_pArticulation->Release();
    }
}

void CSourceRegion::SetSampleRate(DWORD dwSampleRate)

{
    if (m_pArticulation != NULL)
    {
        m_pArticulation->SetSampleRate(dwSampleRate);
    }
}

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()
{
    m_dwProgram = 0;
}

CInstrument::~CInstrument()
{
    while (!m_RegionList.IsEmpty())
    {
        CSourceRegion *pRegion = m_RegionList.RemoveHead();
        delete pRegion;
    }
}

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();
    }
    pRegion = m_RegionList.GetHead();
    for (;pRegion != NULL;pRegion = pRegion->GetNext())
    {
        if (pRegion->m_pArticulation == NULL  && pArticulation)
        {
            pRegion->m_pArticulation = pArticulation;
            pArticulation->AddRef();
        }
    }
}

void CInstrument::SetSampleRate(DWORD dwSampleRate)

{
    CSourceRegion *pRegion = m_RegionList.GetHead();
    for (;pRegion;pRegion = pRegion->GetNext())
    {
        pRegion->SetSampleRate(dwSampleRate);
    }
}

CSourceRegion * CInstrument::ScanForRegion(DWORD dwNoteValue, DWORD dwVelocity, CSourceRegion *pRegion)

{
    if ( pRegion == NULL )
        pRegion = m_RegionList.GetHead(); // Starting search
    else
        pRegion = pRegion->GetNext();     // Continuing search through the rest of the regions

    for (;pRegion;pRegion = pRegion->GetNext())
    {
        if (dwNoteValue >= pRegion->m_bKeyLow  &&
            dwNoteValue <= pRegion->m_bKeyHigh &&
            dwVelocity  >= pRegion->m_bVelocityLow &&
            dwVelocity  <= pRegion->m_bVelocityHigh )
        {
            break ;
        }
    }
    return pRegion;
}

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()

{
    m_dwSampleRate = 22050;
    m_fCSInitialized = FALSE;
    InitializeCriticalSection(&m_CriticalSection);
    // Note: on pre-Blackcomb OS's, this call can raise an exception; if it
    // ever pops in stress, we can add an exception handler and retry loop.
    m_fCSInitialized = TRUE;
    m_dwSynthMemUse = 0;
}

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();
        }

        for(int nCount = 0; nCount < WAVEART_HASH_SIZE; nCount++)
        {
            while(!m_WaveArtList[nCount].IsEmpty())
            {
                CWaveArt* pWaveArt = m_WaveArtList[nCount].RemoveHead();
                if(pWaveArt)
                {
                    pWaveArt->Release();
                }
            }
        }

        DeleteCriticalSection(&m_CriticalSection);
    }
}

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

CInstrument * CInstManager::GetInstrument(DWORD dwProgram, DWORD dwKey, DWORD dwVelocity)

{
    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, dwVelocity, NULL) != NULL)
            {
                break;
            }
            else
            {
                Trace(2,"Warning: No region was found in instrument # %lx that matched note %ld\n",
                    dwProgram,dwKey);
            }
        }
    }
    LeaveCriticalSection(&m_CriticalSection);
    return (pInstrument);
}


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;
}

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

HRESULT CSourceArticulation::Download(DMUS_DOWNLOADINFO * pInfo,
                                void * pvOffsetTable[],
                                DWORD dwIndex,
                                DWORD dwSampleRate,
                                BOOL fNewFormat)
{
    if (fNewFormat)
    {
        DMUS_ARTICULATION2 * pdmArtic =
            (DMUS_ARTICULATION2 *) pvOffsetTable[dwIndex];
        while (pdmArtic)
        {
            if (pdmArtic->ulArtIdx)
            {
                if (pdmArtic->ulArtIdx >= pInfo->dwNumOffsetTableEntries)
                {
                    Trace(1,"Error: Download failed because articulation chunk has an error.\n");
                    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;

                        /* DLS2 */
                        case CONN_DST_EG1_DELAYTIME:
                            m_VolumeEG.m_stDelay = TimeCents2Samples(
                                (TCENT) pConnection->lScale,dwSampleRate);
                            break;
                        case CONN_DST_EG1_HOLDTIME:
                            m_VolumeEG.m_stHold  = TimeCents2Samples(
                                (TCENT) pConnection->lScale,dwSampleRate);
                            break;
                        case CONN_DST_EG2_DELAYTIME:
                            m_PitchEG.m_stDelay  = TimeCents2Samples(
                                (TCENT) pConnection->lScale,dwSampleRate);
                            break;
                        case CONN_DST_EG2_HOLDTIME:
                            m_PitchEG.m_stHold   = TimeCents2Samples(
                                (TCENT) pConnection->lScale,dwSampleRate);
                            break;
                        case CONN_DST_VIB_FREQUENCY :
                            m_LFO2.m_pfFrequency = PitchCents2PitchFract(
                                pConnection->lScale,dwSampleRate);
                            break;
                        case CONN_DST_VIB_STARTDELAY :
                            m_LFO2.m_stDelay = TimeCents2Samples(
                                (TCENT) pConnection->lScale,dwSampleRate);
                            break;
                        case CONN_DST_FILTER_CUTOFF:
                            // First, get the filter cutoff frequency, which is relative to a440.
                            m_Filter.m_prCutoff = (PRELS)
                                (pConnection->lScale >> 16);
                            // Then, calculate the resulting prel, taking into consideration
                            // the sample rate and the base of the filter coefficient lookup
                            // table, relative to the sample rate (FILTER_FREQ_RANGE).
                            // This number can then be used directly look up the coefficients in the
                            // filter table.
                            m_Filter.m_prCutoffSRAdjust = (PRELS)
                                FILTER_FREQ_RANGE - m_Filter.m_prSampleRate + m_Filter.m_prCutoff;
                            break;
                        case CONN_DST_FILTER_Q:
                            m_Filter.m_vrQ = (VRELS)
                                (pConnection->lScale >> 16); //>>>>>>>> not really VRELS, but 1/10th's
                            m_Filter.m_iQIndex = (DWORD)
                                ((m_Filter.m_vrQ / 15.0f) + 0.5f);
                            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;

                            /* DLS2 */
                            case CONN_DST_FILTER_CUTOFF:
                                m_LFO.m_prCutoffScale = (PRELS)
                                    (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;

                            /* DLS2 */
                            case CONN_DST_FILTER_CUTOFF:
                                m_LFO.m_prMWCutoffScale = (PRELS)
                                    ((long) (pConnection->lScale >> 16));
                                break;
                            }
                            break;

                        /* DLS2 */
                        case CONN_SRC_CHANNELPRESSURE :
                            switch (pConnection->usDestination)
                            {
                            case CONN_DST_ATTENUATION :
                                m_LFO.m_vrCPVolumeScale = (VRELS)
                                    ((long) (pConnection->lScale >> 16));
                                break;
                            case CONN_DST_PITCH :
                                m_LFO.m_prCPPitchScale  = (PRELS)
                                    ((long) (pConnection->lScale >> 16));
                                break;

                            /* DLS2 */
                            case CONN_DST_FILTER_CUTOFF:
                                m_LFO.m_prCPCutoffScale = (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;

                        /* DLS2 */
                        case CONN_DST_FILTER_CUTOFF:
                            m_Filter.m_prVelScale = (PRELS)
                                ((long) (pConnection->lScale >> 16));
                            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;

                        /* DLS2 */
                        case CONN_DST_EG1_HOLDTIME :
                            m_PitchEG.m_trKeyDecayScale = (TRELS)
                                ((long) (pConnection->lScale >> 16));
                            break;
                        case CONN_DST_EG2_HOLDTIME :
                            m_PitchEG.m_trKeyDecayScale = (TRELS)
                                ((long) (pConnection->lScale >> 16));
                        case CONN_DST_FILTER_CUTOFF :
                            m_Filter.m_prKeyScale = (PRELS)
                                ((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;

                        /* DLS2 */
                        case CONN_DST_FILTER_CUTOFF:
                            m_PitchEG.m_prCutoffScale = (PRELS)
                                ((long) (pConnection->lScale >> 16));
                            break;
                        }
                        break;

                    /* DLS2 */
                    case CONN_SRC_VIBRATO :
                        switch (pConnection->usControl)
                        {
                        case CONN_SRC_NONE :
                            switch (pConnection->usDestination)
                            {
                            case CONN_DST_PITCH :
                                m_LFO2.m_prPitchScale = (PRELS)
                                    ((long) (pConnection->lScale >> 16));
                                break;
                            }
                            break;
                        case CONN_SRC_CC1 :
                            switch (pConnection->usDestination)
                            {
                            case CONN_DST_PITCH :
                                m_LFO2.m_prMWPitchScale = (PRELS)
                                    ((long) (pConnection->lScale >> 16));
                                break;
                            }
                            break;
                        case CONN_SRC_CHANNELPRESSURE :
                            switch (pConnection->usDestination)
                            {
                            case CONN_DST_PITCH :
                                m_LFO2.m_prCPPitchScale  = (PRELS)
                                    ((long) (pConnection->lScale >> 16));
                                break;
                            }
                            break;
                        }
                        break;
                    }
                }
            }
            if (pdmArtic->ulNextArtIdx)
            {
                if (pdmArtic->ulNextArtIdx >= pInfo->dwNumOffsetTableEntries)
                {
                    Trace(1,"Error: Download failed because articulation chunk has an error.\n");
                    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)
            {
                Trace(1,"Error: Download failed because articulation chunk has an error.\n");
                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;
}

HRESULT CSourceRegion::Download(DMUS_DOWNLOADINFO * pInfo,
                                void * pvOffsetTable[],
                                DWORD *pdwRegionIX,
                                DWORD dwSampleRate,
                                BOOL fNewFormat)
{
    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;
    m_bVelocityHigh = (BYTE) pdmRegion->RangeVelocity.usHigh;
    m_bVelocityLow  = (BYTE) pdmRegion->RangeVelocity.usLow;

    //
    // Fix DLS Designer bug
    // Designer was putting velocity ranges that fail
    // on DLS2 synths
    //
    if ( m_bVelocityHigh == 0 && m_bVelocityLow == 0 )
        m_bVelocityHigh = 127;

    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_dwLoopType = pdmRegion->WLOOP[0].ulType;
    }
    m_Sample.m_dwSampleRate = dwSampleRate;

    m_sWaveLinkOptions = pdmRegion->WaveLink.fusOptions;
    m_dwChannel = pdmRegion->WaveLink.ulChannel;

    if ( (m_dwChannel != WAVELINK_CHANNEL_LEFT) && !IsMultiChannel() )
    {
        Trace(1, "Download failed: Attempt to use a non-mono channel without setting the multichannel flag.\n");
        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)
        {
            Trace(1,"Error: Download failed because articulation chunk has an error.\n");
            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;
}



HRESULT CInstManager::DownloadInstrument(LPHANDLE phDownload,
                                         DMUS_DOWNLOADINFO *pInfo,
                                         void *pvOffsetTable[],
                                         void *pvData,
                                         BOOL fNewFormat)

{
    DMUS_INSTRUMENT *pdmInstrument = (DMUS_INSTRUMENT *) pvData;
    CInstrument *pInstrument = new CInstrument;
    if (pInstrument)
    {
        Trace(3,"Downloading instrument %lx\n",pdmInstrument->ulPatch);
        pInstrument->m_dwProgram = pdmInstrument->ulPatch;

        DWORD dwRegionIX = pdmInstrument->ulFirstRegionIdx;
        pdmInstrument->ulFirstRegionIdx = 0; // Clear to avoid loops.
        while (dwRegionIX)
        {
            if (dwRegionIX >= pInfo->dwNumOffsetTableEntries)
            {
                Trace(1,"Error: Download failed because instrument has error in region list.\n");
                delete pInstrument;
                return DMUS_E_BADINSTRUMENT;
            }
            CSourceRegion *pRegion = new CSourceRegion;
            if (!pRegion)
            {
                delete pInstrument;
                return E_OUTOFMEMORY;
            }
            pInstrument->m_RegionList.AddHead(pRegion);
            HRESULT hr = pRegion->Download(pInfo, pvOffsetTable, &dwRegionIX, m_dwSampleRate, fNewFormat);
            if (FAILED(hr))
            {
                delete pInstrument;
                return hr;
            }
            EnterCriticalSection(&m_CriticalSection);
            CWave *pWave = m_WavePool[pRegion->m_Sample.m_dwID % WAVE_HASH_SIZE].GetHead();
            for (;pWave;pWave = pWave->GetNext())
            {
                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);
        }
        if (pdmInstrument->ulGlobalArtIdx)
        {
            if (pdmInstrument->ulGlobalArtIdx >= pInfo->dwNumOffsetTableEntries)
            {
                Trace(1,"Error: Download failed because of out of range articulation chunk.\n");
                delete pInstrument;
                return DMUS_E_BADARTICULATION;
            }

            CSourceArticulation *pArticulation = new CSourceArticulation;
            if (pArticulation)
            {
                pArticulation->Init(m_dwSampleRate);
                HRESULT hr = pArticulation->Download(pInfo, pvOffsetTable,
                        pdmInstrument->ulGlobalArtIdx, m_dwSampleRate, fNewFormat);
                if (FAILED(hr))
                {
                    delete pInstrument;
                    delete pArticulation;
                    return hr;
                }
                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;
                }
            }
            else
            {
                delete pInstrument;
                return E_OUTOFMEMORY;
            }
        }
        else
        {
            for (CSourceRegion *pr = pInstrument->m_RegionList.GetHead();
                 pr != NULL;
                 pr = pr->GetNext())
            {
                if (pr->m_pArticulation == NULL)
                {
                    Trace(1,"Error: Download failed because region has no articulation.\n");
                    delete pInstrument;
                    return DMUS_E_NOARTICULATION;
                }
            }
        }
        EnterCriticalSection(&m_CriticalSection);
        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;
        return S_OK;
    }
    return E_OUTOFMEMORY;
}

HRESULT CInstManager::DownloadWave(LPHANDLE phDownload,
                                   DMUS_DOWNLOADINFO *pInfo,
                                   void *pvOffsetTable[],
                                   void *pvData)
{
    DMUS_WAVE *pdmWave = (DMUS_WAVE *) pvData;
    if (pdmWave->WaveformatEx.wFormatTag != WAVE_FORMAT_PCM)
    {
        Trace(1,"Error: Download failed because wave data is not PCM format.\n");
        return DMUS_E_NOTPCM;
    }

    if (pdmWave->WaveformatEx.nChannels != 1)
    {
        Trace(1,"Error: Download failed because wave data is not mono.\n");
        return DMUS_E_NOTMONO;
    }

    if (pdmWave->ulWaveDataIdx >= pInfo->dwNumOffsetTableEntries)
    {
        Trace(1,"Error: Download failed because wave data is at invalid location.\n");
        return DMUS_E_BADWAVE;
    }

    CWave *pWave = new CWave;
    if (pWave)
    {
        DMUS_WAVEDATA *pdmWaveData= (DMUS_WAVEDATA *)
            pvOffsetTable[pdmWave->ulWaveDataIdx];
        pWave->m_dwID = pInfo->dwDLId;
        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 (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;
        }
        else
        {
            Trace(1,"Error: Downloading wave %ld, bad wave format.\n",pInfo->dwDLId);
            delete pWave;
            return DMUS_E_BADWAVE;
        }
        pWave->m_dwSampleLength++;  // We always add one sample to the end for interpolation.
        EnterCriticalSection(&m_CriticalSection);
        m_WavePool[pWave->m_dwID % WAVE_HASH_SIZE].AddHead(pWave);
        LeaveCriticalSection(&m_CriticalSection);
        *phDownload = (HANDLE) pWave;
        pWave->AddRef();

        // Track memory usage
        m_dwSynthMemUse += (pWave->m_bSampleType == SFORMAT_16)?pWave->m_dwSampleLength << 1: pWave->m_dwSampleLength;
        Trace(3,"Downloading wave %ld memory usage %ld\n",pInfo->dwDLId,m_dwSynthMemUse);

        return S_OK;
    }
    return E_OUTOFMEMORY;
}

HRESULT CInstManager::Download(LPHANDLE phDownload,
                               void * pvData,
                               LPBOOL pbFree)


{
    V_INAME(IDirectMusicSynth::Download);
    V_BUFPTR_READ(pvData,sizeof(DMUS_DOWNLOADINFO));

    HRESULT hr = DMUS_E_UNKNOWNDOWNLOAD;
    void ** ppvOffsetTable;     // Array of pointers to chunks in data.
    DMUS_DOWNLOADINFO * pInfo = (DMUS_DOWNLOADINFO *) pvData;
    DMUS_OFFSETTABLE* pOffsetTable = (DMUS_OFFSETTABLE *)(((BYTE*)pvData) + sizeof(DMUS_DOWNLOADINFO));
    char *pcData = (char *) pvData;

    V_BUFPTR_READ(pvData,pInfo->cbSize);

    // Code fails if pInfo->dwNumOffsetTableEntries == 0
    // Sanity check here for debug
    assert(pInfo->dwNumOffsetTableEntries);

    ppvOffsetTable = new void *[pInfo->dwNumOffsetTableEntries];
    if (ppvOffsetTable) // Create the pointer array and validate.
    {
        DWORD dwIndex;
        for (dwIndex = 0; dwIndex < pInfo->dwNumOffsetTableEntries; dwIndex++)
        {
            if (pOffsetTable->ulOffsetTable[dwIndex] >= pInfo->cbSize)
            {
                delete[] ppvOffsetTable;
                Trace(1,"Error: Download failed because of corrupt download tables.\n");
                return DMUS_E_BADOFFSETTABLE;   // Bad!
            }
            ppvOffsetTable[dwIndex] = (void *) &pcData[pOffsetTable->ulOffsetTable[dwIndex]];
        }
        if (pInfo->dwDLType == DMUS_DOWNLOADINFO_INSTRUMENT) // Instrument.
        {
            *pbFree = TRUE;
            hr = DownloadInstrument(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0],FALSE);
        }
        else if (pInfo->dwDLType == DMUS_DOWNLOADINFO_INSTRUMENT2) // New instrument format.
        {
            *pbFree = TRUE;
            hr = DownloadInstrument(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0],TRUE);
        }
        else if (pInfo->dwDLType == DMUS_DOWNLOADINFO_WAVE) // Wave.
        {
            *pbFree = FALSE;
            hr = DownloadWave(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0]);
        }
        else if (pInfo->dwDLType == DMUS_DOWNLOADINFO_WAVEARTICULATION) // Wave onshot & streaming
        {
            *pbFree = TRUE;
            hr = DownloadWaveArticulation(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0]);
        }
        else if (pInfo->dwDLType == DMUS_DOWNLOADINFO_STREAMINGWAVE) // Streaming
        {
            *pbFree = FALSE;
            hr = DownloadWaveRaw(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0]);
        }
        else if (pInfo->dwDLType == DMUS_DOWNLOADINFO_ONESHOTWAVE) // Wave onshot
        {
            *pbFree = FALSE;
            hr = DownloadWaveRaw(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0]);
        }

        delete[] ppvOffsetTable;
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }
    return hr;
}

HRESULT CInstManager::Unload(HANDLE hDownload,
                             HRESULT ( CALLBACK *lpFreeHandle)(HANDLE,HANDLE),
                             HANDLE hUserData)

{
    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())
        {
            if (pInstrument == (CInstrument *) hDownload)
            {
                Trace(3,"Unloading instrument %lx\n",pInstrument->m_dwProgram);
                m_InstrumentList[dwIndex].Remove(pInstrument);
                delete pInstrument;
                LeaveCriticalSection(&m_CriticalSection);
                return S_OK;
            }
        }
    }
    for (dwIndex = 0; dwIndex < WAVE_HASH_SIZE; dwIndex++)
    {
        CWave *pWave = m_WavePool[dwIndex].GetHead();
        for (;pWave != NULL;pWave = pWave->GetNext())
        {
            if (pWave == (CWave *) hDownload)
            {
                // Track memory usage
                m_dwSynthMemUse -= (pWave->m_bSampleType == SFORMAT_16)?pWave->m_dwSampleLength << 1: pWave->m_dwSampleLength;

                Trace(3,"Unloading wave %ld memory usage %ld\n",pWave->m_dwID,m_dwSynthMemUse);
                m_WavePool[dwIndex].Remove(pWave);

                pWave->m_hUserData = hUserData;
                pWave->m_lpFreeHandle = lpFreeHandle;
                pWave->Release();
                LeaveCriticalSection(&m_CriticalSection);
                return S_OK;
            }
        }
    }
    for (dwIndex = 0; dwIndex < WAVE_HASH_SIZE; dwIndex++)
    {
        CWaveArt* pWaveArt = m_WaveArtList[dwIndex].GetHead();
        for (;pWaveArt != NULL;pWaveArt = pWaveArt->GetNext())
        {
            if (pWaveArt == (CWaveArt *) hDownload)
            {
                Trace(3,"Unloading wave articulation %ld\n",pWaveArt->m_dwID,m_dwSynthMemUse);
                m_WaveArtList[dwIndex].Remove(pWaveArt);

                pWaveArt->Release();
                LeaveCriticalSection(&m_CriticalSection);
                return S_OK;
            }
        }
    }
    LeaveCriticalSection(&m_CriticalSection);
    Trace(1,"Error: Unload failed - downloaded object not found.\n");
    return E_FAIL;
}

//////////////////////////////////////////////////////////
// Directx8 Methods

CWave * CInstManager::GetWave(DWORD dwDLId)
{
    EnterCriticalSection(&m_CriticalSection);
    CWave *pWave = m_WavePool[dwDLId % WAVE_HASH_SIZE].GetHead();
    for (;pWave;pWave = pWave->GetNext())
    {
        if (dwDLId == pWave->m_dwID)
        {
            break;
        }
    }
    LeaveCriticalSection(&m_CriticalSection);

    return pWave;
}

CWaveArt *  CInstManager::GetWaveArt(DWORD dwDLId)
{
    EnterCriticalSection(&m_CriticalSection);
    CWaveArt *pWaveArt = m_WaveArtList[dwDLId % WAVEART_HASH_SIZE].GetHead();
    for (;pWaveArt;pWaveArt = pWaveArt->GetNext())
    {
        if (dwDLId == pWaveArt->m_dwID)
        {
            break;
        }
    }
    LeaveCriticalSection(&m_CriticalSection);

    return pWaveArt;
}

HRESULT CInstManager::DownloadWaveArticulation(LPHANDLE phDownload,
                                   DMUS_DOWNLOADINFO *pInfo,
                                   void *pvOffsetTable[],
                                   void *pvData)
{
    DMUS_WAVEARTDL* pWaveArtDl  = (DMUS_WAVEARTDL*)pvData;
    WAVEFORMATEX *pWaveformatEx = (WAVEFORMATEX *) pvOffsetTable[1];
    DWORD *dwDlId = (DWORD*)pvOffsetTable[2];
    DWORD i;

    CWaveArt* pWaveArt = new CWaveArt();
    if ( pWaveArt )
    {
        pWaveArt->m_dwID = pInfo->dwDLId;
        pWaveArt->m_WaveArtDl = *pWaveArtDl;;
        pWaveArt->m_WaveformatEx = *pWaveformatEx;
        if (pWaveformatEx->wBitsPerSample == 8)
        {
            pWaveArt->m_bSampleType = SFORMAT_8;
        }
        else if (pWaveformatEx->wBitsPerSample == 16)
        {
            pWaveArt->m_bSampleType = SFORMAT_16;
        }
        else
        {
            Trace(1,"Error: Download failed because wave data is %ld bits instead of 8 or 16.\n",(long) pWaveformatEx->wBitsPerSample);
            delete pWaveArt;
            return DMUS_E_BADWAVE;
        }

        for ( i = 0; i < pWaveArtDl->ulBuffers; i++ )
        {
            // Get wave buffer and fill header with waveformat data
            CWave *pWave = GetWave(dwDlId[i]);
            assert(pWave);

            if (!pWave)
            {
                delete pWaveArt;
                return E_POINTER;
            }

            pWave->m_dwSampleRate = pWaveformatEx->nSamplesPerSec;

            if (pWaveformatEx->wBitsPerSample == 8)
            {
                DWORD dwX;
                char *pData = (char *) pWave->m_pnWave;
                for (dwX = 0; dwX < pWave->m_dwSampleLength; dwX++)
                {
                    pData[dwX] -= (char) 128;
                }
                pWave->m_bSampleType = SFORMAT_8;
            }
            else if (pWaveformatEx->wBitsPerSample == 16)
            {
                pWave->m_dwSampleLength >>= 1;
                pWave->m_bSampleType = SFORMAT_16;
            }
            else
            {
                Trace(1,"Error: Download failed because wave data is %ld bits instead of 8 or 16.\n",(long) pWaveformatEx->wBitsPerSample);
                delete pWaveArt;
                return DMUS_E_BADWAVE;
            }
            pWave->m_dwSampleLength++;  // We always add one sample to the end for interpolation.

            // Default is to duplicate last sample. This will be overrwritten for
            // streaming waves.
            //
            if (pWave->m_dwSampleLength > 1)
            {
                if (pWave->m_bSampleType == SFORMAT_8)
                {
                    char* pb = (char*)pWave->m_pnWave;
                    pb[pWave->m_dwSampleLength - 1] = pb[pWave->m_dwSampleLength - 2];
                }
                else
                {
                    short *pn = pWave->m_pnWave;
                    pn[pWave->m_dwSampleLength - 1] = pn[pWave->m_dwSampleLength - 2];
                }
            }

            // Create a WaveBuffer listitem and save the wave in and add it to the circular buffer list
            CWaveBuffer* pWavBuf = new CWaveBuffer();
            if ( pWavBuf == NULL )
            {
                delete pWaveArt;
                return E_OUTOFMEMORY;
            }
            pWavBuf->m_pWave = pWave;

            // This Articulation will be handling streaming data
            if ( pWave->m_bStream )
                pWaveArt->m_bStream = TRUE;

            pWaveArt->m_pWaves.AddTail(pWavBuf);
        }

        EnterCriticalSection(&m_CriticalSection);
        if (pWaveArt)
        {
            CWaveBuffer* pCurrentBuffer = pWaveArt->m_pWaves.GetHead();
            for (; pCurrentBuffer; pCurrentBuffer = pCurrentBuffer->GetNext() )
            {
                if (pCurrentBuffer->m_pWave)
                {
                    pCurrentBuffer->m_pWave->AddRef();
                }
            }
        }
        m_WaveArtList[pWaveArt->m_dwID % WAVEART_HASH_SIZE].AddHead(pWaveArt);
        LeaveCriticalSection(&m_CriticalSection);

        *phDownload = (HANDLE) pWaveArt;

        return S_OK;
    }
    return E_OUTOFMEMORY;
}

HRESULT CInstManager::DownloadWaveRaw(LPHANDLE phDownload,
                                   DMUS_DOWNLOADINFO *pInfo,
                                   void *pvOffsetTable[],
                                   void *pvData)
{
    CWave *pWave = new CWave;
    if (pWave)
    {
        DMUS_WAVEDATA *pdmWaveData= (DMUS_WAVEDATA *)pvData;
        Trace(3,"Downloading raw wave data%ld\n",pInfo->dwDLId);

        pWave->m_dwID = pInfo->dwDLId;
        pWave->m_hUserData = NULL;
        pWave->m_lpFreeHandle = NULL;
        pWave->m_dwSampleLength = pdmWaveData->cbSize;
        pWave->m_pnWave = (short *) &pdmWaveData->byData[0];

        if ( pInfo->dwDLType == DMUS_DOWNLOADINFO_STREAMINGWAVE )
        {
            pWave->m_bStream = TRUE;
            pWave->m_bValid = TRUE;
        }

        EnterCriticalSection(&m_CriticalSection);
        m_WavePool[pWave->m_dwID % WAVE_HASH_SIZE].AddHead(pWave);
        LeaveCriticalSection(&m_CriticalSection);

        *phDownload = (HANDLE) pWave;
        pWave->AddRef();

        m_dwSynthMemUse += pWave->m_dwSampleLength;

        return S_OK;
    }
    return E_OUTOFMEMORY;
}