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

936 lines
28 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) 1998-1999 Microsoft Corporation
//
// File: spsttrk.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)
// SPstTrk.cpp : Implementation of CSPstTrk
#include "SPstTrk.h"
#include "debug.h"
#include "..\shared\Validate.h"
/////////////////////////////////////////////////////////////////////////////
// CSPstTrk
CSPstTrk::CSPstTrk() :
m_bRequiresSave(0), m_pPerformance(NULL),
m_pComposer(NULL),
m_fNotifyRecompose(FALSE),
// m_pSegment(NULL),
m_cRef(1),
m_fCSInitialized(FALSE)
{
InterlockedIncrement(&g_cComponent);
// Do this first since it might throw an exception
//
::InitializeCriticalSection( &m_CriticalSection );
m_fCSInitialized = TRUE;
}
// This assumes cloning on measure boundaries
CSPstTrk::CSPstTrk(const CSPstTrk& rTrack, MUSIC_TIME mtStart, MUSIC_TIME mtEnd) :
m_bRequiresSave(0), m_pPerformance(NULL),
m_pComposer(NULL),
m_fNotifyRecompose(FALSE),
//m_pSegment(NULL),
m_cRef(1),
m_fCSInitialized(FALSE)
{
InterlockedIncrement(&g_cComponent);
// Do this first since it might throw an exception
//
::InitializeCriticalSection( &m_CriticalSection );
m_fCSInitialized = TRUE;
BOOL fStarted = FALSE;
WORD wMeasure = 0;
TListItem<DMSignPostStruct>* pScan = rTrack.m_SignPostList.GetHead();
TListItem<DMSignPostStruct>* pPrevious = NULL;
for(; pScan; pScan = pScan->GetNext())
{
DMSignPostStruct& 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<DMSignPostStruct>* pNew = new TListItem<DMSignPostStruct>;
if (pNew)
{
DMSignPostStruct& rNew = pNew->GetItemValue();
rNew.m_mtTime = rScan.m_mtTime - mtStart;
rNew.m_wMeasure = rScan.m_wMeasure - wMeasure;
rNew.m_dwChords = rScan.m_dwChords;
m_SignPostList.AddTail(pNew);
}
}
else break;
}
if (pPrevious)
{
TListItem<DMSignPostStruct>* pNew = new TListItem<DMSignPostStruct>;
if (pNew)
{
DMSignPostStruct& rNew = pNew->GetItemValue();
rNew.m_mtTime = 0;
rNew.m_wMeasure = 0;
rNew.m_dwChords = pPrevious->GetItemValue().m_dwChords;
m_SignPostList.AddHead(pNew);
}
}
}
CSPstTrk::~CSPstTrk()
{
if (m_pComposer)
{
delete m_pComposer;
}
if (m_fCSInitialized)
{
::DeleteCriticalSection( &m_CriticalSection );
}
InterlockedDecrement(&g_cComponent);
}
void CSPstTrk::Clear()
{
m_SignPostList.CleanUp();
}
STDMETHODIMP CSPstTrk::QueryInterface(
const IID &iid,
void **ppv)
{
V_INAME(CSPstTrk::QueryInterface);
V_PTRPTR_WRITE(ppv);
V_REFGUID(iid);
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) CSPstTrk::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) CSPstTrk::Release()
{
if (!InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}
return m_cRef;
}
HRESULT CSPstTrk::Init(
/*[in]*/ IDirectMusicSegment* pSegment
)
{
return S_OK; // if I return an error, dmime gives me an assertion failure
}
HRESULT CSPstTrk::InitPlay(
/*[in]*/ IDirectMusicSegmentState* pSegmentState,
/*[in]*/ IDirectMusicPerformance* pPerformance,
/*[out]*/ void** ppStateData,
/*[in]*/ DWORD dwTrackID,
/*[in]*/ DWORD dwFlags
)
{
EnterCriticalSection(&m_CriticalSection);
// get rid of any existing composer object
if (m_pComposer)
{
delete m_pComposer;
m_pComposer = NULL;
}
IDirectMusicSegment* pSegment = NULL;
HRESULT hr = pSegmentState->GetSegment(&pSegment);
if (SUCCEEDED(hr))
{
m_pComposer = new CDMCompos;
if(!m_pComposer)
{
hr = E_OUTOFMEMORY;
}
pSegment->Release();
}
else
{
Trace(2, "WARNING: InitPlay (Signpost Track): Segment State does not contain a segment.\n");
hr = S_OK; // Let it succeed anyway. Just means we can't compose on the fly.
}
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
HRESULT CSPstTrk::EndPlay(
/*[in]*/ void* pStateData
)
{
EnterCriticalSection(&m_CriticalSection);
// get rid of any existing composer object
if (m_pComposer)
{
delete m_pComposer;
m_pComposer = NULL;
}
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
HRESULT CSPstTrk::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 fStart = (dwFlags & DMUS_TRACKF_START) ? true : false;
bool fLoop = (dwFlags & DMUS_TRACKF_LOOP) ? true : false;
bool fCompose = (dwFlags & DMUS_TRACKF_RECOMPOSE) ? true : false;
bool fPlayOff = (dwFlags & DMUS_TRACKF_PLAY_OFF) ? true : false;
EnterCriticalSection(&m_CriticalSection);
if ( fStart || fLoop )
{
if ( fCompose && !fPlayOff )
{
IDirectMusicSegment* pSegment = NULL;
if (SUCCEEDED(pSegState->GetSegment(&pSegment)))
{
// call ComposeSegmentFromTemplateEx on this segment
if (m_pComposer)
{
// Should an activity level be allowed if desired?
// This could be handled via a SetParam.
m_pComposer->ComposeSegmentFromTemplateEx(
NULL,
pSegment,
0, // ignore activity level, don't clone
0, // for activity level
NULL,
NULL
);
// if we recomposed, send a recompose notification
SendNotification(mtStart + mtOffset, pPerf, pSegment, pSegState, dwFlags);
}
pSegment->Release();
}
}
}
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
HRESULT CSPstTrk::SendNotification(MUSIC_TIME mtTime,
IDirectMusicPerformance* pPerf,
IDirectMusicSegment* pSegment,
IDirectMusicSegmentState* pSegState,
DWORD dwFlags)
{
if (!m_fNotifyRecompose || (dwFlags & DMUS_TRACKF_NOTIFY_OFF))
{
return S_OK;
}
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 = DMUS_NOTIFICATION_RECOMPOSE;
pEvent->guidNotificationType = GUID_NOTIFICATION_RECOMPOSE;
if (FAILED(pSegment->GetTrackGroup(this, &pEvent->dwGroupID)))
{
pEvent->dwGroupID = 0xffffffff;
}
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 CSPstTrk::GetPriority(
/*[out]*/ DWORD* pPriority
)
{
return E_NOTIMPL;
}
HRESULT CSPstTrk::GetParam(
REFGUID rCommandGuid,
MUSIC_TIME mtTime,
MUSIC_TIME* pmtNext,
void* pData
)
{
return E_NOTIMPL;
}
HRESULT CSPstTrk::SetParam(
REFGUID rCommandGuid,
MUSIC_TIME mtTime,
void __RPC_FAR *pData)
{
return E_NOTIMPL;
}
// IPersist methods
HRESULT CSPstTrk::GetClassID( LPCLSID pClassID )
{
V_INAME(CSPstTrk::GetClassID);
V_PTR_WRITE(pClassID, CLSID);
*pClassID = CLSID_DirectMusicSignPostTrack;
return S_OK;
}
// IDirectMusicCommon Methods
HRESULT CSPstTrk::GetName(
/*[out]*/ BSTR* pbstrName
)
{
return E_NOTIMPL;
}
HRESULT CSPstTrk::IsParamSupported(
/*[in]*/ REFGUID rGuid
)
{
return E_NOTIMPL;
}
// IPersistStream methods
HRESULT CSPstTrk::IsDirty()
{
return m_bRequiresSave ? S_OK : S_FALSE;
}
HRESULT CSPstTrk::Save( LPSTREAM pStream, BOOL fClearDirty )
{
V_INAME(CSPstTrk::Save);
V_INTERFACE(pStream);
IAARIFFStream* pRIFF = NULL;
MMCKINFO ck;
HRESULT hr;
DWORD cb;
DWORD dwSize;
DMUS_IO_SIGNPOST oSignPost;
TListItem<DMSignPostStruct>* pSignPost;
EnterCriticalSection( &m_CriticalSection );
hr = AllocRIFFStream( pStream, &pRIFF );
if ( FAILED( hr ) )
{
goto ON_END;
}
ck.ckid = DMUS_FOURCC_SIGNPOST_TRACK_CHUNK;
if( pRIFF->CreateChunk( &ck, 0 ) == 0 )
{
dwSize = sizeof( oSignPost );
hr = pStream->Write( &dwSize, sizeof( dwSize ), &cb );
if( FAILED( hr ) || cb != sizeof( dwSize ) )
{
if (SUCCEEDED(hr)) hr = E_FAIL;
goto ON_END;
}
for( pSignPost = m_SignPostList.GetHead(); pSignPost != NULL ; pSignPost = pSignPost->GetNext() )
{
DMSignPostStruct& rSignPost = pSignPost->GetItemValue();
memset( &oSignPost, 0, sizeof( oSignPost ) );
oSignPost.mtTime = rSignPost.m_mtTime;
oSignPost.wMeasure = rSignPost.m_wMeasure;
oSignPost.dwChords = rSignPost.m_dwChords;
if( FAILED( pStream->Write( &oSignPost, sizeof( oSignPost ), &cb ) ) ||
cb != sizeof( oSignPost ) )
{
break;
}
}
if( pSignPost == NULL &&
pRIFF->Ascend( &ck, 0 ) == 0 )
{
hr = S_OK;
}
}
ON_END:
if (pRIFF) pRIFF->Release();
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
HRESULT CSPstTrk::GetSizeMax( ULARGE_INTEGER* /*pcbSize*/ )
{
return E_NOTIMPL;
}
BOOL Less(DMSignPostStruct& SP1, DMSignPostStruct& SP2)
{ return SP1.m_wMeasure < SP2.m_wMeasure; }
HRESULT CSPstTrk::Load(LPSTREAM pStream )
{
V_INAME(CSPstTrk::Load);
V_INTERFACE(pStream);
HRESULT hr = E_FAIL;
DWORD dwPos;
IAARIFFStream* pRIFF;
EnterCriticalSection( &m_CriticalSection );
Clear();
dwPos = StreamTell( pStream );
StreamSeek( pStream, dwPos, STREAM_SEEK_SET );
MMCKINFO ck;
long lFileSize = 0;
DWORD dwNodeSize;
DWORD cb;
DMUS_IO_SIGNPOST iSignPost;
ck.ckid = DMUS_FOURCC_SIGNPOST_TRACK_CHUNK;
if( SUCCEEDED( AllocRIFFStream( pStream, &pRIFF ) ) &&
pRIFF->Descend( &ck, NULL, MMIO_FINDCHUNK ) == 0 )
{
lFileSize = (long) ck.cksize;
hr = pStream->Read( &dwNodeSize, sizeof( dwNodeSize ), &cb );
if( SUCCEEDED( hr ) && cb == sizeof( dwNodeSize ) )
{
lFileSize -= 4; // for the size dword
TListItem<DMSignPostStruct>* pSignPost;
if (lFileSize % dwNodeSize)
{
hr = E_FAIL;
}
else
{
while( lFileSize > 0 )
{
//TraceI(0, "File size: %d\n", lFileSize);
pSignPost = new TListItem<DMSignPostStruct>;
if( pSignPost )
{
DMSignPostStruct& rSignPost = pSignPost->GetItemValue();
if( dwNodeSize <= sizeof( iSignPost ) )
{
pStream->Read( &iSignPost, dwNodeSize, NULL );
}
else
{
pStream->Read( &iSignPost, sizeof( iSignPost ), NULL );
DWORD dw = (lFileSize >= sizeof( iSignPost ) ) ? lFileSize - sizeof( iSignPost ) : 0;
StreamSeek( pStream, dw, STREAM_SEEK_CUR );
}
memset( &rSignPost, 0, sizeof( rSignPost ) );
rSignPost.m_mtTime = iSignPost.mtTime;
rSignPost.m_wMeasure = iSignPost.wMeasure;
rSignPost.m_dwChords = iSignPost.dwChords;
m_SignPostList.AddTail(pSignPost);
lFileSize -= dwNodeSize;
}
else break;
}
}
}
if( lFileSize == 0 &&
pRIFF->Ascend( &ck, 0 ) == 0 )
{
hr = S_OK;
m_SignPostList.MergeSort(Less);
}
pRIFF->Release();
}
LeaveCriticalSection( &m_CriticalSection );
return hr;
}
HRESULT STDMETHODCALLTYPE CSPstTrk::AddNotificationType(
/* [in] */ REFGUID rGuidNotify)
{
V_INAME(CPersonalityTrack::AddNotificationType);
V_REFGUID(rGuidNotify);
if( rGuidNotify == GUID_NOTIFICATION_RECOMPOSE )
{
m_fNotifyRecompose = TRUE;
return S_OK;
}
else
{
Trace(2, "WARNING: AddNotificationType (signpost track): Notification type not supported.\n");
return S_FALSE;
}
}
HRESULT STDMETHODCALLTYPE CSPstTrk::RemoveNotificationType(
/* [in] */ REFGUID rGuidNotify)
{
V_INAME(CPersonalityTrack::RemoveNotificationType);
V_REFGUID(rGuidNotify);
if( rGuidNotify == GUID_NOTIFICATION_RECOMPOSE )
{
m_fNotifyRecompose = FALSE;
return S_OK;
}
else
{
Trace(2, "WARNING: RemoveNotificationType (signpost track): Notification type not supported.\n");
return S_FALSE;
}
}
HRESULT STDMETHODCALLTYPE CSPstTrk::Clone(
MUSIC_TIME mtStart,
MUSIC_TIME mtEnd,
IDirectMusicTrack** ppTrack)
{
V_INAME(CSPstTrk::Clone);
V_PTRPTR_WRITE(ppTrack);
HRESULT hr = S_OK;
if(mtStart < 0 )
{
Trace(1, "ERROR: Clone (signpost map): Invalid start time.\n");
return E_INVALIDARG;
}
if(mtStart > mtEnd)
{
Trace(1, "ERROR: Clone (signpost map): Invalid end time.\n");
return E_INVALIDARG;
}
EnterCriticalSection( &m_CriticalSection );
CSPstTrk *pDM;
try
{
pDM = new CSPstTrk(*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;
}
// IDirectMusicTrack8 Methods
// For consistency with other track types
STDMETHODIMP CSPstTrk::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 CSPstTrk::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 CSPstTrk::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 CSPstTrk::Compose(
IUnknown* pContext,
DWORD dwTrackGroup,
IDirectMusicTrack** ppResultTrack)
{
V_INAME(IDirectMusicTrack::Compose)
V_INTERFACE(pContext);
V_PTRPTR_WRITE(ppResultTrack);
EnterCriticalSection(&m_CriticalSection);
HRESULT hr = S_OK;
IDirectMusicTrack* pChordTrack = NULL;
IDirectMusicTrack8* pCommandTrack = NULL;
IDirectMusicStyle* pStyle = NULL;
IDirectMusicTrack8* pChordMapTrack = NULL;
IAARIFFStream* pChordRIFF = NULL;
IStream* pIChordStream = NULL;
IPersistStream* pIChordTrackStream = NULL;
CDMCompos* pComposer = NULL;
MUSIC_TIME mtLength = 0;
IDirectMusicSegment* pTempSeg = NULL;
IDirectMusicSong* pSong = NULL;
if (FAILED(pContext->QueryInterface(IID_IDirectMusicSegment, (void**)&pTempSeg)))
{
if (FAILED(pContext->QueryInterface(IID_IDirectMusicSong, (void**)&pSong)))
{
Trace(1, "ERROR: Compose (signpost track): Missing segment or song.\n");
hr = E_INVALIDARG;
goto ON_END;
}
}
if (pTempSeg)
{
if (FAILED(hr = pTempSeg->GetParam(GUID_IDirectMusicStyle, dwTrackGroup, 0, 0, NULL, (void*)&pStyle)))
{
if (FAILED(hr = pTempSeg->GetParam(GUID_IDirectMusicPatternStyle, dwTrackGroup, 0, 0, NULL, (void*)&pStyle)))
{
goto ON_END;
}
}
hr = pTempSeg->GetTrack(CLSID_DirectMusicChordMapTrack, dwTrackGroup, 0, (IDirectMusicTrack**)&pChordMapTrack);
if (FAILED(hr)) goto ON_END;
if (FAILED(hr = pTempSeg->GetLength(&mtLength))) goto ON_END;
hr = pTempSeg->GetTrack(CLSID_DirectMusicCommandTrack, dwTrackGroup, 0, (IDirectMusicTrack**)&pCommandTrack);
if (FAILED(hr)) goto ON_END;
}
else if (pSong)
{
MUSIC_TIME mtNow = 0;
MUSIC_TIME mtNext = 0;
while (FAILED(hr = pSong->GetParam(GUID_IDirectMusicStyle, dwTrackGroup, 0, mtNow, &mtNext, (void*)&pStyle)))
{
if (SUCCEEDED(hr = pSong->GetParam(GUID_IDirectMusicPatternStyle, dwTrackGroup, 0, mtNow, NULL, (void*)&pStyle)))
{
break;
}
if (mtNext <= 0) goto ON_END;
mtNow = mtNext;
}
IDirectMusicSegment* pSeg = NULL;
DWORD dwSeg = 0;
while (S_OK == hr)
{
if (FAILED(hr = pSong->EnumSegment(dwSeg, &pSeg))) goto ON_END;
if (hr == S_OK)
{
HRESULT hrCommand = S_OK;
HRESULT hrChordMap = S_OK;
MUSIC_TIME mt = 0;
hr = pSeg->GetLength(&mt);
if (FAILED(hr))
{
pSeg->Release();
goto ON_END;
}
IDirectMusicTrack8* pSegTrack = NULL;
IDirectMusicTrack8* pSegTrack2 = NULL;
hrCommand = pSeg->GetTrack(CLSID_DirectMusicCommandTrack, dwTrackGroup, 0, (IDirectMusicTrack**)&pSegTrack);
hrChordMap = pSeg->GetTrack(CLSID_DirectMusicChordMapTrack, dwTrackGroup, 0, (IDirectMusicTrack**)&pSegTrack2);
pSeg->Release();
pSeg = NULL;
if (SUCCEEDED(hrCommand))
{
if (!pCommandTrack)
{
hr = pSegTrack->Clone(0, 0, (IDirectMusicTrack**)&pCommandTrack);
}
if (SUCCEEDED(hr))
{
hr = pCommandTrack->Join(pSegTrack, mtLength, pSong, dwTrackGroup, NULL);
}
pSegTrack->Release();
}
if (SUCCEEDED(hrChordMap))
{
if (!pChordMapTrack)
{
hr = pSegTrack2->Clone(0, 0, (IDirectMusicTrack**)&pChordMapTrack);
}
if (SUCCEEDED(hr))
{
hr = pChordMapTrack->Join(pSegTrack2, mtLength, pSong, dwTrackGroup, NULL);
}
pSegTrack2->Release();
}
if (FAILED(hr)) goto ON_END;
mtLength += mt;
dwSeg++;
}
}
}
pComposer = new CDMCompos;
if(!pComposer)
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
TList<PlayChord> PlayList;
BYTE bRoot = 0;
DWORD dwScale;
hr = pComposer->ComposePlayListFromTemplate(
pStyle, NULL, pChordMapTrack, (IDirectMusicTrack*)this, pCommandTrack, dwTrackGroup,
mtLength, false, 0, PlayList, bRoot, dwScale);
// create a new chord track
DMUS_TIMESIGNATURE TimeSig;
// Fill in the time sig event with default values (4/4, 16th note resolution)
TimeSig.mtTime = 0;
TimeSig.bBeatsPerMeasure = 4;
TimeSig.bBeat = 4;
TimeSig.wGridsPerBeat = 4;
hr = ::CoCreateInstance(
CLSID_DirectMusicChordTrack,
NULL,
CLSCTX_INPROC,
IID_IDirectMusicTrack,
(void**)&pChordTrack
);
if (!SUCCEEDED(hr)) goto ON_END;
hr = CreateStreamOnHGlobal(NULL, TRUE, &pIChordStream);
if (S_OK != hr) goto ON_END;
hr = AllocRIFFStream( pIChordStream, &pChordRIFF);
if (S_OK != hr) goto ON_END;
pComposer->SaveChordList(pChordRIFF, PlayList, bRoot, dwScale, TimeSig);
hr = pChordTrack->QueryInterface(IID_IPersistStream, (void**)&pIChordTrackStream);
if (!SUCCEEDED(hr)) goto ON_END;
StreamSeek(pIChordStream, 0, STREAM_SEEK_SET);
hr = pIChordTrackStream->Load(pIChordStream);
if (!SUCCEEDED(hr)) goto ON_END;
*ppResultTrack = pChordTrack;
}
ON_END:
if (pComposer) pComposer->CleanUp();
if (pStyle) pStyle->Release();
if (pChordRIFF) pChordRIFF->Release();
if (pIChordStream) pIChordStream->Release();
if (pIChordTrackStream) pIChordTrackStream->Release();
if (pCommandTrack) pCommandTrack->Release();
if (pChordMapTrack) pChordMapTrack->Release();
if (pComposer) delete pComposer;
if (pSong) pSong->Release();
if (pTempSeg) pTempSeg->Release();
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
STDMETHODIMP CSPstTrk::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<DMSignPostStruct> ResultList;
CSPstTrk* pResultTrack = NULL;
if (ppResultTrack)
{
hr = Clone(0, mtJoin, ppResultTrack);
pResultTrack = (CSPstTrk*)*ppResultTrack;
while(!pResultTrack->m_SignPostList.IsEmpty())
{
ResultList.AddHead(pResultTrack->m_SignPostList.RemoveHead());
}
}
else
{
pResultTrack = this;
while(!m_SignPostList.IsEmpty() &&
m_SignPostList.GetHead()->GetItemValue().m_mtTime < mtJoin)
{
ResultList.AddHead(m_SignPostList.RemoveHead());
}
m_SignPostList.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
WORD wMeasureOffset = ClocksToMeasure(mtNext + mtOver, TimeSig);
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;
}
}
CSPstTrk* pOtherTrack = (CSPstTrk*)pNewTrack;
TListItem<DMSignPostStruct>* pScan = pOtherTrack->m_SignPostList.GetHead();
for (; pScan; pScan = pScan->GetNext())
{
TListItem<DMSignPostStruct>* pNew = new TListItem<DMSignPostStruct>(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_SignPostList.CleanUp();
while(!ResultList.IsEmpty() )
{
pResultTrack->m_SignPostList.AddHead(ResultList.RemoveHead());
}
}
if (pSong) pSong->Release();
if (pSegment) pSegment->Release();
LeaveCriticalSection(&m_CriticalSection);
return hr;
}