//
// dmband.cpp
// 
// Copyright (c) 1997-1999 Microsoft Corporation
//

#define INITGUID
#include <objbase.h>

#include "debug.h"
#include "dmksctrl.h"
#include "dmusicc.h"
#include "dmusici.h"
#include "..\shared\dmstrm.h"
#include "dmbandp.h"
#include "bandtrk.h"
#include "debug.h"

#define MAX_LEGACY_BAND_NAME 20
#define MAX_LEGACY_COLLECTION_NAME 32

extern long g_cComponent;

static GUID nullGUID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

//////////////////////////////////////////////////////////////////////
// Class CBand

//////////////////////////////////////////////////////////////////////
// CBand::CBand

CBand::CBand() 
{
    m_lTimeLogical = 0;
    m_lTimePhysical = 0;
    m_dwFlags = 0;
    m_dwGroupBits = 0xffffffff;
    m_dwMidiMode = 0;
    m_cRef = 1;
    m_fCSInitialized = FALSE;
    InterlockedIncrement(&g_cComponent);
    // Do this first since it can throw an exception
    //
    InitializeCriticalSection(&m_CriticalSection);
    m_fCSInitialized = TRUE;
    m_dwValidData = 0;
}

//////////////////////////////////////////////////////////////////////
// CBand::~CBand

CBand::~CBand()
{
    if (m_fCSInitialized)
    {
        m_BandInstrumentList.Clear();
        DeleteCriticalSection(&m_CriticalSection);  
    }
    
    InterlockedDecrement(&g_cComponent);
}

void CBandInstrumentList::Clear()

{
    CBandInstrument* pBandInstrument;
    while(pBandInstrument = RemoveHead())
    {
        delete pBandInstrument;
    }
}

//////////////////////////////////////////////////////////////////////
// IUnknown

//////////////////////////////////////////////////////////////////////
// CBand::QueryInterface

STDMETHODIMP 
CBand::QueryInterface(const IID &iid, void **ppv)
{
    V_INAME(CBand::QueryInterface);
    V_PTRPTR_WRITE(ppv);
    V_REFGUID(iid);

    *ppv = NULL;

    if(iid == IID_IUnknown || iid == IID_IDirectMusicBand)
    {
        *ppv = static_cast<IDirectMusicBand*>(this);
    } 
    else if(iid == IID_IDirectMusicBandP)
    {
        *ppv = static_cast<IDirectMusicBandP*>(this);
    }
    else if(iid == IID_IDirectMusicBandPrivate)
    {
        *ppv = static_cast<IDirectMusicBandPrivate*>(this);
    }
    else if(iid == IID_IDirectMusicObject)
    {
        *ppv = static_cast<IDirectMusicObject*>(this);
    }
    else if(iid == IID_IPersistStream)
    {
        *ppv = static_cast<IPersistStream*>(this);
    }
    else if(iid == IID_IPersist)
    {
        *ppv = static_cast<IPersist*>(this);
    }

    if (*ppv == NULL)
        return E_NOINTERFACE;
    
    reinterpret_cast<IUnknown*>(this)->AddRef();
    
    return S_OK;
}

//////////////////////////////////////////////////////////////////////
// CBand::AddRef

STDMETHODIMP_(ULONG)
CBand::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

//////////////////////////////////////////////////////////////////////
// CBand::Release

STDMETHODIMP_(ULONG)
CBand::Release()
{
    if (!InterlockedDecrement(&m_cRef)) 
    {
        m_cRef = 100; // artificial reference count to prevent reentrency due to COM aggregation
        delete this;
        return 0;
    }

    return m_cRef;
}

//////////////////////////////////////////////////////////////////////
// IPersistStream

//////////////////////////////////////////////////////////////////////
// CBand::Load

STDMETHODIMP CBand::Load(IStream* pStream)
{
    V_INAME(CBand::Load);
    V_PTR_READ(pStream, IStream);

    // Get the loader from stream if it has one
    // so we can open required collections
    IDirectMusicLoader* pIDMLoader = NULL;
    IDirectMusicGetLoader *pIDMGetLoader = NULL;
    
    if (SUCCEEDED(pStream->QueryInterface(IID_IDirectMusicGetLoader,(void **)&pIDMGetLoader)))
    {
        pIDMGetLoader->GetLoader(&pIDMLoader);
        pIDMGetLoader->Release();
    }
    else
    {
        Trace(1,"Error: Band unable to reference DLS Collections because IStream does not support Loader.\n");
        return DMUS_E_UNSUPPORTED_STREAM;
    }

    EnterCriticalSection(&m_CriticalSection);

    // If we have been previously loaded, clean up instruments
    if(m_dwFlags & DMB_LOADED)
    {
        m_dwValidData = 0;
        m_BandInstrumentList.Clear();
        m_lTimeLogical = 0;
        m_lTimePhysical = 0;
        m_dwFlags = 0;
    }

    RIFFIO ckMain;
    HRESULT hr = S_OK;

    CRiffParser Parser(pStream);
    Parser.EnterList(&ckMain);  
    if (Parser.NextChunk(&hr))
    {
        if (ckMain.fccType == FOURCC_BAND_FORM)
        {
            hr = LoadLegacyBand(&Parser, pIDMLoader);       
        }
        else if(ckMain.fccType == DMUS_FOURCC_BAND_FORM)
        {
            hr = LoadDirectMusicBand(&Parser, pIDMLoader);
        }
        else
        {
            Trace(1,"Error: Failure Parsing Band - invalid chunk ID.\n");
            hr = DMUS_E_INVALID_BAND;
        }
    }
    
    if(FAILED(hr))
    {
        m_BandInstrumentList.Clear();
    }
    
    if(pIDMLoader)
    {
        pIDMLoader->Release();
    }
    
    LeaveCriticalSection(&m_CriticalSection);

    return hr;
}

//////////////////////////////////////////////////////////////////////
// IDirectMusicBand

//////////////////////////////////////////////////////////////////////
// CBand::CreateSegment

STDMETHODIMP CBand::CreateSegment(IDirectMusicSegment** ppSegment)   
{
    V_INAME(IDirectMusicBand::CreateSegment);
    V_PTRPTR_WRITE(ppSegment);

    HRESULT hr = CoCreateInstance(CLSID_DirectMusicSegment,
                                  NULL,
                                  CLSCTX_INPROC,
                                  IID_IDirectMusicSegment,
                                  (void**)ppSegment);

    if(SUCCEEDED(hr))
    {
        IDirectMusicTrack* pDMTrack = NULL;
        CBandTrk *pBandTrack;

        // Create Band track

        pBandTrack = new CBandTrk;

        if (pBandTrack)
        {
            pBandTrack->QueryInterface(IID_IDirectMusicTrack,(void**)&pDMTrack);
            // Add band to track
            m_lTimePhysical--; // Subtract one from the time when creating the segment.  This is somewhat arbitrary.  (See NT5 bug 226848.)
            hr = pBandTrack->AddBand(static_cast<IDirectMusicBand*>(this));
            m_lTimePhysical++; // add the one back in
            // Set Auto-download to off
            pBandTrack->m_bAutoDownload = false;
            pBandTrack->m_fLockAutoDownload = true;

            // Insert track into segment
            hr = (*ppSegment)->InsertTrack(pDMTrack, 1);
            pDMTrack->Release();
            pBandTrack->Release(); // We don't need the original count created by the constructor.
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if(FAILED(hr))
    {
        if(*ppSegment)
        {
            (*ppSegment)->Release();
            *ppSegment = NULL;
        }
    }

    return hr;
}


HRESULT CBandInstrument::Download(IDirectMusicPerformanceP *pPerformance, 
                                  IDirectMusicAudioPath *pPath,
                                  DWORD dwMidiMode)

{
    DWORD dwPChannel;
    HRESULT hr = S_OK;
    // First, if there is an audiopath, convert the band's pchannel to performance pchannel.
    if (pPath) 
    {
        hr = pPath->ConvertPChannel(m_dwPChannel,&dwPChannel);
        if (FAILED(hr))
        {
            Trace(1,"Error: Couldn't download to Audiopath because pchannel %ld is out of bounds\n",m_dwPChannel);
            // Not a valid pchannel on this audiopath.
            return hr;
        }
    }
    else
    {
        dwPChannel = m_dwPChannel;
    }

    // We need to get the port we will be downloading to. 
    IDirectMusicPort *pPort = NULL;
    DWORD dwGMFlags;
    BOOL fDownload = TRUE;

    hr = pPerformance->GetPortAndFlags(dwPChannel,&pPort,&dwGMFlags);

    // Once we know the port, we can find out whether a download has
    // already occured. And, if not, we'll use that to do the download.
    if (SUCCEEDED(hr))
    {
        CDownloadedInstrument* pDLInstrument = m_DownloadList.GetHead();

        for(; pDLInstrument; pDLInstrument = pDLInstrument->GetNext())
        {
            if (pDLInstrument->m_pPort == pPort)
            {
                // Increment reference counter and leave.
                pDLInstrument->m_cRef++;
                pPort->Release();
                return S_OK;
            }
        }

        // Okay, didn't find it, so we need to create a download record and download it. 

        if(m_fNotInFile && !m_fGMOnly)
        {
            // Unless we've set the GMOnly flag, don't download an instrument 
            // that was automatically generated from the midi
            // parsing to give a patchless channel an instrument.
            fDownload = FALSE;
        }

        else if (m_pIDMCollection == NULL)
        {
            // Can not download this instrument but still want to add a record and continue with others.
            // If instrument is a GM and GS instrument it may still play if GM or GS is supported in hardware.
            fDownload = FALSE;
            Trace(2,"Warning: No collection, unable to download instrument %lx on PChannel %ld\n",m_dwPatch,m_dwPChannel);
        }

        if (m_dwFlags & DMUS_IO_INST_GS)
        {
            // If this is a GS instrument, determine whether it needs to be downloaded.
            if ((dwGMFlags & DM_PORTFLAGS_GM) && (m_dwFlags & DMUS_IO_INST_GM))
            {
                // The synth has a GM set in ROM, and this is a GM instrument,
                // and the instrument does not specifically requests that it use the
                // DLS version in gm.dls.
                if (!(m_dwFlags & DMUS_IO_INST_USE_DEFAULT_GM_SET) )
                {
                    fDownload = FALSE;
                }
            }
            else if (dwGMFlags & DM_PORTFLAGS_GS)
            {
                // If the synth has a GS set, the problem is simpler, since it is going to be very similar to our 
                // gm.dls set, so it is okay to use it.
                fDownload = FALSE;
            }
        }

        if( dwMidiMode ) // if this is anything, it indicates we were loaded from a midi file
        {
            // if we're not an XG file, make sure channel 9 is drums
            if( (dwMidiMode != DMUS_MIDIMODEF_XG) &&
                (m_dwPChannel == 9) )
            {
                m_dwPatch |= 0x80000000;
            }
        }

        // Okay, ready to download...

        if (fDownload)
        {
            hr = DownloadAddRecord(pPort);
            // Use fallbacks for XG mode
            if( FAILED(hr) && dwMidiMode == DMUS_MIDIMODEF_XG )
            {
                DWORD dwOldPatch = m_dwPatch;
                DWORD dwOldFlags = m_dwFlags;
                DWORD dwOldAssignPatch = m_dwAssignPatch;
                // If this band failed, try clearing the MSB. If it was an XG or GS instrument,
                // and the collection doesn't have the instrument, clearing the MSB is a
                // good fallback. If that doesn't work, try clearing the LSB.
                // Also, if this band is XG see if it is on the drum channel. If so, 
                // try setting the drum bit.
                if( (m_dwPatch & 0x00ff0000) == 0x007f0000 )
                {
                    // XG drums. Try GM drums instead, but keep program change
                    m_dwPatch &= 0xff0000ff; // clear MSB and LSB
                    m_dwPatch |= 0x80000000; // set drum bit
                    m_dwFlags |= DMUS_IO_INST_ASSIGN_PATCH;
                    m_dwAssignPatch = dwOldPatch & 0x00ffffff;
                    hr = DownloadAddRecord(pPort);
                    if( FAILED(hr) )
                    {
                        // If that didn't work, try unsetting the program change
                        m_dwPatch = 0x80000000;
                        hr = DownloadAddRecord(pPort);
                    }
                }
                else
                {
                    if( (m_dwPatch & 0x00ff0000) != 0x007e0000 )
                    {
                        m_dwPatch &= 0xffff00ff; // clear LSB
                        hr = DownloadAddRecord(pPort);
                        if( FAILED(hr) )
                        {
                            if( m_dwPatch & 0x0000ff00 )
                            {
                                m_dwPatch &= 0xff0000ff; // clear MSB & LSB
                                hr = DownloadAddRecord(pPort);
                            }
                        }
                    }
                }
                if (FAILED(hr))
                {
                    // Revert back to original values
                    m_dwPatch = dwOldPatch;
                    m_dwFlags = dwOldFlags;
                    m_dwAssignPatch = dwOldAssignPatch;
                }
            }
        }
        pPort->Release();
    }
    else
    {
        Trace(1,"Error: Unable to download to Performance because pchannel %ld is not initialized on the performance.\n",m_dwPChannel);
    }
    return hr;
}



//////////////////////////////////////////////////////////////////////
// CBand::DownloadEx

STDMETHODIMP
CBand::DownloadEx(IUnknown *pAudioPath)  
{
    V_INAME(CBand::DownloadEx);
    V_PTR_READ(pAudioPath, IUnknown);
    BOOL fGotOneDown = FALSE;
    BOOL fNoInit = FALSE;
    IDirectMusicPerformance *pPerformance = NULL;
    IDirectMusicAudioPath *pPath = NULL;

    // If the band doesn't have any instruments, return immediately with S_FALSE.
    if (m_BandInstrumentList.IsEmpty())
    {
        Trace(2,"Warning: Trying to download an empty band\n");
        return S_FALSE;
    }
    HRESULT hr = pAudioPath->QueryInterface(IID_IDirectMusicAudioPath,(void **)&pPath);
    if (SUCCEEDED(hr))
    {
        hr = pPath->GetObjectInPath(0,DMUS_PATH_PERFORMANCE,0,CLSID_DirectMusicPerformance,0,IID_IDirectMusicPerformance,(void **)&pPerformance);
    }
    else
    {
        hr = pAudioPath->QueryInterface(IID_IDirectMusicPerformance,(void **)&pPerformance);
    }
    if (SUCCEEDED(hr))
    {
        EnterCriticalSection(&m_CriticalSection);
        IDirectMusicPerformanceP *pPerfp;
        hr = pPerformance->QueryInterface(IID_IDirectMusicPerformanceP, (void **)&pPerfp);
        if (SUCCEEDED(hr))
        {
            DWORD dwSuccess = 0;
            HRESULT hrTemp = S_OK;
            CBandInstrument* pBandInstrument = m_BandInstrumentList.GetHead();
            for( ; SUCCEEDED(hr) && pBandInstrument != NULL; pBandInstrument = pBandInstrument->GetNext())
            {
                hr = pBandInstrument->Download(pPerfp,pPath,m_dwMidiMode);
                if (FAILED(hr))
                {
                    if (hr == DMUS_E_NOT_INIT)
                    {
                        Trace(1,"Error: Performance is not initialized - Band download terminated.\n");
                        // Performance is not initialized. Leave now.
                        break;
                    }
                    hrTemp = hr;
                    hr = S_FALSE;
                }
                else
                {
                    // At least one succeeded.
                    dwSuccess++;
                }
            }
            // If we had a failure but it was not performance not initialized and we did have at least one
            // successful download, return a partial download success code.
            if (FAILED(hrTemp))
            {
                // Was this a partial download?
                if ((hr != DMUS_E_NOT_INIT) &&  dwSuccess)
                {
                    hr = DMUS_S_PARTIALDOWNLOAD;
                }
                // Otherwise, make sure we don't return S_FALSE for hr!
                else
                {
                    hr = hrTemp;
                }
            }
            pPerfp->Release();
        }
        LeaveCriticalSection(&m_CriticalSection);
    }
    if (pPath) pPath->Release();
    if (pPerformance) pPerformance->Release();
    return hr;
}

STDMETHODIMP
CBand::Download(
    IDirectMusicPerformance* pPerformance)  // @parm Performance to download instruments
                                            // to. The performance manages the mapping
                                            // of PChannels to DirectMusic ports.
{
    V_INAME(CBand::Download);
    V_PTR_READ(pPerformance, IDirectMusicPerformance);
    return DownloadEx(pPerformance);
}

HRESULT CBandInstrument::Unload(IDirectMusicPerformanceP *pPerformance, IDirectMusicAudioPath *pPath)

{
    DWORD dwPChannel;
    HRESULT hr = S_OK;
    // First, if there is an audiopath, convert the band's pchannel to performance pchannel.
    if (pPath) 
    {
        hr = pPath->ConvertPChannel(m_dwPChannel,&dwPChannel);
        if (FAILED(hr))
        {
            Trace(1,"Error: Couldn't download to Audiopath because pchannel %ld is out of bounds\n",m_dwPChannel);
            // Not a valid pchannel on this audiopath.
            return hr;
        }
    }
    else
    {
        dwPChannel = m_dwPChannel;
    }

    // We need to get the port we will be unloading from.
    IDirectMusicPort *pPort = NULL;
    DWORD dwGMFlags;

    hr = pPerformance->GetPortAndFlags(dwPChannel,&pPort,&dwGMFlags);

    if (SUCCEEDED(hr))
    {
        hr = S_FALSE; // Just in case we don't find the download record.
        CDownloadedInstrument* pDLInstrument = m_DownloadList.GetHead();

        for(; pDLInstrument; pDLInstrument = pDLInstrument->GetNext())
        {
            if (pDLInstrument->m_pPort == pPort)
            {
                pDLInstrument->m_cRef--;
                if(!pDLInstrument->m_cRef)
                {
                    m_DownloadList.Remove(pDLInstrument);
                    if (FAILED(pPort->UnloadInstrument(pDLInstrument->m_pDLInstrument)))
                    {
                        Trace(1, "Error: UnloadInstrument %ld failed\n",m_dwPatch);    
                    }
                    pDLInstrument->m_pDLInstrument->Release();
                    pDLInstrument->m_pDLInstrument = NULL;
                    delete pDLInstrument;
                }
                hr = S_OK;
                break;
            }
        }
        pPort->Release();
    } 
    else if (!pPath && m_DownloadList.GetCount() == 1)
    {
        CDownloadedInstrument* pDLInstrument = m_DownloadList.GetHead();

        pDLInstrument->m_cRef--;

        if (!pDLInstrument->m_cRef)
        {
            m_DownloadList.Remove(pDLInstrument);
            if (FAILED(pDLInstrument->m_pPort->UnloadInstrument(pDLInstrument->m_pDLInstrument)))
            {
                Trace(1, "Error: UnloadInstrument %ld failed\n",m_dwPatch);    
            }
            pDLInstrument->m_pDLInstrument->Release();
            pDLInstrument->m_pDLInstrument = NULL;
            delete pDLInstrument;
        }
        hr = S_OK;
    }

    return hr;        
}



//////////////////////////////////////////////////////////////////////
// CBand::UnloadEx

STDMETHODIMP
CBand::UnloadEx(IUnknown *pAudioPath)  

{
    V_INAME(CBand::UnloadEx);
    V_PTR_READ(pAudioPath, IUnknown);

    IDirectMusicPerformance *pPerformance = NULL;
    IDirectMusicAudioPath *pPath = NULL;

    HRESULT hr = pAudioPath->QueryInterface(IID_IDirectMusicAudioPath,(void **)&pPath);
    if (SUCCEEDED(hr))
    {
        hr = pPath->GetObjectInPath(0,DMUS_PATH_PERFORMANCE,0,CLSID_DirectMusicPerformance,0,IID_IDirectMusicPerformance,(void **)&pPerformance);
    }
    else
    {
        hr = pAudioPath->QueryInterface(IID_IDirectMusicPerformance,(void **)&pPerformance);
    }

    if (SUCCEEDED(hr))
    {
        hr = S_FALSE; // Returns this for empty band.
        EnterCriticalSection(&m_CriticalSection);
        IDirectMusicPerformanceP *pPerfp;
        hr = pPerformance->QueryInterface(IID_IDirectMusicPerformanceP, (void **)&pPerfp);
        if (SUCCEEDED(hr))
        {
            CBandInstrument* pBandInstrument = m_BandInstrumentList.GetHead();
            
            for( ; pBandInstrument != NULL; pBandInstrument = pBandInstrument->GetNext())
            {
                hr = pBandInstrument->Unload(pPerfp,pPath);
            }
            pPerfp->Release();
        }
        
        LeaveCriticalSection(&m_CriticalSection);
    }
    if (pPath) pPath->Release();
    if (pPerformance) pPerformance->Release();
    return hr;
}

STDMETHODIMP
CBand::Unload(
    IDirectMusicPerformance* pPerformance)  // @parm Performance to unload instruments
                                            // from. The performance manages the mapping
         
                                            // of PChannels to DirectMusic ports.
{
    V_INAME(CBand::Unload);
    V_PTR_READ(pPerformance, IDirectMusicPerformance);
    return UnloadEx(pPerformance);
}

//////////////////////////////////////////////////////////////////////
// IDirectMusicObject

//////////////////////////////////////////////////////////////////////
// CBand::GetDescriptor

STDMETHODIMP CBand::GetDescriptor(LPDMUS_OBJECTDESC pDesc)
{
    V_INAME(CBand::GetDescriptor);
    V_PTR_WRITE(pDesc, DMUS_OBJECTDESC);
    if (pDesc->dwSize)
    {
        V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC);
    }
    else
    {
        pDesc->dwSize = sizeof(DMUS_OBJECTDESC);
    }
    pDesc->guidClass = CLSID_DirectMusicBand;
    pDesc->guidObject = m_guidObject;
    pDesc->ftDate = m_ftDate;
    pDesc->vVersion = m_vVersion;
    memcpy( pDesc->wszName, m_wszName, sizeof(m_wszName) );
    memcpy( pDesc->wszCategory, m_wszCategory, sizeof(m_wszCategory) );
    memcpy( pDesc->wszFileName, m_wszFileName, sizeof(m_wszFileName) );
    pDesc->dwValidData = ( m_dwValidData | DMUS_OBJ_CLASS );
    return S_OK;
}

//////////////////////////////////////////////////////////////////////
// CBand::SetDescriptor

STDMETHODIMP CBand::SetDescriptor(LPDMUS_OBJECTDESC pDesc)
{
    // Argument validation
    V_INAME(CBand::SetDescriptor);
    V_PTR_READ(pDesc, DMUS_OBJECTDESC);
    if (pDesc->dwSize)
    {
        V_STRUCTPTR_READ(pDesc, DMUS_OBJECTDESC);
    }
    
    HRESULT hr = E_INVALIDARG;
    DWORD dw = 0;

    if( pDesc->dwValidData & DMUS_OBJ_OBJECT )
    {
        m_guidObject = pDesc->guidObject;
        dw |= DMUS_OBJ_OBJECT;
    }
    if( pDesc->dwValidData & DMUS_OBJ_NAME )
    {
        memcpy( m_wszName, pDesc->wszName, sizeof(WCHAR)*DMUS_MAX_NAME );
        dw |= DMUS_OBJ_NAME;
    }
    if( pDesc->dwValidData & DMUS_OBJ_CATEGORY )
    {
        memcpy( m_wszCategory, pDesc->wszCategory, sizeof(WCHAR)*DMUS_MAX_CATEGORY );
        dw |= DMUS_OBJ_CATEGORY;
    }
    if( ( pDesc->dwValidData & DMUS_OBJ_FILENAME ) ||
        ( pDesc->dwValidData & DMUS_OBJ_FULLPATH ) )
    {
        memcpy( m_wszFileName, pDesc->wszFileName, sizeof(WCHAR)*DMUS_MAX_FILENAME );
        dw |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH));
    }
    if( pDesc->dwValidData & DMUS_OBJ_VERSION )
    {
        m_vVersion = pDesc->vVersion;
        dw |= DMUS_OBJ_VERSION;
    }
    if( pDesc->dwValidData & DMUS_OBJ_DATE )
    {
        m_ftDate = pDesc->ftDate;
        dw |= DMUS_OBJ_DATE;
    }
    m_dwValidData |= dw;
    if( pDesc->dwValidData & (~dw) )
    {
        hr = S_FALSE; // there were extra fields we didn't parse;
        pDesc->dwValidData = dw;
    }
    else
    {
        hr = S_OK;
    }
    return hr;
}

//////////////////////////////////////////////////////////////////////
// CBand::ParseDescriptor

STDMETHODIMP CBand::ParseDescriptor(LPSTREAM pStream, LPDMUS_OBJECTDESC pDesc) 
{
    V_INAME(CBand::ParseDescriptor);
    V_PTR_READ(pStream, IStream);
    V_PTR_WRITE(pDesc, DMUS_OBJECTDESC);
    if (pDesc->dwSize)
    {
        V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC);
    }
    else
    {
        pDesc->dwSize = sizeof(DMUS_OBJECTDESC);
    }
    
    RIFFIO ckMain;
    HRESULT hr = S_OK;

    CRiffParser Parser(pStream);
    Parser.EnterList(&ckMain);  
    if (Parser.NextChunk(&hr))
    {
        pDesc->guidClass = CLSID_DirectMusicBand;
        pDesc->dwValidData |= DMUS_OBJ_CLASS;
        if (ckMain.fccType == FOURCC_BAND_FORM)
        {
            hr = ParseLegacyDescriptor(&Parser, pDesc);             
        }
        else if(ckMain.fccType == DMUS_FOURCC_BAND_FORM)
        {
            hr = ParseDirectMusicDescriptor(&Parser, pDesc);
        }
        else
        {
            Trace(2,"Warning: ParseDescriptor failed because this is not a Band file.\n"); 
            hr = DMUS_E_INVALID_BAND;
        }
    }
    return hr;
}

//////////////////////////////////////////////////////////////////////
// Internal

//////////////////////////////////////////////////////////////////////
// CBand::ParseLegacyDescriptor

HRESULT CBand::ParseLegacyDescriptor(CRiffParser *pParser, LPDMUS_OBJECTDESC pDesc)
{
    RIFFIO ckNext;
    HRESULT hr = S_OK;

    pParser->EnterList(&ckNext);
    while(pParser->NextChunk(&hr))
    {
        if (ckNext.ckid == FOURCC_BAND)
        {
            ioBandLegacy Band;
            hr = pParser->Read( &Band, sizeof(Band) );
            if( SUCCEEDED(hr) )
            {
                pDesc->dwValidData |= DMUS_OBJ_NAME;
                wcsncpy(pDesc->wszName, Band.wstrName, MAX_LEGACY_BAND_NAME);
                pDesc->wszName[MAX_LEGACY_BAND_NAME - 1] = 0;
            }
        }
    }
    return hr;
}


//////////////////////////////////////////////////////////////////////
// CBand::ParseDirectMusicDescriptor
HRESULT CBand::ParseDirectMusicDescriptor(CRiffParser *pParser, LPDMUS_OBJECTDESC pDesc)
{
    RIFFIO ckNext;
    RIFFIO ckUNFO;
    HRESULT hr = S_OK;
    DWORD dwValidData = pDesc->dwValidData;

    pParser->EnterList(&ckNext);
    while(pParser->NextChunk(&hr))
    {
        switch(ckNext.ckid)
        {
        case DMUS_FOURCC_GUID_CHUNK:
            hr = pParser->Read( &pDesc->guidObject, sizeof(GUID) );
            dwValidData |= DMUS_OBJ_OBJECT;
            break;
        case DMUS_FOURCC_VERSION_CHUNK:
            hr = pParser->Read( &pDesc->vVersion, sizeof(DMUS_VERSION) );
            dwValidData |= DMUS_OBJ_VERSION;
            break;
        case DMUS_FOURCC_CATEGORY_CHUNK:
            hr = pParser->Read( &pDesc->wszCategory, sizeof(pDesc->wszCategory) );
            pDesc->wszCategory[DMUS_MAX_CATEGORY - 1] = 0;
            dwValidData |= DMUS_OBJ_CATEGORY;
        case DMUS_FOURCC_DATE_CHUNK:
            hr = pParser->Read( &pDesc->ftDate, sizeof(FILETIME) );
            dwValidData |= DMUS_OBJ_DATE;
            break;
        case FOURCC_LIST:
            switch(ckNext.fccType)
            {
            case DMUS_FOURCC_UNFO_LIST:
                pParser->EnterList(&ckUNFO);
                while (pParser->NextChunk(&hr))
                {
                    if (( ckUNFO.ckid == DMUS_FOURCC_UNAM_CHUNK ) || 
                        (ckUNFO.ckid == mmioFOURCC('I','N','A','M')))
                    {
                        hr = pParser->Read(&pDesc->wszName, sizeof(pDesc->wszName));
                        pDesc->wszName[DMUS_MAX_NAME - 1] = 0;
                        dwValidData |= DMUS_OBJ_NAME;
                    }
                }
                pParser->LeaveList();
                break;            
            }
            break;
        }
    }
    if (SUCCEEDED(hr))
    {
        pDesc->dwValidData = dwValidData;
    }
    return hr;
}

//////////////////////////////////////////////////////////////////////
// CBand::LoadLegacyBand

HRESULT CBand::LoadLegacyBand(CRiffParser *pParser, IDirectMusicLoader* pIDMLoader)
{
    RIFFIO ckNext;
    HRESULT hr = S_OK;

    pParser->EnterList(&ckNext);
    while(pParser->NextChunk(&hr))
    {
        if (ckNext.ckid == FOURCC_BAND)
        {
            ioBandLegacy Band;
            hr = pParser->Read( &Band, sizeof(Band) );
            if( SUCCEEDED(hr) )
            {
                wcsncpy(m_wszName, Band.wstrName, MAX_LEGACY_BAND_NAME);
                m_wszName[MAX_LEGACY_BAND_NAME - 1] = 0;
                m_dwValidData |= DMUS_OBJ_NAME;
                hr = BuildLegacyInstrumentList(Band, pIDMLoader);
                if (SUCCEEDED(hr))
                {
                    m_dwFlags |= DMB_LOADED;
                    if(Band.fDefault)
                    {
                        m_dwFlags |= DMB_DEFAULT;
                    }
                }
            }
        }
    }
    return hr;
}

//////////////////////////////////////////////////////////////////////
// CBand::LoadDirectMusicBand

HRESULT CBand::LoadDirectMusicBand(CRiffParser *pParser, IDirectMusicLoader *pIDMLoader)
{
    HRESULT hrDLS = S_OK;

    RIFFIO ckNext;
    RIFFIO ckChild;
    HRESULT hr = S_OK;

    pParser->EnterList(&ckNext);
    while(pParser->NextChunk(&hr))
    {
        switch(ckNext.ckid)
        {
        case DMUS_FOURCC_GUID_CHUNK:
            hr = pParser->Read( &m_guidObject, sizeof(GUID) );
            m_dwValidData |= DMUS_OBJ_OBJECT;
            break;
        case DMUS_FOURCC_VERSION_CHUNK:
            hr = pParser->Read( &m_vVersion, sizeof(DMUS_VERSION) );
            m_dwValidData |= DMUS_OBJ_VERSION;
            break;
        case DMUS_FOURCC_CATEGORY_CHUNK:
            hr = pParser->Read( &m_wszCategory, sizeof(WCHAR)*DMUS_MAX_CATEGORY );
            m_wszCategory[DMUS_MAX_CATEGORY - 1] = 0;
            m_dwValidData |= DMUS_OBJ_CATEGORY;
            break;
        case DMUS_FOURCC_DATE_CHUNK:
            hr = pParser->Read( &m_ftDate, sizeof(FILETIME) );
            m_dwValidData |= DMUS_OBJ_DATE;
            break;
        case FOURCC_LIST:
            switch(ckNext.fccType)
            {
            case DMUS_FOURCC_UNFO_LIST:
                pParser->EnterList(&ckChild);
                while (pParser->NextChunk(&hr))
                {
                    if (( ckChild.ckid == DMUS_FOURCC_UNAM_CHUNK ) || 
                        (ckChild.ckid == mmioFOURCC('I','N','A','M')))
                    {
                        hr = pParser->Read(&m_wszName, sizeof(m_wszName));
                        m_wszName[DMUS_MAX_NAME - 1] = 0;
                        m_dwValidData |= DMUS_OBJ_NAME;
                    }
                }
                pParser->LeaveList();
                break;
            case DMUS_FOURCC_INSTRUMENTS_LIST:
                pParser->EnterList(&ckChild);
                while (pParser->NextChunk(&hr))
                {
                    if ((ckChild.ckid == FOURCC_LIST) && 
                        (ckChild.fccType == DMUS_FOURCC_INSTRUMENT_LIST))
                    {
                        hr = ExtractBandInstrument(pParser, pIDMLoader);
                        if (hr != S_OK)
                        {
                            hrDLS = hr;
                        }
                    }
                }
                pParser->LeaveList();
                break;
            }
        }
    }
    pParser->LeaveList();

    if (hr == S_OK && hrDLS != S_OK)
    {
        hr = hrDLS;
    }

    return hr;
}

//////////////////////////////////////////////////////////////////////
// CBand::BuildLegacyInstrumentList

HRESULT CBand::BuildLegacyInstrumentList(const ioBandLegacy& iob,
                                            IDirectMusicLoader* pIDMLoader)
{
    // Legacy band channel to pchannel translation table
    static char sj_translation_table[] = { -1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3 };

    HRESULT hrGM = S_OK;
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_CriticalSection);
    
    char szCollection[DM_LEGACY_BAND_COLLECTION_NAME_LEN];
    
    for(DWORD i = 0; SUCCEEDED(hr) && i < DMBAND_NUM_LEGACY_INSTRUMENTS; i++)
    {
        CBandInstrument* pBandInstrument = new CBandInstrument();
        if(pBandInstrument)
        {
            if(iob.awDLSBank[i] & 0x8000) 
            {
                // We have a plain old GM collection where MSB & LSB are both zero
                pBandInstrument->m_dwPatch = 0;
                pBandInstrument->m_dwPatch |= (iob.abPatch[i] & 0x7F);
                pBandInstrument->m_dwFlags |= (DMUS_IO_INST_GM | DMUS_IO_INST_GS);
            }
            else
            {
                if(iob.awDLSBank[i] & 0x4000)
                {
                    // We has a GS collection with valid MSB and LSB numbers
                    pBandInstrument->m_dwPatch = 0;
                    pBandInstrument->m_dwPatch |= (iob.abDLSPatch[i] & 0x7F);
                    pBandInstrument->m_dwPatch |= (iob.awDLSBank[i] & 0x7F) << 8; // Set LSB
                    pBandInstrument->m_dwPatch |= ((iob.awDLSBank[i] >> 7) & 0x7F) << 16; // Set MSB
                    pBandInstrument->m_dwFlags |= (DMUS_IO_INST_BANKSELECT | DMUS_IO_INST_GS | DMUS_IO_INST_GM);
                }
                else
                {
                    if(iob.szCollection[0] == '\0')
                    {
                        // We have no unique DLS file so we will assume GM
                        pBandInstrument->m_dwPatch = 0;
                        pBandInstrument->m_dwPatch |= (iob.abPatch[i] & 0x7F);
                        pBandInstrument->m_dwFlags |= (DMUS_IO_INST_GM | DMUS_IO_INST_GS);
                    }
                    else
                    {
                        // We have a unique DLS file
                        pBandInstrument->m_dwPatch = 0;
                        pBandInstrument->m_dwPatch |= (iob.abDLSPatch[i] & 0x7F);
                        pBandInstrument->m_dwPatch |= (iob.awDLSBank[i] & 0x7F) << 8; // Set LSB
                        pBandInstrument->m_dwPatch |= ((iob.awDLSBank[i] >> 7) & 0x7F) << 16; // Set MSB
                        pBandInstrument->m_dwFlags |= (DMUS_IO_INST_BANKSELECT);
                        lstrcpyn(szCollection, iob.szCollection, MAX_LEGACY_COLLECTION_NAME);
                        szCollection[MAX_LEGACY_COLLECTION_NAME - 1] = '\0';
                    }
                }
            }
            
            pBandInstrument->m_dwFlags |= (DMUS_IO_INST_TRANSPOSE | DMUS_IO_INST_PAN | DMUS_IO_INST_VOLUME | DMUS_IO_INST_PATCH);
            pBandInstrument->m_bPan = iob.abPan[i];
            pBandInstrument->m_bVolume = iob.abVolume[i];
            pBandInstrument->m_dwPChannel = sj_translation_table[i + 1];
            // Set drum-kit bit if a drum-kit
            if(pBandInstrument->m_dwPChannel % 16 == 9)
            {
                pBandInstrument->m_dwPatch |= 0x80000000;
            }

            pBandInstrument->m_nTranspose = iob.achOctave[i];
            
            pBandInstrument->m_pIDMCollection = NULL;

            // We will try to load the collection but if we can not we will continure
            // and use the default GM on the card
            
            if(pIDMLoader && (pBandInstrument->m_dwFlags & DMUS_IO_INST_GM || pBandInstrument->m_dwFlags & DMUS_IO_INST_GS))
            {
                HRESULT hrTemp = LoadCollection(&(pBandInstrument->m_pIDMCollection),
                                    NULL,
                                    pIDMLoader);
                if (FAILED(hrTemp))
                {
                    hrGM = hrTemp;
                }
            }
            else if(pIDMLoader)
            {
                HRESULT hrTemp = LoadCollection(&(pBandInstrument->m_pIDMCollection),
                                    szCollection,
                                    pIDMLoader);
                if (FAILED(hrTemp))
                {
                    hrGM = hrTemp;
                }
            }
            
            m_BandInstrumentList.AddHead(pBandInstrument);
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }

    LeaveCriticalSection(&m_CriticalSection);

    // This function expects the caller to cleanup m_BandInstrumentList on any errors
    if (SUCCEEDED(hrGM) || hr != S_OK)
    {
        return hr;
    }
    else
    {
        return DMUS_S_PARTIALLOAD;
    }
}

//////////////////////////////////////////////////////////////////////
// CBand::ExtractBandInstrument

HRESULT CBand::ExtractBandInstrument(CRiffParser *pParser,
                                    IDirectMusicLoader* pIDMLoader)
{
    CBandInstrument* pBandInstrument = new CBandInstrument();
    
    if(pBandInstrument == NULL)
    {
        return E_OUTOFMEMORY;
    }


    RIFFIO ckNext;
    HRESULT hrGM = S_OK;
    HRESULT hr = S_OK;
    
    pParser->EnterList(&ckNext);
    while (pParser->NextChunk(&hr))
    {
        switch(ckNext.ckid)
        {
        case  DMUS_FOURCC_INSTRUMENT_CHUNK:
            {
                DMUS_IO_INSTRUMENT ioDMInst;
                ZeroMemory( &ioDMInst, sizeof(DMUS_IO_INSTRUMENT) );
                hr = pParser->Read(&ioDMInst, sizeof(DMUS_IO_INSTRUMENT));
                if(SUCCEEDED(hr))
                {
                    pBandInstrument->m_dwPatch = ioDMInst.dwPatch;
                    pBandInstrument->m_dwAssignPatch = ioDMInst.dwAssignPatch;
                    pBandInstrument->m_bPan = ioDMInst.bPan;
                    pBandInstrument->m_bVolume = ioDMInst.bVolume;
                    pBandInstrument->m_dwPChannel = ioDMInst.dwPChannel;
                    pBandInstrument->m_nTranspose = ioDMInst.nTranspose;
                    pBandInstrument->m_dwFlags = ioDMInst.dwFlags;
                    pBandInstrument->m_dwChannelPriority = ioDMInst.dwChannelPriority;
                    pBandInstrument->m_nPitchBendRange = ioDMInst.nPitchBendRange;

                    CopyMemory(&(pBandInstrument->m_dwNoteRanges[0]),
                               &(ioDMInst.dwNoteRanges[0]),
                               (sizeof(DWORD) * 4));
                    
                    pBandInstrument->m_pIDMCollection = NULL;

                }
            }
            break;
        case FOURCC_LIST :
            switch(ckNext.fccType)
            {
            case DMUS_FOURCC_REF_LIST:
                // We can only load if we have a valid loader
                if(pIDMLoader)
                {
                    HRESULT hrTemp = GetCollectionRefAndLoad(pParser,pIDMLoader,pBandInstrument);
                    if (FAILED(hrTemp))
                    {
                        hrGM = hrTemp;
                    }

                }
                break;
            }
            break;
        }
    }
    pParser->LeaveList();

    if(SUCCEEDED(hr))
    {
        if(pIDMLoader && 
           pBandInstrument->m_pIDMCollection == NULL &&
           (pBandInstrument->m_dwFlags & (DMUS_IO_INST_GM | DMUS_IO_INST_GS | DMUS_IO_INST_XG)) )
        {
            HRESULT hrTemp = LoadCollection(&(pBandInstrument->m_pIDMCollection),
                                NULL,
                                pIDMLoader);
            if (FAILED(hrTemp))
            {
                hrGM = hrTemp;
            }
        }

        m_BandInstrumentList.AddHead(pBandInstrument);
    }
    else
    {
        delete pBandInstrument;
    }

    if (SUCCEEDED(hrGM) || hr != S_OK)
    {
        return hr;
    }
    else
    {
        return DMUS_S_PARTIALLOAD;
    }
}

//////////////////////////////////////////////////////////////////////
// CBand::GetCollectionRefAndLoad

HRESULT CBand::GetCollectionRefAndLoad(CRiffParser *pParser,
                                        IDirectMusicLoader *pIDMLoader, 
                                        CBandInstrument *pBandInstrument)
{
    DMUS_OBJECTDESC desc;
    desc.dwValidData = 0;
    desc.dwSize = sizeof(desc);

    RIFFIO ckNext;
    HRESULT hr = S_OK;
    pParser->EnterList(&ckNext);
    while (pParser->NextChunk(&hr))
    {
        switch(ckNext.ckid)
        {
        case  DMUS_FOURCC_REF_CHUNK:
            DMUS_IO_REFERENCE ioDMRef;
            hr = pParser->Read(&ioDMRef, sizeof(DMUS_IO_REFERENCE));
            desc.guidClass = ioDMRef.guidClassID;
            desc.dwValidData |= ioDMRef.dwValidData;
            desc.dwValidData |= DMUS_OBJ_CLASS;
            break;
        case DMUS_FOURCC_GUID_CHUNK:
            hr = pParser->Read(&(desc.guidObject), sizeof(GUID));
            desc.dwValidData |=  DMUS_OBJ_OBJECT;
            break;
        case DMUS_FOURCC_DATE_CHUNK:
            hr = pParser->Read(&(desc.ftDate), sizeof(FILETIME));
            desc.dwValidData |=  DMUS_OBJ_DATE;
            break;
        case DMUS_FOURCC_NAME_CHUNK:
            hr = pParser->Read(desc.wszName, sizeof(desc.wszName));
            desc.wszName[DMUS_MAX_NAME - 1] = 0;
            desc.dwValidData |=  DMUS_OBJ_NAME;
            break;
        case DMUS_FOURCC_FILE_CHUNK:
            hr = pParser->Read(desc.wszFileName, sizeof(desc.wszFileName));
            desc.wszFileName[DMUS_MAX_FILENAME - 1] = 0;
            desc.dwValidData |=  DMUS_OBJ_FILENAME;
            break;
        case DMUS_FOURCC_CATEGORY_CHUNK:
            hr = pParser->Read(desc.wszCategory, sizeof(desc.wszCategory));
            desc.wszCategory[DMUS_MAX_CATEGORY - 1] = 0;
            desc.dwValidData |=  DMUS_OBJ_CATEGORY;
            break;
        case DMUS_FOURCC_VERSION_CHUNK:
            DMUS_IO_VERSION dmioVer;
            hr = pParser->Read(&dmioVer, sizeof(DMUS_IO_VERSION));
            desc.vVersion.dwVersionMS = dmioVer.dwVersionMS;
            desc.vVersion.dwVersionLS = dmioVer.dwVersionLS;
            desc.dwValidData |= DMUS_OBJ_VERSION;
            break;
        }
    }
    pParser->LeaveList();

    if(SUCCEEDED(hr))
    {
        hr = pIDMLoader->GetObject(&desc,IID_IDirectMusicCollection, (void**)&(pBandInstrument->m_pIDMCollection));
    }
#ifdef DBG
    if (FAILED(hr))
    {
        if (desc.dwValidData &  DMUS_OBJ_FILENAME)
        {
            Trace(1,"Error: Unable to load DLS Collection from file %ls for instrument %lx\n",
                desc.wszFileName, pBandInstrument->m_dwPatch);
        }
        else if (desc.dwValidData & DMUS_OBJ_NAME)
        {
            Trace(1,"Error: Unable to load DLS Collection %ls for instrument %lx\n",
                desc.wszName, pBandInstrument->m_dwPatch);
        }
        else
        {
            Trace(1,"Error: Unable to load DLS Collection for instrument %lx\n",
                pBandInstrument->m_dwPatch);
        }
    }
#endif
    return hr;
}

//////////////////////////////////////////////////////////////////////
// CBand::Load

HRESULT CBand::Load(DMUS_IO_PATCH_ITEM& rPatchEvent)
{
    // This method is used to load PatchEvents generated by the parsing of a MIDI file.
    // Each PatchEvent represents a program change and possibly a bank select. Using 
    // this information this method will generate a band with one instrument.

    HRESULT hr = S_OK;

    EnterCriticalSection(&m_CriticalSection);
    
    CBandInstrument* pBandInstrument = NULL;

    pBandInstrument = new CBandInstrument();
        
    if(pBandInstrument)
    {
        pBandInstrument->m_dwFlags |= rPatchEvent.dwFlags;

        if(pBandInstrument->m_dwFlags & DMUS_IO_INST_PATCH)
        {
            pBandInstrument->m_dwPatch |= (rPatchEvent.byPChange & 0x7F); // Program change
        }

        if(pBandInstrument->m_dwFlags & DMUS_IO_INST_BANKSELECT)
        {
            pBandInstrument->m_dwPatch |= (rPatchEvent.byLSB & 0x7F) << 8; // Set LSB
            pBandInstrument->m_dwPatch |= (rPatchEvent.byMSB & 0x7F) << 16; // Set MSB
        }

        if(IsGS(rPatchEvent))
        {
            pBandInstrument->m_dwFlags |= DMUS_IO_INST_GS;
            if( (rPatchEvent.byLSB == 0) && (rPatchEvent.byMSB == 0) )
            {
                pBandInstrument->m_dwFlags |= DMUS_IO_INST_GM;
            }
        }

        pBandInstrument->m_dwPChannel = (rPatchEvent.byStatus & 0xF);
        pBandInstrument->m_pIDMCollection = rPatchEvent.pIDMCollection;
        pBandInstrument->m_fNotInFile = rPatchEvent.fNotInFile;
        if(pBandInstrument->m_pIDMCollection)
        {
            (pBandInstrument->m_pIDMCollection)->AddRef();
        }
        
        // Set the time for the band. Since this band will have only one instrument in
        // it we use the time for PatchEvent as the time for the band
        m_lTimeLogical = rPatchEvent.lTime;
        m_lTimePhysical = m_lTimeLogical;
        
        m_BandInstrumentList.AddHead(pBandInstrument);
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }
    
    if(SUCCEEDED(hr))
    {
        m_dwFlags |= DMB_LOADED;
    }
    else
    {
        if(pBandInstrument)
        {
            delete pBandInstrument;
        }
    }

    LeaveCriticalSection(&m_CriticalSection);
    
    return hr;
}

//////////////////////////////////////////////////////////////////////
// CBand::Load

HRESULT CBand::Load(CBandInstrument* pInstrument)
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_CriticalSection);
    
    CBandInstrument* pBandInstrument = NULL;

    pBandInstrument = new CBandInstrument();
        
    if(pBandInstrument)
    {
        pBandInstrument->m_dwPatch = pInstrument->m_dwPatch;
        pBandInstrument->m_dwAssignPatch = pInstrument->m_dwAssignPatch;
        pBandInstrument->m_dwPChannel = pInstrument->m_dwPChannel;
        pBandInstrument->m_dwFlags = pInstrument->m_dwFlags;
        pBandInstrument->m_bPan = pInstrument->m_bPan;
        pBandInstrument->m_bVolume = pInstrument->m_bVolume;
        pBandInstrument->m_nTranspose = pInstrument->m_nTranspose;
        pBandInstrument->m_pIDMCollection = pInstrument->m_pIDMCollection;

        CopyMemory(pBandInstrument->m_dwNoteRanges, pInstrument->m_dwNoteRanges, sizeof(pInstrument->m_dwNoteRanges)); 
        if(pBandInstrument->m_pIDMCollection)
        {
            (pBandInstrument->m_pIDMCollection)->AddRef();
        }

        m_BandInstrumentList.AddHead(pBandInstrument);
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }

    if(SUCCEEDED(hr))
    {
        m_dwFlags |= DMB_LOADED;
    }
    else
    {
        if(pBandInstrument)
        {
            delete pBandInstrument;
        }
    }

    LeaveCriticalSection(&m_CriticalSection);
    
    return hr;
}

//////////////////////////////////////////////////////////////////////
// CBand::SendMessages

HRESULT CBand::SendMessages(CBandTrkStateData* pBTStateData,
                               MUSIC_TIME mtOffset,
                               REFERENCE_TIME rtOffset,
                               bool fClockTime)
{
    if(pBTStateData == NULL)
    {   
        return E_POINTER;
    }
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_CriticalSection);

    CBandInstrument* pInstrument = m_BandInstrumentList.GetHead();
    for( ; pInstrument && SUCCEEDED(hr); pInstrument = pInstrument->GetNext())
    {
        if( pInstrument->m_fNotInFile && !pInstrument->m_fGMOnly )
        {
            // don't send program changes for instruments that were automatically
            // generated by midi file parsing, unless we've set GMOnly.
            continue;
        }
        hr = SendInstrumentAtTime(pInstrument, pBTStateData, m_lTimePhysical, mtOffset, rtOffset, fClockTime);
    }
    
    LeaveCriticalSection(&m_CriticalSection);

    return hr;
}

HRESULT CBand::AllocPMsgFromGenericTemplate(
    DWORD dwType,
    IDirectMusicPerformance *pPerformance,
    DMUS_PMSG **ppMsg,
    ULONG cb,
    DMUS_PMSG *pMsgGenericFields)
{
    HRESULT hr = pPerformance->AllocPMsg(cb, ppMsg);
    if (SUCCEEDED(hr))
    {
        DWORD dwSize = (*ppMsg)->dwSize; // Remember the size.
        assert(dwSize == cb);
        ZeroMemory(*ppMsg, cb); // Clear it - ensures we zero the non-DMUS_PMSG_PART fields.
        CopyMemory(*ppMsg, pMsgGenericFields, sizeof(*pMsgGenericFields)); // Copy the DMUS_PMSG_PART fields.

        // Fill in the correct size and type
        (*ppMsg)->dwSize = dwSize;
        (*ppMsg)->dwType = dwType;
    }
    return hr;
}

HRESULT CBand::StampSendFreePMsg(
                IDirectMusicPerformance *pPerformance,
                IDirectMusicGraph *pGraph,
                DMUS_PMSG *pMsg)
{
    // Let the graph set the delivery parameters.
    HRESULT hr = pGraph->StampPMsg(pMsg);
    if (SUCCEEDED(hr))
        hr = pPerformance->SendPMsg(pMsg);
    if (FAILED(hr))
        hr = pPerformance->FreePMsg(pMsg);
    return hr;
}

HRESULT CBand::SendInstrumentAtTime(CBandInstrument* pInstrument, 
                                       CBandTrkStateData* pBTStateData, 
                                       MUSIC_TIME mtTimeToPlay,
                                       MUSIC_TIME mtOffset,
                                       REFERENCE_TIME rtOffset,
                                       bool fClockTime)
{
    if(pInstrument == NULL || pBTStateData == NULL)
    {
        return E_POINTER;
    }

    IDirectMusicGraph *pGraph = NULL;
    IDirectMusicPerformance *pPerformance = pBTStateData->m_pPerformance;
    DWORD dwVirtualTrackID = pBTStateData->m_dwVirtualTrackID;
    DWORD dwPatch = 0;
    BOOL fMute;
    DWORD dwPChannel;

    // Get the mute/pchannel reassignment.
    MUSIC_TIME mtParam = ( m_lTimeLogical < 0 ) ? 0 : m_lTimeLogical;
    m_PChMap.GetInfo( pInstrument->m_dwPChannel, mtParam, mtOffset, m_dwGroupBits,
        pPerformance, &fMute, &dwPChannel, fClockTime );
    if( fMute )
        return S_OK;

    HRESULT hr = pBTStateData->m_pSegmentState->QueryInterface(IID_IDirectMusicGraph,
                                                               reinterpret_cast<void**>(&pGraph));
    if(FAILED(hr))
        return hr;

    EnterCriticalSection(&m_CriticalSection);

    DMUS_PMSG pmsgGeneric; // template for stamping out the common fields in the various specific kinds of messages
    ZeroMemory(&pmsgGeneric, sizeof(pmsgGeneric));
    if (fClockTime)
    {
        pmsgGeneric.rtTime =  mtTimeToPlay * REF_PER_MIL + rtOffset;
        pmsgGeneric.dwFlags |= DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
    }
    else
    {
        pmsgGeneric.mtTime =  mtTimeToPlay + mtOffset;
        pmsgGeneric.dwFlags |= DMUS_PMSGF_MUSICTIME;
    }
    pmsgGeneric.dwPChannel = dwPChannel;
    pmsgGeneric.dwVirtualTrackID = dwVirtualTrackID;
    pmsgGeneric.dwGroupID = m_dwGroupBits;

    if(pInstrument->m_dwFlags & DMUS_IO_INST_PATCH)
    {
        if(pInstrument->m_dwFlags & DMUS_IO_INST_BANKSELECT)
        {
            if(pInstrument->m_dwFlags & DMUS_IO_INST_ASSIGN_PATCH)
            {
                dwPatch = pInstrument->m_dwAssignPatch & 0x007F7F00;            
            }
            else
            {
                dwPatch = pInstrument->m_dwPatch & 0x007F7F00;

                // if the m_fGMOnly flag is set, and either we're GS or we're XG and
                // the instument's port supports XG, use the full patch
                if (pInstrument->m_fGMOnly || (pInstrument->m_dwFlags & DMUS_IO_INST_XG))
                {
                    bool fXG = XGInHardware(pPerformance,pBTStateData->m_pSegmentState,pInstrument->m_dwPChannel);
                    if(pInstrument->m_fGMOnly)
                    {
                        if ( m_dwMidiMode & DMUS_MIDIMODEF_GS )
                        {
                            dwPatch = pInstrument->m_dwFullPatch & 0x007F7F00;
                        }
                        if ( (m_dwMidiMode & DMUS_MIDIMODEF_XG) && fXG )
                        {
                            dwPatch = pInstrument->m_dwFullPatch & 0x007F7F00;
                        }
                    }
                    // If the instrument is an XG instrument and the hardware doesn't support
                    // XG, strip off the bank selects.
                    if ( (pInstrument->m_dwFlags & DMUS_IO_INST_XG) && !fXG)
                    {
                        dwPatch = 0;
                    }
                }
            }
        }

        // Now, get the program change.
        if(pInstrument->m_dwFlags & DMUS_IO_INST_ASSIGN_PATCH)
        {
            dwPatch |= pInstrument->m_dwAssignPatch & 0x7f; 
        }
        else
        {
            dwPatch |= pInstrument->m_dwPatch & 0x7f;   
        }

        DMUS_PATCH_PMSG *pMsg = NULL;
        hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_PATCH, pPerformance, reinterpret_cast<DMUS_PMSG**>(&pMsg), sizeof(*pMsg), &pmsgGeneric);
        if(SUCCEEDED(hr))
        {
            // DMUS_PATCH_PMSG members that need to be initialized 
            pMsg->byInstrument = (BYTE) dwPatch & 0x7F;
            pMsg->byMSB = (BYTE) ((dwPatch >> 16) & 0x7F);
            pMsg->byLSB = (BYTE) ((dwPatch >> 8) & 0x7F);

            hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast<DMUS_PMSG*>(pMsg));
        }
    }

    if(pInstrument->m_dwFlags & DMUS_IO_INST_TRANSPOSE)
    {
        DMUS_TRANSPOSE_PMSG *pMsg = NULL;
        hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_TRANSPOSE, pPerformance, reinterpret_cast<DMUS_PMSG**>(&pMsg), sizeof(*pMsg), &pmsgGeneric);
        if(SUCCEEDED(hr))
        {
            // DMUS_TRANSPOSE_PMSG members that need to be initialized 
            pMsg->nTranspose = pInstrument->m_nTranspose;

            hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast<DMUS_PMSG*>(pMsg));
        }
    }

    if(pInstrument->m_dwFlags & DMUS_IO_INST_VOLUME)
    {
        // Set Volume
        DMUS_MIDI_PMSG* pMsg = NULL;
        hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_MIDI, pPerformance, reinterpret_cast<DMUS_PMSG**>(&pMsg), sizeof(*pMsg), &pmsgGeneric);

        if(SUCCEEDED(hr))
        {
            // DMUS_MIDI_PMSG members that need to be initialized 
            pMsg->bStatus = MIDI_CONTROL_CHANGE;
            pMsg->bByte1 = MIDI_CC_VOLUME;
            pMsg->bByte2 = pInstrument->m_bVolume;

            hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast<DMUS_PMSG*>(pMsg));
        }
    }

    if(pInstrument->m_dwFlags & DMUS_IO_INST_PAN)
    {
        // Set Pan
        DMUS_MIDI_PMSG* pMsg = NULL;
        hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_MIDI, pPerformance, reinterpret_cast<DMUS_PMSG**>(&pMsg), sizeof(*pMsg), &pmsgGeneric);

        if(SUCCEEDED(hr))
        {
            // DMUS_MIDI_PMSG members that need to be initialized 
            pMsg->bStatus = MIDI_CONTROL_CHANGE;
            pMsg->bByte1 = MIDI_CC_PAN;
            pMsg->bByte2 = pInstrument->m_bPan;

            hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast<DMUS_PMSG*>(pMsg));
        }
    }

    if(pInstrument->m_dwFlags & DMUS_IO_INST_CHANNEL_PRIORITY)
    {
        DMUS_CHANNEL_PRIORITY_PMSG *pMsg = NULL;
        hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_CHANNEL_PRIORITY, pPerformance, reinterpret_cast<DMUS_PMSG**>(&pMsg), sizeof(*pMsg), &pmsgGeneric);
        if(SUCCEEDED(hr))
        {
            // DMUS_CHANNEL_PRIORITY_PMSG members that need to be initialized 
            pMsg->dwChannelPriority = pInstrument->m_dwChannelPriority;

            hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast<DMUS_PMSG*>(pMsg));
        }
    }

    if(pInstrument->m_dwFlags & DMUS_IO_INST_PITCHBENDRANGE)
    {
        DMUS_CURVE_PMSG *pMsg = NULL;
        hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_CURVE, pPerformance, reinterpret_cast<DMUS_PMSG**>(&pMsg), sizeof(*pMsg), &pmsgGeneric);
        if(SUCCEEDED(hr))
        {
            pMsg->dwFlags |= DMUS_PMSGF_DX8; // pitch band is a DX8-only flag

            // DMUS_CURVE_PMSG members that need to be initialized 
            pMsg->nEndValue = pInstrument->m_nPitchBendRange << 7;
            pMsg->nOffset = static_cast<short>(m_lTimePhysical - m_lTimeLogical);
            pMsg->bType = DMUS_CURVET_RPNCURVE;
            pMsg->bCurveShape = DMUS_CURVES_INSTANT;
            pMsg->wParamType = RPN_PITCHBEND;
            // Leave as zero: mtDuration, mtOriginalStart, mtResetDuration, nStartValue, nResetValue,
            //                wMeasure, bBeat, bGrid, wMergeIndex

            hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast<DMUS_PMSG*>(pMsg));
        }
    }

    pGraph->Release();
    
    LeaveCriticalSection(&m_CriticalSection);

    return hr;
}


//////////////////////////////////////////////////////////////////////
// CBand::LoadCollection

HRESULT CBand::LoadCollection(IDirectMusicCollection** ppIDMCollection,
                                 char* pszCollection,
                                 IDirectMusicLoader* pIDMLoader)
{
    // Any changes made to this function should also be made to LoadCollection
    // in dmime.dll

    assert(ppIDMCollection);
    assert(pIDMLoader);

    DMUS_OBJECTDESC desc;
    ZeroMemory(&desc, sizeof(desc));
    desc.dwSize = sizeof(desc);

    desc.guidClass = CLSID_DirectMusicCollection;

    if(pszCollection == NULL)
    {
        desc.guidObject = GUID_DefaultGMCollection;
        desc.dwValidData |= (DMUS_OBJ_CLASS | DMUS_OBJ_OBJECT);
    }
    else
    {
        MultiByteToWideChar(CP_ACP, 0, pszCollection, -1, desc.wszName, DMUS_MAX_NAME);
        desc.dwValidData |= (DMUS_OBJ_CLASS | DMUS_OBJ_NAME);
    }

    HRESULT hr = pIDMLoader->GetObject(&desc,IID_IDirectMusicCollection, (void**)ppIDMCollection);
    
    return hr;
}

//////////////////////////////////////////////////////////////////////
// CBand::GetPChannelCount

DWORD CBand::GetPChannelCount()
{
    EnterCriticalSection(&m_CriticalSection);

    DWORD dwCount = 0;
    CBandInstrument* pInstrument = m_BandInstrumentList.GetHead();
    for( ; pInstrument; pInstrument = pInstrument->GetNext())
    {
        dwCount++;
    }
    
    LeaveCriticalSection(&m_CriticalSection);

    return dwCount;
}

//////////////////////////////////////////////////////////////////////
// CBand::GetPChannels

HRESULT CBand::GetPChannels(DWORD *pdwPChannels, DWORD *pdwNumWritten)
{
    assert(pdwPChannels);
    assert(pdwNumWritten);

    EnterCriticalSection(&m_CriticalSection);
    
    *pdwNumWritten = 0;

    CBandInstrument* pInstrument = m_BandInstrumentList.GetHead();
    for(; pInstrument; pInstrument = pInstrument->GetNext())
    {
        *pdwPChannels++ = pInstrument->m_dwPChannel;
        (*pdwNumWritten)++;
    }

    LeaveCriticalSection(&m_CriticalSection);

    return S_OK;
}

HRESULT CBandInstrument::BuildNoteRangeArray(DWORD *pNoteRangeMap, DMUS_NOTERANGE **ppNoteRanges, DWORD *pdwNumNoteRanges)
{
    const int c_iIn = 0;
    const int c_iOut = 1;

    HRESULT hr = S_OK;

    // Count the number of DMUS_NOTERANGE structures we need to allocate
    DWORD dwNRNum = 0;
    int nState = c_iOut;
    for(int i = 0; i < 4; i++)
    {
        DWORD dwTemp = pNoteRangeMap[i];
        DWORD dwBitPos = 0;
        while(dwBitPos < 32)
        {
            if(dwTemp & 0x1ul)
            {
                if(nState == c_iOut)
                {
                    nState = c_iIn;
                    dwNRNum++;
                }
            }
            else
            {
                nState = c_iOut;
            }
            
            dwTemp = dwTemp >> 1;
            dwBitPos++;
        }   
    }

    // If the NoteRangeMap is empty or full we do nothing
    // since this will cause NULL to be returned which means we 
    // want to download the complete instrument
    if(dwNRNum && dwNRNum < 128)
    {
        *ppNoteRanges = new DMUS_NOTERANGE[dwNRNum];
        if(*ppNoteRanges)
        {
            DWORD dwNRIdx = 0;

            for(dwNRIdx = 0; dwNRIdx < dwNRNum; dwNRIdx++)
            {
                (*ppNoteRanges)[dwNRIdx].dwLowNote = 0;
                (*ppNoteRanges)[dwNRIdx].dwHighNote = 127;
            }

            dwNRIdx = 0;
            nState = c_iOut;
            for(int i = 0; i < 4; i++)
            {
                DWORD dwTemp = pNoteRangeMap[i];
                DWORD dwBitPos = 0;
                while(dwBitPos < 32)
                {
                    if(dwTemp & 0x1ul)
                    {
                        if(nState == c_iOut)
                        {
                            nState = c_iIn;
                            (*ppNoteRanges)[dwNRIdx].dwLowNote = dwBitPos + (i * 32);
                        }
                    }
                    else if(nState == c_iIn)
                    {
                        (*ppNoteRanges)[dwNRIdx].dwHighNote = dwBitPos + (i * 32) - 1;
                        nState = c_iOut;
                        dwNRIdx++;
                    }
                    
                    dwTemp = dwTemp >> 1;
                    dwBitPos++;
                }   
            }

            assert(nState == c_iIn ? dwNRIdx == dwNRNum - 1 : dwNRIdx == dwNRNum);

            *pdwNumNoteRanges = dwNRNum;
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }
    else
    {
        *ppNoteRanges = NULL;
        *pdwNumNoteRanges = 0;
    }
    
    return hr;  
}


//////////////////////////////////////////////////////////////////////
// CBand::IsGS

bool CBand::IsGS(DMUS_IO_PATCH_ITEM& rPatchEvent)
{
    BYTE    bMSB = 0;
    BYTE    bPatch = 0;

    if( rPatchEvent.dwFlags & DMUS_IO_INST_BANKSELECT )
    {
        if( rPatchEvent.byLSB != 0 ) return FALSE; // LSB must be 0 for GS
        bMSB = rPatchEvent.byMSB;
    }
    if( rPatchEvent.dwFlags & DMUS_IO_INST_PATCH )
    {
        bPatch = rPatchEvent.byPChange & 0x7F;
    }

    if( bMSB == 0)
    {
        // If this is a drum kit (on MIDI channel 10)
        if( (rPatchEvent.byStatus  & 0xF) == 10 )
        {
            if ((bPatch == 0x0)  ||
                (bPatch == 0x08) ||
                (bPatch == 0x10) ||
                (bPatch == 0x18) ||
                (bPatch == 0x19) ||
                (bPatch == 0x20) ||
                (bPatch == 0x28) ||
                (bPatch == 0x30) || 
                (bPatch == 0x38) )
            {
                return  true;
            }
            else
                return false;
        }
        else return true;//is GM
    }
    // check for GS
    switch (bMSB)
    {
        case 6:
        case 7:
            if (bPatch == 0x7D) return true;
            break;
        case 24:
            if ((bPatch == 0x04) || (bPatch == 0x06)) return true;
            break;
        case 9:
            if ((bPatch == 0x0E) || (bPatch == 0x76) || (bPatch == 0x7D)) return true;
            break;
        case 2:
            if ( (bPatch == 0x66) || (bPatch == 0x78) || ((bPatch > 0x79)&&(bPatch < 0x80) )) return true;
            break;
        case 3:
            if ((bPatch > 0x79) && (bPatch < 0x80)) return true;
            break;
        case 4:
        case 5:
            if ( (bPatch == 0x7A) || ((bPatch > 0x7B)&&(bPatch < 0x7F) )) return true;
            break;
        case 32:
            if ((bPatch == 0x10) ||
                (bPatch == 0x11) ||
                (bPatch == 0x18) ||
                (bPatch == 0x34) ) return true;
            break;
        case 1:
            if ((bPatch == 0x26) ||
                (bPatch == 0x39) ||
                (bPatch == 0x3C) ||
                (bPatch == 0x50) ||
                (bPatch == 0x51) ||
                (bPatch == 0x62) ||
                (bPatch == 0x66) ||
                (bPatch == 0x68) ||
                ((bPatch > 0x77) && (bPatch < 0x80))) return true;
                break;
        case 16:
            switch (bPatch)
            {
                case 0x00:
                    return true;
                    break;
                case 0x04:
                    return true;
                    break;
                case 0x05:
                    return true;
                    break;
                case 0x06:
                    return true;
                    break;
                case 0x10:
                    return true;
                    break;
                case 0x13:
                    return true;
                    break;
                case 0x18:
                    return true;
                    break;
                case 0x19:
                    return true;
                    break;
                case 0x1C:
                    return true;
                    break;
                case 0x27:
                    return true;
                    break;
                case 0x3E:
                    return true;
                    break;
                case 0x3F:
                    return true;
                    break;
                default:
                    return false;
            }
            break;
        case 8:
            if ((bPatch < 0x07) || ((bPatch == 0x7D)))
            {
                return true;
            }
            else if ((bPatch > 0x3F) && (bPatch < 0x50))
            {
                return false;
            }
            else if ((bPatch > 0x4F) && (bPatch < 0x72)  )
            {
                if ((bPatch == 0x50) || 
                    (bPatch == 0x51) ||
                    (bPatch == 0x6B))
                {
                    return true;
                }
                return false;
            }
            else if ((bPatch > 0x1F) && (bPatch < 0x40))
            {
                if ((bPatch > 0x25) && (bPatch < 0x29) ||
                    (bPatch > 0x3C)  ||
                    (bPatch == 0x30) || 
                    (bPatch == 0x32) )
                {
                    return true;
                }
                return false;
            }
            else if ((bPatch > 0x0A) && (bPatch < 0x12) && 
                     (bPatch != 0x0D) && (bPatch != 0x0F))
            {
                return true;
            }
            else if ((bPatch > 0x0F) && (bPatch < 0x20))
            {
                if (bPatch > 0x17)
                {
                    return true;
                }
                else if ( (bPatch == 0x13) || (bPatch == 0x15) )
                    return true;
                else
                    return false;
            }
            break;
        default:
            return false;
    }
    return false;
}

HRESULT CBandInstrument::DownloadAddRecord(IDirectMusicPort *pPort)

{
    IDirectMusicInstrument* pInstrument = NULL;
    
    HRESULT hr = m_pIDMCollection->GetInstrument(m_dwPatch, &pInstrument);
    
    if (FAILED(hr) && (m_dwFlags & (DMUS_IO_INST_GM | DMUS_IO_INST_GS | DMUS_IO_INST_XG)))
    {
        // If drums, set to standard drums.
        if (m_dwPatch & 0x80000000)
        {
            m_dwPatch = 0;
        }
        // Else make this a GM melodic instrument.
        else
        {
            m_dwPatch &= 0x7F;
        }
        hr = m_pIDMCollection->GetInstrument(m_dwPatch, &pInstrument);
    }

    if(SUCCEEDED(hr) && m_dwFlags & DMUS_IO_INST_ASSIGN_PATCH)
    {
        hr = pInstrument->SetPatch(m_dwAssignPatch);
    }
    
    if(SUCCEEDED(hr))
    {
        CDownloadedInstrument* pDLInstrument = new CDownloadedInstrument;

        if(pDLInstrument)
        {
            pDLInstrument->m_pPort = pPort;
            pPort->AddRef();
            pDLInstrument->m_cRef = 1;

            DMUS_NOTERANGE *pNoteRanges = NULL;
            DWORD dwNumNoteRanges = 0;
            if(m_dwFlags & DMUS_IO_INST_NOTERANGES)
            {
                BuildNoteRangeArray(m_dwNoteRanges, &pNoteRanges, &dwNumNoteRanges);
            }
            hr = pPort->DownloadInstrument( pInstrument, 
                                            &pDLInstrument->m_pDLInstrument, 
                                            pNoteRanges, 
                                            dwNumNoteRanges );
            if (pNoteRanges)
            {
                delete [] pNoteRanges;
            }

            if(SUCCEEDED(hr))
            {
                m_DownloadList.AddHead(pDLInstrument);              
            }
            else
            {
                delete pDLInstrument;
#ifdef DBG
                if (hr == DMUS_E_NOT_INIT)
                {
                    Trace(0,"Error: Download failed because performance not initialized\n"); 
                }
                else
                {
                    Trace(1,"Error: Unable to download instrument %lx to PChannel %ld\n",
                        m_dwPatch,m_dwPChannel); 
                }
#endif
            }
        }
        else
        {
            hr = E_OUTOFMEMORY;
            Trace(0,"Error: Memory allocation failure - Unable to download instrument\n"); 
        }
    }
    else
    {
        Trace(1,"Error: Unable to download instrument %lx; not in dls collection\n",m_dwPatch);
    }

    if (pInstrument)
    {
        pInstrument->Release();
    }
    
    return hr;
}

//////////////////////////////////////////////////////////////////////
// CBand::XGInHardware

bool CBand::XGInHardware(
            IDirectMusicPerformance *pPerformance,
            IDirectMusicSegmentState *pSegState,
            DWORD dwPChannel)
{
    DWORD dwGMFlags = 0;        
    // If this is playing via an audiopath, we need to access the audiopath to 
    // convert the pchannels so we can use them to access the right port.
    IDirectMusicSegmentState8 *pState8;
    if (SUCCEEDED(pSegState->QueryInterface(IID_IDirectMusicSegmentState8,(void **) &pState8)))
    {
        IDirectMusicAudioPath *pAudioPath;
        if (SUCCEEDED(pState8->GetObjectInPath(DMUS_PCHANNEL_ALL,DMUS_PATH_AUDIOPATH,0,
            GUID_All_Objects,0,IID_IDirectMusicAudioPath,(void **) &pAudioPath)))
        {
            pAudioPath->ConvertPChannel(dwPChannel, &dwPChannel);
            pAudioPath->Release();
        }
        pState8->Release();
    }
    // Now, use the PChannel and the performance to read the flags.
    IDirectMusicPort *pPort = NULL;
    IDirectMusicPerformanceP *pPerfp;
    if (SUCCEEDED(pPerformance->QueryInterface(IID_IDirectMusicPerformanceP, (void **)&pPerfp)))
    {
        if (SUCCEEDED(pPerfp->GetPortAndFlags(dwPChannel,&pPort,&dwGMFlags)))
        {
            pPort->Release();
        }
        pPerfp->Release();
    }
    return ((dwGMFlags & DM_PORTFLAGS_XG) && TRUE);
}


//////////////////////////////////////////////////////////////////////
// CBand::MakeGMOnly

HRESULT CBand::MakeGMOnly()
{
    EnterCriticalSection(&m_CriticalSection);

    CBandInstrument* pBandInstrument = m_BandInstrumentList.GetHead();

    for( ; pBandInstrument != NULL; pBandInstrument = pBandInstrument->GetNext())
    {
        pBandInstrument->m_fGMOnly = true;
        pBandInstrument->m_dwFullPatch = pBandInstrument->m_dwPatch;

        DWORD dwTemp = pBandInstrument->m_dwPatch;
        pBandInstrument->m_dwPatch = (dwTemp & 0x7F);

        // If a drum kit set drum kit flag
        if( m_dwMidiMode == DMUS_MIDIMODEF_XG )
        {
            if( (dwTemp & 0x00ff0000) == 0x007f0000 )
            {
                // XG drums. Keep this msb, as it is taken care of in the :Download function.
                pBandInstrument->m_dwPatch |= 0x007f0000;
            }
        }
        if(dwTemp & 0x80000000)
        {
            pBandInstrument->m_dwPatch |= 0x80000000;
        }
    }
    
    LeaveCriticalSection(&m_CriticalSection);

    return S_OK;
}

//////////////////////////////////////////////////////////////////////
// CBand::ConnectToDLSCollection

HRESULT CBand::ConnectToDLSCollection(IDirectMusicCollection *pCollection)
{
    assert(pCollection);

    EnterCriticalSection(&m_CriticalSection);

    CBandInstrument* pBandInstrument = m_BandInstrumentList.GetHead();

    for( ; pBandInstrument != NULL; pBandInstrument = pBandInstrument->GetNext())
    {
        if(pBandInstrument->m_pIDMCollection == NULL)
        {
            pCollection->AddRef();
            pBandInstrument->m_pIDMCollection = pCollection;
        }
        else
        {
            if( m_dwMidiMode ) // if this is anything, it indicates we were loaded from a midi file
            {
                // if we're not an XG file, make sure channel 9 is drums
                if( (m_dwMidiMode != DMUS_MIDIMODEF_XG) &&
                    (pBandInstrument->m_dwPChannel == 9) )
                {
                    pBandInstrument->m_dwPatch |= 0x80000000;
                }
            }
            // if we get an instrument from this collection, set the band's collection
            // pointer to it instead.
            IDirectMusicInstrument* pInstrument = NULL;
            
            if( SUCCEEDED( pCollection->GetInstrument(pBandInstrument->m_dwPatch, &pInstrument)))
            {
                pBandInstrument->m_pIDMCollection->Release();
                pBandInstrument->m_pIDMCollection = pCollection;
                pCollection->AddRef();
                pInstrument->Release();
            }
        }
    }

    LeaveCriticalSection(&m_CriticalSection);   
    
    return S_OK;
}