windows-nt/Source/XPSP1/NT/multimedia/directx/dmusic/dmband/dmband.cpp
2020-09-26 16:20:57 +08:00

2285 lines
71 KiB
C++

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