You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1127 lines
35 KiB
1127 lines
35 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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<DMChord>* pScan = rTrack.m_ChordList.GetHead();
|
|
TListItem<DMChord>* 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<DMChord>* pNew = new TListItem<DMChord>;
|
|
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<DMSubChord>* pSubScan = rScan.m_SubChordList.GetHead();
|
|
for(; pSubScan; pSubScan = pSubScan->GetNext())
|
|
{
|
|
DMSubChord& rSubScan = pSubScan->GetItemValue();
|
|
TListItem<DMSubChord>* pSubNew = new TListItem<DMSubChord>;
|
|
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<DMChord>* pNew = new TListItem<DMChord>;
|
|
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<DMSubChord>* pSubPrevious = rPrevious.m_SubChordList.GetHead();
|
|
for(; pSubPrevious; pSubPrevious = pSubPrevious->GetNext())
|
|
{
|
|
DMSubChord& rSubPrevious = pSubPrevious->GetItemValue();
|
|
TListItem<DMSubChord>* pSubNew = new TListItem<DMSubChord>;
|
|
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<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) 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<DMChord>* 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<DMChord>* 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<DMChord>* pChord = m_ChordList.GetHead();
|
|
TListItem<DMChord>* 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<DMChord>* pChord = m_ChordList.GetHead();
|
|
TListItem<DMChord>* 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<DMChord>* pChordItem = m_ChordList.GetHead();
|
|
TListItem<DMChord>* pPrevious = NULL;
|
|
TListItem<DMChord>* pChord = new TListItem<DMChord>;
|
|
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<DMChord>* 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<DMChord>* pChord = new TListItem<DMChord>;
|
|
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<DMSubChord>* pSub = new TListItem<DMSubChord>;
|
|
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<DMChord>* pScan = pOtherTrack->m_ChordList.GetHead();
|
|
for (; pScan; pScan = pScan->GetNext())
|
|
{
|
|
DMChord& rScan = pScan->GetItemValue();
|
|
TListItem<DMChord>* pNew = new TListItem<DMChord>;
|
|
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<DMSubChord>* pSubScan = rScan.m_SubChordList.GetHead();
|
|
for(; pSubScan; pSubScan = pSubScan->GetNext())
|
|
{
|
|
DMSubChord& rSubScan = pSubScan->GetItemValue();
|
|
TListItem<DMSubChord>* pSubNew = new TListItem<DMSubChord>;
|
|
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;
|
|
}
|
|
|