//+------------------------------------------------------------------------- // // Copyright (c) 1998-2001 Microsoft Corporation // // File: chordtrk.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) // ChordTrack.cpp : Implementation of CChordTrack //#include "stdafx.h" //#include "Section.h" #include "ChordTrk.h" #include "debug.h" #include "..\shared\Validate.h" ///////////////////////////////////////////////////////////////////////////// // CChordTrack CChordTrack::CChordTrack() : m_bRequiresSave(0), m_bRoot(0), m_dwScalePattern(0), m_cRef(1), m_fNotifyChord(FALSE), m_fCSInitialized(FALSE) { InterlockedIncrement(&g_cComponent); ::InitializeCriticalSection( &m_CriticalSection ); m_fCSInitialized = TRUE; } // This currently only supports cloning on measure boundaries // (otherwise time sig info would be needed to get the beats right) CChordTrack::CChordTrack(const CChordTrack& rTrack, MUSIC_TIME mtStart, MUSIC_TIME mtEnd) : m_bRequiresSave(0), m_bRoot(0), m_dwScalePattern(0), m_cRef(1), m_fNotifyChord(rTrack.m_fNotifyChord), m_fCSInitialized(FALSE) { InterlockedIncrement(&g_cComponent); ::InitializeCriticalSection( &m_CriticalSection ); m_fCSInitialized = TRUE; TListItem* pScan = rTrack.m_ChordList.GetHead(); TListItem* pPrevious = NULL; WORD wMeasure = 0; BOOL fStarted = FALSE; for(; pScan; pScan = pScan->GetNext()) { DMChord& rScan = pScan->GetItemValue(); if (rScan.m_mtTime < mtStart) { pPrevious = pScan; } else if (rScan.m_mtTime < mtEnd) { if (rScan.m_mtTime == mtStart) { pPrevious = NULL; } if (!fStarted) { fStarted = TRUE; wMeasure = rScan.m_wMeasure; } TListItem* pNew = new TListItem; if (pNew) { DMChord& rNew = pNew->GetItemValue(); rNew.m_strName = rScan.m_strName; rNew.m_mtTime = rScan.m_mtTime - mtStart; rNew.m_wMeasure = rScan.m_wMeasure - wMeasure; rNew.m_bBeat = rScan.m_bBeat; rNew.m_bKey = rScan.m_bKey; rNew.m_dwScale = rScan.m_dwScale; TListItem* pSubScan = rScan.m_SubChordList.GetHead(); for(; pSubScan; pSubScan = pSubScan->GetNext()) { DMSubChord& rSubScan = pSubScan->GetItemValue(); TListItem* pSubNew = new TListItem; if (pSubNew) { DMSubChord& rSubNew = pSubNew->GetItemValue(); rSubNew.m_dwChordPattern = rSubScan.m_dwChordPattern; rSubNew.m_dwScalePattern = rSubScan.m_dwScalePattern; rSubNew.m_dwInversionPoints = rSubScan.m_dwInversionPoints; rSubNew.m_dwLevels = rSubScan.m_dwLevels; rSubNew.m_bChordRoot = rSubScan.m_bChordRoot; rSubNew.m_bScaleRoot = rSubScan.m_bScaleRoot; rNew.m_SubChordList.AddTail(pSubNew); } } m_ChordList.AddTail(pNew); } } else break; } if (pPrevious) { DMChord& rPrevious = pPrevious->GetItemValue(); TListItem* pNew = new TListItem; if (pNew) { DMChord& rNew = pNew->GetItemValue(); rNew.m_strName = rPrevious.m_strName; rNew.m_mtTime = 0; rNew.m_wMeasure = 0; rNew.m_bBeat = 0; rNew.m_bKey = rPrevious.m_bKey; rNew.m_dwScale = rPrevious.m_dwScale; TListItem* pSubPrevious = rPrevious.m_SubChordList.GetHead(); for(; pSubPrevious; pSubPrevious = pSubPrevious->GetNext()) { DMSubChord& rSubPrevious = pSubPrevious->GetItemValue(); TListItem* pSubNew = new TListItem; if (pSubNew) { DMSubChord& rSubNew = pSubNew->GetItemValue(); rSubNew.m_dwChordPattern = rSubPrevious.m_dwChordPattern; rSubNew.m_dwScalePattern = rSubPrevious.m_dwScalePattern; rSubNew.m_dwInversionPoints = rSubPrevious.m_dwInversionPoints; rSubNew.m_dwLevels = rSubPrevious.m_dwLevels; rSubNew.m_bChordRoot = rSubPrevious.m_bChordRoot; rSubNew.m_bScaleRoot = rSubPrevious.m_bScaleRoot; rNew.m_SubChordList.AddTail(pSubNew); } } m_ChordList.AddHead(pNew); } } } CChordTrack::~CChordTrack() { if (m_fCSInitialized) { ::DeleteCriticalSection( &m_CriticalSection ); } InterlockedDecrement(&g_cComponent); } void CChordTrack::Clear() { m_ChordList.CleanUp(); } STDMETHODIMP CChordTrack::QueryInterface( const IID &iid, void **ppv) { V_INAME(CChordTrack::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) CChordTrack::AddRef() { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CChordTrack::Release() { if (!InterlockedDecrement(&m_cRef)) { delete this; return 0; } return m_cRef; } // CChordTrack Methods HRESULT CChordTrack::Init( /*[in]*/ IDirectMusicSegment* pSegment ) { V_INAME(CChordTrack::Init); V_INTERFACE(pSegment); return S_OK; } // state data is not needed for now typedef DWORD ChordStateData; HRESULT CChordTrack::InitPlay( /*[in]*/ IDirectMusicSegmentState* pSegmentState, /*[in]*/ IDirectMusicPerformance* pPerformance, /*[out]*/ void** ppStateData, /*[in]*/ DWORD dwTrackID, /*[in]*/ DWORD dwFlags ) { ChordStateData* pStateData = new ChordStateData; if( NULL == pStateData ) return E_OUTOFMEMORY; *pStateData = 0; *ppStateData = pStateData; EnterCriticalSection( &m_CriticalSection ); LeaveCriticalSection( &m_CriticalSection ); return S_OK; } HRESULT CChordTrack::EndPlay( /*[in]*/ void* pStateData ) { if( pStateData ) { V_INAME(IDirectMusicTrack::EndPlay); V_BUFPTR_WRITE(pStateData, sizeof(ChordStateData)); ChordStateData* pSD = (ChordStateData*)pStateData; delete pSD; } return S_OK; } HRESULT CChordTrack::SendNotification(REFGUID rguidType, MUSIC_TIME mtTime, IDirectMusicPerformance* pPerf, IDirectMusicSegmentState* pSegState, DWORD dwFlags) { if (dwFlags & DMUS_TRACKF_NOTIFY_OFF) { return S_OK; } IDirectMusicSegment* pSegment = NULL; 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; pSegState->QueryInterface(IID_IUnknown, (void**)&pEvent->punkUser); pEvent->dwNotificationOption = DMUS_NOTIFICATION_CHORD; pEvent->guidNotificationType = rguidType; 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 )) { if (rguidType == GUID_NOTIFICATION_PRIVATE_CHORD) { //stamp this with the internal Performance Tool and process immediately pEvent->dwFlags |= DMUS_PMSGF_TOOL_IMMEDIATE; pPerf->QueryInterface(IID_IDirectMusicTool, (void**)&pEvent->pTool); pEvent->pGraph = pGraph; } else { pEvent->dwFlags |= DMUS_PMSGF_TOOL_ATTIME; pGraph->StampPMsg((DMUS_PMSG*) pEvent ); pGraph->Release(); } } hr = pPerf->SendPMsg((DMUS_PMSG*) pEvent ); if( FAILED(hr) ) { pPerf->FreePMsg((DMUS_PMSG*) pEvent ); } } return hr; } HRESULT CChordTrack::Play( /*[in]*/ void* pStateData, /*[in]*/ MUSIC_TIME mtStart, /*[in]*/ MUSIC_TIME mtEnd, /*[in]*/ MUSIC_TIME mtOffset, DWORD dwFlags, IDirectMusicPerformance* pPerf, IDirectMusicSegmentState* pSegState, DWORD dwVirtualID ) { V_INAME(CChordTrack::Play); V_BUFPTR_WRITE( pStateData, sizeof(ChordStateData)); V_INTERFACE(pPerf); V_INTERFACE(pSegState); bool fNotifyPastChord = false; TListItem* pLastChord = NULL; // if the dirty flag is set, a controlling segment either just stopped or just started. // send a private notification to sync with the current chord in this segment. if ( (dwFlags & DMUS_TRACKF_DIRTY) ) { SendNotification(GUID_NOTIFICATION_PRIVATE_CHORD, mtStart + mtOffset, pPerf, pSegState, dwFlags); } // If we're seeking and not flushing, we need to notify for the chord that happens // before the current start time (if there is one) if ( (dwFlags & DMUS_TRACKF_SEEK) && !(dwFlags & DMUS_TRACKF_FLUSH) ) { fNotifyPastChord = true; } HRESULT hr = S_OK; EnterCriticalSection( &m_CriticalSection ); TListItem* pChord = m_ChordList.GetHead(); for(; pChord && SUCCEEDED(hr); pChord = pChord->GetNext()) { MUSIC_TIME mtChordTime = pChord->GetItemValue().m_mtTime; if (mtChordTime < mtStart && fNotifyPastChord) { pLastChord = pChord; } else if (mtStart <= mtChordTime && mtChordTime < mtEnd) { if (pLastChord) { SendNotification(GUID_NOTIFICATION_PRIVATE_CHORD, mtStart + mtOffset, pPerf, pSegState, dwFlags); if (m_fNotifyChord) { hr = SendNotification(GUID_NOTIFICATION_CHORD, mtStart + mtOffset, pPerf, pSegState, dwFlags); } pLastChord = NULL; } if (SUCCEEDED(hr)) { SendNotification(GUID_NOTIFICATION_PRIVATE_CHORD, mtChordTime + mtOffset, pPerf, pSegState, dwFlags); if (m_fNotifyChord) { hr = SendNotification(GUID_NOTIFICATION_CHORD, mtChordTime + mtOffset, pPerf, pSegState, dwFlags); } } } else if (mtChordTime >= mtEnd) { if (pLastChord) { SendNotification(GUID_NOTIFICATION_PRIVATE_CHORD, mtStart + mtOffset, pPerf, pSegState, dwFlags); if (m_fNotifyChord) { hr = SendNotification(GUID_NOTIFICATION_CHORD, mtStart + mtOffset, pPerf, pSegState, dwFlags); } } break; } } LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT CChordTrack::GetPriority( /*[out]*/ DWORD* pPriority ) { return E_NOTIMPL; } HRESULT CChordTrack::GetChord( MUSIC_TIME mtTime, MUSIC_TIME* pmtNext, DMUS_CHORD_PARAM* pChordParam) { TListItem* pChord = m_ChordList.GetHead(); TListItem* pNext = pNext = pChord->GetNext(); for(; pNext; pNext = pNext->GetNext()) { if (pNext->GetItemValue().m_mtTime <= mtTime) // may be it, but we need a next time { pChord = pNext; } else // passed it { break; } } *pChordParam = pChord->GetItemValue(); if (pmtNext) { if (pNext) { *pmtNext = pNext->GetItemValue().m_mtTime - mtTime; } else { MUSIC_TIME mtLength = 0; *pmtNext = mtLength; } } TraceI(4, "Current time: %d, Time of Chord: %d\n", mtTime, pChord->GetItemValue().m_mtTime); return S_OK; } HRESULT CChordTrack::GetRhythm( MUSIC_TIME mtTime, MUSIC_TIME* pmtNext, DMUS_RHYTHM_PARAM* pRhythmParam) { DirectMusicTimeSig TimeSig = pRhythmParam->TimeSig; WORD wMeasure = (WORD)TimeSig.ClocksToMeasure(mtTime); TListItem* pChord = m_ChordList.GetHead(); TListItem* pNext = NULL; DWORD dwPattern = 0; for( ; pChord; pChord = pChord->GetNext()) { DMChord& rChord = pChord->GetItemValue(); pNext = pChord->GetNext(); if (rChord.m_wMeasure > wMeasure) // passed the target measure { break; } else if (wMeasure == rChord.m_wMeasure && !rChord.m_fSilent) // found (non-silent) part of the pattern { dwPattern |= 1 << rChord.m_bBeat; } } // DMChord& ChordResult = pChord->GetItemValue(); pRhythmParam->dwRhythmPattern = dwPattern; if (pmtNext) { if (pNext) { *pmtNext = pNext->GetItemValue().m_mtTime - mtTime; // RSW: bug 167740 } else { MUSIC_TIME mtLength = 0; *pmtNext = mtLength; } } return S_OK; } // Returns either the Chord in effect at the beat containing mtTime, // or the Rhythm pattern for the measure containing mtTime, depending // on the value of dwCommand. // ppData points to a struct containing an input time signature // (used for converting mtTime to measures and beats) and either a list // of subchords (if we're returning a chord) or a DWORD containing a rhythm // pattern (if that's what's being returned). HRESULT CChordTrack::GetParam( REFGUID rCommandGuid, MUSIC_TIME mtTime, MUSIC_TIME* pmtNext, void *pData) { V_INAME(CChordTrack::GetParam); V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME); V_REFGUID(rCommandGuid); if( NULL == pData ) { return E_POINTER; } HRESULT hr = DMUS_E_NOT_FOUND; EnterCriticalSection( &m_CriticalSection ); if (m_ChordList.GetHead()) // Something's in the chord list { if (rCommandGuid == GUID_ChordParam) { hr = GetChord(mtTime, pmtNext, (DMUS_CHORD_PARAM*)pData); } else if (rCommandGuid == GUID_RhythmParam) { hr = GetRhythm(mtTime, pmtNext, (DMUS_RHYTHM_PARAM*)pData); } else { hr = DMUS_E_GET_UNSUPPORTED; } } LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT CChordTrack::SetParam( REFGUID rCommandGuid, MUSIC_TIME mtTime, void __RPC_FAR *pData) { V_INAME(CChordTrack::SetParam); V_REFGUID(rCommandGuid); if( NULL == pData ) { return E_POINTER; } HRESULT hr = S_OK; EnterCriticalSection( &m_CriticalSection ); if (rCommandGuid == GUID_ChordParam) { DMUS_CHORD_PARAM* pChordParam = (DMUS_CHORD_PARAM*)(pData); TListItem* pChordItem = m_ChordList.GetHead(); TListItem* pPrevious = NULL; TListItem* pChord = new TListItem; if (!pChord) { hr = E_OUTOFMEMORY; } else { DMChord& rChord = pChord->GetItemValue(); rChord = (DMChord) *pChordParam; rChord.m_mtTime = mtTime; rChord.m_wMeasure = 0; // what value should this have? rChord.m_bBeat = 0; // what value should this have? for(; pChordItem != NULL; pChordItem = pChordItem->GetNext()) { if (pChordItem->GetItemValue().m_mtTime >= mtTime) break; pPrevious = pChordItem; } if (pPrevious) { pPrevious->SetNext(pChord); pChord->SetNext(pChordItem); } else // pChordItem is current head of list { m_ChordList.AddHead(pChord); } if (pChordItem && pChordItem->GetItemValue().m_mtTime == mtTime) { // remove it pChord->SetNext(pChordItem->GetNext()); pChordItem->SetNext(NULL); delete pChordItem; } } } else hr = DMUS_E_SET_UNSUPPORTED; LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT CChordTrack::IsParamSupported( /*[in]*/ REFGUID rGuid ) { V_INAME(CChordTrack::IsParamSupported); V_REFGUID(rGuid); return (rGuid == GUID_ChordParam || rGuid == GUID_RhythmParam) ? S_OK : DMUS_E_TYPE_UNSUPPORTED; } // IPersist methods HRESULT CChordTrack::GetClassID( LPCLSID pClassID ) { V_INAME(CChordTrack::GetClassID); V_PTR_WRITE(pClassID, CLSID); *pClassID = CLSID_DirectMusicChordTrack; return S_OK; } // IPersistStream methods HRESULT CChordTrack::IsDirty() { return m_bRequiresSave ? S_OK : S_FALSE; } HRESULT CChordTrack::Save( LPSTREAM pStream, BOOL fClearDirty ) { V_INAME(CChordTrack::Save); V_INTERFACE(pStream); IAARIFFStream* pRIFF = NULL; MMCKINFO ck; MMCKINFO ckHeader; DWORD cb; HRESULT hr; TListItem* pChord; EnterCriticalSection( &m_CriticalSection ); hr = AllocRIFFStream( pStream, &pRIFF ); if (!SUCCEEDED(hr)) { goto ON_END; } ck.fccType = DMUS_FOURCC_CHORDTRACK_LIST; hr = pRIFF->CreateChunk(&ck,MMIO_CREATELIST); if (SUCCEEDED(hr)) { DWORD dwRoot = m_bRoot; DWORD dwScale = m_dwScalePattern | (dwRoot << 24); ckHeader.ckid = DMUS_FOURCC_CHORDTRACKHEADER_CHUNK; hr = pRIFF->CreateChunk(&ckHeader, 0); if (FAILED(hr)) { goto ON_END; } hr = pStream->Write( &dwScale, sizeof( dwScale ), &cb ); if (FAILED(hr)) { goto ON_END; } hr = pRIFF->Ascend( &ckHeader, 0 ); if (hr != S_OK) { goto ON_END; } for( pChord = m_ChordList.GetHead() ; pChord != NULL ; pChord = pChord->GetNext() ) { hr = pChord->GetItemValue().Save(pRIFF); if (FAILED(hr)) { goto ON_END; } } if( pChord == NULL && pRIFF->Ascend( &ck, 0 ) == 0 ) { hr = S_OK; } } ON_END: if (pRIFF) pRIFF->Release( ); LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT CChordTrack::GetSizeMax( ULARGE_INTEGER* /*pcbSize*/ ) { return E_NOTIMPL; } BOOL Greater(DMChord& Chord1, DMChord& Chord2) { if (Chord1.m_wMeasure > Chord2.m_wMeasure) return TRUE; else if (Chord1.m_wMeasure < Chord2.m_wMeasure) return FALSE; else // same measure; compare beats return Chord1.m_bBeat > Chord2.m_bBeat; } BOOL Less(DMChord& Chord1, DMChord& Chord2) { if (Chord1.m_wMeasure < Chord2.m_wMeasure) return TRUE; else if (Chord1.m_wMeasure > Chord2.m_wMeasure) return FALSE; else // same measure; compare beats return Chord1.m_bBeat < Chord2.m_bBeat; } HRESULT CChordTrack::Load(LPSTREAM pStream ) { V_INAME(CChordTrack::Load); V_INTERFACE(pStream); long lFileSize = 0; DWORD dwChunkSize; MMCKINFO ckMain; MMCKINFO ck; memset(&ck, 0, sizeof(ck)); MMCKINFO ckHeader; IAARIFFStream* pRIFF = NULL; // FOURCC id = 0; HRESULT hr = E_FAIL; DWORD dwPos; EnterCriticalSection( &m_CriticalSection ); Clear(); dwPos = StreamTell( pStream ); StreamSeek( pStream, dwPos, STREAM_SEEK_SET ); if( SUCCEEDED( AllocRIFFStream( pStream, &pRIFF ) ) ) { ckMain.fccType = DMUS_FOURCC_CHORDTRACK_LIST; if( pRIFF->Descend( &ckMain, NULL, MMIO_FINDLIST ) == 0) { lFileSize = ckMain.cksize - 4; // subtract off the list type DWORD dwScale; DWORD cb; if (pRIFF->Descend(&ckHeader, &ckMain, 0) == 0) { if (ckHeader.ckid == DMUS_FOURCC_CHORDTRACKHEADER_CHUNK ) { lFileSize -= 8; // chunk id + chunk size: double words lFileSize -= ckHeader.cksize; hr = pStream->Read( &dwScale, sizeof( dwScale ), &cb ); if (FAILED(hr) || cb != sizeof( dwScale ) ) { if (SUCCEEDED(hr)) hr = DMUS_E_CHUNKNOTFOUND; pRIFF->Ascend( &ckHeader, 0 ); goto END; } hr = pRIFF->Ascend( &ckHeader, 0 ); if (FAILED(hr)) { goto END; } } else { hr = DMUS_E_CHUNKNOTFOUND; goto END; } } else { hr = DMUS_E_CHUNKNOTFOUND; goto END; } m_bRoot = (BYTE) (dwScale >> 24); if (m_bRoot > 23) m_bRoot %= 24; m_dwScalePattern = dwScale & 0xffffff; while (lFileSize > 0) { if (pRIFF->Descend(&ck, &ckMain, 0) == 0) { dwChunkSize = ck.cksize; if (ck.ckid == mmioFOURCC('c','r','d','b') ) { TListItem* pChord = new TListItem; if (!pChord) break; DMChord& rChord = pChord->GetItemValue(); if (FAILED(LoadChordChunk(pStream, rChord))) break; m_ChordList.AddTail(pChord); } // Otherwise, ignore the chunk. // In either case, ascend and subtract off the chunk size if (pRIFF->Ascend( &ck, 0 ) != 0) break; lFileSize -= 8; // chunk id + chunk size: double words lFileSize -= dwChunkSize; } else break; } if (lFileSize == 0 && pRIFF->Ascend( &ck, 0 ) == 0) { hr = S_OK; m_ChordList.MergeSort(Less); } else { hr = E_FAIL; } } } END: if (pRIFF) pRIFF->Release(); LeaveCriticalSection( &m_CriticalSection ); return hr; } HRESULT CChordTrack::LoadChordChunk(LPSTREAM pStream, DMChord& rChord)//, DWORD dwChunkSize) { DWORD dwChordSize; DWORD dwSubChordSize; DWORD dwSubChordCount; DWORD cb; HRESULT hr; DMUS_IO_CHORD iChord; DMUS_IO_SUBCHORD iSubChord; memset(&iChord , 0, sizeof(iChord)); memset(&iSubChord , 0, sizeof(iSubChord)); hr = pStream->Read( &dwChordSize, sizeof( dwChordSize ), &cb ); if (FAILED(hr) || cb != sizeof( dwChordSize ) ) { return E_FAIL; } //dwChunkSize -= 2; // for the size word if( dwChordSize <= sizeof( DMUS_IO_CHORD ) ) { pStream->Read( &iChord, dwChordSize, NULL ); } else { pStream->Read( &iChord, sizeof( DMUS_IO_CHORD ), NULL ); StreamSeek( pStream, dwChordSize - sizeof( DMUS_IO_CHORD ), STREAM_SEEK_CUR ); } memset( &rChord, 0, sizeof( rChord) ); rChord.m_strName = iChord.wszName; rChord.m_mtTime = iChord.mtTime; rChord.m_bBeat = iChord.bBeat; rChord.m_wMeasure = iChord.wMeasure; rChord.m_bKey = m_bRoot; rChord.m_dwScale = m_dwScalePattern; rChord.m_fSilent = (iChord.bFlags & DMUS_CHORDKEYF_SILENT) ? true : false; hr = pStream->Read( &dwSubChordCount, sizeof( dwSubChordCount ), &cb ); if (FAILED(hr) || cb != sizeof( dwSubChordCount ) ) { return E_FAIL; } //wChunkSize -= 2; // for the count word hr = pStream->Read( &dwSubChordSize, sizeof( dwSubChordSize ), &cb ); if (FAILED(hr) || cb != sizeof( dwSubChordSize ) ) { return E_FAIL; } //wChunkSize -= 2; // for the size word for (; dwSubChordCount > 0; dwSubChordCount--) { if( dwSubChordSize <= sizeof( DMUS_IO_SUBCHORD ) ) { pStream->Read( &iSubChord, dwSubChordSize, NULL ); } else { pStream->Read( &iSubChord, sizeof( DMUS_IO_SUBCHORD ), NULL ); StreamSeek( pStream, dwSubChordSize - sizeof( DMUS_IO_SUBCHORD ), STREAM_SEEK_CUR ); } TListItem* pSub = new TListItem; if( pSub ) { DMSubChord& rSubChord = pSub->GetItemValue(); memset( &rSubChord, 0, sizeof( rSubChord) ); rSubChord.m_dwChordPattern = iSubChord.dwChordPattern; rSubChord.m_dwScalePattern = iSubChord.dwScalePattern; rSubChord.m_dwInversionPoints = iSubChord.dwInversionPoints; rSubChord.m_dwLevels = iSubChord.dwLevels; rSubChord.m_bChordRoot = iSubChord.bChordRoot; rSubChord.m_bScaleRoot = iSubChord.bScaleRoot; rChord.m_SubChordList.AddTail(pSub); } else { return E_FAIL; } } return S_OK; } HRESULT STDMETHODCALLTYPE CChordTrack::AddNotificationType( /* [in] */ REFGUID rGuidNotify) { V_INAME(CChordTrack::AddNotificationType); V_REFGUID(rGuidNotify); if( rGuidNotify == GUID_NOTIFICATION_CHORD ) { m_fNotifyChord = TRUE; return S_OK; } else { return S_FALSE; } } HRESULT STDMETHODCALLTYPE CChordTrack::RemoveNotificationType( /* [in] */ REFGUID rGuidNotify) { V_INAME(CChordTrack::RemoveNotificationType); V_REFGUID(rGuidNotify); if( rGuidNotify == GUID_NOTIFICATION_CHORD ) { m_fNotifyChord = FALSE; return S_OK; } else { return S_FALSE; } } HRESULT STDMETHODCALLTYPE CChordTrack::Clone( MUSIC_TIME mtStart, MUSIC_TIME mtEnd, IDirectMusicTrack** ppTrack) { V_INAME(CChordTrack::Clone); V_PTRPTR_WRITE(ppTrack); HRESULT hr = S_OK; if(mtStart < 0 ) { return E_INVALIDARG; } if(mtStart > mtEnd) { return E_INVALIDARG; } EnterCriticalSection( &m_CriticalSection ); CChordTrack *pDM; try { pDM = new CChordTrack(*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 CChordTrack::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 CChordTrack::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 CChordTrack::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 CChordTrack::Compose( IUnknown* pContext, DWORD dwTrackGroup, IDirectMusicTrack** ppResultTrack) { return E_NOTIMPL; } STDMETHODIMP CChordTrack::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); if (ppResultTrack) { hr = Clone(0, mtJoin, ppResultTrack); if (SUCCEEDED(hr)) { hr = ((CChordTrack*)*ppResultTrack)->JoinInternal(pNewTrack, mtJoin, pContext, dwTrackGroup); } } else { hr = JoinInternal(pNewTrack, mtJoin, pContext, dwTrackGroup); } LeaveCriticalSection(&m_CriticalSection); return hr; } HRESULT CChordTrack::JoinInternal(IDirectMusicTrack* pNewTrack, MUSIC_TIME mtJoin, IUnknown* pContext, DWORD dwTrackGroup) { HRESULT hr = S_OK; 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; } } CChordTrack* pOtherTrack = (CChordTrack*)pNewTrack; TListItem* pScan = pOtherTrack->m_ChordList.GetHead(); for (; pScan; pScan = pScan->GetNext()) { DMChord& rScan = pScan->GetItemValue(); TListItem* pNew = new TListItem; if (pNew) { DMChord& rNew = pNew->GetItemValue(); rNew.m_mtTime = rScan.m_mtTime + mtJoin; rNew.m_strName = rScan.m_strName; rNew.m_wMeasure = rScan.m_wMeasure + wMeasure; rNew.m_bBeat = rScan.m_bBeat; rNew.m_bKey = rScan.m_bKey; rNew.m_dwScale = rScan.m_dwScale; TListItem* pSubScan = rScan.m_SubChordList.GetHead(); for(; pSubScan; pSubScan = pSubScan->GetNext()) { DMSubChord& rSubScan = pSubScan->GetItemValue(); TListItem* pSubNew = new TListItem; if (pSubNew) { DMSubChord& rSubNew = pSubNew->GetItemValue(); rSubNew.m_dwChordPattern = rSubScan.m_dwChordPattern; rSubNew.m_dwScalePattern = rSubScan.m_dwScalePattern; rSubNew.m_dwInversionPoints = rSubScan.m_dwInversionPoints; rSubNew.m_dwLevels = rSubScan.m_dwLevels; rSubNew.m_bChordRoot = rSubScan.m_bChordRoot; rSubNew.m_bScaleRoot = rSubScan.m_bScaleRoot; rNew.m_SubChordList.AddTail(pSubNew); } } m_ChordList.AddTail(pNew); } else { hr = E_OUTOFMEMORY; break; } } if (pSong) pSong->Release(); if (pSegment) pSegment->Release(); return hr; }