//+------------------------------------------------------------------------- // // 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 #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* pScan = rTrack.m_CommandList.GetHead(); TListItem* 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* pNew = new TListItem; 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* pNew = new TListItem; 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(this); } else if (iid == IID_IPersistStream) { *ppv = static_cast(this); } else { *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast(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* 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* 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* 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* 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* pCommand = m_CommandList.GetHead(); TListItem* pPrevious = NULL; TListItem* pNew = new TListItem; 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* 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* pCommand; if (lFileSize % dwNodeSize) { hr = E_FAIL; } else { while( lFileSize > 0 ) { pCommand = new TListItem; 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* 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 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* pScan = pOtherTrack->m_CommandList.GetHead(); for (; pScan; pScan = pScan->GetNext()) { TListItem* pNew = new TListItem(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; }