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