2285 lines
71 KiB
C++
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;
|
|
}
|
|
|