windows-nt/Source/XPSP1/NT/multimedia/directx/dmusic/dmime/tempotrk.cpp

986 lines
28 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
// Copyright (c) 1998-1999 Microsoft Corporation
// 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)
// TempoTrk.cpp : Implementation of CTempoTrack
#include "dmime.h"
#include "TempoTrk.h"
#include "dmusici.h"
#include "dmusicf.h"
#include "debug.h"
#include "dmperf.h"
#include "..\shared\Validate.h"
#include "debug.h"
#define ASSERT assert
/////////////////////////////////////////////////////////////////////////////
// CTempoTrack
void CTempoTrack::Construct()
{
InterlockedIncrement(&g_cComponent);
m_cRef = 1;
m_dwValidate = 0;
m_fCSInitialized = FALSE;
InitializeCriticalSection(&m_CrSec);
m_fCSInitialized = TRUE;
}
CTempoTrack::CTempoTrack()
{
Construct();
m_fActive = TRUE;
m_fStateSetBySetParam = FALSE;
}
CTempoTrack::CTempoTrack(
const CTempoTrack& rTrack, MUSIC_TIME mtStart, MUSIC_TIME mtEnd)
{
Construct();
m_fActive = rTrack.m_fActive;
m_fStateSetBySetParam = rTrack.m_fStateSetBySetParam;
TListItem<DMUS_IO_TEMPO_ITEM>* pScan = rTrack.m_TempoEventList.GetHead();
//1////////////////////////////////////////
TListItem<DMUS_IO_TEMPO_ITEM>* pPrevious = NULL;
//1////////////////////////////////////////
for(; pScan; pScan = pScan->GetNext())
{
DMUS_IO_TEMPO_ITEM& rScan = pScan->GetItemValue();
//2////////////////////////////////////////
if (rScan.lTime < mtStart)
{
pPrevious = pScan;
}
//2////////////////////////////////////////
else if (rScan.lTime < mtEnd)
{
//3////////////////////////////////////////
if (rScan.lTime == mtStart)
{
pPrevious = NULL;
}
//3////////////////////////////////////////
TListItem<DMUS_IO_TEMPO_ITEM>* pNew = new TListItem<DMUS_IO_TEMPO_ITEM>;
if (pNew)
{
DMUS_IO_TEMPO_ITEM& rNew = pNew->GetItemValue();
memcpy( &rNew, &rScan, sizeof(DMUS_IO_TEMPO_ITEM) );
rNew.lTime = rScan.lTime - mtStart;
m_TempoEventList.AddHead(pNew); // instead of AddTail, which is n^2. We reverse below.
}
}
else break;
}
m_TempoEventList.Reverse(); // for above AddHead.
//4////////////////////////////////////////
if (pPrevious)
{
DMUS_IO_TEMPO_ITEM& rPrevious = pPrevious->GetItemValue();
TListItem<DMUS_IO_TEMPO_ITEM>* pNew = new TListItem<DMUS_IO_TEMPO_ITEM>;
if (pNew)
{
DMUS_IO_TEMPO_ITEM& rNew = pNew->GetItemValue();
memcpy( &rNew, &rPrevious, sizeof(DMUS_IO_TEMPO_ITEM) );
rNew.lTime = 0;
m_TempoEventList.AddHead(pNew);
}
}
//4////////////////////////////////////////
}
CTempoTrack::~CTempoTrack()
{
if (m_fCSInitialized)
{
DeleteCriticalSection(&m_CrSec);
}
InterlockedDecrement(&g_cComponent);
}
// @method:(INTERNAL) HRESULT | IDirectMusicTempoTrack | QueryInterface | Standard QueryInterface implementation for <i IDirectMusicTempoTrack>
//
// @parm const IID & | iid | Interface to query for
// @parm void ** | ppv | The requested interface will be returned here
//
// @rdesc Returns one of the following:
//
// @flag S_OK | If the interface is supported and was returned
// @flag E_NOINTERFACE | If the object does not support the given interface.
//
// @mfunc:(INTERNAL)
//
//
STDMETHODIMP CTempoTrack::QueryInterface(
const IID &iid, // @parm Interface to query for
void **ppv) // @parm The requested interface will be returned here
{
V_INAME(CTempoTrack::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_IPersistStream)
{
*ppv = static_cast<IPersistStream*>(this);
} else
{
*ppv = NULL;
Trace(4,"Warning: Request to query unknown interface on Tempo Track\n");
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(this)->AddRef();
return S_OK;
}
// @method:(INTERNAL) HRESULT | IDirectMusicTempoTrack | AddRef | Standard AddRef implementation for <i IDirectMusicTempoTrack>
//
// @rdesc Returns the new reference count for this object.
//
// @mfunc:(INTERNAL)
//
//
STDMETHODIMP_(ULONG) CTempoTrack::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
// @method:(INTERNAL) HRESULT | IDirectMusicTempoTrack | Release | Standard Release implementation for <i IDirectMusicTempoTrack>
//
// @rdesc Returns the new reference count for this object.
//
// @mfunc:(INTERNAL)
//
//
STDMETHODIMP_(ULONG) CTempoTrack::Release()
{
if (!InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}
return m_cRef;
}
/////////////////////////////////////////////////////////////////////////////
// IPersist
HRESULT CTempoTrack::GetClassID( CLSID* pClassID )
{
V_INAME(CTempoTrack::GetClassID);
V_PTR_WRITE(pClassID, CLSID);
*pClassID = CLSID_DirectMusicTempoTrack;
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// IPersistStream functions
HRESULT CTempoTrack::IsDirty()
{
return S_FALSE;
}
/*
@method HRESULT | ITempoTrack | Load |
Call this with an IStream filled with DMUS_IO_TEMPO_ITEM's, sorted in time order.
@parm IStream* | pIStream |
A stream of DMUS_IO_TEMPO_ITEM's, sorted in time order. The seek pointer should be
set to the first event. The stream should only contain Tempo events and
nothing more.
@rvalue E_INVALIDARG | If pIStream == NULL
@rvalue S_OK
@comm The <p pIStream> will be AddRef'd inside this function and held
until the TempoTrack is released.
*/
HRESULT CTempoTrack::Load( IStream* pIStream )
{
V_INAME(CTempoTrack::Load);
V_INTERFACE(pIStream);
HRESULT hr = S_OK;
EnterCriticalSection(&m_CrSec);
m_dwValidate++; // used to validate state data that's out there
if( m_TempoEventList.GetHead() )
{
TListItem<DMUS_IO_TEMPO_ITEM>* pItem;
while( pItem = m_TempoEventList.RemoveHead() )
{
delete pItem;
}
}
// copy contents of the stream into the list.
LARGE_INTEGER li;
DMUS_IO_TEMPO_ITEM tempoEvent;
// read in the chunk id
DWORD dwChunk, dwSubSize;
long lSize;
pIStream->Read( &dwChunk, sizeof(DWORD), NULL );
if( dwChunk != DMUS_FOURCC_TEMPO_TRACK )
{
Trace(1,"Error: Invalid data in tempo track.\n");
LeaveCriticalSection(&m_CrSec);
return DMUS_E_CHUNKNOTFOUND;
}
// read in the overall size
pIStream->Read( &lSize, sizeof(long), NULL );
// read in the size of the data structures
if( FAILED( pIStream->Read( &dwSubSize, sizeof(DWORD), NULL )))
{
// Check to make sure our reads are succeeding (we can safely
// assume the previous reads worked if we got this far.)
Trace(1,"Error: Unable to read tempo track.\n");
LeaveCriticalSection(&m_CrSec);
return DMUS_E_CANNOTREAD;
}
lSize -= sizeof(DWORD);
DWORD dwRead, dwSeek;
if( dwSubSize > sizeof(DMUS_IO_TEMPO_ITEM) )
{
dwRead = sizeof(DMUS_IO_TEMPO_ITEM);
dwSeek = dwSubSize - dwRead;
li.HighPart = 0;
li.LowPart = dwSeek;
}
else
{
dwRead = dwSubSize;
dwSeek = 0;
}
if( dwRead )
{
while( lSize > 0 )
{
if( FAILED( pIStream->Read( &tempoEvent, dwRead, NULL )))
{
Trace(1,"Error: Failure reading tempo track.\n");
hr = DMUS_E_CANNOTREAD;
break;
}
lSize -= dwRead;
if( dwSeek )
{
if( FAILED( pIStream->Seek( li, STREAM_SEEK_CUR, NULL )))
{
Trace(1,"Error: Failure reading tempo track.\n");
hr = DMUS_E_CANNOTSEEK;
break;
}
lSize -= dwSeek;
}
TListItem<DMUS_IO_TEMPO_ITEM>* pNew =
new TListItem<DMUS_IO_TEMPO_ITEM>(tempoEvent);
if (pNew)
{
m_TempoEventList.AddHead(pNew); // instead of AddTail, which is n^2. We reverse below.
}
}
m_TempoEventList.Reverse();
}
else
{
Trace(1,"Error: Failure reading tempo track.\n");
hr = DMUS_E_CANNOTREAD;
}
LeaveCriticalSection(&m_CrSec);
return hr;
}
HRESULT CTempoTrack::Save( IStream* pIStream, BOOL fClearDirty )
{
return E_NOTIMPL;
}
HRESULT CTempoTrack::GetSizeMax( ULARGE_INTEGER FAR* pcbSize )
{
return E_NOTIMPL;
}
// IDirectMusicTrack
HRESULT STDMETHODCALLTYPE CTempoTrack::IsParamSupported(
/* [in] */ REFGUID rguid)
{
V_INAME(IDirectMusicTrack::IsParamSupported);
V_REFGUID(rguid);
if (m_fStateSetBySetParam)
{
if( m_fActive )
{
if( rguid == GUID_DisableTempo ) return S_OK;
if( rguid == GUID_TempoParam ) return S_OK;
if( rguid == GUID_PrivateTempoParam ) return S_OK;
if( rguid == GUID_EnableTempo ) return DMUS_E_TYPE_DISABLED;
}
else
{
if( rguid == GUID_EnableTempo ) return S_OK;
if( rguid == GUID_DisableTempo ) return DMUS_E_TYPE_DISABLED;
if( rguid == GUID_PrivateTempoParam ) return DMUS_E_TYPE_DISABLED;
if( rguid == GUID_TempoParam ) return DMUS_E_TYPE_DISABLED;
}
}
else
{
if(( rguid == GUID_DisableTempo ) ||
( rguid == GUID_TempoParam ) ||
( rguid == GUID_PrivateTempoParam ) ||
( rguid == GUID_EnableTempo )) return S_OK;
}
return DMUS_E_TYPE_UNSUPPORTED;
}
//////////////////////////////////////////////////////////////////////
// IDirectMusicTrack::Init
HRESULT CTempoTrack::Init(
/* [in] */ IDirectMusicSegment *pSegment)
{
return S_OK;
}
HRESULT CTempoTrack::InitPlay(
/* [in] */ IDirectMusicSegmentState *pSegmentState,
/* [in] */ IDirectMusicPerformance *pPerformance,
/* [out] */ void **ppStateData,
/* [in] */ DWORD dwTrackID,
/* [in] */ DWORD dwFlags)
{
V_INAME(IDirectMusicTrack::InitPlay);
V_PTRPTR_WRITE(ppStateData);
V_INTERFACE(pSegmentState);
V_INTERFACE(pPerformance);
TempoStateData* pStateData;
pStateData = new TempoStateData;
if( NULL == pStateData )
return E_OUTOFMEMORY;
*ppStateData = pStateData;
if (m_fStateSetBySetParam)
{
pStateData->fActive = m_fActive;
}
else
{
pStateData->fActive = ((dwFlags & DMUS_SEGF_CONTROL) ||
!(dwFlags & DMUS_SEGF_SECONDARY));
}
pStateData->dwVirtualTrackID = dwTrackID;
pStateData->pPerformance = pPerformance; // weak reference, no addref.
pStateData->pSegState = pSegmentState; // weak reference, no addref.
pStateData->pCurrentTempo = m_TempoEventList.GetHead();
pStateData->dwValidate = m_dwValidate;
return S_OK;
}
HRESULT CTempoTrack::EndPlay(
/* [in] */ void *pStateData)
{
ASSERT( pStateData );
if( pStateData )
{
V_INAME(IDirectMusicTrack::EndPlay);
V_BUFPTR_WRITE(pStateData, sizeof(TempoStateData));
TempoStateData* pSD = (TempoStateData*)pStateData;
delete pSD;
}
return S_OK;
}
STDMETHODIMP CTempoTrack::PlayEx(void* pStateData,REFERENCE_TIME rtStart,
REFERENCE_TIME rtEnd,REFERENCE_TIME rtOffset,
DWORD dwFlags,IDirectMusicPerformance* pPerf,
IDirectMusicSegmentState* pSegSt,DWORD dwVirtualID)
{
V_INAME(IDirectMusicTrack::PlayEx);
V_BUFPTR_WRITE( pStateData, sizeof(TempoStateData));
V_INTERFACE(pPerf);
V_INTERFACE(pSegSt);
HRESULT hr;
EnterCriticalSection(&m_CrSec);
if (dwFlags & DMUS_TRACKF_CLOCK)
{
// Convert all reference times to millisecond times. Then, just use same MUSIC_TIME
// variables.
hr = Play(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 = Play(pStateData,(MUSIC_TIME)rtStart,(MUSIC_TIME)rtEnd,
(MUSIC_TIME)rtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE);
}
LeaveCriticalSection(&m_CrSec);
return hr;
}
STDMETHODIMP CTempoTrack::Play(
void *pStateData,
MUSIC_TIME mtStart,
MUSIC_TIME mtEnd,
MUSIC_TIME mtOffset,
DWORD dwFlags,
IDirectMusicPerformance* pPerf,
IDirectMusicSegmentState* pSegSt,
DWORD dwVirtualID)
{
V_INAME(IDirectMusicTrack::Play);
V_BUFPTR_WRITE( pStateData, sizeof(TempoStateData));
V_INTERFACE(pPerf);
V_INTERFACE(pSegSt);
EnterCriticalSection(&m_CrSec);
HRESULT hr = Play(pStateData,mtStart,mtEnd,mtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE);
LeaveCriticalSection(&m_CrSec);
return hr;
}
HRESULT CTempoTrack::Play(
void *pStateData,
MUSIC_TIME mtStart,
MUSIC_TIME mtEnd,
MUSIC_TIME mtOffset,
REFERENCE_TIME rtOffset,
DWORD dwFlags,
IDirectMusicPerformance* pPerf,
IDirectMusicSegmentState* pSegSt,
DWORD dwVirtualID,
BOOL fClockTime)
{
if (dwFlags & DMUS_TRACKF_PLAY_OFF)
{
return S_OK;
}
HRESULT hr = DMUS_S_END;
IDirectMusicGraph* pGraph = NULL;
TempoStateData* pSD = (TempoStateData*)pStateData;
BOOL fSeek = (dwFlags & DMUS_TRACKF_SEEK) ? TRUE : FALSE;
// if mtStart is 0 and dwFlags contains DMUS_TRACKF_START, we want to be sure to
// send out any negative time events. So, we'll set mtStart to -768.
if( (mtStart == 0) && ( dwFlags & DMUS_TRACKF_START ))
{
mtStart = -768;
}
// if pSD->pCurrentTempo is NULL, and we're in a normal Play call (dwFlags is 0)
// this means that we either have no events, or we got to the end of the event
// list previously. So, it's safe to just return.
if( (pSD->pCurrentTempo == NULL) && (dwFlags == 0) )
{
return S_FALSE;
}
if( pSD->dwValidate != m_dwValidate )
{
pSD->dwValidate = m_dwValidate;
pSD->pCurrentTempo = NULL;
}
if (!pSD->pCurrentTempo)
{
pSD->pCurrentTempo = m_TempoEventList.GetHead();
}
if (!pSD->pCurrentTempo)
{
return DMUS_S_END;
}
// if the previous end time isn't the same as the current start time,
// we need to seek to the right position.
if( fSeek || ( pSD->mtPrevEnd != mtStart ))
{
TempoStateData tempData;
BOOL fFlag = TRUE;
tempData = *pSD; // put this in so we can use Seek in other functions such as GetParam
if( !fSeek && (dwFlags & DMUS_TRACKF_DIRTY ))
{
fFlag = FALSE;
}
Seek( &tempData, mtStart, fFlag );
*pSD = tempData;
}
pSD->mtPrevEnd = mtEnd;
if( FAILED( pSD->pSegState->QueryInterface( IID_IDirectMusicGraph,
(void**)&pGraph )))
{
pGraph = NULL;
}
for (; pSD->pCurrentTempo; pSD->pCurrentTempo = pSD->pCurrentTempo->GetNext())
{
DMUS_IO_TEMPO_ITEM& rTempoEvent = pSD->pCurrentTempo->GetItemValue();
if( rTempoEvent.lTime >= mtEnd )
{
// this time is in the future. Return now to retain the same
// seek pointers for next time.
hr = S_OK;
break;
}
if( rTempoEvent.lTime < mtStart )
{
if( dwFlags & DMUS_TRACKF_FLUSH )
{
// this time is in the past, and this call to Play is in response to an
// invalidate. We don't want to replay stuff before the start time.
continue;
}
else if( !( dwFlags & DMUS_TRACKF_START) && !(dwFlags & DMUS_TRACKF_SEEK) )
{
// we really only want to play events earlier than mtStart on account
// of a START or SEEK (that isn't a FLUSH.)
continue;
}
}
if( pSD->fActive )
{
DMUS_TEMPO_PMSG* pTempo;
if( SUCCEEDED( pSD->pPerformance->AllocPMsg( sizeof(DMUS_TEMPO_PMSG),
(DMUS_PMSG**)&pTempo )))
{
if( rTempoEvent.lTime < mtStart )
{
// this only happens in the case where we've puposefully seeked
// and need to time stamp this event with the start time
if (fClockTime)
{
pTempo->rtTime = (mtStart * REF_PER_MIL) + rtOffset;
pTempo->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
}
else
{
pTempo->mtTime = mtStart + mtOffset;
pTempo->dwFlags = DMUS_PMSGF_MUSICTIME;
}
}
else
{
if (fClockTime)
{
pTempo->rtTime = (rTempoEvent.lTime * REF_PER_MIL) + rtOffset;
pTempo->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
}
else
{
pTempo->mtTime = rTempoEvent.lTime + mtOffset;
pTempo->dwFlags = DMUS_PMSGF_MUSICTIME;
}
}
pTempo->dblTempo = rTempoEvent.dblTempo;
pTempo->dwVirtualTrackID = pSD->dwVirtualTrackID;
pTempo->dwType = DMUS_PMSGT_TEMPO;
pTempo->dwGroupID = 0xffffffff;
if( pGraph )
{
pGraph->StampPMsg( (DMUS_PMSG*)pTempo );
}
if(FAILED(pSD->pPerformance->SendPMsg( (DMUS_PMSG*)pTempo )))
{
pSD->pPerformance->FreePMsg( (DMUS_PMSG*)pTempo );
}
}
}
}
if( pGraph )
{
pGraph->Release();
}
return hr;
}
// if fGetPrevious is TRUE, seek to the event prior to mtTime. Otherwise, seek to
// the event on or after mtTime
HRESULT CTempoTrack::Seek(
/* [in] */ TempoStateData *pSD,
/* [in] */ MUSIC_TIME mtTime, BOOL fGetPrevious)
{
TListItem<DMUS_IO_TEMPO_ITEM>* pScan = pSD->pCurrentTempo;
if (!pScan)
{
pScan = m_TempoEventList.GetHead();
}
if (!pScan)
{
return S_FALSE;
}
// if the event's time is on or past mtTime, we need to go to the beginning
if (pScan->GetItemValue().lTime >= mtTime)
{
pScan = m_TempoEventList.GetHead();
}
pSD->pCurrentTempo = pScan;
for (; pScan; pScan = pScan->GetNext())
{
if (pScan->GetItemValue().lTime >= mtTime)
{
if (!fGetPrevious)
{
pSD->pCurrentTempo = pScan;
}
break;
}
pSD->pCurrentTempo = pScan;
}
return S_OK;
}
STDMETHODIMP CTempoTrack::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;
}
STDMETHODIMP CTempoTrack::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);
}
HRESULT CTempoTrack::GetParam(
REFGUID rguid,
MUSIC_TIME mtTime,
MUSIC_TIME* pmtNext,
void *pData)
{
V_INAME(IDirectMusicTrack::GetParam);
V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME);
V_REFGUID(rguid);
HRESULT hr = DMUS_E_GET_UNSUPPORTED;
if( NULL == pData )
{
return E_POINTER;
}
if( rguid == GUID_PrivateTempoParam )
{
DMUS_TEMPO_PARAM TempoData;
PrivateTempo* pPrivateTempoData = (PrivateTempo*)pData;
hr = GetParam(GUID_TempoParam, mtTime, pmtNext, (void*)&TempoData);
if (hr == S_OK)
{
pPrivateTempoData->dblTempo = TempoData.dblTempo;
pPrivateTempoData->mtTime = 0; // must be set by the caller
pPrivateTempoData->mtDelta = TempoData.mtTime;
pPrivateTempoData->fLast = (pmtNext && !*pmtNext);
}
else if (hr == DMUS_E_NOT_FOUND) // the tempo track was empty
{
pPrivateTempoData->fLast = true;
}
}
else if( rguid == GUID_TempoParam )
{
if( !m_fActive )
{
return DMUS_E_TYPE_DISABLED;
}
DMUS_TEMPO_PARAM* pTempoData = (DMUS_TEMPO_PARAM*)pData;
TListItem<DMUS_IO_TEMPO_ITEM>* pScan = m_TempoEventList.GetHead();
TListItem<DMUS_IO_TEMPO_ITEM>* pPrevious = pScan;
if (!pScan)
{
return DMUS_E_NOT_FOUND;
}
for (; pScan; pScan = pScan->GetNext())
{
if (pScan->GetItemValue().lTime > mtTime)
{
break;
}
pPrevious = pScan;
}
DMUS_IO_TEMPO_ITEM& rTempoEvent = pPrevious->GetItemValue();
pTempoData->dblTempo = rTempoEvent.dblTempo;
pTempoData->mtTime = rTempoEvent.lTime - mtTime;
if (pmtNext)
{
*pmtNext = 0;
}
if (pScan)
{
DMUS_IO_TEMPO_ITEM& rNextTempoEvent = pScan->GetItemValue();
if (pmtNext)
{
*pmtNext = rNextTempoEvent.lTime - mtTime;
}
}
hr = S_OK;
}
return hr;
}
// Q: if all tracks are time-stamped, why do we need mtTime?
HRESULT CTempoTrack::SetParam(
REFGUID rguid,
MUSIC_TIME mtTime,
void *pData)
{
V_INAME(IDirectMusicTrack::SetParam);
V_REFGUID(rguid);
EnterCriticalSection(&m_CrSec);
HRESULT hr = DMUS_E_SET_UNSUPPORTED;
if( rguid == GUID_DisableTempo )
{
if (m_fStateSetBySetParam && !m_fActive)
{ // Already been disabled.
hr = DMUS_E_TYPE_DISABLED;
}
else
{
m_fStateSetBySetParam = TRUE;
m_fActive = FALSE;
hr = S_OK;
}
}
else if( rguid == GUID_EnableTempo )
{
if (m_fStateSetBySetParam && m_fActive)
{ // Already been enabled.
hr = DMUS_E_TYPE_DISABLED;
}
else
{
m_fStateSetBySetParam = TRUE;
m_fActive = TRUE;
hr = S_OK;
}
}
else if( rguid == GUID_TempoParam )
{
if (!m_fActive)
{ // Oops, app intentionally disabled tempo.
hr = DMUS_E_TYPE_DISABLED;
}
else
{
if( NULL == pData )
{
LeaveCriticalSection(&m_CrSec);
return E_POINTER;
}
DMUS_TEMPO_PARAM* pTempoData = (DMUS_TEMPO_PARAM*)pData;
TListItem<DMUS_IO_TEMPO_ITEM>* pScan = m_TempoEventList.GetHead();
TListItem<DMUS_IO_TEMPO_ITEM>* pPrevious = NULL;
for (; pScan; pScan = pScan->GetNext())
{
if (pScan->GetItemValue().lTime >= mtTime)
{
break;
}
pPrevious = pScan;
}
// Make a new DMUS_IO_TEMPO_ITEM and insert it after pPrevious
TListItem<DMUS_IO_TEMPO_ITEM>* pNew = new TListItem<DMUS_IO_TEMPO_ITEM>;
if (!pNew)
{
LeaveCriticalSection(&m_CrSec);
return E_OUTOFMEMORY;
}
DMUS_IO_TEMPO_ITEM& rTempoEvent = pNew->GetItemValue();
rTempoEvent.dblTempo = pTempoData->dblTempo;
/*
// I believe the fix for 204160 was supposed to change this line to what
// follows the comment. RSW
rTempoEvent.lTime = pTempoData->mtTime;
*/
rTempoEvent.lTime = mtTime;
if (pPrevious)
{
pNew->SetNext(pScan);
pPrevious->SetNext(pNew);
}
else
{
m_TempoEventList.AddHead(pNew);
}
if (pScan && pScan->GetItemValue().lTime == mtTime)
{
// remove it
pNew->SetNext(pScan->GetNext());
pScan->SetNext(NULL);
delete pScan;
}
m_dwValidate++;
hr = S_OK;
}
}
LeaveCriticalSection(&m_CrSec);
return hr;
}
HRESULT STDMETHODCALLTYPE CTempoTrack::AddNotificationType(
/* [in] */ REFGUID rguidNotification)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CTempoTrack::RemoveNotificationType(
/* [in] */ REFGUID rguidNotification)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CTempoTrack::Clone(
MUSIC_TIME mtStart,
MUSIC_TIME mtEnd,
IDirectMusicTrack** ppTrack)
{
V_INAME(IDirectMusicTrack::Clone);
V_PTRPTR_WRITE(ppTrack);
HRESULT hr = S_OK;
if(mtStart < 0 )
{
return E_INVALIDARG;
}
if(mtStart > mtEnd)
{
return E_INVALIDARG;
}
EnterCriticalSection(&m_CrSec);
CTempoTrack *pDM;
try
{
pDM = new CTempoTrack(*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;
}
STDMETHODIMP CTempoTrack::Compose(
IUnknown* pContext,
DWORD dwTrackGroup,
IDirectMusicTrack** ppResultTrack)
{
return E_NOTIMPL;
}
STDMETHODIMP CTempoTrack::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_CrSec);
if (ppResultTrack)
{
hr = Clone(0, mtJoin, ppResultTrack);
if (SUCCEEDED(hr))
{
hr = ((CTempoTrack*)*ppResultTrack)->JoinInternal(pNewTrack, mtJoin, dwTrackGroup);
}
}
else
{
hr = JoinInternal(pNewTrack, mtJoin, dwTrackGroup);
}
LeaveCriticalSection(&m_CrSec);
return hr;
}
HRESULT CTempoTrack::JoinInternal(
IDirectMusicTrack* pNewTrack,
MUSIC_TIME mtJoin,
DWORD dwTrackGroup)
{
HRESULT hr = S_OK;
CTempoTrack* pOtherTrack = (CTempoTrack*)pNewTrack;
TListItem<DMUS_IO_TEMPO_ITEM>* pScan = pOtherTrack->m_TempoEventList.GetHead();
for (; pScan; pScan = pScan->GetNext())
{
DMUS_IO_TEMPO_ITEM& rScan = pScan->GetItemValue();
TListItem<DMUS_IO_TEMPO_ITEM>* pNew = new TListItem<DMUS_IO_TEMPO_ITEM>;
if (pNew)
{
DMUS_IO_TEMPO_ITEM& rNew = pNew->GetItemValue();
rNew.lTime = rScan.lTime + mtJoin;
rNew.dblTempo = rScan.dblTempo;
m_TempoEventList.AddTail(pNew);
}
else
{
hr = E_OUTOFMEMORY;
break;
}
}
return hr;
}