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

593 lines
14 KiB
C++

// READ THIS!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// 4530: C++ exception handler used, but unwind semantics are not enabled. Specify -GX
//
// We disable this because we use exceptions and do *not* specify -GX (USE_NATIVE_EH in
// sources).
//
// The one place we use exceptions is around construction of objects that call
// InitializeCriticalSection. We guarantee that it is safe to use in this case with
// the restriction given by not using -GX (automatic objects in the call chain between
// throw and handler are not destructed). Turning on -GX buys us nothing but +10% to code
// size because of the unwind code.
//
// Any other use of exceptions must follow these restrictions or -GX must be turned on.
//
// READ THIS!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
#pragma warning(disable:4530)
// MarkTrk.cpp : Implementation of CMarkerTrack
#include "dmime.h"
#include "..\shared\dmstrm.h"
#include "MarkTrk.h"
#include "dmusici.h"
#include "dmusicf.h"
#include "debug.h"
#include "..\shared\Validate.h"
#include "debug.h"
#define ASSERT assert
/////////////////////////////////////////////////////////////////////////////
// CMarkerTrack
void CMarkerTrack::Construct()
{
InterlockedIncrement(&g_cComponent);
m_cRef = 1;
m_fCSInitialized = FALSE;
InitializeCriticalSection(&m_CrSec);
m_fCSInitialized = TRUE;
m_dwValidate = 0;
}
CMarkerTrack::CMarkerTrack()
{
Construct();
}
CMarkerTrack::CMarkerTrack(
CMarkerTrack *pSourceTrack, MUSIC_TIME mtStart, MUSIC_TIME mtEnd)
{
Construct();
// Clone the valid start point list.
CValidStartItem* pVScan = pSourceTrack->m_ValidStartList.GetHead();
CValidStartItem* pVPrevious = NULL;
for(; pVScan; pVScan = pVScan->GetNext())
{
if (pVScan->m_ValidStart.mtTime < mtStart)
{
pVPrevious = pVScan;
}
else if (pVScan->m_ValidStart.mtTime < mtEnd)
{
if (pVScan->m_ValidStart.mtTime == mtStart)
{
pVPrevious = NULL;
}
CValidStartItem* pNew = new CValidStartItem;
if (pNew)
{
pNew->m_ValidStart.mtTime = pVScan->m_ValidStart.mtTime - mtStart;
m_ValidStartList.AddHead(pNew); // instead of AddTail, which is n^2. We reverse below.
}
}
else break;
}
m_ValidStartList.Reverse(); // Now, put list in order.
// Then, install the time signature that precedes the clone.
if (pVPrevious)
{
CValidStartItem* pNew = new CValidStartItem;
if (pNew)
{
pNew->m_ValidStart.mtTime = 0;
m_ValidStartList.AddHead(pNew);
}
}
// Clone the play marker list. Gee, this is identical code...
CPlayMarkerItem* pPScan = pSourceTrack->m_PlayMarkerList.GetHead();
CPlayMarkerItem* pPPrevious = NULL;
for(; pPScan; pPScan = pPScan->GetNext())
{
if (pPScan->m_PlayMarker.mtTime < mtStart)
{
pPPrevious = pPScan;
}
else if (pPScan->m_PlayMarker.mtTime < mtEnd)
{
if (pPScan->m_PlayMarker.mtTime == mtStart)
{
pPPrevious = NULL;
}
CPlayMarkerItem* pNew = new CPlayMarkerItem;
if (pNew)
{
pNew->m_PlayMarker.mtTime = pPScan->m_PlayMarker.mtTime - mtStart;
m_PlayMarkerList.AddHead(pNew); // instead of AddTail, which is n^2. We reverse below.
}
}
else break;
}
m_PlayMarkerList.Reverse(); // Now, put list in order.
// Then, install the time signature that precedes the clone.
if (pPPrevious)
{
CPlayMarkerItem* pNew = new CPlayMarkerItem;
if (pNew)
{
pNew->m_PlayMarker.mtTime = 0;
m_PlayMarkerList.AddHead(pNew);
}
}
}
void CMarkerTrack::Clear()
{
CValidStartItem* pStart;
while( pStart = m_ValidStartList.RemoveHead() )
{
delete pStart;
}
CPlayMarkerItem* pPlay;
while( pPlay = m_PlayMarkerList.RemoveHead() )
{
delete pPlay;
}
}
CMarkerTrack::~CMarkerTrack()
{
Clear();
if (m_fCSInitialized)
{
DeleteCriticalSection(&m_CrSec);
}
InterlockedDecrement(&g_cComponent);
}
STDMETHODIMP CMarkerTrack::QueryInterface(
const IID &iid, // @parm Interface to query for
void **ppv) // @parm The requested interface will be returned here
{
V_INAME(CMarkerTrack::QueryInterface);
V_PTRPTR_WRITE(ppv);
V_REFGUID(iid);
if (iid == IID_IUnknown || iid == IID_IDirectMusicTrack)
{
*ppv = static_cast<IDirectMusicTrack*>(this);
} else
if (iid == IID_IPersistStream)
{
*ppv = static_cast<IPersistStream*>(this);
} else
{
*ppv = NULL;
Trace(4,"Warning: Request to query unknown interface on Marker Track\n");
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(this)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) CMarkerTrack::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) CMarkerTrack::Release()
{
if (!InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}
return m_cRef;
}
/////////////////////////////////////////////////////////////////////////////
// IPersist
HRESULT CMarkerTrack::GetClassID( CLSID* pClassID )
{
V_INAME(CMarkerTrack::GetClassID);
V_PTR_WRITE(pClassID, CLSID);
*pClassID = CLSID_DirectMusicMarkerTrack;
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// IPersistStream functions
HRESULT CMarkerTrack::IsDirty()
{
return S_FALSE;
}
HRESULT CMarkerTrack::Load( IStream* pIStream )
{
V_INAME(CMarkerTrack::Load);
V_INTERFACE(pIStream);
CRiffParser Parser(pIStream);
EnterCriticalSection(&m_CrSec);
m_dwValidate++; // used to validate state data that's out there
RIFFIO ckMain;
HRESULT hr = S_OK;
Parser.EnterList(&ckMain);
if (Parser.NextChunk(&hr) && (ckMain.fccType == DMUS_FOURCC_MARKERTRACK_LIST))
{
Clear();
RIFFIO ckNext; // Descends into the children chunks.
Parser.EnterList(&ckNext);
while (Parser.NextChunk(&hr))
{
switch(ckNext.ckid)
{
case DMUS_FOURCC_VALIDSTART_CHUNK :
hr = LoadValidStartList(&Parser,ckNext.cksize);
break;
case DMUS_FOURCC_PLAYMARKER_CHUNK :
hr = LoadPlayMarkerList(&Parser,ckNext.cksize);
break;
}
}
Parser.LeaveList();
}
else
{
Trace(1,"Error: Invalid Marker Track.\n");
hr = DMUS_E_CHUNKNOTFOUND;
}
Parser.LeaveList();
LeaveCriticalSection(&m_CrSec);
return hr;
}
HRESULT CMarkerTrack::LoadPlayMarkerList( CRiffParser *pParser, long lChunkSize )
{
HRESULT hr;
// copy contents of the stream into the list.
DWORD dwSubSize;
// read in the size of the data structures
hr = pParser->Read( &dwSubSize, sizeof(DWORD));
if (SUCCEEDED(hr))
{
lChunkSize -= sizeof(DWORD);
DWORD dwRead, dwSeek;
if( dwSubSize > sizeof(DMUS_IO_PLAY_MARKER) )
{
dwRead = sizeof(DMUS_IO_PLAY_MARKER);
dwSeek = dwSubSize - dwRead;
}
else
{
dwRead = dwSubSize;
dwSeek = 0;
}
if( 0 == dwRead )
{
Trace(1,"Error: Invalid Marker Track.\n");
hr = DMUS_E_CANNOTREAD;
}
else
{
while( lChunkSize > 0 )
{
CPlayMarkerItem *pNew = new CPlayMarkerItem;
if (pNew)
{
if( FAILED( pParser->Read( &pNew->m_PlayMarker, dwRead)))
{
delete pNew;
hr = DMUS_E_CANNOTREAD;
break;
}
m_PlayMarkerList.AddHead(pNew); // Insert in reverse order for speed.
lChunkSize -= dwRead;
if( dwSeek )
{
if( FAILED( pParser->Skip(dwSeek)))
{
hr = DMUS_E_CANNOTSEEK;
break;
}
lChunkSize -= dwSeek;
}
}
}
m_PlayMarkerList.Reverse(); // Reverse to put in time order.
}
}
return hr;
}
HRESULT CMarkerTrack::LoadValidStartList( CRiffParser *pParser, long lChunkSize )
{
HRESULT hr;
// copy contents of the stream into the list.
DWORD dwSubSize;
// read in the size of the data structures
hr = pParser->Read( &dwSubSize, sizeof(DWORD));
if (SUCCEEDED(hr))
{
lChunkSize -= sizeof(DWORD);
DWORD dwRead, dwSeek;
if( dwSubSize > sizeof(DMUS_IO_VALID_START) )
{
dwRead = sizeof(DMUS_IO_VALID_START);
dwSeek = dwSubSize - dwRead;
}
else
{
dwRead = dwSubSize;
dwSeek = 0;
}
if( 0 == dwRead )
{
hr = DMUS_E_CANNOTREAD;
}
else
{
while( lChunkSize > 0 )
{
CValidStartItem *pNew = new CValidStartItem;
if (pNew)
{
if( FAILED( pParser->Read( &pNew->m_ValidStart, dwRead)))
{
delete pNew;
hr = DMUS_E_CANNOTREAD;
break;
}
m_ValidStartList.AddHead(pNew); // Insert in reverse order for speed.
lChunkSize -= dwRead;
if( dwSeek )
{
if( FAILED( pParser->Skip(dwSeek)))
{
hr = DMUS_E_CANNOTSEEK;
break;
}
lChunkSize -= dwSeek;
}
}
}
m_ValidStartList.Reverse(); // Reverse to put in time order.
}
}
return hr;
}
HRESULT CMarkerTrack::Save( IStream* pIStream, BOOL fClearDirty )
{
return E_NOTIMPL;
}
HRESULT CMarkerTrack::GetSizeMax( ULARGE_INTEGER FAR* pcbSize )
{
return E_NOTIMPL;
}
// IDirectMusicTrack
HRESULT STDMETHODCALLTYPE CMarkerTrack::IsParamSupported(
/* [in] */ REFGUID rguid)
{
V_INAME(CMarkerTrack::IsParamSupported);
V_REFGUID(rguid);
if ((rguid == GUID_Valid_Start_Time) ||
(rguid == GUID_Play_Marker))
return S_OK;
return DMUS_E_TYPE_UNSUPPORTED;
}
//////////////////////////////////////////////////////////////////////
// IDirectMusicTrack::Init
HRESULT CMarkerTrack::Init(
/* [in] */ IDirectMusicSegment *pSegment)
{
return S_OK;
}
HRESULT CMarkerTrack::InitPlay(
/* [in] */ IDirectMusicSegmentState *pSegmentState,
/* [in] */ IDirectMusicPerformance *pPerformance,
/* [out] */ void **ppStateData,
/* [in] */ DWORD dwTrackID,
/* [in] */ DWORD dwFlags)
{
return S_OK;
}
HRESULT CMarkerTrack::EndPlay(
/* [in] */ void *pStateData)
{
return S_OK;
}
HRESULT CMarkerTrack::Play(
/* [in] */ void *pStateData,
/* [in] */ MUSIC_TIME mtStart,
/* [in] */ MUSIC_TIME mtEnd,
/* [in] */ MUSIC_TIME mtOffset,
DWORD dwFlags,
IDirectMusicPerformance* pPerf,
IDirectMusicSegmentState* pSegSt,
DWORD dwVirtualID
)
{
return S_OK;
}
HRESULT CMarkerTrack::GetParam(
REFGUID rguid,
MUSIC_TIME mtTime,
MUSIC_TIME* pmtNext,
void *pData)
{
V_INAME(CMarkerTrack::GetParam);
V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME);
V_REFGUID(rguid);
HRESULT hr = DMUS_E_GET_UNSUPPORTED;
EnterCriticalSection(&m_CrSec);
if( NULL == pData )
{
hr = E_POINTER;
}
else if( GUID_Valid_Start_Time == rguid )
{
DMUS_VALID_START_PARAM* pValidStartData = (DMUS_VALID_START_PARAM*)pData;
CValidStartItem* pScan = m_ValidStartList.GetHead();
for (; pScan; pScan = pScan->GetNext())
{
if (pScan->m_ValidStart.mtTime >= mtTime)
{
pValidStartData->mtTime = pScan->m_ValidStart.mtTime - mtTime;
break;
}
}
if (pScan)
{
if (pmtNext)
{
if (pScan && (pScan = pScan->GetNext()))
{
*pmtNext = pScan->m_ValidStart.mtTime - mtTime;
}
else
{
*pmtNext = 0;
}
}
hr = S_OK;
}
else
{
hr = DMUS_E_NOT_FOUND;
}
}
else if( GUID_Play_Marker == rguid )
{
// This is a little different. The marker should be the one in existence
// BEFORE, not after the requested time.
DMUS_PLAY_MARKER_PARAM* pPlayMarkerData = (DMUS_PLAY_MARKER_PARAM*)pData;
CPlayMarkerItem* pScan = m_PlayMarkerList.GetHead();
CPlayMarkerItem* pNext;
// For fallback, treat it as if there were a marker at the start of the segment, but return S_FALSE.
hr = S_FALSE;
pPlayMarkerData->mtTime = -mtTime;
for (; pScan; pScan = pNext)
{
pNext = pScan->GetNext();
if (pScan->m_PlayMarker.mtTime <= mtTime)
{
if (!pNext || (pNext->m_PlayMarker.mtTime > mtTime))
{
pPlayMarkerData->mtTime = pScan->m_PlayMarker.mtTime - mtTime;
if (pmtNext && pNext)
{
*pmtNext = pNext->m_PlayMarker.mtTime - mtTime;
}
hr = S_OK;
break;
}
}
else
{
// Didn't find a marker before the requested time.
if (pmtNext)
{
*pmtNext = pScan->m_PlayMarker.mtTime - mtTime;
}
break;
}
}
}
#ifdef DBG
if (hr == DMUS_E_GET_UNSUPPORTED)
{
Trace(1,"Error: MarkerTrack does not support requested GetParam call.\n");
}
#endif
LeaveCriticalSection(&m_CrSec);
return hr;
}
HRESULT CMarkerTrack::SetParam(
REFGUID rguid,
MUSIC_TIME mtTime,
void *pData)
{
return DMUS_E_SET_UNSUPPORTED;
}
HRESULT STDMETHODCALLTYPE CMarkerTrack::AddNotificationType(
/* [in] */ REFGUID rguidNotification)
{
return S_FALSE;
}
HRESULT STDMETHODCALLTYPE CMarkerTrack::RemoveNotificationType(
/* [in] */ REFGUID rguidNotification)
{
return S_FALSE;
}
HRESULT STDMETHODCALLTYPE CMarkerTrack::Clone(
MUSIC_TIME mtStart,
MUSIC_TIME mtEnd,
IDirectMusicTrack** ppTrack)
{
V_INAME(IDirectMusicTrack::Clone);
V_PTRPTR_WRITE(ppTrack);
HRESULT hr = S_OK;
if(mtStart < 0 )
{
Trace(1,"Error: Unable to clone marker track because the start point is less than 0.\n");
return E_INVALIDARG;
}
if(mtStart > mtEnd)
{
Trace(1,"Error: Unable to clone marker track because the start point is greater than the length.\n");
return E_INVALIDARG;
}
EnterCriticalSection(&m_CrSec);
CMarkerTrack *pDM;
try
{
pDM = new CMarkerTrack(this, mtStart, mtEnd);
}
catch( ... )
{
pDM = NULL;
}
LeaveCriticalSection(&m_CrSec);
if (pDM == NULL) {
return E_OUTOFMEMORY;
}
hr = pDM->QueryInterface(IID_IDirectMusicTrack, (void**)ppTrack);
pDM->Release();
return hr;
}