//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) 1998-1999 Microsoft Corporation // // File: motiftrk.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) // MotifTrk.cpp : Implementation of CMotifTrack //#include "stdafx.h" //#include "Section.h" #include "MotifTrk.h" #include // for random number generator #include // to seed random number generator #include "debug.h" #include "dmusici.h" #include "debug.h" #include "..\shared\Validate.h" ///////////////////////////////////////////////////////////////////////////// // MotifTrackState MotifTrackState::MotifTrackState() : m_mtMotifStart(0) { } MotifTrackState::~MotifTrackState() { } HRESULT MotifTrackState::Play( MUSIC_TIME mtStart, MUSIC_TIME mtEnd, MUSIC_TIME mtOffset, REFERENCE_TIME rtOffset, IDirectMusicPerformance* pPerformance, DWORD dwFlags, BOOL fClockTime ) { m_mtPerformanceOffset = mtOffset; BOOL fStart = (dwFlags & DMUS_TRACKF_START) ? TRUE : FALSE; BOOL fSeek = (dwFlags & DMUS_TRACKF_SEEK) ? TRUE : FALSE; BOOL fLoop = (dwFlags & DMUS_TRACKF_LOOP) ? TRUE : FALSE; BOOL fControl = (dwFlags & DMUS_TRACKF_DIRTY) ? TRUE : FALSE; if (fControl) // We need to make sure we get chords on beat boundaries { GetNextChord(mtStart, mtOffset, pPerformance, fStart); } MUSIC_TIME mtNotify = mtStart ? PatternTimeSig().CeilingBeat(mtStart) : 0; if( m_fStateActive && m_pPatternTrack->m_fNotifyMeasureBeat && !fClockTime && ( mtNotify < mtEnd ) ) { mtNotify = NotifyMeasureBeat( mtNotify, mtEnd, mtOffset, pPerformance, dwFlags ); } bool fReLoop = false; DWORD dwPartFlags = PLAYPARTSF_FIRST_CALL; if (fStart || fLoop || fSeek) dwPartFlags |= PLAYPARTSF_START; if (fClockTime) dwPartFlags |= PLAYPARTSF_CLOCKTIME; if ( fLoop || (mtStart > 0 && (fStart || fSeek || fControl)) ) dwPartFlags |= PLAYPARTSF_FLUSH; PlayParts(mtStart, mtEnd, mtOffset, rtOffset, 0, pPerformance, dwPartFlags, dwFlags, fReLoop); if (fReLoop) { dwPartFlags = PLAYPARTSF_RELOOP; if (fClockTime) dwPartFlags |= PLAYPARTSF_CLOCKTIME; PlayParts(mtStart, mtEnd, mtOffset, rtOffset, 0, pPerformance, dwPartFlags, dwFlags, fReLoop); } if( m_fStateActive && m_pPatternTrack->m_fNotifyMeasureBeat && !fClockTime && ( mtNotify < mtEnd ) ) { NotifyMeasureBeat( mtNotify, mtEnd, mtOffset, pPerformance, dwFlags ); } return S_OK; } ///////////////////////////////////////////////////////////////////////////// // MotifTrackInfo MotifTrackInfo::MotifTrackInfo() : m_pPattern(NULL) { m_dwPatternTag = DMUS_PATTERN_MOTIF; } MotifTrackInfo::~MotifTrackInfo() { if (m_pPattern) m_pPattern->Release(); } HRESULT MotifTrackInfo::Init( /*[in]*/ IDirectMusicSegment* pSegment ) { HRESULT hr = S_OK; return hr; } HRESULT MotifTrackInfo::InitPlay( /*[in]*/ IDirectMusicTrack* pParentrack, /*[in]*/ IDirectMusicSegmentState* pSegmentState, /*[in]*/ IDirectMusicPerformance* pPerformance, /*[out]*/ void** ppStateData, /*[in]*/ DWORD dwTrackID, /*[in]*/ DWORD dwFlags ) { IDirectMusicSegment* pSegment = NULL; MotifTrackState* pStateData = new MotifTrackState; if( NULL == pStateData ) { return E_OUTOFMEMORY; } *ppStateData = pStateData; StatePair SP(pSegmentState, pStateData); TListItem* pPair = new TListItem(SP); if (!pPair) return E_OUTOFMEMORY; m_StateList.AddHead(pPair); TListItem* pHead = m_pISList.GetHead(); if (!pHead || !pHead->GetItemValue().m_pStyle) return E_FAIL; pHead->GetItemValue().m_pStyle->GetStyleInfo((void **)&pStateData->m_pStyle); pStateData->m_pTrack = pParentrack; pStateData->m_pPatternTrack = this; pStateData->m_dwVirtualTrackID = dwTrackID; pStateData->m_pPattern = NULL; pStateData->InitPattern(m_pPattern, 0); pStateData->m_pSegState = pSegmentState; // weak reference, no addref. pStateData->m_pPerformance = pPerformance; // weak reference, no addref. pStateData->m_mtPerformanceOffset = 0; pStateData->m_mtCurrentChordTime = 0; pStateData->m_mtNextChordTime = 0; pStateData->m_mtMotifStart = 0; HRESULT hr = pStateData->ResetMappings(); if (FAILED(hr)) return hr; if (m_fStateSetBySetParam) { pStateData->m_fStateActive = m_fActive; } else { pStateData->m_fStateActive = !(dwFlags & (DMUS_SEGF_CONTROL | DMUS_SEGF_SECONDARY)); } if (m_lRandomNumberSeed) { pStateData->InitVariationSeeds(m_lRandomNumberSeed); } if( SUCCEEDED( pSegmentState->GetSegment(&pSegment))) { if (FAILED(pSegment->GetTrackGroup(pStateData->m_pTrack, &pStateData->m_dwGroupID))) { pStateData->m_dwGroupID = 0xffffffff; } pSegment->Release(); } return S_OK; } ///////////////////////////////////////////////////////////////////////////// // CMotifTrack CMotifTrack::CMotifTrack() : m_bRequiresSave(0), m_cRef(1), m_fCSInitialized(FALSE) { InterlockedIncrement(&g_cComponent); ::InitializeCriticalSection( &m_CriticalSection ); m_fCSInitialized = TRUE; srand((unsigned int)time(NULL)); m_pTrackInfo = new MotifTrackInfo; } CMotifTrack::CMotifTrack(const CMotifTrack& rTrack, MUSIC_TIME mtStart, MUSIC_TIME mtEnd) : m_bRequiresSave(0), m_cRef(1), m_fCSInitialized(FALSE) { InterlockedIncrement(&g_cComponent); ::InitializeCriticalSection( &m_CriticalSection ); m_fCSInitialized = TRUE; srand((unsigned int)time(NULL)); m_pTrackInfo = new MotifTrackInfo((MotifTrackInfo*)rTrack.m_pTrackInfo, mtStart, mtEnd); } CMotifTrack::~CMotifTrack() { if (m_pTrackInfo) { delete m_pTrackInfo; } if (m_fCSInitialized) { ::DeleteCriticalSection( &m_CriticalSection ); } InterlockedDecrement(&g_cComponent); } STDMETHODIMP CMotifTrack::QueryInterface( const IID &iid, void **ppv) { V_INAME(CMotifTrack::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 if (iid == IID_IMotifTrack) { *ppv = static_cast(this); } else { *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast(this)->AddRef(); return S_OK; } STDMETHODIMP_(ULONG) CMotifTrack::AddRef() { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CMotifTrack::Release() { if (!InterlockedDecrement(&m_cRef)) { delete this; return 0; } return m_cRef; } ////////////////////////////////////////////////////////////////////// // IDirectMusicTrack::Init HRESULT CMotifTrack::Init( /* [in] */ IDirectMusicSegment __RPC_FAR *pSegment) { V_INAME(CMotifTrack::Init); V_INTERFACE(pSegment); HRESULT hr = S_OK; if (!m_pTrackInfo) return DMUS_E_NOT_INIT; EnterCriticalSection( &m_CriticalSection ); hr = m_pTrackInfo->MergePChannels(); if (SUCCEEDED(hr)) { pSegment->SetPChannelsUsed(m_pTrackInfo->m_dwPChannels, m_pTrackInfo->m_pdwPChannels); hr = m_pTrackInfo->Init(pSegment); } LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT CMotifTrack::InitPlay( /*[in]*/ IDirectMusicSegmentState* pSegmentState, /*[in]*/ IDirectMusicPerformance* pPerformance, /*[out]*/ void** ppStateData, /*[in]*/ DWORD dwTrackID, /*[in]*/ DWORD dwFlags ) { V_INAME(CMotifTrack::InitPlay); V_PTRPTR_WRITE(ppStateData); V_INTERFACE(pSegmentState); V_INTERFACE(pPerformance); EnterCriticalSection( &m_CriticalSection ); HRESULT hr = S_OK; if (!m_pTrackInfo) { LeaveCriticalSection( &m_CriticalSection ); return DMUS_E_NOT_INIT; } hr = m_pTrackInfo->InitPlay(this, pSegmentState, pPerformance, ppStateData, dwTrackID, dwFlags); LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT CMotifTrack::EndPlay( /*[in]*/ void* pStateData ) { V_INAME(CMotifTrack::EndPlay); V_BUFPTR_WRITE(pStateData, sizeof(MotifTrackState)); HRESULT hr = DMUS_E_NOT_INIT; EnterCriticalSection( &m_CriticalSection ); if (m_pTrackInfo) { hr = m_pTrackInfo->EndPlay((MotifTrackState*)pStateData); } LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT CMotifTrack::Play( /*[in]*/ void* pStateData, /*[in]*/ MUSIC_TIME mtStart, /*[in]*/ MUSIC_TIME mtEnd, /*[in]*/ MUSIC_TIME mtOffset, REFERENCE_TIME rtOffset, DWORD dwFlags, IDirectMusicPerformance* pPerf, IDirectMusicSegmentState* pSegState, DWORD dwVirtualID, BOOL fClockTime ) { V_INAME(CMotifTrack::Play); V_BUFPTR_WRITE( pStateData, sizeof(MotifTrackState)); V_INTERFACE(pPerf); V_INTERFACE(pSegState); HRESULT hr = DMUS_E_NOT_INIT; EnterCriticalSection( &m_CriticalSection ); if (!m_pTrackInfo) { LeaveCriticalSection( &m_CriticalSection ); return DMUS_E_NOT_INIT; } MotifTrackState* pSD = (MotifTrackState *)pStateData; if (pSD && pSD->m_pMappings) { BOOL fStart = (dwFlags & DMUS_TRACKF_START) ? TRUE : FALSE; BOOL fSeek = (dwFlags & DMUS_TRACKF_SEEK) ? TRUE : FALSE; BOOL fLoop = (dwFlags & DMUS_TRACKF_LOOP) ? TRUE : FALSE; BOOL fControl = (dwFlags & DMUS_TRACKF_DIRTY) ? TRUE : FALSE; if (fStart || fSeek || fLoop || fControl) { pSD->m_fNewPattern = TRUE; pSD->m_mtCurrentChordTime = 0; pSD->m_mtNextChordTime = 0; pSD->m_mtLaterChordTime = 0; // pSD->m_CurrentChord.bSubChordCount = 0; for (DWORD dw = 0; dw < m_pTrackInfo->m_dwPChannels; dw++) { pSD->m_pMappings[dw].m_mtTime = 0; pSD->m_pMappings[dw].m_dwPChannelMap = m_pTrackInfo->m_pdwPChannels[dw]; pSD->m_pMappings[dw].m_fMute = FALSE; } } hr = pSD->Play(mtStart, mtEnd, mtOffset, rtOffset, pPerf, dwFlags, fClockTime); } LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT CMotifTrack::GetPriority( /*[out]*/ DWORD* pPriority ) { return E_NOTIMPL; } HRESULT CMotifTrack::GetParam( REFGUID rCommandGuid, MUSIC_TIME mtTime, MUSIC_TIME* pmtNext, void *pData) { V_INAME(CMotifTrack::GetParam); V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME); V_PTR_WRITE(pData,1); V_REFGUID(rCommandGuid); EnterCriticalSection( &m_CriticalSection ); if (!m_pTrackInfo) { LeaveCriticalSection( &m_CriticalSection ); return DMUS_E_NOT_INIT; } HRESULT hr = S_OK; if( GUID_Valid_Start_Time == rCommandGuid ) { if (m_pTrackInfo->m_dwPatternTag != DMUS_PATTERN_MOTIF) hr = E_FAIL; else { MotifTrackInfo* pTrackInfo = (MotifTrackInfo*)m_pTrackInfo; if (!pTrackInfo->m_pPattern) hr = E_POINTER; else { DMUS_VALID_START_PARAM* pValidStartData = (DMUS_VALID_START_PARAM*)pData; TListItem* pScan = pTrackInfo->m_pPattern->m_StartTimeList.GetHead(); for (; pScan; pScan = pScan->GetNext()) { if (pScan->GetItemValue() >= mtTime) { pValidStartData->mtTime = pScan->GetItemValue() - mtTime; break; } } if (!pScan) hr = DMUS_E_NOT_FOUND; else { if (pmtNext) { if (pScan = pScan->GetNext()) { *pmtNext = pScan->GetItemValue() - mtTime; } else { *pmtNext = 0; } } hr = S_OK; } } } } else { hr = DMUS_E_GET_UNSUPPORTED; } LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT CMotifTrack::SetParam( REFGUID rguid, MUSIC_TIME mtTime, void __RPC_FAR *pData) { V_INAME(CMotifTrack::SetParam); V_PTR_WRITE_OPT(pData,1); V_REFGUID(rguid); EnterCriticalSection( &m_CriticalSection ); if (!m_pTrackInfo) { LeaveCriticalSection( &m_CriticalSection ); return DMUS_E_NOT_INIT; } HRESULT hr = DMUS_E_SET_UNSUPPORTED; if( rguid == GUID_EnableTimeSig ) { if( m_pTrackInfo->m_fStateSetBySetParam && m_pTrackInfo->m_fActive ) { hr = DMUS_E_TYPE_DISABLED; } else { m_pTrackInfo->m_fStateSetBySetParam = TRUE; m_pTrackInfo->m_fActive = TRUE; hr = S_OK; } } else if( rguid == GUID_DisableTimeSig ) { if( m_pTrackInfo->m_fStateSetBySetParam && !m_pTrackInfo->m_fActive ) { hr = DMUS_E_TYPE_DISABLED; } else { m_pTrackInfo->m_fStateSetBySetParam = TRUE; m_pTrackInfo->m_fActive = FALSE; hr = S_OK; } } else if ( rguid == GUID_SeedVariations ) { if (pData) { m_pTrackInfo->m_lRandomNumberSeed = *((long*) pData); hr = S_OK; } else hr = E_POINTER; } LeaveCriticalSection( &m_CriticalSection ); return hr; } // IPersist methods HRESULT CMotifTrack::GetClassID( LPCLSID pClassID ) { V_INAME(CMotifTrack::GetClassID); V_PTR_WRITE(pClassID, CLSID); *pClassID = CLSID_DirectMusicMotifTrack; return S_OK; } HRESULT CMotifTrack::IsParamSupported( /*[in]*/ REFGUID rGuid ) { V_INAME(CMotifTrack::IsParamSupported); V_REFGUID(rGuid); if (!m_pTrackInfo) { return DMUS_E_NOT_INIT; } if ( rGuid == GUID_SeedVariations || rGuid == GUID_Valid_Start_Time ) { return S_OK; } else if (m_pTrackInfo->m_fStateSetBySetParam) { if( m_pTrackInfo->m_fActive ) { if( rGuid == GUID_DisableTimeSig ) return S_OK; if( rGuid == GUID_EnableTimeSig ) return DMUS_E_TYPE_DISABLED; } else { if( rGuid == GUID_EnableTimeSig ) return S_OK; if( rGuid == GUID_DisableTimeSig ) return DMUS_E_TYPE_DISABLED; } } else { if(( rGuid == GUID_DisableTimeSig ) || ( rGuid == GUID_EnableTimeSig ) ) { return S_OK; } } return DMUS_E_TYPE_UNSUPPORTED; } // IPersistStream methods HRESULT CMotifTrack::IsDirty() { return m_bRequiresSave ? S_OK : S_FALSE; } HRESULT CMotifTrack::Save( LPSTREAM pStream, BOOL fClearDirty ) { return E_NOTIMPL; } HRESULT CMotifTrack::GetSizeMax( ULARGE_INTEGER* /*pcbSize*/ ) { return E_NOTIMPL; } HRESULT CMotifTrack::Load(LPSTREAM pStream ) { return E_NOTIMPL; } HRESULT CMotifTrack::SetTrack(IUnknown* pStyle, void* pPattern) { if (!pStyle) return E_POINTER; HRESULT hr = E_FAIL; EnterCriticalSection( &m_CriticalSection ); if (!m_pTrackInfo) { LeaveCriticalSection( &m_CriticalSection ); return DMUS_E_NOT_INIT; } MotifTrackInfo* pTrackInfo = (MotifTrackInfo*)m_pTrackInfo; if (m_pTrackInfo->m_dwPatternTag == DMUS_PATTERN_MOTIF) { IDMStyle* pIS = NULL; hr = pStyle->QueryInterface(IID_IDMStyle, (void**)&pIS); if (SUCCEEDED(hr)) { if (pTrackInfo->m_pPattern) pTrackInfo->m_pPattern->Release(); pTrackInfo->m_pPattern = (CDirectMusicPattern*) pPattern; if (pTrackInfo->m_pPattern) pTrackInfo->m_pPattern->AddRef(); pTrackInfo->InitTrackVariations(pTrackInfo->m_pPattern); TListItem* pNew = new TListItem; if (!pNew) hr = E_OUTOFMEMORY; else { pNew->GetItemValue().m_mtTime = 0; pNew->GetItemValue().m_pStyle = pIS; pTrackInfo->m_pISList.AddTail(pNew); hr = S_OK; } pIS->Release(); } } LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT STDMETHODCALLTYPE CMotifTrack::AddNotificationType( /* [in] */ REFGUID rGuidNotify) { V_INAME(CMotifTrack::AddNotificationType); V_REFGUID(rGuidNotify); HRESULT hr = S_OK; EnterCriticalSection( &m_CriticalSection ); if (m_pTrackInfo) hr = m_pTrackInfo->AddNotificationType(rGuidNotify); else hr = DMUS_E_NOT_INIT; LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT STDMETHODCALLTYPE CMotifTrack::RemoveNotificationType( /* [in] */ REFGUID rGuidNotify) { V_INAME(CMotifTrack::RemoveNotificationType); V_REFGUID(rGuidNotify); HRESULT hr = S_OK; EnterCriticalSection( &m_CriticalSection ); if (m_pTrackInfo) hr = m_pTrackInfo->RemoveNotificationType(rGuidNotify); else hr = DMUS_E_NOT_INIT; LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT STDMETHODCALLTYPE CMotifTrack::Clone( MUSIC_TIME mtStart, MUSIC_TIME mtEnd, IDirectMusicTrack** ppTrack) { V_INAME(CMotifTrack::Clone); V_PTRPTR_WRITE(ppTrack); HRESULT hr = S_OK; if(mtStart < 0 ) { return E_INVALIDARG; } if(mtStart > mtEnd) { return E_INVALIDARG; } EnterCriticalSection( &m_CriticalSection ); CMotifTrack *pDM; try { pDM = new CMotifTrack(*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; } STDMETHODIMP CMotifTrack::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 CMotifTrack::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); } STDMETHODIMP CMotifTrack::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); 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_CriticalSection); return hr; } STDMETHODIMP CMotifTrack::Play( void *pStateData, // @parm State data pointer, from . MUSIC_TIME mtStart, // @parm The start time to play. MUSIC_TIME mtEnd, // @parm The end time to play. MUSIC_TIME mtOffset,// @parm The offset to add to all messages sent to // . DWORD dwFlags, // @parm Flags that indicate the state of this call. // See . If dwFlags == 0, this is a // normal Play call continuing playback from the previous // Play call. IDirectMusicPerformance* pPerf, // @parm The , used to // call , // , etc. IDirectMusicSegmentState* pSegSt, // @parm The this // track belongs to. QueryInterface() can be called on this to // obtain the SegmentState's in order to // call , for instance. DWORD dwVirtualID // @parm This track's virtual track id, which must be set // on any 's m_dwVirtualTrackID member that // will be queued to . ) { V_INAME(IDirectMusicTrack::Play); V_INTERFACE(pPerf); V_INTERFACE(pSegSt); EnterCriticalSection(&m_CriticalSection); HRESULT hr = Play(pStateData,mtStart,mtEnd,mtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE); LeaveCriticalSection(&m_CriticalSection); return hr; } STDMETHODIMP CMotifTrack::Compose( IUnknown* pContext, DWORD dwTrackGroup, IDirectMusicTrack** ppResultTrack) { return E_NOTIMPL; } STDMETHODIMP CMotifTrack::Join( IDirectMusicTrack* pNewTrack, MUSIC_TIME mtJoin, IUnknown* pContext, DWORD dwTrackGroup, IDirectMusicTrack** ppResultTrack) { return E_NOTIMPL; }