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

1522 lines
44 KiB
C++

//
// bandtrk.cpp
//
// Copyright (c) 1997-2001 Microsoft Corporation
//
#include "debug.h"
#include "dmusicc.h"
#include "dmusici.h"
#include "dmusicf.h"
#include "..\shared\dmstrm.h"
#include "..\shared\validate.h"
#include "bandtrk.h"
extern long g_cComponent;
//////////////////////////////////////////////////////////////////////
// Class CBandTrk
//////////////////////////////////////////////////////////////////////
// CBandTrk::CBandTrk
CBandTrk::CBandTrk() :
m_dwValidate(0),
m_bAutoDownload(false),
m_fLockAutoDownload(false),
m_dwFlags(0),
m_cRef(1),
m_fCSInitialized(FALSE)
{
InterlockedIncrement(&g_cComponent);
InitializeCriticalSection(&m_CriticalSection);
// Note: on pre-Blackcomb OS's, this call can raise an exception; if it
// ever pops in stress, we can add an exception handler and retry loop.
// (Not all calls to 'new CBandTrk' are protected in handlers.)
m_fCSInitialized = TRUE;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::~CBandTrk
CBandTrk::~CBandTrk()
{
if (m_fCSInitialized)
{
m_MidiModeList.CleanUp();
while(!BandList.IsEmpty())
{
CBand* pBand = BandList.RemoveHead();
pBand->Release();
}
DeleteCriticalSection(&m_CriticalSection);
}
InterlockedDecrement(&g_cComponent);
}
//////////////////////////////////////////////////////////////////////
// IUnknown
//////////////////////////////////////////////////////////////////////
// CBandTrk::QueryInterface
STDMETHODIMP CBandTrk::QueryInterface(const IID &iid, void **ppv)
{
V_INAME(CBandTrk::QueryInterface);
V_PTRPTR_WRITE(ppv);
V_REFGUID(iid);
if(iid == IID_IUnknown || iid == IID_IDirectMusicTrack || iid == IID_IDirectMusicTrack8)
{
*ppv = static_cast<IDirectMusicTrack8*>(this);
}
else if(iid == IID_IDirectMusicBandTrk)
{
*ppv = static_cast<IDirectMusicBandTrk*>(this);
}
else if(iid == IID_IPersistStream)
{
*ppv = static_cast<IPersistStream*>(this);
}
else if(iid == IID_IPersist)
{
*ppv = static_cast<IPersist*>(this);
}
else
{
Trace(4,"Warning: Request to query unknown interface on Band Track object\n");
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(this)->AddRef();
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::AddRef
STDMETHODIMP_(ULONG) CBandTrk::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::Release
STDMETHODIMP_(ULONG) CBandTrk::Release()
{
if(!InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}
return m_cRef;
}
/////////////////////////////////////////////////////////////////////////////
// IPersist
HRESULT CBandTrk::GetClassID( CLSID* pClassID )
{
V_INAME(CBandTrk::GetClassID);
V_PTR_WRITE(pClassID, CLSID);
*pClassID = CLSID_DirectMusicBandTrack;
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// IPersistStream
//////////////////////////////////////////////////////////////////////
// CBandTrk::Load
STDMETHODIMP CBandTrk::Load(IStream* pIStream)
{
V_INAME(CBandTrk::Load);
V_PTR_READ(pIStream, IStream);
HRESULT hrDLS = S_OK;
EnterCriticalSection(&m_CriticalSection);
m_MidiModeList.CleanUp();
// If we have been previously loaded, cleanup bands
if(!BandList.IsEmpty())
{
m_bAutoDownload = true;
while(!BandList.IsEmpty())
{
CBand* pBand = BandList.RemoveHead();
pBand->Release();
}
++m_dwValidate;
}
CRiffParser Parser(pIStream);
RIFFIO ckMain;
HRESULT hr = S_OK;
Parser.EnterList(&ckMain);
if (Parser.NextChunk(&hr))
{
if ((ckMain.ckid == FOURCC_RIFF) &&
(ckMain.fccType == DMUS_FOURCC_BANDTRACK_FORM))
{
RIFFIO ckNext; // Descends into the children chunks.
Parser.EnterList(&ckNext);
while (Parser.NextChunk(&hr))
{
switch(ckNext.ckid)
{
case DMUS_FOURCC_BANDTRACK_CHUNK:
DMUS_IO_BAND_TRACK_HEADER ioDMBndTrkHdr;
hr = Parser.Read(&ioDMBndTrkHdr, sizeof(DMUS_IO_BAND_TRACK_HEADER));
if(SUCCEEDED(hr))
{
m_bAutoDownload = ioDMBndTrkHdr.bAutoDownload ? true : false;
m_fLockAutoDownload = true;
}
break;
case FOURCC_LIST:
switch(ckNext.fccType)
{
case DMUS_FOURCC_BANDS_LIST:
hr = BuildDirectMusicBandList(&Parser);
if (hr != S_OK)
{
hrDLS = hr;
}
break;
}
}
}
Parser.LeaveList();
}
}
Parser.LeaveList();
LeaveCriticalSection(&m_CriticalSection);
if (hr == S_OK && hrDLS != S_OK)
{
hr = hrDLS;
}
return hr;
}
//////////////////////////////////////////////////////////////////////
// IDirectMusicTrack
//////////////////////////////////////////////////////////////////////
// CBandTrk::Init
STDMETHODIMP CBandTrk::Init(IDirectMusicSegment* pSegment)
{
V_INAME(CBandTrk::Init);
V_INTERFACE(pSegment);
HRESULT hr = S_OK;
DWORD dwNumPChannels = 0;
DWORD *pdwPChannels = NULL;
EnterCriticalSection(&m_CriticalSection);
CBand* pBand = BandList.GetHead();
for(; pBand; pBand = pBand->GetNext())
{
dwNumPChannels += pBand->GetPChannelCount();
}
if(dwNumPChannels > 0)
{
pdwPChannels = new DWORD[dwNumPChannels];
if(pdwPChannels)
{
pBand = BandList.GetHead();
for(DWORD dwPos = 0; pBand; pBand = pBand->GetNext())
{
DWORD dwNumWritten;
pBand->GetPChannels(pdwPChannels + dwPos, &dwNumWritten);
dwPos += dwNumWritten;
}
}
else
{
hr = E_OUTOFMEMORY;
}
if(SUCCEEDED(hr))
{
hr = pSegment->SetPChannelsUsed(dwNumPChannels, pdwPChannels);
}
delete [] pdwPChannels;
}
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::InitPlay
STDMETHODIMP CBandTrk::InitPlay(IDirectMusicSegmentState* pSegmentState,
IDirectMusicPerformance* pPerformance,
void** ppStateData,
DWORD dwVirtualTrackID,
DWORD dwFlags)
{
V_INAME(CBandTrk::InitPlay);
V_INTERFACE(pSegmentState);
V_INTERFACE(pPerformance);
assert(ppStateData);
EnterCriticalSection(&m_CriticalSection);
CBandTrkStateData* pBandTrkStateData = new CBandTrkStateData;
// If we can not allocate the memory we need to set ppStateData to NULL
// and return S_OK since the caller always expects S_OK;
*ppStateData = pBandTrkStateData;
if(pBandTrkStateData == NULL)
{
LeaveCriticalSection(&m_CriticalSection);
return E_OUTOFMEMORY;
}
// Need to save State Data
pBandTrkStateData->m_pSegmentState = pSegmentState;
pBandTrkStateData->m_pPerformance = pPerformance;
pBandTrkStateData->m_dwVirtualTrackID = dwVirtualTrackID; // Determines instance of Band Track
CBand* pBand = BandList.GetHead();
pBandTrkStateData->m_pNextBandToSPE = pBand;
BOOL fGlobal; // if the performance has been set with an autodownload preference,
// use that. otherwise, assume autodownloading is off, unless it has
// been locked (i.e. specified on the band track.)
if( SUCCEEDED( pPerformance->GetGlobalParam( GUID_PerfAutoDownload, &fGlobal, sizeof(BOOL) )))
{
if( !m_fLockAutoDownload )
{
// it might seem like we can just assign m_bAutoDownload = fGlobal,
// but that's bitten me before, so I'm being paranoid today. (markburt)
if( fGlobal )
{
m_bAutoDownload = true;
}
else
{
m_bAutoDownload = false;
}
}
}
else if( !m_fLockAutoDownload )
{
m_bAutoDownload = false;
}
// Call SetParam to download all instruments used by the track's bands
// This is the auto-download feature that can be turned off with a call to SetParam
if(m_bAutoDownload)
{
IDirectMusicAudioPath *pPath = NULL;
IDirectMusicSegmentState8 *pState8;
if (SUCCEEDED(pSegmentState->QueryInterface(IID_IDirectMusicSegmentState8,(void **)&pState8)))
{
pState8->GetObjectInPath(0,DMUS_PATH_AUDIOPATH,0,GUID_NULL,0,
IID_IDirectMusicAudioPath,(void **) &pPath);
pState8->Release();
}
if (pPath)
{
SetParam(GUID_DownloadToAudioPath,0,(void *)pPath);
pPath->Release();
}
else
{
SetParam(GUID_DownloadToAudioPath, 0, (void *)pPerformance);
}
}
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::EndPlay
STDMETHODIMP CBandTrk::EndPlay(void* pStateData)
{
assert(pStateData);
EnterCriticalSection(&m_CriticalSection);
// Call SetParam to unload all instruments used by the track's bands
// This is the auto-unload feature that can be turned off with a call to SetParam
if(m_bAutoDownload)
{
IDirectMusicPerformance *pPerformance = ((CBandTrkStateData *)pStateData)->m_pPerformance;
IDirectMusicSegmentState *pSegmentState = ((CBandTrkStateData *)pStateData)->m_pSegmentState;
IDirectMusicAudioPath *pPath = NULL;
IDirectMusicSegmentState8 *pState8;
if (SUCCEEDED(pSegmentState->QueryInterface(IID_IDirectMusicSegmentState8,(void **)&pState8)))
{
pState8->GetObjectInPath(0,DMUS_PATH_AUDIOPATH,0,GUID_NULL,0,
IID_IDirectMusicAudioPath,(void **) &pPath);
pState8->Release();
}
if (pPath)
{
SetParam(GUID_UnloadFromAudioPath,0,(void *)pPath);
pPath->Release();
}
else
{
SetParam(GUID_UnloadFromAudioPath, 0, (void *)pPerformance);
}
}
if(pStateData)
{
delete ((CBandTrkStateData *)pStateData);
}
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::PlayEx
STDMETHODIMP CBandTrk::PlayEx(void* pStateData,REFERENCE_TIME rtStart,
REFERENCE_TIME rtEnd,REFERENCE_TIME rtOffset,
DWORD dwFlags,IDirectMusicPerformance* pPerf,
IDirectMusicSegmentState* pSegSt,DWORD dwVirtualID)
{
HRESULT hr;
EnterCriticalSection(&m_CriticalSection);
if (dwFlags & DMUS_TRACKF_CLOCK)
{
// Convert all reference times to millisecond times. Then, just use same MUSIC_TIME
// variables.
hr = PlayMusicOrClock(pStateData,(MUSIC_TIME)(rtStart / REF_PER_MIL),(MUSIC_TIME)(rtEnd / REF_PER_MIL),
(MUSIC_TIME)(rtOffset / REF_PER_MIL),rtOffset,dwFlags,pPerf,pSegSt,dwVirtualID,TRUE);
}
else
{
hr = PlayMusicOrClock(pStateData,(MUSIC_TIME)rtStart,(MUSIC_TIME)rtEnd,
(MUSIC_TIME)rtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE);
}
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::Play
STDMETHODIMP CBandTrk::Play(
void *pStateData,
MUSIC_TIME mtStart,
MUSIC_TIME mtEnd,
MUSIC_TIME mtOffset,
DWORD dwFlags,
IDirectMusicPerformance* pPerf,
IDirectMusicSegmentState* pSegSt,
DWORD dwVirtualID)
{
EnterCriticalSection(&m_CriticalSection);
HRESULT hr = PlayMusicOrClock(pStateData,mtStart,mtEnd,mtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE);
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
HRESULT CBandTrk::PlayMusicOrClock(
void *pStateData,
MUSIC_TIME mtStart,
MUSIC_TIME mtEnd,
MUSIC_TIME mtOffset,
REFERENCE_TIME rtOffset,
DWORD dwFlags,
IDirectMusicPerformance* pPerf,
IDirectMusicSegmentState* pSegSt,
DWORD dwVirtualID,
bool fClockTime)
{
assert(pPerf);
assert(pSegSt);
assert(pStateData);
// Caller expects S_OK or S_END. Since we have no state info we can not do anything
if(pStateData == NULL)
{
return DMUS_S_END;
}
EnterCriticalSection(&m_CriticalSection);
if( dwFlags & (DMUS_TRACKF_SEEK | DMUS_TRACKF_FLUSH | DMUS_TRACKF_DIRTY |
DMUS_TRACKF_LOOP) )
{
// need to reset the PChannel Map in case of any of these flags.
CBand* pBand = BandList.GetHead();
DWORD dwGroupBits = 0xffffffff;
IDirectMusicSegment* pSeg;
if( SUCCEEDED(pSegSt->GetSegment(&pSeg)))
{
pSeg->GetTrackGroup(this, &dwGroupBits);
pSeg->Release();
}
for(; pBand; pBand = pBand->GetNext())
{
pBand->m_PChMap.Reset();
pBand->m_dwGroupBits = dwGroupBits;
}
}
CBandTrkStateData* pBandTrkStateData = (CBandTrkStateData *)pStateData;
// Seek if we're starting, looping, or if we've been reloaded
if ((dwFlags & DMUS_TRACKF_LOOP) || (dwFlags & DMUS_TRACKF_START) || (pBandTrkStateData->dwValidate != m_dwValidate))
{
// When we start playing a segment, we need to catch up with all the band changes
// that happened before the start point. The instruments that sound when we start
// playing in the middle of a segment should sound the same as if we had played the
// segment to that point from the beginning.
pBandTrkStateData->m_fPlayPreviousInSeek = !!(dwFlags & DMUS_TRACKF_START);
Seek(pBandTrkStateData, mtStart, mtOffset, rtOffset, fClockTime);
pBandTrkStateData->dwValidate = m_dwValidate; // if we were reloading, we're now adjusted
}
// Send all Patch changes between mtStart & mtEnd
// If any fail try next one
CBand* pBand = (CBand *)(pBandTrkStateData->m_pNextBandToSPE);
for( ; pBand && pBand->m_lTimeLogical < mtEnd;
pBand = pBand->GetNext())
{
pBand->SendMessages(pBandTrkStateData, mtOffset, rtOffset, fClockTime);
}
// Save position for next time
pBandTrkStateData->m_pNextBandToSPE = pBand;
LeaveCriticalSection(&m_CriticalSection);
return pBand == NULL ? DMUS_S_END : S_OK;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::GetParam
STDMETHODIMP CBandTrk::GetParam(REFGUID rguidDataType,
MUSIC_TIME mtTime,
MUSIC_TIME* pmtNext,
void* pData)
{
V_INAME(CBandTrk::GetParam);
V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME);
V_PTR_WRITE(pData,1);
V_REFGUID(rguidDataType);
HRESULT hr = S_OK;
EnterCriticalSection( &m_CriticalSection );
if (rguidDataType == GUID_BandParam)
{
CBand* pScan = BandList.GetHead();
if (pScan)
{
CBand* pBand = pScan;
for (pScan = pScan->GetNext(); pScan; pScan = pScan->GetNext())
{
if (mtTime < pScan->m_lTimeLogical) break;
pBand = pScan;
}
// make a copy of the band found
CBand *pNewBand = new CBand;
if (pNewBand)
{
CBandInstrument* pBandInstrument = pBand->m_BandInstrumentList.GetHead();
for(; pBandInstrument && SUCCEEDED(hr); pBandInstrument = pBandInstrument->GetNext())
{
hr = pNewBand->Load(pBandInstrument);
}
if (FAILED(hr))
{
// Don't leak.
delete pNewBand;
}
else
{
pNewBand->m_lTimeLogical = pBand->m_lTimeLogical;
pNewBand->m_lTimePhysical = pBand->m_lTimePhysical;
pNewBand->m_dwFlags |= DMB_LOADED;
pNewBand->m_dwMidiMode = pBand->m_dwMidiMode;
}
}
else
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
IDirectMusicBand* pIDMBand = NULL;
pNewBand->QueryInterface(IID_IDirectMusicBand, (void**)&pIDMBand);
// The constructor initialized the ref countto 1, so release the QI
pNewBand->Release();
DMUS_BAND_PARAM *pBandParam = reinterpret_cast<DMUS_BAND_PARAM *>(pData);
pBandParam->pBand = pIDMBand;
pBandParam->mtTimePhysical = pBand->m_lTimePhysical;
if (pmtNext)
{
*pmtNext = (pScan != NULL) ? pScan->m_lTimeLogical : 0;
}
hr = S_OK;
}
}
else
{
Trace(4,"Warning: Band Track unable to find Band for GetParam call.\n");
hr = DMUS_E_NOT_FOUND;
}
}
else
{
hr = DMUS_E_GET_UNSUPPORTED;
}
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::SetParam
STDMETHODIMP CBandTrk::SetParam(REFGUID rguidDataType,
MUSIC_TIME mtTime,
void* pData)
{
V_INAME(CBandTrk::SetParam);
V_REFGUID(rguidDataType);
HRESULT hr = S_OK;
if((pData == NULL)
&& (rguidDataType != GUID_Enable_Auto_Download)
&& (rguidDataType != GUID_Disable_Auto_Download)
&& (rguidDataType != GUID_Clear_All_Bands)
&& (rguidDataType != GUID_IgnoreBankSelectForGM))
{
Trace(1,"Error: Invalid NULL pointer passed to Band Track for SetParam call.\n");
return E_POINTER;
}
EnterCriticalSection(&m_CriticalSection);
if(rguidDataType == GUID_DownloadToAudioPath)
{
IDirectMusicAudioPath* pPath = (IDirectMusicAudioPath*)pData;
V_INTERFACE(pPath);
HRESULT hrFail = S_OK;
DWORD dwSuccess = 0;
CBand* pBand = BandList.GetHead();
for(; pBand; pBand = pBand->GetNext())
{
if (FAILED(hr = pBand->DownloadEx(pPath))) // If not S_OK, download is only partial.
{
hrFail = hr;
}
else
{
dwSuccess++;
}
}
// If we had a failure, return it if we had no successes.
// Else return S_FALSE for partial success.
if (FAILED(hrFail) && dwSuccess)
{
hr = S_FALSE;
}
}
else if(rguidDataType == GUID_UnloadFromAudioPath)
{
IDirectMusicAudioPath* pPath = (IDirectMusicAudioPath*)pData;
V_INTERFACE(pPath);
CBand* pBand = BandList.GetHead();
for(; pBand; pBand = pBand->GetNext())
{
pBand->UnloadEx(pPath);
}
}
else if(rguidDataType == GUID_Download)
{
IDirectMusicPerformance* pPerf = (IDirectMusicPerformance*)pData;
V_INTERFACE(pPerf);
CBand* pBand = BandList.GetHead();
for(; pBand; pBand = pBand->GetNext())
{
if (pBand->DownloadEx(pPerf) != S_OK) // If not S_OK, download is only partial.
{
hr = S_FALSE;
}
}
}
else if(rguidDataType == GUID_Unload)
{
IDirectMusicPerformance* pPerf = (IDirectMusicPerformance*)pData;
V_INTERFACE(pPerf);
CBand* pBand = BandList.GetHead();
for(; pBand; pBand = pBand->GetNext())
{
pBand->UnloadEx(pPerf);
}
}
else if(rguidDataType == GUID_Enable_Auto_Download)
{
m_bAutoDownload = true;
m_fLockAutoDownload = true;
}
else if(rguidDataType == GUID_Disable_Auto_Download)
{
m_bAutoDownload = false;
m_fLockAutoDownload = true;
}
else if(rguidDataType == GUID_Clear_All_Bands)
{
while(!BandList.IsEmpty())
{
CBand* pBand = BandList.RemoveHead();
pBand->Release();
}
}
else if(rguidDataType == GUID_BandParam)
{
DMUS_BAND_PARAM *pBandParam = reinterpret_cast<DMUS_BAND_PARAM *>(pData);
IDirectMusicBand *pBand = pBandParam->pBand;
V_INTERFACE(pBand);
// If you can QI pData for private interface IDirectMusicBandPrivate
// pBand is of type CBand.
IDirectMusicBandPrivate *pBandPrivate = NULL;
hr = pBand->QueryInterface(IID_IDirectMusicBandPrivate, (void **)&pBandPrivate);
if(FAILED(hr))
{
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
pBandPrivate->Release();
CBand *pBandObject = static_cast<CBand *>(pBand);
pBandObject->m_lTimeLogical = mtTime;
pBandObject->m_lTimePhysical = pBandParam->mtTimePhysical;
hr = AddBand(pBand);
}
else if(rguidDataType == GUID_IDirectMusicBand)
{
IDirectMusicBand *pBand = (IDirectMusicBand *)pData;
V_INTERFACE(pBand);
// If you can QI pData for private interface IDirectMusicBandPrivate
// pData is of type CBand.
IDirectMusicBandPrivate *pBandPrivate = NULL;
hr = pBand->QueryInterface(IID_IDirectMusicBandPrivate, (void **)&pBandPrivate);
if(FAILED(hr))
{
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
pBandPrivate->Release();
CBand *pBandObject = static_cast<CBand *>(pBand);
pBandObject->m_lTimeLogical = mtTime;
pBandObject->m_lTimePhysical = pBandObject->m_lTimeLogical;
hr = AddBand(pBand);
}
else if(rguidDataType == GUID_IgnoreBankSelectForGM)
{
CBand* pBand = BandList.GetHead();
for(; pBand; pBand = pBand->GetNext())
{
pBand->MakeGMOnly();
}
}
else if(rguidDataType == GUID_ConnectToDLSCollection)
{
IDirectMusicCollection* pCollect = (IDirectMusicCollection*)pData;
V_INTERFACE(pData);
CBand* pBand = BandList.GetHead();
for(; pBand; pBand = pBand->GetNext())
{
pBand->ConnectToDLSCollection(pCollect);
}
}
else
{
Trace(3,"Warning: Invalid SetParam call on Band Track, GUID is unknown.\n");
hr = DMUS_E_TYPE_UNSUPPORTED;
}
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::GetParamEx
STDMETHODIMP CBandTrk::GetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime,
REFERENCE_TIME* prtNext,void* pParam,void * pStateData, DWORD dwFlags)
{
HRESULT hr;
MUSIC_TIME mtNext;
if (dwFlags & DMUS_TRACK_PARAMF_CLOCK)
{
hr = GetParam(rguidType,(MUSIC_TIME) (rtTime / REF_PER_MIL), &mtNext, pParam);
if (prtNext)
{
*prtNext = mtNext * REF_PER_MIL;
}
}
else
{
hr = GetParam(rguidType,(MUSIC_TIME) rtTime, &mtNext, pParam);
if (prtNext)
{
*prtNext = mtNext;
}
}
return hr;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::SetParamEx
STDMETHODIMP CBandTrk::SetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime,
void* pParam, void * pStateData, DWORD dwFlags)
{
if (dwFlags & DMUS_TRACK_PARAMF_CLOCK)
{
rtTime /= REF_PER_MIL;
}
return SetParam(rguidType, (MUSIC_TIME) rtTime, pParam);
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::IsParamSupported
STDMETHODIMP CBandTrk::IsParamSupported(REFGUID rguidDataType)
{
V_INAME(CBandTrk::IsParamSupported);
V_REFGUID(rguidDataType);
// Return S_OK if the object supports the GUID and S_FALSE otherwise
if(rguidDataType == GUID_Download ||
rguidDataType == GUID_Unload ||
rguidDataType == GUID_DownloadToAudioPath ||
rguidDataType == GUID_UnloadFromAudioPath ||
rguidDataType == GUID_Enable_Auto_Download ||
rguidDataType == GUID_Disable_Auto_Download ||
rguidDataType == GUID_Clear_All_Bands ||
rguidDataType == GUID_IDirectMusicBand ||
rguidDataType == GUID_BandParam ||
rguidDataType == GUID_IgnoreBankSelectForGM ||
rguidDataType == GUID_ConnectToDLSCollection)
{
return S_OK;
}
else
{
return DMUS_E_TYPE_UNSUPPORTED;
}
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::AddNotificationType
STDMETHODIMP CBandTrk::AddNotificationType(REFGUID rguidNotify)
{
return E_NOTIMPL;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::RemoveNotificationType
STDMETHODIMP CBandTrk::RemoveNotificationType(REFGUID rguidNotify)
{
return E_NOTIMPL;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::Clone
STDMETHODIMP CBandTrk::Clone(MUSIC_TIME mtStart,
MUSIC_TIME mtEnd,
IDirectMusicTrack** ppTrack)
{
V_INAME(CBandTrk::Clone);
V_PTRPTR_WRITE(ppTrack);
if ((mtStart < 0 ) || (mtStart > mtEnd))
{
Trace(1,"Error: Invalid range %ld to %ld sent to Band Track Clone command.\n",mtStart,mtEnd);
return E_INVALIDARG;
}
HRESULT hr = E_OUTOFMEMORY;
IDirectMusicBandTrk *pBandTrack = NULL;
CBandTrk *pNew = new CBandTrk;
if (pNew)
{
hr = pNew->QueryInterface(IID_IDirectMusicBandTrk,(void**)&pBandTrack);
if(SUCCEEDED(hr))
{
hr = LoadClone(pBandTrack, mtStart, mtEnd);
if(SUCCEEDED(hr))
{
hr = pBandTrack->QueryInterface(IID_IDirectMusicTrack, (void **)ppTrack);
if (SUCCEEDED(hr))
{
pBandTrack->Release();
}
}
pBandTrack->Release();
}
if (FAILED(hr))
{
delete pNew;
}
}
return hr;
}
//////////////////////////////////////////////////////////////////////
// IDirectMusicCommon
//////////////////////////////////////////////////////////////////////
// CBandTrk::GetName
STDMETHODIMP CBandTrk::GetName(BSTR* pbstrName)
{
return E_NOTIMPL;
}
//////////////////////////////////////////////////////////////////////
// IDirectMusicBandTrk
//////////////////////////////////////////////////////////////////////
// CBandTrk::AddBand
STDMETHODIMP CBandTrk::AddBand(DMUS_IO_PATCH_ITEM* pPatchEvent)
{
if(pPatchEvent == NULL)
{
return E_POINTER;
}
CBand *pNewBand = new CBand;
HRESULT hr;
if(pNewBand == NULL)
{
hr = E_OUTOFMEMORY;
}
else
{
hr = pNewBand->Load(*pPatchEvent);
}
if(SUCCEEDED(hr))
{
hr = InsertBand(pNewBand);
}
if(FAILED(hr) && pNewBand)
{
delete pNewBand;
}
return hr;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::AddBand
HRESULT CBandTrk::AddBand(IDirectMusicBand* pIDMBand)
{
if(pIDMBand == NULL)
{
return E_POINTER;
}
// If you can QI pIDMBand for private interface IDirectMusicBandPrivate
// pIDMBand is of type CBand.
IDirectMusicBandPrivate* pIDMBandP = NULL;
HRESULT hr = pIDMBand->QueryInterface(IID_IDirectMusicBandPrivate, (void **)&pIDMBandP);
if(SUCCEEDED(hr))
{
pIDMBandP->Release();
CBand *pNewBand = (CBand *) pIDMBand;
pNewBand->AddRef();
hr = InsertBand(pNewBand);
if(FAILED(hr))
{
pNewBand->Release();
}
}
return hr;
}
//////////////////////////////////////////////////////////////////////
// Internal
//////////////////////////////////////////////////////////////////////
// CBandTrk::BuildDirectMusicBandList
// This method loads all of the bands.
HRESULT CBandTrk::BuildDirectMusicBandList(CRiffParser *pParser)
{
RIFFIO ckNext;
HRESULT hrDLS = S_OK;
HRESULT hr = S_OK;
pParser->EnterList(&ckNext);
while (pParser->NextChunk(&hr))
{
switch(ckNext.ckid)
{
case FOURCC_LIST :
switch(ckNext.fccType)
{
case DMUS_FOURCC_BAND_LIST:
hr = ExtractBand(pParser);
if (hr != S_OK)
{
hrDLS = hr;
}
break;
}
break;
}
}
pParser->LeaveList();
if (hr == S_OK && hrDLS != S_OK)
{
hr = hrDLS;
}
return hr;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::ExtractBand
HRESULT
CBandTrk::ExtractBand(CRiffParser *pParser)
{
HRESULT hrDLS = S_OK;
RIFFIO ckNext;
CBand *pBand = new CBand;
if(pBand == NULL)
{
return E_OUTOFMEMORY;
}
HRESULT hr = S_OK;
bool fFoundChunk2 = false;
pParser->EnterList(&ckNext);
while (pParser->NextChunk(&hr))
{
switch(ckNext.ckid)
{
case DMUS_FOURCC_BANDITEM_CHUNK2:
fFoundChunk2 = true;
DMUS_IO_BAND_ITEM_HEADER2 ioDMBndItemHdr2;
hr = pParser->Read(&ioDMBndItemHdr2, sizeof(DMUS_IO_BAND_ITEM_HEADER2));
if(SUCCEEDED(hr))
{
pBand->m_lTimeLogical = ioDMBndItemHdr2.lBandTimeLogical;
pBand->m_lTimePhysical = ioDMBndItemHdr2.lBandTimePhysical;
}
break;
case DMUS_FOURCC_BANDITEM_CHUNK:
// if there is both a CHUNK and a CHUNK2, use the info from CHUNK2
if (fFoundChunk2)
break;
DMUS_IO_BAND_ITEM_HEADER ioDMBndItemHdr;
hr = pParser->Read(&ioDMBndItemHdr, sizeof(DMUS_IO_BAND_ITEM_HEADER));
if(SUCCEEDED(hr))
{
pBand->m_lTimeLogical = ioDMBndItemHdr.lBandTime;
pBand->m_lTimePhysical = pBand->m_lTimeLogical;
}
break;
case FOURCC_RIFF:
switch(ckNext.fccType)
{
case DMUS_FOURCC_BAND_FORM:
pParser->SeekBack();
hr = LoadBand(pParser->GetStream(), pBand);
pParser->SeekForward();
if (hr != S_OK)
{
hrDLS = hr;
}
break;
}
break;
default:
break;
}
}
pParser->LeaveList();
if(SUCCEEDED(hr))
{
hr = AddBand(pBand);
}
pBand->Release();
if (hr == S_OK && hrDLS != S_OK)
{
hr = hrDLS;
}
return hr;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::LoadBand
HRESULT CBandTrk::LoadBand(IStream *pIStream, CBand* pBand)
{
assert(pIStream);
assert(pBand);
IPersistStream *pIPersistStream = NULL;
HRESULT hr = pBand->QueryInterface(IID_IPersistStream, (void **)&pIPersistStream);
if(SUCCEEDED(hr))
{
hr = pIPersistStream->Load(pIStream);
pIPersistStream->Release();
}
return hr;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::LoadClone
HRESULT CBandTrk::LoadClone(IDirectMusicBandTrk* pBandTrack,
MUSIC_TIME mtStart,
MUSIC_TIME mtEnd)
{
assert(pBandTrack);
assert(mtStart <= mtEnd);
EnterCriticalSection(&m_CriticalSection);
HRESULT hr = S_OK;
if (mtStart > 0)
{
// We will take all the bands before the start time and create a single new band
// at logical time zero, physical time either -1 or one tick before the physical time
// of the first band after the start time, that accumulates all the instrument changes
// from the earlier bands.
TList<SeekEvent> SEList; // Build a list of all the instrument changes for the new band
DWORD dwLastMidiMode = 0; // Keep track of the MIDI mode of the last band we encounter
for( CBand* pBand = BandList.GetHead();
pBand && pBand->m_lTimeLogical < mtStart;
pBand = pBand->GetNext())
{
for(CBandInstrument* pInstrument = (pBand->m_BandInstrumentList).GetHead();
pInstrument && SUCCEEDED(hr);
pInstrument = pInstrument->GetNext())
{
// replace if we already have an entry on that channel
hr = FindSEReplaceInstr(SEList,
pInstrument->m_dwPChannel,
pInstrument);
// otherwise add an entry
if(hr == S_FALSE)
{
TListItem<SeekEvent>* pSEListItem = new TListItem<SeekEvent>;
if(pSEListItem)
{
SeekEvent& rSeekEvent = pSEListItem->GetItemValue();
rSeekEvent.m_dwPChannel = pInstrument->m_dwPChannel;
rSeekEvent.m_pInstrument = pInstrument;
rSeekEvent.m_pParentBand = pBand;
dwLastMidiMode = pBand->m_dwMidiMode;
SEList.AddHead(pSEListItem);
}
else
{
hr = E_OUTOFMEMORY;
}
}
}
}
// Make sure the physical time of the new band is less than any bands being cloned.
MUSIC_TIME mtNewPhysicalTime = -1;
if (pBand && pBand->m_lTimePhysical <= mtStart)
{
mtNewPhysicalTime = (pBand->m_lTimePhysical - mtStart) - 1;
}
// Create the new band from the instrument list
TListItem<SeekEvent>* pSEListItem = SEList.GetHead();
if(SUCCEEDED(hr) && pSEListItem)
{
CBand *pNewBand = new CBand;
if(pNewBand)
{
for(; pSEListItem && SUCCEEDED(hr); pSEListItem = pSEListItem->GetNext())
{
SeekEvent& rSeekEvent = pSEListItem->GetItemValue();
hr = pNewBand->Load(rSeekEvent.m_pInstrument);
}
pNewBand->m_lTimeLogical = 0;
pNewBand->m_lTimePhysical = mtNewPhysicalTime;
pNewBand->m_dwFlags |= DMB_LOADED;
pNewBand->m_dwMidiMode = dwLastMidiMode;
if(SUCCEEDED(hr))
{
hr = pBandTrack->AddBand(pNewBand);
}
pNewBand->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
}
}
// Copy all the bands between the start and the end time
if(SUCCEEDED(hr))
{
for(CBand* pBand = BandList.GetHead();
pBand && SUCCEEDED(hr);
pBand = pBand->GetNext())
{
// If mtStart is 0, accept bands with negative times.
if ((!mtStart || (pBand->m_lTimeLogical >= mtStart)) && pBand->m_lTimeLogical < mtEnd)
{
CBand *pNewBand = new CBand;
if (pNewBand)
{
CBandInstrument* pBandInstrument = pBand->m_BandInstrumentList.GetHead();
for(; pBandInstrument && SUCCEEDED(hr); pBandInstrument = pBandInstrument->GetNext())
{
hr = pNewBand->Load(pBandInstrument);
}
pNewBand->m_lTimeLogical = pBand->m_lTimeLogical - mtStart;
pNewBand->m_lTimePhysical = pBand->m_lTimePhysical - mtStart;
pNewBand->m_dwFlags |= DMB_LOADED;
pNewBand->m_dwMidiMode = pBand->m_dwMidiMode;
if(SUCCEEDED(hr))
{
hr = pBandTrack->AddBand(pNewBand);
}
pNewBand->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
}
}
}
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::Seek
HRESULT CBandTrk::Seek(CBandTrkStateData* pBandTrkStateData,
MUSIC_TIME mtStart,
MUSIC_TIME mtOffset,
REFERENCE_TIME rtOffset,
bool fClockTime)
{
assert(pBandTrkStateData);
EnterCriticalSection(&m_CriticalSection);
HRESULT hr = S_OK;
CBand *pBand;
int iPrevBandCount = 0; // count how many bands before mtStart
for (pBand = BandList.GetHead();
pBand && pBand->m_lTimeLogical < mtStart;
pBand = pBand->GetNext())
{
++iPrevBandCount;
}
// pBand now holds the first band >= mtStart (or NULL if none)
// This is the next band that will be played.
assert(!pBand || pBand->m_lTimeLogical >= mtStart);
if (pBandTrkStateData->m_fPlayPreviousInSeek)
{
// When this flag is on not only do we need to find the first band, but we also
// need to play all the bands before the start point and schedule them to play
// in the correct order just beforehand.
// (Note that we're going to order them according to their logical times. If
// two bands's logical/physical times cross each other we'll play them in
// incorrect order in terms of physical time. That's OK because giving
// band A with a logical time before band B, yet giving A a physical time
// after B is considered an authoring inconsistency. We'll play band A first.)
// We will line up the bands just before the following time...
MUSIC_TIME mtPrevBandQueueStart =
(pBand && pBand->m_lTimePhysical < mtStart)
? pBand->m_lTimePhysical // put previous bands before next band to play if (due to anticipation) its physical time precedes the start time we're seeking
: mtStart; // otherwise put them just before the start time
for (pBand = BandList.GetHead();
pBand && pBand->m_lTimeLogical < mtStart;
pBand = pBand->GetNext())
{
CBandInstrument* pInstrument = (pBand->m_BandInstrumentList).GetHead();
for (; pInstrument && SUCCEEDED(hr); pInstrument = pInstrument->GetNext())
{
pBand->SendInstrumentAtTime(pInstrument, pBandTrkStateData, mtPrevBandQueueStart - iPrevBandCount, mtOffset, rtOffset, fClockTime);
}
--iPrevBandCount;
}
assert(iPrevBandCount == 0);
}
if(SUCCEEDED(hr))
{
// Set the state data to the next band to play
assert(!pBand || pBand->m_lTimeLogical >= mtStart);
pBandTrkStateData->m_pNextBandToSPE = pBand;
}
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::FindSEReplaceInstr
// If SEList contains an entry on channel dwPChannel, replace the instrument with pInstrument and return S_OK
// Otherwise return S_FALSE
HRESULT CBandTrk::FindSEReplaceInstr(TList<SeekEvent>& SEList,
DWORD dwPChannel,
CBandInstrument* pInstrument)
{
assert(pInstrument);
EnterCriticalSection(&m_CriticalSection);
TListItem<SeekEvent>* pSEListItem = SEList.GetHead();
for( ; pSEListItem; pSEListItem = pSEListItem->GetNext())
{
SeekEvent& rSeekEvent = pSEListItem->GetItemValue();
if(rSeekEvent.m_dwPChannel == dwPChannel)
{
rSeekEvent.m_pInstrument = pInstrument;
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
}
LeaveCriticalSection(&m_CriticalSection);
return S_FALSE;
}
//////////////////////////////////////////////////////////////////////
// CBandTrk::InsertBand
HRESULT CBandTrk::InsertBand(CBand* pNewBand)
{
if (!pNewBand) return E_POINTER;
EnterCriticalSection(&m_CriticalSection);
TListItem<StampedGMGSXG>* pPair = m_MidiModeList.GetHead();
for ( ; pPair; pPair = pPair->GetNext() )
{
StampedGMGSXG& rPair = pPair->GetItemValue();
if (rPair.mtTime > pNewBand->m_lTimeLogical)
{
break;
}
pNewBand->SetGMGSXGMode(rPair.dwMidiMode);
}
CBand* pBand = BandList.GetHead();
CBand* pPrevBand = NULL;
if(pBand == NULL)
{
// Handles case where there is no band in the list
BandList.AddHead(pNewBand);
}
else
{
while(pBand != NULL && pNewBand->m_lTimeLogical > pBand->m_lTimeLogical)
{
pPrevBand = pBand;
pBand = pBand->GetNext();
}
if(pPrevBand)
{
// Handles the cases of inserting a band in the middle of list
// and at the end
CBand* pTemp = pPrevBand->GetNext();
pPrevBand->SetNext(pNewBand);
pNewBand->SetNext(pTemp);
}
else
{
// Handles case where pNewBand->m_lTimeLogical < all pBand->m_lTimeLogical in list
BandList.AddHead(pNewBand);
}
}
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
STDMETHODIMP CBandTrk::Compose(
IUnknown* pContext,
DWORD dwTrackGroup,
IDirectMusicTrack** ppResultTrack)
{
return E_NOTIMPL;
}
STDMETHODIMP CBandTrk::Join(
IDirectMusicTrack* pNewTrack,
MUSIC_TIME mtJoin,
IUnknown* pContext,
DWORD dwTrackGroup,
IDirectMusicTrack** ppResultTrack)
{
V_INAME(IDirectMusicTrack::Join);
V_INTERFACE(pNewTrack);
V_INTERFACE_OPT(pContext);
V_PTRPTR_WRITE_OPT(ppResultTrack);
HRESULT hr = S_OK;
EnterCriticalSection(&m_CriticalSection);
if (ppResultTrack)
{
hr = Clone(0, mtJoin, ppResultTrack);
if (SUCCEEDED(hr))
{
hr = ((CBandTrk*)*ppResultTrack)->JoinInternal(pNewTrack, mtJoin, dwTrackGroup);
}
}
else
{
hr = JoinInternal(pNewTrack, mtJoin, dwTrackGroup);
}
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
HRESULT CBandTrk::JoinInternal(
IDirectMusicTrack* pNewTrack,
MUSIC_TIME mtJoin,
DWORD dwTrackGroup)
{
HRESULT hr = S_OK;
CBandTrk* pOtherTrack = (CBandTrk*)pNewTrack;
for(CBand* pBand = pOtherTrack->BandList.GetHead();
pBand && SUCCEEDED(hr);
pBand = pBand->GetNext())
{
CBand *pNewBand = new CBand;
if (pNewBand)
{
CBandInstrument* pBandInstrument = pBand->m_BandInstrumentList.GetHead();
for(; pBandInstrument && SUCCEEDED(hr); pBandInstrument = pBandInstrument->GetNext())
{
hr = pNewBand->Load(pBandInstrument);
}
pNewBand->m_lTimeLogical = pBand->m_lTimeLogical + mtJoin;
pNewBand->m_lTimePhysical = pBand->m_lTimePhysical + mtJoin;
pNewBand->m_dwFlags |= DMB_LOADED;
pNewBand->m_dwMidiMode = pBand->m_dwMidiMode;
if(SUCCEEDED(hr))
{
hr = AddBand(pNewBand);
}
pNewBand->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}