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

1072 lines
32 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) 1998-1999 Microsoft Corporation
//
// File: cmmdtrk.cpp
//
//--------------------------------------------------------------------------
// 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)
// CommandTrack.cpp : Implementation of CCommandTrack
#include <objbase.h>
#include "CmmdTrk.h"
#include "debug.h"
#include "debug.h"
#include "..\shared\Validate.h"
/////////////////////////////////////////////////////////////////////////////
// CCommandTrack
CCommandTrack::CCommandTrack() : m_bRequiresSave(0),
m_cRef(1), m_fNotifyCommand(FALSE), m_fCSInitialized(FALSE), m_pNextCommand(NULL)
{
InterlockedIncrement(&g_cComponent);
::InitializeCriticalSection( &m_CriticalSection );
m_fCSInitialized = TRUE;
}
// it is assumed that cloning of commands will only happen on measure boundaries
CCommandTrack::CCommandTrack(const CCommandTrack& rTrack, MUSIC_TIME mtStart, MUSIC_TIME mtEnd) :
m_bRequiresSave(0),
m_cRef(1), m_fNotifyCommand(rTrack.m_fNotifyCommand),
m_fCSInitialized(FALSE), m_pNextCommand(NULL)
{
InterlockedIncrement(&g_cComponent);
::InitializeCriticalSection( &m_CriticalSection );
m_fCSInitialized = TRUE;
WORD wMeasure = 0;
BOOL fStarted = FALSE;
TListItem<DMCommand>* pScan = rTrack.m_CommandList.GetHead();
TListItem<DMCommand>* pPrevious = NULL;
for(; pScan; pScan = pScan->GetNext())
{
DMCommand& rScan = pScan->GetItemValue();
if (rScan.m_mtTime < mtStart)
{
pPrevious = pScan;
}
else if (rScan.m_mtTime < mtEnd)
{
if (!fStarted)
{
fStarted = TRUE;
wMeasure = rScan.m_wMeasure;
}
if (rScan.m_mtTime == mtStart)
{
pPrevious = NULL;
}
TListItem<DMCommand>* pNew = new TListItem<DMCommand>;
if (pNew)
{
DMCommand& rNew = pNew->GetItemValue();
rNew.m_mtTime = rScan.m_mtTime - mtStart;
rNew.m_wMeasure = rScan.m_wMeasure - wMeasure;
rNew.m_bBeat = rScan.m_bBeat;
rNew.m_bCommand = rScan.m_bCommand;
rNew.m_bGrooveLevel = rScan.m_bGrooveLevel;
rNew.m_bGrooveRange = rScan.m_bGrooveRange;
rNew.m_bRepeatMode = rScan.m_bRepeatMode;
m_CommandList.AddTail(pNew);
}
}
else break;
}
if (pPrevious)
{
TListItem<DMCommand>* pNew = new TListItem<DMCommand>;
if (pNew)
{
DMCommand& rNew = pNew->GetItemValue();
rNew.m_mtTime = 0;
rNew.m_wMeasure = 0;
rNew.m_bBeat = 0;
// Since embellishments are only supposed to last for a bar,
// start the new segment with a regular groove.
rNew.m_bCommand = DMUS_COMMANDT_GROOVE;
// Keep the groove level and range of the previous bar.
rNew.m_bGrooveLevel = pPrevious->GetItemValue().m_bGrooveLevel;
rNew.m_bGrooveRange = pPrevious->GetItemValue().m_bGrooveRange;
// Default to random selection of the pattern.
rNew.m_bRepeatMode = DMUS_PATTERNT_RANDOM;
m_CommandList.AddHead(pNew);
}
}
}
CCommandTrack::~CCommandTrack()
{
if (m_fCSInitialized)
{
::DeleteCriticalSection( &m_CriticalSection );
}
if (m_pNextCommand)
{
delete m_pNextCommand;
}
InterlockedDecrement(&g_cComponent);
}
void CCommandTrack::Clear()
{
m_CommandList.CleanUp();
}
STDMETHODIMP CCommandTrack::QueryInterface(
const IID &iid,
void **ppv)
{
V_INAME(CCommandTrack::QueryInterface);
V_REFGUID(iid);
V_PTRPTR_WRITE(ppv);
if (iid == IID_IUnknown || iid == IID_IDirectMusicTrack || iid == IID_IDirectMusicTrack8)
{
*ppv = static_cast<IDirectMusicTrack*>(this);
}
else if (iid == IID_IPersistStream)
{
*ppv = static_cast<IPersistStream*>(this);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(this)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) CCommandTrack::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) CCommandTrack::Release()
{
if (!InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}
return m_cRef;
}
// CCommandTrack Methods
HRESULT CCommandTrack::Init(
/*[in]*/ IDirectMusicSegment* pSegment
)
{
V_INAME(CCommandTrack::Init);
V_INTERFACE(pSegment);
return S_OK;
}
HRESULT CCommandTrack::InitPlay(
/*[in]*/ IDirectMusicSegmentState* pSegmentState,
/*[in]*/ IDirectMusicPerformance* pPerformance,
/*[out]*/ void** ppStateData,
/*[in]*/ DWORD dwTrackID,
/*[in]*/ DWORD dwFlags
)
{
HRESULT hr = S_OK;
CommandStateData* pStateData;
EnterCriticalSection( &m_CriticalSection );
pStateData = new CommandStateData;
if( NULL == pStateData )
{
hr = E_OUTOFMEMORY;
}
else
{
*pStateData = 0;
*ppStateData = pStateData;
}
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
HRESULT CCommandTrack::EndPlay(
/*[in]*/ void* pStateData
)
{
if( pStateData )
{
V_INAME(IDirectMusicTrack::EndPlay);
V_BUFPTR_WRITE(pStateData, sizeof(CommandStateData));
CommandStateData* pSD = (CommandStateData*)pStateData;
delete pSD;
}
if (m_pNextCommand) delete m_pNextCommand;
m_pNextCommand = NULL;
return S_OK;
}
HRESULT CCommandTrack::SendNotification(DWORD dwCommand,
MUSIC_TIME mtTime,
IDirectMusicPerformance* pPerf,
IDirectMusicSegmentState* pSegState,
DWORD dwFlags)
{
if (dwFlags & DMUS_TRACKF_NOTIFY_OFF)
{
return S_OK;
}
IDirectMusicSegment* pSegment = NULL;
DWORD dwOption = DMUS_NOTIFICATION_GROOVE;
if (dwCommand != DMUS_COMMANDT_GROOVE)
{
dwOption = DMUS_NOTIFICATION_EMBELLISHMENT;
}
DMUS_NOTIFICATION_PMSG* pEvent = NULL;
HRESULT hr = pPerf->AllocPMsg( sizeof(DMUS_NOTIFICATION_PMSG), (DMUS_PMSG**)&pEvent );
if( SUCCEEDED( hr ))
{
pEvent->dwField1 = 0;
pEvent->dwField2 = 0;
pEvent->dwType = DMUS_PMSGT_NOTIFICATION;
pEvent->mtTime = mtTime;
pEvent->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_ATTIME;
pSegState->QueryInterface(IID_IUnknown, (void**)&pEvent->punkUser);
pEvent->dwNotificationOption = dwOption;
pEvent->guidNotificationType = GUID_NOTIFICATION_COMMAND;
if( SUCCEEDED( pSegState->GetSegment(&pSegment)))
{
if (FAILED(pSegment->GetTrackGroup(this, &pEvent->dwGroupID)))
{
pEvent->dwGroupID = 0xffffffff;
}
pSegment->Release();
}
IDirectMusicGraph* pGraph;
hr = pSegState->QueryInterface( IID_IDirectMusicGraph, (void**)&pGraph );
if( SUCCEEDED( hr ))
{
pGraph->StampPMsg((DMUS_PMSG*) pEvent );
pGraph->Release();
}
hr = pPerf->SendPMsg((DMUS_PMSG*) pEvent );
if( FAILED(hr) )
{
pPerf->FreePMsg((DMUS_PMSG*) pEvent );
}
}
return hr;
}
HRESULT CCommandTrack::Play(
/*[in]*/ void* pStateData,
/*[in]*/ MUSIC_TIME mtStart,
/*[in]*/ MUSIC_TIME mtEnd,
/*[in]*/ MUSIC_TIME mtOffset,
DWORD dwFlags,
IDirectMusicPerformance* pPerf,
IDirectMusicSegmentState* pSegState,
DWORD dwVirtualID
)
{
bool fNotifyPastCommand = false;
TListItem<DMCommand>* pLastCommand = NULL;
// If we're seeking and not flushing, we need to notify for the command that happens
// before the current start time (if there is one)
if ( (dwFlags & DMUS_TRACKF_SEEK) && !(dwFlags & DMUS_TRACKF_FLUSH) )
{
fNotifyPastCommand = true;
}
HRESULT hr = S_OK;
EnterCriticalSection( &m_CriticalSection );
if (m_fNotifyCommand)
{
TListItem<DMCommand>* pCommand = m_CommandList.GetHead();
for(; pCommand && SUCCEEDED(hr); pCommand = pCommand->GetNext())
{
MUSIC_TIME mtCommandTime = pCommand->GetItemValue().m_mtTime;
if (mtCommandTime < mtStart && fNotifyPastCommand)
{
pLastCommand = pCommand;
}
else if (mtStart <= mtCommandTime && mtCommandTime < mtEnd)
{
if (pLastCommand)
{
hr = SendNotification(pLastCommand->GetItemValue().m_bCommand,
mtStart + mtOffset, pPerf, pSegState, dwFlags);
pLastCommand = NULL;
}
if (SUCCEEDED(hr))
{
hr = SendNotification(pCommand->GetItemValue().m_bCommand,
mtCommandTime + mtOffset, pPerf, pSegState, dwFlags);
}
}
else if (mtCommandTime >= mtEnd)
{
if (pLastCommand)
{
hr = SendNotification(pLastCommand->GetItemValue().m_bCommand,
mtStart + mtOffset, pPerf, pSegState, dwFlags);
}
break;
}
}
}
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
HRESULT CCommandTrack::GetPriority(
/*[out]*/ DWORD* pPriority
)
{
return E_NOTIMPL;
}
// Returns the Command in effect at the measure containing mtTime.
HRESULT CCommandTrack::GetParam(
REFGUID rCommandGuid,
MUSIC_TIME mtTime,
MUSIC_TIME* pmtNext,
void *pData)
{
V_INAME(CCommandTrack::GetParam);
V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME);
V_REFGUID(rCommandGuid);
if (rCommandGuid == GUID_CommandParam2)
{
return GetParam2(mtTime, pmtNext, (DMUS_COMMAND_PARAM_2*) pData);
}
else if (rCommandGuid == GUID_CommandParamNext)
{
return GetParamNext(mtTime, pmtNext, (DMUS_COMMAND_PARAM_2*) pData);
}
else if (rCommandGuid != GUID_CommandParam)
{
return DMUS_E_GET_UNSUPPORTED;
}
HRESULT hr = S_OK;
if (pData) // Something got passed in
{
EnterCriticalSection( &m_CriticalSection );
DMUS_COMMAND_PARAM* pCommandParam = (DMUS_COMMAND_PARAM*) pData;
// Default to the old groove C
BYTE bCommand = DMUS_COMMANDT_GROOVE;
BYTE bGrooveLevel = 62;
BYTE bGrooveRange = 0;
BYTE bRepeatMode = DMUS_PATTERNT_RANDOM;
TListItem<DMCommand>* pNext = m_CommandList.GetHead();
const int epsilon = DMUS_PPQ / 16; // leave a 64th note on either side...
for( ; pNext; pNext = pNext->GetNext())
{
if ( abs(pNext->GetItemValue().m_mtTime - mtTime) < epsilon ) // this is it
{
bCommand = pNext->GetItemValue().m_bCommand;
bGrooveLevel = pNext->GetItemValue().m_bGrooveLevel;
bGrooveRange = pNext->GetItemValue().m_bGrooveRange;
bRepeatMode = pNext->GetItemValue().m_bRepeatMode;
pNext = pNext->GetNext();
break;
}
else if (pNext->GetItemValue().m_mtTime < mtTime) // may be it, but we need a next time
{
bGrooveLevel = pNext->GetItemValue().m_bGrooveLevel;
bGrooveRange = pNext->GetItemValue().m_bGrooveRange;
bRepeatMode = pNext->GetItemValue().m_bRepeatMode;
}
else // passed it
{
break;
}
}
hr = S_OK;
pCommandParam->bCommand = bCommand;
pCommandParam->bGrooveLevel = bGrooveLevel;
pCommandParam->bGrooveRange = bGrooveRange;
pCommandParam->bRepeatMode = bRepeatMode;
if (pmtNext)
{
if (pNext)
{
*pmtNext = pNext->GetItemValue().m_mtTime - mtTime; // RSW: bug 167740
}
else
{
MUSIC_TIME mtLength = 0;
*pmtNext = mtLength;
}
}
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
else
return E_POINTER;
}
HRESULT CCommandTrack::GetParam2(
MUSIC_TIME mtTime,
MUSIC_TIME* pmtNext,
DMUS_COMMAND_PARAM_2* pCommandParam)
{
//TraceI(0, "GetParam time: %d\n", mtTime);
HRESULT hr = S_OK;
if (pCommandParam) // Something got passed in
{
EnterCriticalSection( &m_CriticalSection );
// Default to the old groove C
MUSIC_TIME mtCommandTime = 0;
BYTE bCommand = DMUS_COMMANDT_GROOVE;
BYTE bGrooveLevel = 62;
BYTE bGrooveRange = 0;
BYTE bRepeatMode = DMUS_PATTERNT_RANDOM;
TListItem<DMCommand>* pNext = m_CommandList.GetHead();
for( ; pNext; pNext = pNext->GetNext())
{
if (pNext->GetItemValue().m_mtTime <= mtTime) // may be it, but we need a next time
{
mtCommandTime = pNext->GetItemValue().m_mtTime - mtTime;
bCommand = pNext->GetItemValue().m_bCommand;
bGrooveLevel = pNext->GetItemValue().m_bGrooveLevel;
bGrooveRange = pNext->GetItemValue().m_bGrooveRange;
bRepeatMode = pNext->GetItemValue().m_bRepeatMode;
}
else // passed it
{
break;
}
}
hr = S_OK;
pCommandParam->mtTime = mtCommandTime;
pCommandParam->bCommand = bCommand;
pCommandParam->bGrooveLevel = bGrooveLevel;
pCommandParam->bGrooveRange = bGrooveRange;
pCommandParam->bRepeatMode = bRepeatMode;
if (pmtNext)
{
if (pNext)
{
*pmtNext = pNext->GetItemValue().m_mtTime - mtTime; // RSW: bug 167740
}
else
{
*pmtNext = 0;
}
}
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
else
return E_POINTER;
}
HRESULT CCommandTrack::GetParamNext(
MUSIC_TIME mtTime,
MUSIC_TIME* pmtNext,
DMUS_COMMAND_PARAM_2* pCommandParam)
{
HRESULT hr = S_OK;
if (pCommandParam) // Something got passed in
{
EnterCriticalSection( &m_CriticalSection );
if (m_pNextCommand)
{
*pCommandParam = *m_pNextCommand;
}
else
{
hr = DMUS_E_NOT_FOUND;
/*// Default to the old groove C
pCommandParam->bCommand = DMUS_COMMANDT_GROOVE;
pCommandParam->bGrooveLevel = 62;
pCommandParam->bGrooveRange = 0;*/
}
if (pmtNext)
{
*pmtNext = 0;
}
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
else
return E_POINTER;
}
HRESULT CCommandTrack::SetParam(
REFGUID rCommandGuid,
MUSIC_TIME mtTime,
void __RPC_FAR *pData)
{
V_INAME(CCommandTrack::SetParam);
V_REFGUID(rCommandGuid);
if (rCommandGuid == GUID_CommandParamNext)
{
return SetParamNext(mtTime, (DMUS_COMMAND_PARAM_2*) pData);
}
else if (rCommandGuid != GUID_CommandParam)
{
return DMUS_E_SET_UNSUPPORTED;
}
HRESULT hr = S_OK;
if (pData) // Something got passed in
{
EnterCriticalSection( &m_CriticalSection );
DMUS_COMMAND_PARAM* pCommandParam = (DMUS_COMMAND_PARAM*) pData;
//DirectMusicTimeSig TimeSig = ((CommandData *)(pData))->m_TimeSig;
//WORD wMeasure = ClocksToMeasure(mtTime, TimeSig);
TListItem<DMCommand>* pCommand = m_CommandList.GetHead();
TListItem<DMCommand>* pPrevious = NULL;
TListItem<DMCommand>* pNew = new TListItem<DMCommand>;
if (!pNew)
{
hr = E_OUTOFMEMORY;
}
else
{
DMCommand& rNew = pNew->GetItemValue();
rNew.m_mtTime = mtTime;
rNew.m_bBeat = 0;
rNew.m_bCommand = pCommandParam->bCommand;
rNew.m_bGrooveLevel = pCommandParam->bGrooveLevel;
rNew.m_bGrooveRange = pCommandParam->bGrooveRange;
rNew.m_bRepeatMode = pCommandParam->bRepeatMode;
for (; pCommand != NULL; pCommand = pCommand->GetNext())
{
DMCommand& rCommand = pCommand->GetItemValue();
if (rCommand.m_mtTime >= mtTime) break;
pPrevious = pCommand;
}
if (pPrevious)
{
pPrevious->SetNext(pNew);
pNew->SetNext(pCommand);
}
else // pCommand is current head of list
{
m_CommandList.AddHead(pNew);
}
if (pCommand && pCommand->GetItemValue().m_mtTime == mtTime)
{
// remove it
pNew->SetNext(pCommand->GetNext());
pCommand->SetNext(NULL);
delete pCommand;
}
hr = S_OK;
}
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
else
return E_POINTER;
}
HRESULT CCommandTrack::SetParamNext(
MUSIC_TIME mtTime,
DMUS_COMMAND_PARAM_2* pCommandParam)
{
EnterCriticalSection( &m_CriticalSection );
if (m_pNextCommand)
{
delete m_pNextCommand;
m_pNextCommand = NULL;
}
if (pCommandParam)
{
m_pNextCommand = new DMUS_COMMAND_PARAM_2;
if (!m_pNextCommand)
{
LeaveCriticalSection( &m_CriticalSection );
return E_OUTOFMEMORY;
}
*m_pNextCommand = *pCommandParam;
}
// Otherwise, allow a null pCommandParam to act as a reset.
LeaveCriticalSection( &m_CriticalSection );
return S_OK;
}
// IPersist methods
HRESULT CCommandTrack::GetClassID( LPCLSID pClassID )
{
V_INAME(CCommandTrack::GetClassID);
V_PTR_WRITE(pClassID, CLSID);
*pClassID = CLSID_DirectMusicCommandTrack;
return S_OK;
}
HRESULT CCommandTrack::IsParamSupported(
/*[in]*/ REFGUID rGuid
)
{
V_INAME(CCommandTrack::IsParamSupported);
V_REFGUID(rGuid);
if (rGuid == GUID_CommandParam ||
rGuid == GUID_CommandParam2 ||
rGuid == GUID_CommandParamNext)
{
return S_OK;
}
else
{
return DMUS_E_TYPE_UNSUPPORTED;
}
}
// IPersistStream methods
HRESULT CCommandTrack::IsDirty()
{
return m_bRequiresSave ? S_OK : S_FALSE;
}
HRESULT CCommandTrack::Save( LPSTREAM pStream, BOOL fClearDirty )
{
V_INAME(CCommandTrack::Save);
V_INTERFACE(pStream);
IAARIFFStream* pRIFF ;
MMCKINFO ck;
HRESULT hr;
DWORD cb;
DWORD dwSize;
DMUS_IO_COMMAND iCommand;
TListItem<DMCommand>* pCommand;
EnterCriticalSection( &m_CriticalSection );
hr = AllocRIFFStream( pStream, &pRIFF );
if (!SUCCEEDED(hr))
{
goto ON_END;
}
hr = E_FAIL;
ck.ckid = FOURCC_COMMAND;
if( pRIFF->CreateChunk( &ck, 0 ) == 0 )
{
dwSize = sizeof( DMUS_IO_COMMAND );
hr = pStream->Write( &dwSize, sizeof( dwSize ), &cb );
if( FAILED( hr ) || cb != sizeof( dwSize ) )
{
if (SUCCEEDED(hr)) hr = E_FAIL;
goto ON_END;
}
for( pCommand = m_CommandList.GetHead(); pCommand != NULL ; pCommand = pCommand->GetNext() )
{
DMCommand& rCommand = pCommand->GetItemValue();
memset( &iCommand, 0, sizeof( iCommand ) );
iCommand.mtTime = rCommand.m_mtTime;
iCommand.wMeasure = rCommand.m_wMeasure;
iCommand.bBeat = rCommand.m_bBeat;
iCommand.bCommand = rCommand.m_bCommand;
iCommand.bGrooveLevel = rCommand.m_bGrooveLevel;
iCommand.bGrooveRange = rCommand.m_bGrooveRange;
iCommand.bRepeatMode = rCommand.m_bRepeatMode;
if( FAILED( pStream->Write( &iCommand, sizeof( iCommand ), &cb ) ) ||
cb != sizeof( iCommand ) )
{
break;
}
}
if( pCommand == NULL &&
pRIFF->Ascend( &ck, 0 ) == 0 )
{
hr = S_OK;
}
}
ON_END:
if (pRIFF) pRIFF->Release();
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
HRESULT CCommandTrack::GetSizeMax( ULARGE_INTEGER* /*pcbSize*/ )
{
return E_NOTIMPL;
}
BOOL Greater(DMCommand& Command1, DMCommand& Command2)
{ return Command1.m_wMeasure > Command2.m_wMeasure; }
BOOL Less(DMCommand& Command1, DMCommand& Command2)
{ return Command1.m_wMeasure < Command2.m_wMeasure; }
HRESULT CCommandTrack::Load(LPSTREAM pStream )
{
V_INAME(CCommandTrack::Load);
V_INTERFACE(pStream);
long lFileSize = 0;
DWORD dwNodeSize;
DWORD cb;
MMCKINFO ck;
IAARIFFStream* pRIFF;
// FOURCC id = 0;
HRESULT hr = E_FAIL;
DMUS_IO_COMMAND iCommand;
DWORD dwPos;
EnterCriticalSection( &m_CriticalSection );
Clear();
dwPos = StreamTell( pStream );
StreamSeek( pStream, dwPos, STREAM_SEEK_SET );
ck.ckid = FOURCC_COMMAND;
if( SUCCEEDED( AllocRIFFStream( pStream, &pRIFF ) ) &&
pRIFF->Descend( &ck, NULL, MMIO_FINDCHUNK ) == 0 )
{
lFileSize = ck.cksize;
hr = pStream->Read( &dwNodeSize, sizeof( dwNodeSize ), &cb );
if( SUCCEEDED( hr ) && cb == sizeof( dwNodeSize ) )
{
lFileSize -= 4; // for the size dword
TListItem<DMCommand>* pCommand;
if (lFileSize % dwNodeSize)
{
hr = E_FAIL;
}
else
{
while( lFileSize > 0 )
{
pCommand = new TListItem<DMCommand>;
if( pCommand )
{
DMCommand& rCommand = pCommand->GetItemValue();
if( dwNodeSize <= sizeof( DMUS_IO_COMMAND ) )
{
pStream->Read( &iCommand, dwNodeSize, NULL );
}
else
{
pStream->Read( &iCommand, sizeof( DMUS_IO_COMMAND ), NULL );
StreamSeek( pStream, lFileSize - sizeof( DMUS_IO_COMMAND ), STREAM_SEEK_CUR );
}
memset( &rCommand, 0, sizeof( rCommand ) );
rCommand.m_mtTime = iCommand.mtTime;
rCommand.m_wMeasure = iCommand.wMeasure;
rCommand.m_bBeat = iCommand.bBeat;
rCommand.m_bCommand = iCommand.bCommand;
rCommand.m_bGrooveLevel = iCommand.bGrooveLevel;
if( iCommand.bGrooveRange > 100 )
{
Trace(0, "Warning: Illegal value %d read for Command's groove range.\n", iCommand.bGrooveRange);
rCommand.m_bGrooveRange = 0;
}
else
{
rCommand.m_bGrooveRange = iCommand.bGrooveRange;
}
if( iCommand.bRepeatMode > DMUS_PATTERNT_RANDOM_ROW )
{
Trace(0, "Warning: Illegal value %d read for Command's repeat mode.\n", iCommand.bRepeatMode);
rCommand.m_bRepeatMode = 0;
}
else
{
rCommand.m_bRepeatMode = iCommand.bRepeatMode;
}
m_CommandList.AddTail(pCommand);
lFileSize -= dwNodeSize;
}
else break;
}
}
}
if( lFileSize == 0 &&
pRIFF->Ascend( &ck, 0 ) == 0 )
{
hr = S_OK;
m_CommandList.MergeSort(Less);
BYTE bLastGrooveLevel = 62;
TListItem<DMCommand>* pCommand = m_CommandList.GetHead();
for(; pCommand != NULL; pCommand = pCommand->GetNext())
{
if (!pCommand->GetItemValue().m_bGrooveLevel)
{
pCommand->GetItemValue().m_bGrooveLevel = bLastGrooveLevel;
}
else
{
bLastGrooveLevel = pCommand->GetItemValue().m_bGrooveLevel;
}
}
}
pRIFF->Release();
}
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
HRESULT STDMETHODCALLTYPE CCommandTrack::AddNotificationType(
/* [in] */ REFGUID rGuidNotify)
{
V_INAME(CCommandTrack::AddNotificationType);
V_REFGUID(rGuidNotify);
if( rGuidNotify == GUID_NOTIFICATION_COMMAND )
{
m_fNotifyCommand = TRUE;
return S_OK;
}
else
{
return S_FALSE;
}
}
HRESULT STDMETHODCALLTYPE CCommandTrack::RemoveNotificationType(
/* [in] */ REFGUID rGuidNotify)
{
V_INAME(CCommandTrack::RemoveNotificationType);
V_REFGUID(rGuidNotify);
if( rGuidNotify == GUID_NOTIFICATION_COMMAND )
{
m_fNotifyCommand = FALSE;
return S_OK;
}
else
{
return S_FALSE;
}
}
HRESULT STDMETHODCALLTYPE CCommandTrack::Clone(
MUSIC_TIME mtStart,
MUSIC_TIME mtEnd,
IDirectMusicTrack** ppTrack)
{
V_INAME(CCommandTrack::Clone);
V_PTRPTR_WRITE(ppTrack);
HRESULT hr = S_OK;
if(mtStart < 0 )
{
return E_INVALIDARG;
}
if(mtStart > mtEnd)
{
return E_INVALIDARG;
}
EnterCriticalSection( &m_CriticalSection );
CCommandTrack *pDM;
try
{
pDM = new CCommandTrack(*this, mtStart, mtEnd);
}
catch( ... )
{
pDM = NULL;
}
if (pDM == NULL) {
LeaveCriticalSection( &m_CriticalSection );
return E_OUTOFMEMORY;
}
hr = pDM->QueryInterface(IID_IDirectMusicTrack, (void**)ppTrack);
pDM->Release();
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
// For consistency with other track types
STDMETHODIMP CCommandTrack::GetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime,
REFERENCE_TIME* prtNext,void* pParam,void * pStateData, DWORD dwFlags)
{
HRESULT hr;
MUSIC_TIME mtNext;
hr = GetParam(rguidType,(MUSIC_TIME) rtTime, &mtNext, pParam);
if (prtNext)
{
*prtNext = mtNext;
}
return hr;
}
// For consistency with other track types
STDMETHODIMP CCommandTrack::SetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime,
void* pParam, void * pStateData, DWORD dwFlags)
{
return SetParam(rguidType, (MUSIC_TIME) rtTime , pParam);
}
// For consistency with other track types
STDMETHODIMP CCommandTrack::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_INTERFACE(pPerf);
V_INTERFACE(pSegSt);
HRESULT hr;
EnterCriticalSection(&m_CriticalSection);
hr = Play(pStateData, (MUSIC_TIME)rtStart, (MUSIC_TIME)rtEnd,
(MUSIC_TIME)rtOffset, dwFlags, pPerf, pSegSt, dwVirtualID);
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
STDMETHODIMP CCommandTrack::Compose(
IUnknown* pContext,
DWORD dwTrackGroup,
IDirectMusicTrack** ppResultTrack)
{
return E_NOTIMPL;
}
STDMETHODIMP CCommandTrack::Join(
IDirectMusicTrack* pNewTrack,
MUSIC_TIME mtJoin,
IUnknown* pContext,
DWORD dwTrackGroup,
IDirectMusicTrack** ppResultTrack)
{
V_INAME(IDirectMusicTrack::Join);
V_INTERFACE(pNewTrack);
V_INTERFACE(pContext);
V_PTRPTR_WRITE_OPT(ppResultTrack);
HRESULT hr = S_OK;
EnterCriticalSection(&m_CriticalSection);
TList<DMCommand> ResultList;
CCommandTrack* pResultTrack = NULL;
if (ppResultTrack)
{
hr = Clone(0, mtJoin, ppResultTrack);
pResultTrack = (CCommandTrack*)*ppResultTrack;
while(!pResultTrack->m_CommandList.IsEmpty())
{
ResultList.AddHead(pResultTrack->m_CommandList.RemoveHead());
}
}
else
{
pResultTrack = this;
while(!m_CommandList.IsEmpty() &&
m_CommandList.GetHead()->GetItemValue().m_mtTime < mtJoin)
{
ResultList.AddHead(m_CommandList.RemoveHead());
}
m_CommandList.CleanUp();
}
WORD wMeasure = 0;
HRESULT hrTimeSig = S_OK;
MUSIC_TIME mtTimeSig = 0;
MUSIC_TIME mtOver = 0;
IDirectMusicSong* pSong = NULL;
IDirectMusicSegment* pSegment = NULL;
if (FAILED(pContext->QueryInterface(IID_IDirectMusicSegment, (void**)&pSegment)))
{
if (FAILED(pContext->QueryInterface(IID_IDirectMusicSong, (void**)&pSong)))
{
hrTimeSig = E_FAIL;
}
}
while (SUCCEEDED(hrTimeSig) && mtTimeSig < mtJoin)
{
DMUS_TIMESIGNATURE TimeSig;
MUSIC_TIME mtNext = 0;
if (pSegment)
{
hrTimeSig = pSegment->GetParam(GUID_TimeSignature, dwTrackGroup, 0, mtTimeSig, &mtNext, (void*)&TimeSig);
}
else
{
hrTimeSig = pSong->GetParam(GUID_TimeSignature, dwTrackGroup, 0, mtTimeSig, &mtNext, (void*)&TimeSig);
}
if (SUCCEEDED(hrTimeSig))
{
if (!mtNext) mtNext = mtJoin - mtTimeSig; // means no more time sigs
DirectMusicTimeSig DMTimeSig = TimeSig;
WORD wMeasureOffset = (WORD)DMTimeSig.ClocksToMeasure(mtNext + mtOver);
MUSIC_TIME mtMeasureOffset = (MUSIC_TIME) wMeasureOffset;
// The following line crashes on certain builds on certain machines.
// mtOver = mtMeasureOffset ? (mtNext % mtMeasureOffset) : 0;
if (mtMeasureOffset)
{
mtOver = mtNext % mtMeasureOffset;
}
else
{
mtOver = 0;
}
wMeasure += wMeasureOffset;
mtTimeSig += mtNext;
}
}
CCommandTrack* pOtherTrack = (CCommandTrack*)pNewTrack;
TListItem<DMCommand>* pScan = pOtherTrack->m_CommandList.GetHead();
for (; pScan; pScan = pScan->GetNext())
{
TListItem<DMCommand>* pNew = new TListItem<DMCommand>(pScan->GetItemValue());
if (pNew)
{
pNew->GetItemValue().m_mtTime += mtJoin;
pNew->GetItemValue().m_wMeasure += wMeasure;
ResultList.AddHead(pNew);
}
else
{
ResultList.CleanUp();
hr = E_OUTOFMEMORY;
break;
}
}
if (SUCCEEDED(hr))
{
pResultTrack->m_CommandList.CleanUp();
while(!ResultList.IsEmpty() )
{
pResultTrack->m_CommandList.AddHead(ResultList.RemoveHead());
}
}
if (pSong) pSong->Release();
if (pSegment) pSegment->Release();
LeaveCriticalSection(&m_CriticalSection);
return hr;
}