1522 lines
44 KiB
C++
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;
|
|
}
|