3369 lines
111 KiB
C++
3369 lines
111 KiB
C++
// Copyright (c) 1998-2001 Microsoft Corporation
|
|
// dmsegobj.cpp : Implementation of CSegment
|
|
|
|
#include "dmime.h"
|
|
#include "DMSegObj.h"
|
|
#include "DMSStObj.h"
|
|
#include "DMGraph.h"
|
|
#include "dmusici.h"
|
|
#include "tlist.h"
|
|
#include "midifile.h"
|
|
#include "dmusicc.h"
|
|
#include "dmusicf.h"
|
|
#include "dmperf.h"
|
|
#include "wavtrack.h"
|
|
#include "..\shared\validp.h"
|
|
#include "..\shared\dmstrm.h"
|
|
#include "..\shared\Validate.h"
|
|
#include "..\dmstyle\dmstyle.h"
|
|
#include "..\dmcompos\dmcompp.h"
|
|
#include "debug.h"
|
|
#include "dmscriptautguids.h"
|
|
#include "tempotrk.h"
|
|
#include <strsafe.h>
|
|
#define ASSERT assert
|
|
|
|
// @doc EXTERNAL
|
|
|
|
long g_lNewTrackID = 0; // shared by all instances of Segments, this keeps track of the
|
|
// next available TrackID when creating new Track states.
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CSegment
|
|
|
|
void CSegment::Init()
|
|
{
|
|
InitializeCriticalSection(&m_CriticalSection);
|
|
m_pSong = NULL;
|
|
m_dwNextPlayFlags = 0;
|
|
m_dwNextPlayID = 0xFFFFFFFF;
|
|
m_dwPlayID = 0;
|
|
// m_fPartialLoad = FALSE;
|
|
m_mtLength = 1;
|
|
m_mtStart = 0;
|
|
m_mtLoopStart = 0;
|
|
m_mtLoopEnd = 0;
|
|
m_rtLength = 0;
|
|
m_dwRepeats = 0;
|
|
m_dwResolution = 0;
|
|
m_dwNumPChannels = 0;
|
|
m_paPChannels = NULL;
|
|
m_pGraph = NULL;
|
|
m_pAudioPathConfig = NULL;
|
|
m_pUnkDispatch = NULL;
|
|
m_dwSegFlags = 0;
|
|
m_cRef = 0;
|
|
m_dwVersion = 0; // Init to 6.1 behavior.
|
|
m_dwValidData = DMUS_OBJ_CLASS; // upon creation, only this data is valid
|
|
memset(&m_guidObject,0,sizeof(m_guidObject));
|
|
memset(&m_ftDate, 0,sizeof(m_ftDate));
|
|
memset(&m_vVersion, 0,sizeof(m_vVersion));
|
|
m_fZombie = false;
|
|
InterlockedIncrement(&g_cComponent);
|
|
TraceI(2, "Segment %lx created\n", this );
|
|
}
|
|
|
|
CSegment::CSegment()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
CSegment::CSegment(DMUS_IO_SEGMENT_HEADER *pHeader, CSegment *pSource)
|
|
{
|
|
Init();
|
|
AddRef(); // so that this doesn't get deleted in Track::Init...
|
|
// Force the version to at least 8 so audiopath functionality will be turned on.
|
|
m_dwVersion = 8;
|
|
m_dwResolution = pHeader->dwResolution;
|
|
m_mtLength = pHeader->mtLength;
|
|
m_mtStart = pHeader->mtPlayStart;
|
|
m_mtLoopStart = pHeader->mtLoopStart;
|
|
m_mtLoopEnd = pHeader->mtLoopEnd;
|
|
m_dwRepeats = pHeader->dwRepeats;
|
|
m_dwSegFlags = pHeader->dwFlags;
|
|
if (m_dwSegFlags & DMUS_SEGIOF_REFLENGTH)
|
|
{
|
|
m_rtLength = pHeader->rtLength;
|
|
}
|
|
else
|
|
{
|
|
m_rtLength = 0;
|
|
}
|
|
if (pSource)
|
|
{
|
|
pSource->m_TrackList.CreateCopyWithBlankState(&m_TrackList);
|
|
CTrack *pTrack = m_TrackList.GetHead();
|
|
for (;pTrack;pTrack = pTrack->GetNext())
|
|
{
|
|
pTrack->m_pTrack->Init( this );
|
|
}
|
|
}
|
|
}
|
|
|
|
CSegment::~CSegment()
|
|
{
|
|
if (m_pUnkDispatch)
|
|
m_pUnkDispatch->Release(); // free IDispatch implementation we may have borrowed
|
|
Clear(false);
|
|
DeleteCriticalSection(&m_CriticalSection);
|
|
InterlockedDecrement(&g_cComponent);
|
|
TraceI(2, "Segment %lx destroyed\n", this );
|
|
}
|
|
|
|
void CSegment::Clear(bool fZombie)
|
|
{
|
|
m_TrackList.Clear();
|
|
if (m_pAudioPathConfig)
|
|
{
|
|
m_pAudioPathConfig->Release();
|
|
m_pAudioPathConfig = NULL;
|
|
}
|
|
SetGraph(NULL); // shut down the graph and release it
|
|
// We need the following stuff to hang around if the segment is being zombied.
|
|
if (!fZombie)
|
|
{
|
|
// remove all notifies
|
|
CNotificationItem* pItem = m_NotificationList.GetHead();
|
|
while( pItem )
|
|
{
|
|
CNotificationItem* pNext = pItem->GetNext();
|
|
m_NotificationList.Remove( pItem );
|
|
delete pItem;
|
|
pItem = pNext;
|
|
}
|
|
if( m_paPChannels )
|
|
{
|
|
delete [] m_paPChannels;
|
|
m_paPChannels = NULL;
|
|
}
|
|
m_dwNumPChannels = 0;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP_(void) CSegment::Zombie()
|
|
{
|
|
Clear(true);
|
|
m_fZombie = true;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::QueryInterface(
|
|
const IID &iid, // @parm Interface to query for
|
|
void **ppv) // @parm The requested interface will be returned here
|
|
{
|
|
V_INAME(CSegment::QueryInterface);
|
|
V_PTRPTR_WRITE(ppv);
|
|
V_REFGUID(iid);
|
|
|
|
*ppv = NULL;
|
|
if (iid == IID_IUnknown || iid == IID_IDirectMusicSegment)
|
|
{
|
|
*ppv = static_cast<IDirectMusicSegment*>(this);
|
|
}
|
|
else if (iid == IID_CSegment)
|
|
{
|
|
*ppv = static_cast<CSegment*>(this);
|
|
}
|
|
else if (iid == IID_IDirectMusicSegment8)
|
|
{
|
|
m_dwVersion = 8;
|
|
*ppv = static_cast<IDirectMusicSegment*>(this);
|
|
}
|
|
else if (iid == IID_IDirectMusicSegment8P)
|
|
{
|
|
*ppv = static_cast<IDirectMusicSegment8P*>(this);
|
|
}
|
|
else if (iid == IID_IDirectMusicSegment2)
|
|
{
|
|
m_dwVersion = 2;
|
|
*ppv = static_cast<IDirectMusicSegment*>(this);
|
|
}
|
|
else if (iid == IID_IPersistStream)
|
|
{
|
|
*ppv = static_cast<IPersistStream*>(this);
|
|
}
|
|
else if(iid == IID_IDirectMusicObject)
|
|
{
|
|
*ppv = static_cast<IDirectMusicObject*>(this);
|
|
}
|
|
else if (iid == IID_IDirectMusicObjectP)
|
|
{
|
|
*ppv = static_cast<IDirectMusicObjectP*>(this);
|
|
}
|
|
else if (iid == IID_IDispatch)
|
|
{
|
|
// A helper scripting object implements IDispatch, which we expose from the
|
|
// Performance object via COM aggregation.
|
|
if (!m_pUnkDispatch)
|
|
{
|
|
// Create the helper object
|
|
::CoCreateInstance(
|
|
CLSID_AutDirectMusicSegment,
|
|
static_cast<IDirectMusicSegment*>(this),
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IUnknown,
|
|
reinterpret_cast<void**>(&m_pUnkDispatch));
|
|
}
|
|
if (m_pUnkDispatch)
|
|
{
|
|
return m_pUnkDispatch->QueryInterface(IID_IDispatch, ppv);
|
|
}
|
|
}
|
|
|
|
if (*ppv == NULL)
|
|
{
|
|
Trace(4,"Warning: Segment queried for unknown interface.\n");
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
reinterpret_cast<IUnknown*>(this)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CSegment::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CSegment::Release()
|
|
{
|
|
if (!InterlockedDecrement(&m_cRef))
|
|
{
|
|
m_cRef = 100; // artificial reference count to prevent reentrency due to COM aggregation
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return m_cRef;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::GetLength(
|
|
MUSIC_TIME *pmtLength) // @parm Returns the Segment's length.
|
|
{
|
|
V_INAME(IDirectMusicSegment::GetLength);
|
|
V_PTR_WRITE(pmtLength, MUSIC_TIME);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetLength after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
*pmtLength = m_mtLength;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSegment::SetLength(
|
|
MUSIC_TIME mtLength) // @parm The desired length.
|
|
{
|
|
if( mtLength <=0 )
|
|
{
|
|
Trace(1,"Error: Can not set segment length to a negative number (%ld.)\n",mtLength);
|
|
return E_INVALIDARG;
|
|
}
|
|
if(( mtLength <= m_mtStart ) || ( mtLength < m_mtLoopEnd ))
|
|
{
|
|
Trace(1,"Error: Can not set segment length to %ld, which is either less that the start time %ld or the loop end %ld\n",
|
|
mtLength,m_mtStart,m_mtLoopEnd);
|
|
return DMUS_E_OUT_OF_RANGE;
|
|
}
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetLength after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
m_mtLength = mtLength;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::GetRepeats(
|
|
DWORD *pdwRepeats) // @parm Returns the number of repeats.
|
|
{
|
|
V_INAME(IDirectMusicSegment::GetRepeats);
|
|
V_PTR_WRITE(pdwRepeats, DWORD);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetRepeats after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
*pdwRepeats = m_dwRepeats;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::SetRepeats(
|
|
DWORD dwRepeats) // @parm The desired number of repeats.
|
|
{
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetRepeats after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
m_dwRepeats = dwRepeats;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::GetDefaultResolution(
|
|
DWORD *pdwResolution) // @parm Returns the default resolution. (See <t DMPLAYSEGFLAGS>.)
|
|
{
|
|
V_INAME(IDirectMusicSegment::GetDefaultResolution);
|
|
V_PTR_WRITE(pdwResolution, DWORD);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetDefaultResolution after the segment has been garbage collected.\n");
|
|
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
*pdwResolution = m_dwResolution;
|
|
return S_OK;
|
|
}
|
|
|
|
#define LEGAL_RES_FLAGS (DMUS_SEGF_SECONDARY | \
|
|
DMUS_SEGF_QUEUE | \
|
|
DMUS_SEGF_CONTROL | \
|
|
DMUS_SEGF_AFTERPREPARETIME | \
|
|
DMUS_SEGF_GRID | \
|
|
DMUS_SEGF_BEAT | \
|
|
DMUS_SEGF_MEASURE | \
|
|
DMUS_SEGF_NOINVALIDATE | \
|
|
DMUS_SEGF_ALIGN | \
|
|
DMUS_SEGF_VALID_START_BEAT | \
|
|
DMUS_SEGF_VALID_START_GRID | \
|
|
DMUS_SEGF_VALID_START_TICK | \
|
|
DMUS_SEGF_AFTERQUEUETIME | \
|
|
DMUS_SEGF_AFTERLATENCYTIME | \
|
|
DMUS_SEGF_SEGMENTEND | \
|
|
DMUS_SEGF_MARKER | \
|
|
DMUS_SEGF_TIMESIG_ALWAYS | \
|
|
DMUS_SEGF_USE_AUDIOPATH | \
|
|
DMUS_SEGF_VALID_START_MEASURE)
|
|
|
|
STDMETHODIMP CSegment::SetDefaultResolution(
|
|
DWORD dwResolution) // @parm The desired default resolution. (See <t DMPLAYSEGFLAGS>.)
|
|
{
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetDefaultResolution after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
#ifdef DBG
|
|
if ((dwResolution & LEGAL_RES_FLAGS) != dwResolution)
|
|
{
|
|
Trace(1,"Warning: Attempt to set resolution includes inappropriate or non-existant flag: %lx\n",
|
|
dwResolution & ~LEGAL_RES_FLAGS);
|
|
}
|
|
#endif
|
|
m_dwResolution = dwResolution;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::GetHeaderChunk(
|
|
DWORD *pdwSize, /* Size of passed header chunk. Also, returns size written. */
|
|
DMUS_IO_SEGMENT_HEADER *pHeader)
|
|
{
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetHeaderChunk after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
DMUS_IO_SEGMENT_HEADER Header;
|
|
Header.dwFlags = m_dwSegFlags;
|
|
Header.dwRepeats = m_dwRepeats;
|
|
Header.dwResolution = m_dwResolution;
|
|
Header.mtLength = m_mtLength;
|
|
Header.mtLoopEnd = m_mtLoopEnd;
|
|
Header.mtLoopStart = m_mtLoopStart;
|
|
Header.mtPlayStart = m_mtStart;
|
|
Header.dwReserved = 0;
|
|
Header.rtLength = m_rtLength;
|
|
if (pdwSize && pHeader)
|
|
{
|
|
*pdwSize = min(sizeof(Header),*pdwSize);
|
|
memcpy(pHeader,&Header,*pdwSize);
|
|
return S_OK;
|
|
}
|
|
Trace(1,"Error: GetHeaderChunk() was passed a NULL for either pdwSize or pHeader.\n");
|
|
return E_POINTER;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::SetHeaderChunk(
|
|
DWORD dwSize, /* Size of passed header chunk. */
|
|
DMUS_IO_SEGMENT_HEADER *pHeader)
|
|
{
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetHeaderChunk after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
if (pHeader)
|
|
{
|
|
DMUS_IO_SEGMENT_HEADER Header;
|
|
dwSize = min(sizeof(Header),dwSize);
|
|
// Initialize all fields so we don't have to worry about the passed size.
|
|
Header.dwFlags = m_dwSegFlags;
|
|
Header.dwRepeats = m_dwRepeats;
|
|
Header.dwResolution = m_dwResolution;
|
|
Header.mtLength = m_mtLength;
|
|
Header.mtLoopEnd = m_mtLoopEnd;
|
|
Header.mtLoopStart = m_mtLoopStart;
|
|
Header.mtPlayStart = m_mtStart;
|
|
Header.dwReserved = 0;
|
|
Header.rtLength = m_rtLength;
|
|
memcpy(&Header,pHeader,dwSize);
|
|
m_dwSegFlags = Header.dwFlags;
|
|
m_dwRepeats = Header.dwRepeats;
|
|
m_dwResolution = Header.dwResolution;
|
|
m_mtLength = Header.mtLength;
|
|
m_mtLoopEnd = Header.mtLoopEnd;
|
|
m_mtLoopStart = Header.mtLoopStart;
|
|
m_mtStart = Header.mtPlayStart;
|
|
m_rtLength = Header.rtLength;
|
|
return S_OK;
|
|
}
|
|
Trace(1,"Error: SetHeaderChunk() was passed a NULL for pHeader.\n");
|
|
return E_POINTER;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::SetTrackPriority(
|
|
REFGUID rguidTrackClassID, /* ClassID of Track. */
|
|
DWORD dwGroupBits, /* Group bits. */
|
|
DWORD dwIndex, /* Nth track. */
|
|
DWORD dwPriority) /* Priority to set. */
|
|
{
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetTrackPriority after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
HRESULT hr = DMUS_E_TRACK_NOT_FOUND;
|
|
CTrack* pCTrack;
|
|
DWORD dwCounter = dwIndex;
|
|
DWORD dwMax = dwIndex;
|
|
if (dwIndex == DMUS_SEG_ALLTRACKS)
|
|
{
|
|
dwCounter = 0;
|
|
dwMax = DMUS_SEG_ALLTRACKS;
|
|
}
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
while (pCTrack = GetTrack(rguidTrackClassID,dwGroupBits,dwCounter))
|
|
{
|
|
pCTrack->m_dwPriority = dwPriority;
|
|
hr = S_OK;
|
|
dwCounter++;
|
|
if (dwCounter > dwMax) break;
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::SetAudioPathConfig(
|
|
IUnknown *pAudioPathConfig)
|
|
{
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetAudioPathConfig after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
if (m_dwVersion < 8) m_dwVersion = 8;
|
|
if (m_pAudioPathConfig)
|
|
{
|
|
m_pAudioPathConfig->Release();
|
|
m_pAudioPathConfig = NULL;
|
|
}
|
|
if (pAudioPathConfig)
|
|
{
|
|
return pAudioPathConfig->QueryInterface(IID_CAudioPathConfig,(void **) &m_pAudioPathConfig);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::GetTrack(
|
|
REFCLSID rType,
|
|
DWORD dwGroupBits,
|
|
DWORD dwIndex,
|
|
IDirectMusicTrack **ppTrack)
|
|
{
|
|
V_INAME(IDirectMusicSegment::GetTrack);
|
|
V_PTRPTR_WRITE(ppTrack);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetTrack after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
CTrack* pCTrack;
|
|
HRESULT hr;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
pCTrack = GetTrack(rType,dwGroupBits,dwIndex);
|
|
if (pCTrack)
|
|
{
|
|
*ppTrack = pCTrack->m_pTrack;
|
|
pCTrack->m_pTrack->AddRef();
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
Trace(1,"Error: GetTrack could not find the requested track at index %ld.\n",dwIndex);
|
|
hr = DMUS_E_NOT_FOUND;
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
CTrack *CSegment::GetTrack(
|
|
REFCLSID rType,
|
|
DWORD dwGroupBits,
|
|
DWORD dwIndex)
|
|
{
|
|
CTrack* pCTrack;
|
|
pCTrack = m_TrackList.GetHead();
|
|
while( pCTrack )
|
|
{
|
|
ASSERT(pCTrack->m_pTrack);
|
|
if( pCTrack->m_dwGroupBits & dwGroupBits )
|
|
{
|
|
if( (GUID_NULL == rType) || (pCTrack->m_guidClassID == rType))
|
|
{
|
|
if( 0 == dwIndex )
|
|
{
|
|
break;
|
|
}
|
|
dwIndex--;
|
|
}
|
|
}
|
|
pCTrack = pCTrack->GetNext();
|
|
}
|
|
return pCTrack;
|
|
}
|
|
|
|
BOOL CSegment::IsTempoSource()
|
|
|
|
{
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
BOOL fHasTempo = (NULL != GetTrackByParam(NULL, GUID_TempoParam,-1,0, FALSE));
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return fHasTempo;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::GetTrackGroup(
|
|
IDirectMusicTrack* pTrack, // @parm The Track to find the group bits.
|
|
DWORD* pdwGroupBits)// @parm Returns the group(s) to which a Track belongs.
|
|
// Each bit in <p pdwGroupBits> corresponds to a Track
|
|
// group.
|
|
{
|
|
V_INAME(IDirectMusicSegment::GetTrackGroup);
|
|
V_INTERFACE(pTrack);
|
|
V_PTR_WRITE(pdwGroupBits,DWORD);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetTrackGroup after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
CTrack* pCTrack;
|
|
HRESULT hr = DMUS_E_NOT_FOUND;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
pCTrack = m_TrackList.GetHead();
|
|
while( pCTrack )
|
|
{
|
|
ASSERT(pCTrack->m_pTrack);
|
|
if( pCTrack->m_pTrack == pTrack )
|
|
{
|
|
*pdwGroupBits = pCTrack->m_dwGroupBits;
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
pCTrack = pCTrack->GetNext();
|
|
}
|
|
#ifdef DBG
|
|
if (hr == DMUS_E_NOT_FOUND)
|
|
{
|
|
Trace(1,"Error: GetTrackGroup could not find the requested track.\n");
|
|
}
|
|
#endif
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
CTrack * CSegment::GetTrackByParam( CTrack * pCTrack,
|
|
REFGUID rguidType,DWORD dwGroupBits,DWORD dwIndex, BOOL fDontCheck)
|
|
{
|
|
// If the caller was already part way through the list, it passes the current
|
|
// track. Otherwise, NULL to indicate start at the top.
|
|
if (pCTrack)
|
|
{
|
|
pCTrack = pCTrack->GetNext();
|
|
}
|
|
else
|
|
{
|
|
pCTrack = m_TrackList.GetHead();
|
|
}
|
|
while( pCTrack )
|
|
{
|
|
ASSERT(pCTrack->m_pTrack);
|
|
if( (pCTrack->m_dwGroupBits & dwGroupBits ) && (fDontCheck ||
|
|
(pCTrack->m_dwFlags & DMUS_TRACKCONFIG_CONTROL_ENABLED)))
|
|
{
|
|
if( (GUID_NULL == rguidType) ||
|
|
(pCTrack->m_pTrack->IsParamSupported( rguidType ) == S_OK ))
|
|
{
|
|
if( 0 == dwIndex )
|
|
{
|
|
return pCTrack;
|
|
}
|
|
dwIndex--;
|
|
}
|
|
}
|
|
pCTrack = pCTrack->GetNext();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
HRESULT CSegment::GetTrackByParam(
|
|
REFGUID rguidType, // The command type of the Track to find. A value of GUID_NULL
|
|
// will get any track.
|
|
DWORD dwGroupBits, // Which track groups to scan for the track in. A value of 0
|
|
// is invalid. Each bit in <p dwGroupBits> corresponds to a Track
|
|
// group. To scan all tracks regardless of groups, set all bits in
|
|
// this parameter (0xffffffff).
|
|
DWORD dwIndex, // The index into the list of tracks of type <p rguidType>
|
|
// and in group <p dwGroupBits> to return. 0 means the first
|
|
// one found, 1 would be the second, etc. If multiple groups are
|
|
// selected in <p dwGroupBits>, this index will indicate the nth
|
|
// track of type <p pCommandGuid> encountered in the union
|
|
// of the groups selected.
|
|
IDirectMusicTrack **ppTrack) // Returns the Track (AddRef'd), or NULL if the
|
|
// Track isn't found.
|
|
{
|
|
HRESULT hr;
|
|
CTrack* pCTrack;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
pCTrack = GetTrackByParam(NULL,rguidType,dwGroupBits,dwIndex,TRUE);
|
|
if (pCTrack)
|
|
{
|
|
*ppTrack = pCTrack->m_pTrack;
|
|
pCTrack->m_pTrack->AddRef();
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = DMUS_E_NOT_FOUND;
|
|
// Don't think we need an error message here since SetParam also does one...
|
|
// Trace(1,"Error: Could not find the requested track for SetParam.\n");
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::InsertTrack(
|
|
IDirectMusicTrack *pTrack, // @parm The Track to add to the Segment.
|
|
DWORD dwGroupBits ) // @parm Identifies the group(s) this should be inserted into.
|
|
// May not be 0.
|
|
{
|
|
V_INAME(IDirectMusicSegment::InsertTrack);
|
|
V_INTERFACE(pTrack);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::InsertTrack after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
return InsertTrack(pTrack,dwGroupBits,DMUS_TRACKCONFIG_DEFAULT,0, 0);
|
|
}
|
|
|
|
HRESULT CSegment::InsertTrack(
|
|
IDirectMusicTrack *pTrack,
|
|
DWORD dwGroupBits,
|
|
DWORD dwFlags,
|
|
DWORD dwPriority,
|
|
DWORD dwPosition)
|
|
{
|
|
CTrack* pCTrack;
|
|
|
|
if( 0 == dwGroupBits )
|
|
{
|
|
Trace(1,"Error: InsertTrack called with dwGroupBits set to 0.\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
if( FAILED( pTrack->Init( this ) ))
|
|
{
|
|
TraceI(1,"Error: Track failed to initialize\n");
|
|
return DMUS_E_NOT_INIT;
|
|
}
|
|
pCTrack = new CTrack;
|
|
if( NULL == pCTrack )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
pCTrack->m_pTrack = pTrack;
|
|
pTrack->QueryInterface(IID_IDirectMusicTrack8,(void **) &pCTrack->m_pTrack8);
|
|
IPersist *pPersist;
|
|
if (S_OK == pTrack->QueryInterface(IID_IPersistStream,(void **) &pPersist))
|
|
{
|
|
pPersist->GetClassID( &pCTrack->m_guidClassID );
|
|
pPersist->Release();
|
|
}
|
|
pCTrack->m_dwGroupBits = dwGroupBits;
|
|
pCTrack->m_dwFlags = dwFlags;
|
|
pCTrack->m_dwPriority = dwPriority;
|
|
pCTrack->m_dwPosition = dwPosition;
|
|
pTrack->AddRef();
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
// Add the track based on position.
|
|
CTrack* pScan = m_TrackList.GetHead();
|
|
CTrack* pPrevTrack = NULL;
|
|
for (; pScan; pScan = pScan->GetNext())
|
|
{
|
|
if (pCTrack->Less(pScan))
|
|
{
|
|
break;
|
|
}
|
|
pPrevTrack = pScan;
|
|
}
|
|
if (pPrevTrack)
|
|
{
|
|
pPrevTrack->SetNext(pCTrack);
|
|
pCTrack->SetNext(pScan);
|
|
}
|
|
else
|
|
{
|
|
m_TrackList.AddHead( pCTrack );
|
|
}
|
|
|
|
// send notifications to track
|
|
CNotificationItem* pItem = m_NotificationList.GetHead();
|
|
while( pItem )
|
|
{
|
|
pTrack->AddNotificationType( pItem->guidNotificationType );
|
|
pItem = pItem->GetNext();
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::RemoveTrack(
|
|
IDirectMusicTrack *pTrack) // @parm The Track to remove from the Segment's Track list.
|
|
{
|
|
V_INAME(IDirectMusicSegment::RemoveTrack);
|
|
V_INTERFACE(pTrack);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::RemoveTrack after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
HRESULT hr = S_FALSE;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
CTrack* pCTrackTemp;
|
|
pCTrackTemp = m_TrackList.GetHead();
|
|
while( pCTrackTemp )
|
|
{
|
|
if( pTrack == pCTrackTemp->m_pTrack )
|
|
{
|
|
hr = S_OK;
|
|
m_TrackList.Remove( pCTrackTemp );
|
|
delete pCTrackTemp;
|
|
break;
|
|
}
|
|
pCTrackTemp = pCTrackTemp->GetNext();
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
#ifdef DBG
|
|
if (hr == S_FALSE)
|
|
{
|
|
Trace(1,"Warning: RemoveTrack failed because the requested track is not in the segment.\n");
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSegment::CreateSegmentState(
|
|
CSegState **ppSegState,
|
|
CPerformance *pPerformance,
|
|
IDirectMusicAudioPath *pAudioPath,
|
|
DWORD dwFlags)
|
|
|
|
{
|
|
IDirectMusicSegmentState* pSegmentState;
|
|
CSegState *pState = new CSegState;
|
|
if (pState)
|
|
{
|
|
pState->QueryInterface( IID_IDirectMusicSegmentState,
|
|
(void**)&pSegmentState);
|
|
pState->m_dwVersion = m_dwVersion;
|
|
pState->Release();
|
|
}
|
|
else
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
if( FAILED( m_TrackList.CreateCopyWithBlankState(&pState->m_TrackList)))
|
|
{
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
pState->Release();
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
// set the segstate's parent and performance
|
|
pState->PrivateInit( this, pPerformance );
|
|
|
|
if (m_pGraph)
|
|
{
|
|
m_pGraph->Clone((IDirectMusicGraph **) &pState->m_pGraph);
|
|
}
|
|
pState->InitRoute(pAudioPath);
|
|
CTrack* pCTrack = pState->m_TrackList.GetHead();
|
|
while( pCTrack )
|
|
{
|
|
DWORD dwTempID;
|
|
InterlockedIncrement(&g_lNewTrackID);
|
|
dwTempID = g_lNewTrackID;
|
|
if (!pState->m_dwFirstTrackID)
|
|
pState->m_dwFirstTrackID = dwTempID;
|
|
pState->m_dwLastTrackID = dwTempID;
|
|
ASSERT(pCTrack->m_pTrack);
|
|
if( FAILED(pCTrack->m_pTrack->InitPlay(
|
|
pSegmentState, (IDirectMusicPerformance *) pPerformance,
|
|
&pCTrack->m_pTrackState, dwTempID, dwFlags )))
|
|
{
|
|
pCTrack->m_pTrackState = NULL;
|
|
}
|
|
pCTrack->m_dwVirtualID = dwTempID;
|
|
pCTrack = pCTrack->GetNext();
|
|
}
|
|
*ppSegState = pState;
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return S_OK;
|
|
}
|
|
|
|
/* The following function is kept around only for DX6.1 compatibility just
|
|
in case some mindless bureaucrat actually uses this somehow.
|
|
For internal use, we've switched to the function above.
|
|
*/
|
|
|
|
STDMETHODIMP CSegment::InitPlay(
|
|
IDirectMusicSegmentState **ppSegState, // @parm Returns the SegmentState created
|
|
// by this method call. It is returned with a reference count of 1, thus a
|
|
// call to its Release will fully release it.
|
|
IDirectMusicPerformance *pPerformance, // @parm The IDirectMusicPerformance pointer.
|
|
// This is needed by the Segment and SegmentState in order to call methods on
|
|
// the Performance object. This pointer is not AddRef'd. It is a weak reference
|
|
// because it is assumed that the Performance will outlive the Segment.
|
|
DWORD dwFlags) // @parm Same flags that were set with the call
|
|
// to PlaySegment. These are passed to the tracks, who may want to know
|
|
// if the track was played as a primary, controlling, or secondary segment.
|
|
{
|
|
V_INAME(IDirectMusicSegment::InitPlay);
|
|
V_INTERFACE(pPerformance);
|
|
V_PTRPTR_WRITE(ppSegState);
|
|
|
|
if (m_dwVersion)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
IDirectMusicSegmentState* pSegmentState;
|
|
CSegState *pState = new CSegState;
|
|
if (pState)
|
|
{
|
|
pState->QueryInterface( IID_IDirectMusicSegmentState,
|
|
(void**)&pSegmentState);
|
|
pState->m_dwVersion = m_dwVersion;
|
|
pState->Release();
|
|
if (pPerformance)
|
|
{
|
|
// QI addref's the performance but we want only a weak refrenece with the segment state
|
|
HRESULT hr = pPerformance->QueryInterface(IID_CPerformance,(void **) &pState->m_pPerformance);
|
|
if(FAILED(hr))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
pPerformance->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
*ppSegState = pSegmentState;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::GetGraph(
|
|
IDirectMusicGraph** ppGraph // @parm Returns the Tool Graph pointer.
|
|
)
|
|
{
|
|
V_INAME(IDirectMusicSegment::GetGraph);
|
|
V_PTRPTR_WRITE(ppGraph);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetGraph after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
if( NULL == m_pGraph )
|
|
{
|
|
Trace(1,"Error: GetGraph failed because segment does not have a tool graph.\n");
|
|
return DMUS_E_NOT_FOUND;
|
|
}
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
*ppGraph = m_pGraph;
|
|
m_pGraph->AddRef();
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::SetGraph(
|
|
IDirectMusicGraph* pGraph // @parm The Tool Graph pointer. May be NULL to
|
|
// clear out the Segment graph.
|
|
)
|
|
{
|
|
V_INAME(IDirectMusicSegment::SetGraph);
|
|
V_INTERFACE_OPT(pGraph);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetGraph after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
if( m_pGraph )
|
|
{
|
|
m_pGraph->Release();
|
|
}
|
|
m_pGraph = (CGraph *) pGraph;
|
|
if( pGraph )
|
|
{
|
|
pGraph->AddRef();
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CSegment::SetClockTimeDuration(REFERENCE_TIME rtDuration)
|
|
|
|
{
|
|
m_rtLength = rtDuration;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CSegment::SetFlags(DWORD dwFlags)
|
|
|
|
{
|
|
m_dwSegFlags = dwFlags;
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
Check to see if this notification is already being tracked.
|
|
*/
|
|
CNotificationItem* CSegment::FindNotification( REFGUID rguidNotification )
|
|
{
|
|
CNotificationItem* pItem;
|
|
|
|
pItem = m_NotificationList.GetHead();
|
|
while(pItem)
|
|
{
|
|
if( rguidNotification == pItem->guidNotificationType )
|
|
{
|
|
break;
|
|
}
|
|
pItem = pItem->GetNext();
|
|
}
|
|
return pItem;
|
|
}
|
|
|
|
void CSegment::AddNotificationTypeToAllTracks( REFGUID rguidNotification )
|
|
{
|
|
CTrack* pTrack;
|
|
|
|
// add the notify to the tracks
|
|
pTrack = m_TrackList.GetHead();
|
|
while( pTrack )
|
|
{
|
|
pTrack->m_pTrack->AddNotificationType( rguidNotification );
|
|
pTrack = pTrack->GetNext();
|
|
}
|
|
}
|
|
|
|
void CSegment::RemoveNotificationTypeFromAllTracks( REFGUID rguidNotification )
|
|
{
|
|
CTrack* pTrack;
|
|
|
|
// add the notify to the tracks
|
|
pTrack = m_TrackList.GetHead();
|
|
while( pTrack )
|
|
{
|
|
pTrack->m_pTrack->RemoveNotificationType( rguidNotification );
|
|
pTrack = pTrack->GetNext();
|
|
}
|
|
}
|
|
|
|
HRESULT CSegment::AddNotificationType(
|
|
REFGUID rguidNotification, BOOL fFromPerformance)
|
|
{
|
|
CNotificationItem* pItem;
|
|
HRESULT hr = S_OK;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
pItem = FindNotification( rguidNotification );
|
|
if (pItem)
|
|
{
|
|
// If the item was installed previously, but by
|
|
// a difference source (performance vs. app)
|
|
// then treat this as a normal addition.
|
|
// Otherwise, indicate that the same operation
|
|
// was done twice.
|
|
if (pItem->fFromPerformance == fFromPerformance)
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Clear the fFromPerformance flag since this has
|
|
// now been added by the app and the performance.
|
|
pItem->fFromPerformance = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pItem = new CNotificationItem;
|
|
if( NULL == pItem )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
pItem->fFromPerformance = fFromPerformance;
|
|
pItem->guidNotificationType = rguidNotification;
|
|
m_NotificationList.Cat( pItem );
|
|
AddNotificationTypeToAllTracks( rguidNotification );
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::AddNotificationType(
|
|
REFGUID rguidNotification) // @parm The notification guid to add.
|
|
{
|
|
V_INAME(IDirectMusicSegment::AddNotificationType);
|
|
V_REFGUID(rguidNotification);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::AddNotificationType after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
return AddNotificationType(rguidNotification,FALSE);
|
|
}
|
|
|
|
HRESULT CSegment::RemoveNotificationType(
|
|
REFGUID rguidNotification,BOOL fFromPerformance)
|
|
{
|
|
|
|
CNotificationItem* pItem;
|
|
HRESULT hr = S_OK;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
if( GUID_NULL == rguidNotification )
|
|
{
|
|
CNotificationList TempList;
|
|
while( pItem = m_NotificationList.RemoveHead() )
|
|
{
|
|
// If this is being called on an item that was installed by the
|
|
// performance OR we are calling this directly from the app,
|
|
// go ahead and remove. However, do not remove in the specific
|
|
// case where the app installed the notification and the performance
|
|
// is clearing notifications.
|
|
if (pItem->fFromPerformance || !fFromPerformance)
|
|
{
|
|
RemoveNotificationTypeFromAllTracks( pItem->guidNotificationType );
|
|
delete pItem;
|
|
}
|
|
else
|
|
{
|
|
TempList.AddHead(pItem);
|
|
}
|
|
}
|
|
// Now, put the saved notifications back.
|
|
while (pItem = TempList.RemoveHead())
|
|
{
|
|
m_NotificationList.AddHead(pItem);
|
|
}
|
|
}
|
|
else if( pItem = FindNotification( rguidNotification ))
|
|
{
|
|
m_NotificationList.Remove( pItem );
|
|
delete pItem;
|
|
RemoveNotificationTypeFromAllTracks( rguidNotification );
|
|
}
|
|
else
|
|
{
|
|
Trace(2,"Warning: Unable to remove requested notification from segment, it was not currently installed.\n");
|
|
hr = S_FALSE;
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::RemoveNotificationType(
|
|
REFGUID rguidNotification) // @parm The notification guid to remove. GUID_NULL to remove all notifies.
|
|
{
|
|
V_INAME(IDirectMusicSegment::RemoveNotificationType);
|
|
V_REFGUID(rguidNotification);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::RemoveNotificationType after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
return RemoveNotificationType(rguidNotification,FALSE);
|
|
}
|
|
|
|
STDMETHODIMP CSegment::GetParam(
|
|
REFGUID rguidType, // @parm The type of data to obtain.
|
|
DWORD dwGroupBits, // @parm The group the desired track is in. Use 0xffffffff
|
|
// for all groups.
|
|
DWORD dwIndex, // @parm Identifies which track, by index, in the group
|
|
// identified by <p dwGroupBits> to obtain the data from.
|
|
MUSIC_TIME mtTime, // @parm The segment time from which to obtain the data.
|
|
MUSIC_TIME* pmtNext, // @parm Returns the segment time until which the data is valid. <p pmtNext>
|
|
// may be NULL. If this returns a value of 0, it means that this
|
|
// data will either be always valid, or it is unknown when it will
|
|
// become invalid.
|
|
void* pParam) // @parm The struture in which to return the data. Each
|
|
// <p rguidType> identifies a particular structure of a
|
|
// particular size. It is important that this field contain
|
|
// the correct structure of the correct size. Otherwise,
|
|
// fatal results can occur.
|
|
{
|
|
V_INAME(IDirectMusicSegment::GetParam);
|
|
V_REFGUID(rguidType);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetParam after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
HRESULT hr = DMUS_E_TRACK_NOT_FOUND;
|
|
BOOL fMultipleTry = FALSE;
|
|
if (dwIndex == DMUS_SEG_ANYTRACK)
|
|
{
|
|
dwIndex = 0;
|
|
// App must be using IDirectMusicSegment8 interface for this to be enabled...
|
|
// Nah, nobody would ever have a use for an index that high, so this is safe.
|
|
fMultipleTry = TRUE; // (m_dwVersion > 2);
|
|
}
|
|
CTrack* pCTrack;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
pCTrack = GetTrackByParam(NULL,rguidType,dwGroupBits,dwIndex,FALSE);
|
|
while (pCTrack)
|
|
{
|
|
if (pCTrack->m_pTrack8)
|
|
{
|
|
REFERENCE_TIME rtNext, *prtNext;
|
|
// We need to store the next time in a 64 bit pointer. But, don't
|
|
// make 'em fill it in unless the caller requested it.
|
|
if (pmtNext)
|
|
{
|
|
prtNext = &rtNext;
|
|
}
|
|
else
|
|
{
|
|
prtNext = NULL;
|
|
}
|
|
hr = pCTrack->m_pTrack8->GetParamEx( rguidType, mtTime, prtNext, pParam, NULL, 0 );
|
|
if (pmtNext)
|
|
{
|
|
*pmtNext = (MUSIC_TIME) rtNext;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = pCTrack->m_pTrack->GetParam( rguidType, mtTime, pmtNext, pParam );
|
|
if( pmtNext && (( *pmtNext == 0 ) || (*pmtNext > (m_mtLength - mtTime))))
|
|
{
|
|
*pmtNext = m_mtLength - mtTime;
|
|
}
|
|
}
|
|
// If nothing was found and dwIndex was DMUS_SEG_ANYTRACK, try again...
|
|
if (fMultipleTry && (hr == DMUS_E_NOT_FOUND))
|
|
{
|
|
pCTrack = GetTrackByParam( pCTrack, rguidType, dwGroupBits, 0, FALSE);
|
|
}
|
|
else
|
|
{
|
|
pCTrack = NULL;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
#ifdef DBG
|
|
if (hr == DMUS_E_TRACK_NOT_FOUND)
|
|
{
|
|
Trace(2,"Warning: Segment GetParam failed to find a track.\n");
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSegment::SetParam(
|
|
REFGUID rguidType, // @parm The type of data to set.
|
|
DWORD dwGroupBits, // @parm The group the desired track is in. Use 0xffffffff
|
|
// for all groups.
|
|
DWORD dwIndex, // @parm Identifies which track, by index, in the group
|
|
// identified by <p dwGroupBits> to set the data.
|
|
MUSIC_TIME mtTime, // @parm The time at which to set the data.
|
|
void* pParam) // @parm The struture containing the data to set. Each
|
|
// <p rguidType> identifies a particular structure of a
|
|
// particular size. It is important that this field contain
|
|
// the correct structure of the correct size. Otherwise,
|
|
// fatal results can occur.
|
|
{
|
|
V_INAME(IDirectMusicSegment::SetParam);
|
|
V_REFGUID(rguidType);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetParam after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
HRESULT hr = DMUS_E_TRACK_NOT_FOUND;
|
|
IDirectMusicTrack* pTrack;
|
|
DWORD dwCounter = dwIndex;
|
|
DWORD dwMax = dwIndex;
|
|
if (dwIndex == DMUS_SEG_ALLTRACKS)
|
|
{
|
|
dwCounter = 0;
|
|
dwMax = DMUS_SEG_ALLTRACKS;
|
|
}
|
|
while (SUCCEEDED( GetTrackByParam( rguidType, dwGroupBits, dwCounter, &pTrack )))
|
|
{
|
|
hr = pTrack->SetParam( rguidType, mtTime, pParam );
|
|
pTrack->Release();
|
|
dwCounter++;
|
|
if (dwCounter > dwMax) break;
|
|
}
|
|
#ifdef DBG
|
|
if (hr == DMUS_E_TRACK_NOT_FOUND)
|
|
{
|
|
Trace(2,"Warning: Segment SetParam failed to find the requested track.\n");
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::Download(IUnknown *pAudioPath)
|
|
|
|
{
|
|
V_INAME(IDirectMusicSegment::Download);
|
|
V_INTERFACE(pAudioPath);
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::Download after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
// Validate that pAudioPath is either a performance or an audio path
|
|
IDirectMusicPerformance* pPerf = NULL;
|
|
if ( FAILED(hr = pAudioPath->QueryInterface(IID_IDirectMusicPerformance, (void**)&pPerf)) )
|
|
{
|
|
IDirectMusicAudioPath* pAP = NULL;
|
|
if ( FAILED(hr = pAudioPath->QueryInterface(IID_IDirectMusicAudioPath, (void**)&pAP)) )
|
|
{
|
|
return hr; // nothing to release, since all the QI's failed.
|
|
}
|
|
else
|
|
{
|
|
pAP->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPerf->Release();
|
|
}
|
|
|
|
hr = SetParam(GUID_DownloadToAudioPath,-1,DMUS_SEG_ALLTRACKS,0,pAudioPath);
|
|
if (hr == DMUS_E_TRACK_NOT_FOUND)
|
|
{
|
|
Trace(2,"Attempted download to a segment that has no tracks that support downloading (wave and band tracks.)\n");
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::Unload(IUnknown *pAudioPath)
|
|
|
|
{
|
|
V_INAME(IDirectMusicSegment::Unload);
|
|
V_INTERFACE(pAudioPath);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::Unload after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
HRESULT hr = SetParam(GUID_UnloadFromAudioPath,-1,DMUS_SEG_ALLTRACKS,0,pAudioPath);
|
|
if (hr == DMUS_E_TRACK_NOT_FOUND)
|
|
{
|
|
Trace(2,"Attempted unload from a segment that has no tracks that support downloading (wave and band tracks.)\n");
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::SetTrackConfig(REFGUID rguidTrackClassID,
|
|
DWORD dwGroup, DWORD dwIndex,
|
|
DWORD dwFlagsOn, DWORD dwFlagsOff)
|
|
{
|
|
V_INAME(IDirectMusicSegment::SetTrackConfig);
|
|
V_REFGUID(rguidTrackClassID);
|
|
if (rguidTrackClassID == GUID_NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetTrackConfig after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
HRESULT hr = DMUS_E_TRACK_NOT_FOUND;
|
|
CTrack* pCTrack;
|
|
DWORD dwCounter = dwIndex;
|
|
DWORD dwMax = dwIndex;
|
|
if (dwIndex == DMUS_SEG_ALLTRACKS)
|
|
{
|
|
dwCounter = 0;
|
|
dwMax = DMUS_SEG_ALLTRACKS;
|
|
}
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
while (pCTrack = GetTrack(rguidTrackClassID,dwGroup,dwCounter))
|
|
{
|
|
pCTrack->m_dwFlags &= ~dwFlagsOff;
|
|
pCTrack->m_dwFlags |= dwFlagsOn;
|
|
hr = S_OK;
|
|
dwCounter++;
|
|
if (dwCounter > dwMax) break;
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
#ifdef DBG
|
|
if (hr == DMUS_E_TRACK_NOT_FOUND)
|
|
{
|
|
Trace(1,"Error: Segment SetTrackConfig failed to find the requested track.\n");
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSegment::GetTrackConfig(REFGUID rguidTrackClassID,
|
|
DWORD dwGroup, DWORD dwIndex, DWORD *pdwFlags)
|
|
{
|
|
|
|
HRESULT hr = DMUS_E_TRACK_NOT_FOUND;
|
|
CTrack* pCTrack;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
pCTrack = GetTrack(rguidTrackClassID,dwGroup,dwIndex);
|
|
if (pCTrack)
|
|
{
|
|
*pdwFlags = pCTrack->m_dwFlags;
|
|
hr = S_OK;
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSegment::Clone(
|
|
MUSIC_TIME mtStart, // @parm The start of the part to clone. If less than 0,
|
|
// or greater than the length of the Segment, 0 will be used.
|
|
MUSIC_TIME mtEnd, // @parm The end of the part to clone. If past the end of the
|
|
// Segment, it will clone to the end. Also, a value of 0 or
|
|
// anything less than <p mtStart> will also clone to the end.
|
|
IDirectMusicSegment** ppSegment // @parm Returns the created Segment, if successful.
|
|
// It is caller's responsibility to call Release() when finished
|
|
// with it.
|
|
)
|
|
{
|
|
V_INAME(IDirectMusicSegment::Clone);
|
|
V_PTRPTR_WRITE(ppSegment);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::Clone after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
CSegment* pCSegment;
|
|
HRESULT hr = S_OK;
|
|
|
|
if( (mtEnd < mtStart) || (mtEnd > m_mtLength) )
|
|
{
|
|
mtEnd = m_mtLength;
|
|
}
|
|
if( ( mtEnd == 0 ) && ( mtStart == 0 ))
|
|
{
|
|
mtEnd = m_mtLength;
|
|
}
|
|
if( (mtStart < 0) || (mtStart > m_mtLength) )
|
|
{
|
|
mtStart = 0;
|
|
}
|
|
pCSegment = new CSegment;
|
|
if (pCSegment == NULL) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
// Addref to 1 and assign to ppSegment.
|
|
pCSegment->AddRef();
|
|
(*ppSegment) = (IDirectMusicSegment *) pCSegment;
|
|
if( m_pGraph )
|
|
{
|
|
pCSegment->m_pGraph = m_pGraph;
|
|
m_pGraph->AddRef();
|
|
}
|
|
if (m_pAudioPathConfig)
|
|
{
|
|
pCSegment->m_pAudioPathConfig = m_pAudioPathConfig;
|
|
m_pAudioPathConfig->AddRef();
|
|
}
|
|
pCSegment->m_dwRepeats = m_dwRepeats;
|
|
pCSegment->m_dwResolution = m_dwResolution;
|
|
pCSegment->m_dwSegFlags = m_dwSegFlags;
|
|
pCSegment->m_mtLength = mtEnd - mtStart;
|
|
pCSegment->m_rtLength = m_rtLength;
|
|
pCSegment->m_mtStart = m_mtStart;
|
|
pCSegment->m_mtLoopStart = m_mtLoopStart;
|
|
pCSegment->m_mtLoopEnd = m_mtLoopEnd;
|
|
pCSegment->m_dwValidData = m_dwValidData;
|
|
pCSegment->m_guidObject = m_guidObject;
|
|
pCSegment->m_ftDate = m_ftDate;
|
|
pCSegment->m_vVersion = m_vVersion;
|
|
StringCchCopyW(pCSegment->m_wszName, DMUS_MAX_NAME, m_wszName);
|
|
StringCchCopyW(pCSegment->m_wszCategory, DMUS_MAX_CATEGORY, m_wszCategory);
|
|
StringCchCopyW(pCSegment->m_wszFileName, DMUS_MAX_FILENAME, m_wszFileName);
|
|
pCSegment->m_dwVersion = m_dwVersion;
|
|
pCSegment->m_dwLoadID = m_dwLoadID;
|
|
pCSegment->m_dwPlayID = m_dwPlayID;
|
|
pCSegment->m_dwNextPlayID = m_dwNextPlayID;
|
|
pCSegment->m_dwNextPlayFlags = m_dwNextPlayFlags;
|
|
|
|
CTrack* pCTrack;
|
|
IDirectMusicTrack* pTrack;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
pCTrack = m_TrackList.GetHead();
|
|
while( pCTrack )
|
|
{
|
|
if( SUCCEEDED( pCTrack->m_pTrack->Clone( mtStart, mtEnd, &pTrack )))
|
|
{
|
|
if( FAILED( pCSegment->InsertTrack( pTrack, pCTrack->m_dwGroupBits, pCTrack->m_dwFlags, pCTrack->m_dwPriority, pCTrack->m_dwPosition )))
|
|
{
|
|
Trace(1,"Warning: Insertion of cloned track failed, cloned segment is incomplete.\n");
|
|
hr = S_FALSE;
|
|
}
|
|
pTrack->Release();
|
|
}
|
|
else
|
|
{
|
|
Trace(1,"Warning: Track clone failed, cloned segment is incomplete.\n");
|
|
hr = S_FALSE;
|
|
}
|
|
pCTrack = pCTrack->GetNext();
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::GetAudioPathConfig(IUnknown ** ppAudioPathConfig)
|
|
|
|
{
|
|
V_INAME(IDirectMusicSegment::GetAudioPathConfig);
|
|
V_PTRPTR_WRITE(ppAudioPathConfig);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetAudioPathConfig after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
HRESULT hr;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
if (m_pAudioPathConfig)
|
|
{
|
|
hr = m_pAudioPathConfig->QueryInterface(IID_IUnknown,(void **)ppAudioPathConfig);
|
|
}
|
|
else
|
|
{
|
|
Trace(2,"Warning: No embedded audiopath configuration in the segment.\n");
|
|
hr = DMUS_E_NO_AUDIOPATH_CONFIG;
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSegment::GetObjectInPath(DWORD dwPChannel, /* PChannel to search. */
|
|
DWORD dwStage, /* Which stage in the path. */
|
|
DWORD dwBuffer,
|
|
REFGUID guidObject, /* ClassID of object. */
|
|
DWORD dwIndex, /* Which object of that class. */
|
|
REFGUID iidInterface,/* Requested COM interface. */
|
|
void ** ppObject)
|
|
|
|
{
|
|
V_INAME(IDirectMusicSegment::GetObjectInPath);
|
|
V_PTRPTR_WRITE(ppObject);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetObjectInPath after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
HRESULT hr = DMUS_E_NOT_FOUND;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
if (dwStage == DMUS_PATH_SEGMENT_TRACK)
|
|
{
|
|
CTrack * pCTrack = GetTrack(guidObject,-1,dwIndex);
|
|
if (pCTrack)
|
|
{
|
|
if (pCTrack->m_pTrack)
|
|
{
|
|
hr = pCTrack->m_pTrack->QueryInterface(iidInterface,ppObject);
|
|
}
|
|
}
|
|
}
|
|
else if (dwStage == DMUS_PATH_SEGMENT_GRAPH)
|
|
{
|
|
if (dwIndex == 0)
|
|
{
|
|
if (!m_pGraph)
|
|
{
|
|
m_pGraph = new CGraph;
|
|
}
|
|
if (m_pGraph)
|
|
{
|
|
hr = m_pGraph->QueryInterface(iidInterface,ppObject);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
else if (dwStage == DMUS_PATH_SEGMENT_TOOL)
|
|
{
|
|
if (!m_pGraph)
|
|
{
|
|
m_pGraph = new CGraph;
|
|
}
|
|
if (m_pGraph)
|
|
{
|
|
hr = m_pGraph->GetObjectInPath(dwPChannel,guidObject,dwIndex,iidInterface,ppObject);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else if (dwStage >= DMUS_PATH_BUFFER)
|
|
{
|
|
// Nothing here now. But, in DX9, we may add support for addressing the buffer configuration
|
|
// and DMOS in it.
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::Compose(MUSIC_TIME mtTime,
|
|
IDirectMusicSegment* pFromSegment,
|
|
IDirectMusicSegment* pToSegment,
|
|
IDirectMusicSegment** ppComposedSegment)
|
|
|
|
{
|
|
V_INAME(IDirectMusicSegment::Compose);
|
|
V_INTERFACE_OPT(pFromSegment);
|
|
V_INTERFACE_OPT(pToSegment);
|
|
V_PTRPTR_WRITE_OPT(ppComposedSegment);
|
|
#ifdef DBG
|
|
if (pFromSegment)
|
|
{
|
|
MUSIC_TIME mtLength, mtLoopEnd, mtLoopStart;
|
|
DWORD dwRepeats;
|
|
// To calculate the full length, we need to access the loop parameters.
|
|
pFromSegment->GetLoopPoints(&mtLoopStart,&mtLoopEnd);
|
|
pFromSegment->GetRepeats(&dwRepeats);
|
|
pFromSegment->GetLength(&mtLength);
|
|
// If repeats is set to infinite, the total length will be greater than 32 bits.
|
|
LONGLONG llTotalLength = dwRepeats * (mtLoopEnd - mtLoopStart) + mtLength;
|
|
if (mtTime >= (llTotalLength & 0x7FFFFFFF))
|
|
{
|
|
Trace(2,"Warning: A time value of %ld was passed to Compose for a segment of length %ld.\n",
|
|
mtTime, (long) llTotalLength);
|
|
}
|
|
}
|
|
#endif
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::Compose after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
|
|
if (ppComposedSegment)
|
|
{
|
|
hr = Clone(0, m_mtLength, ppComposedSegment);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ((CSegment*)*ppComposedSegment)->ComposeTransition(mtTime, pFromSegment, pToSegment);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = ComposeTransition(mtTime, pFromSegment, pToSegment);
|
|
}
|
|
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSegment::ComposeTransition(MUSIC_TIME mtTime,
|
|
IDirectMusicSegment* pFromSegment,
|
|
IDirectMusicSegment* pToSegment)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
bool fTrackPadded = false;
|
|
|
|
// Compute amount of time to pad any tracks that need padding.
|
|
DMUS_TIMESIGNATURE TimeSig;
|
|
if (!pFromSegment ||
|
|
FAILED(pFromSegment->GetParam(GUID_TimeSignature, 0xffffffff, 0, mtTime, NULL, (void*) &TimeSig)))
|
|
{
|
|
TimeSig.mtTime = 0;
|
|
TimeSig.bBeatsPerMeasure = 4;
|
|
TimeSig.bBeat = 4;
|
|
TimeSig.wGridsPerBeat = 4;
|
|
}
|
|
else // avoid divide-by-zero
|
|
{
|
|
if (!TimeSig.bBeat) TimeSig.bBeat = 4;
|
|
}
|
|
MUSIC_TIME mtBar = ( DMUS_PPQ * 4 * TimeSig.bBeatsPerMeasure ) / TimeSig.bBeat;
|
|
MUSIC_TIME mtStartPad = min(mtBar, mtTime);
|
|
if (!pFromSegment) mtStartPad = 0;
|
|
MUSIC_TIME mtToLength = 0;
|
|
if (pToSegment) pToSegment->GetLength(&mtToLength);
|
|
MUSIC_TIME mtEndPad = min(mtBar, mtToLength);
|
|
|
|
// Instantiate tracks
|
|
CTrack* pTrack = m_TrackList.GetHead();
|
|
for (; pTrack; pTrack = pTrack->GetNext())
|
|
{
|
|
pTrack->m_dwInternalFlags &= ~(TRACKINTERNAL_START_PADDED | TRACKINTERNAL_END_PADDED);
|
|
IDirectMusicTrack* pTransTrack1 = NULL;
|
|
IDirectMusicTrack* pTransTrack2 = NULL;
|
|
GUID guidClassID;
|
|
memset(&guidClassID, 0, sizeof(guidClassID));
|
|
IPersist* pPersist = NULL;
|
|
if (SUCCEEDED(pTrack->m_pTrack->QueryInterface(IID_IPersistStream, (void**)&pPersist)))
|
|
{
|
|
pPersist->GetClassID(&guidClassID);
|
|
pPersist->Release();
|
|
}
|
|
DWORD dwTrackGroup = 0;
|
|
GetTrackGroup(pTrack->m_pTrack, &dwTrackGroup);
|
|
|
|
// Get track info
|
|
if (pTrack->m_dwFlags & COMPOSE_TRANSITION1)
|
|
{
|
|
// Clone the appropriate track, with length m_mtLength
|
|
MUSIC_TIME mtStart = 0;
|
|
if (pTrack->m_dwFlags & DMUS_TRACKCONFIG_TRANS1_FROMSEGCURRENT)
|
|
{
|
|
mtStart = mtTime;
|
|
}
|
|
MUSIC_TIME mtEnd = mtStart + m_mtLength;
|
|
IDirectMusicTrack* pSourceTrack = NULL;
|
|
if ( (pTrack->m_dwFlags & DMUS_TRACKCONFIG_TRANS1_FROMSEGSTART) ||
|
|
(pTrack->m_dwFlags & DMUS_TRACKCONFIG_TRANS1_FROMSEGCURRENT) )
|
|
{
|
|
if (pFromSegment)
|
|
{
|
|
hr = pFromSegment->GetTrack(guidClassID, dwTrackGroup, 0, &pSourceTrack);
|
|
}
|
|
}
|
|
else if (pTrack->m_dwFlags & DMUS_TRACKCONFIG_TRANS1_TOSEGSTART)
|
|
{
|
|
if (pToSegment)
|
|
{
|
|
hr = pToSegment->GetTrack(guidClassID, dwTrackGroup, 0, &pSourceTrack);
|
|
}
|
|
}
|
|
if (pSourceTrack)
|
|
{
|
|
hr = pSourceTrack->Clone(mtStart, mtEnd, &pTransTrack1);
|
|
pSourceTrack->Release();
|
|
pSourceTrack = NULL;
|
|
}
|
|
}
|
|
if (!pTransTrack1)
|
|
{
|
|
pTransTrack1 = pTrack->m_pTrack;
|
|
pTransTrack1->AddRef();
|
|
|
|
}
|
|
if (pTransTrack1)
|
|
{
|
|
// Pad the track with an extra bar of header and trailer, by cloning header and trailer
|
|
// tracks (from From and To segments, respectively --- *not* using transition flags) and
|
|
// joining them onto the transition segment track.
|
|
IDirectMusicTrack* pStartPadTrack = NULL;
|
|
IDirectMusicTrack* pEndPadTrack = NULL;
|
|
IDirectMusicTrack* pSourceTrack = NULL;
|
|
if (pFromSegment && mtStartPad)
|
|
{
|
|
hr = pFromSegment->GetTrack(guidClassID, dwTrackGroup, 0, &pSourceTrack);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pSourceTrack->Clone(mtTime - mtStartPad, mtTime, &pStartPadTrack);
|
|
pSourceTrack->Release();
|
|
pSourceTrack = NULL;
|
|
}
|
|
}
|
|
if (pToSegment && mtEndPad)
|
|
{
|
|
hr = pToSegment->GetTrack(guidClassID, dwTrackGroup, 0, &pSourceTrack);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pSourceTrack->Clone(0, mtEndPad, &pEndPadTrack);
|
|
pSourceTrack->Release();
|
|
pSourceTrack = NULL;
|
|
}
|
|
}
|
|
IDirectMusicTrack8* pTrack8 = NULL;
|
|
if (pEndPadTrack)
|
|
{
|
|
if (SUCCEEDED(pTransTrack1->QueryInterface(IID_IDirectMusicTrack8, (void**)&pTrack8)))
|
|
{
|
|
if (SUCCEEDED(pTrack8->Join(pEndPadTrack, m_mtLength, (IDirectMusicSegment*)this, dwTrackGroup, NULL)))
|
|
{
|
|
fTrackPadded = true;
|
|
pTrack->m_dwInternalFlags |= TRACKINTERNAL_END_PADDED;
|
|
}
|
|
pTrack8->Release();
|
|
}
|
|
pEndPadTrack->Release();
|
|
}
|
|
if (SUCCEEDED(hr) && pStartPadTrack)
|
|
{
|
|
if (SUCCEEDED(hr = pStartPadTrack->QueryInterface(IID_IDirectMusicTrack8, (void**)&pTrack8)))
|
|
{
|
|
if (SUCCEEDED(pTrack8->Join(pTransTrack1, mtStartPad, (IDirectMusicSegment*)this, dwTrackGroup, NULL)))
|
|
{
|
|
fTrackPadded = true;
|
|
pTrack->m_dwInternalFlags |= TRACKINTERNAL_START_PADDED;
|
|
pTransTrack1->Release();
|
|
pTransTrack1 = pStartPadTrack;
|
|
}
|
|
else
|
|
{
|
|
pStartPadTrack->Release();
|
|
}
|
|
pTrack8->Release();
|
|
}
|
|
else
|
|
{
|
|
pStartPadTrack->Release();
|
|
}
|
|
}
|
|
else if(pStartPadTrack)
|
|
{
|
|
pStartPadTrack->Release();
|
|
}
|
|
|
|
// Replace the current track with the instantiated one
|
|
IDirectMusicTrack8* pTempTrack8 = NULL;
|
|
pTransTrack1->QueryInterface(IID_IDirectMusicTrack8, (void**)&pTempTrack8);
|
|
if (pTrack->m_pTrack) pTrack->m_pTrack->Release();
|
|
pTrack->m_pTrack = pTransTrack1;
|
|
pTrack->m_pTrack->Init( this );
|
|
if (pTrack->m_pTrack8) pTrack->m_pTrack8->Release();
|
|
pTrack->m_pTrack8 = pTempTrack8;
|
|
}
|
|
|
|
if (FAILED(hr)) break;
|
|
}
|
|
MUSIC_TIME mtOldLength = m_mtLength;
|
|
if (fTrackPadded) // any tracks got joined with header/trailer info
|
|
{
|
|
// pad the length of the segment, to account for the header/trailer
|
|
m_mtLength += mtStartPad + mtEndPad;
|
|
}
|
|
|
|
// Compose
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ComposeInternal();
|
|
}
|
|
|
|
// Back end
|
|
if (fTrackPadded) // any tracks got joined with header/trailer info
|
|
{
|
|
// Trim header and trailer from each track that was joined, using Clone.
|
|
pTrack = m_TrackList.GetHead();
|
|
for (; pTrack; pTrack = pTrack->GetNext())
|
|
{
|
|
if ( (pTrack->m_pTrack) && (pTrack->m_dwInternalFlags & TRACKINTERNAL_START_PADDED) )
|
|
{
|
|
IDirectMusicTrack* pTempTrack = NULL;
|
|
IDirectMusicTrack8* pTempTrack8 = NULL;
|
|
pTrack->m_pTrack->Clone(mtStartPad, mtOldLength + mtStartPad, &pTempTrack);
|
|
pTempTrack->QueryInterface(IID_IDirectMusicTrack8, (void**)&pTempTrack8);
|
|
pTrack->m_pTrack->Release();
|
|
pTrack->m_pTrack = pTempTrack;
|
|
pTrack->m_pTrack->Init( this );
|
|
if (pTrack->m_pTrack8) pTrack->m_pTrack8->Release();
|
|
pTrack->m_pTrack8 = pTempTrack8;
|
|
}
|
|
else if ( (pTrack->m_pTrack) && (pTrack->m_dwInternalFlags & TRACKINTERNAL_END_PADDED) )
|
|
{
|
|
IDirectMusicTrack* pTempTrack = NULL;
|
|
IDirectMusicTrack8* pTempTrack8 = NULL;
|
|
pTrack->m_pTrack->Clone(0, mtOldLength, &pTempTrack);
|
|
pTempTrack->QueryInterface(IID_IDirectMusicTrack8, (void**)&pTempTrack8);
|
|
pTrack->m_pTrack->Release();
|
|
pTrack->m_pTrack = pTempTrack;
|
|
pTrack->m_pTrack->Init( this );
|
|
if (pTrack->m_pTrack8) pTrack->m_pTrack8->Release();
|
|
pTrack->m_pTrack8 = pTempTrack8;
|
|
}
|
|
pTrack->m_dwInternalFlags &= ~(TRACKINTERNAL_START_PADDED | TRACKINTERNAL_END_PADDED);
|
|
}
|
|
// Return the length of the segment to its original value.
|
|
m_mtLength = mtOldLength;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSegment::ComposeInternal()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TList<CTrack*> TrackList;
|
|
// Find the composing tracks and put them in priority order
|
|
CTrack* pTrack = m_TrackList.GetHead();
|
|
for (; pTrack; pTrack = pTrack->GetNext())
|
|
{
|
|
if (pTrack->m_dwFlags & DMUS_TRACKCONFIG_COMPOSING)
|
|
{
|
|
TListItem<CTrack*>* pTrackItem = new TListItem<CTrack*>(pTrack);
|
|
if (!pTrackItem)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
TListItem<CTrack*>* pMaster = TrackList.GetHead();
|
|
TListItem<CTrack*>* pPrevious = NULL;
|
|
for (; pMaster; pMaster = pMaster->GetNext())
|
|
{
|
|
CTrack*& rpMaster = pMaster->GetItemValue();
|
|
if (pTrack->m_dwPriority > rpMaster->m_dwPriority) break;
|
|
pPrevious = pMaster;
|
|
}
|
|
if (!pPrevious) // this has higher priority than anything in the list
|
|
{
|
|
TrackList.AddHead(pTrackItem);
|
|
}
|
|
else // lower priority than pPrevious, higher than pMaster
|
|
{
|
|
pTrackItem->SetNext(pMaster);
|
|
pPrevious->SetNext(pTrackItem);
|
|
}
|
|
}
|
|
}
|
|
if (FAILED(hr)) break;
|
|
}
|
|
// Compose a new track from each from each composing track; put the results
|
|
// in the segment (remove any existing composed tracks)
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TListItem<CTrack*>* pTrackItem = TrackList.GetHead();
|
|
for (; pTrackItem; pTrackItem = pTrackItem->GetNext())
|
|
{
|
|
CTrack*& rpTrack = pTrackItem->GetItemValue();
|
|
IDirectMusicTrack8* pComposedTrack = NULL;
|
|
hr = rpTrack->m_pTrack8->Compose((IDirectMusicSegment*)this, rpTrack->m_dwGroupBits, (IDirectMusicTrack**)&pComposedTrack);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Remove any tracks of this type (in the same group) from the segment.
|
|
IDirectMusicTrack* pOldTrack = NULL;
|
|
GUID guidClassId;
|
|
memset(&guidClassId, 0, sizeof(guidClassId));
|
|
IPersistStream* pPersist = NULL;
|
|
if (SUCCEEDED(pComposedTrack->QueryInterface(IID_IPersistStream, (void**)&pPersist)) )
|
|
{
|
|
if (SUCCEEDED(pPersist->GetClassID(&guidClassId)) &&
|
|
SUCCEEDED( GetTrack( guidClassId, rpTrack->m_dwGroupBits, 0, &pOldTrack ) ) )
|
|
{
|
|
RemoveTrack( pOldTrack );
|
|
pOldTrack->Release();
|
|
}
|
|
pPersist->Release();
|
|
}
|
|
hr = InsertTrack(pComposedTrack, rpTrack->m_dwGroupBits);
|
|
pComposedTrack->Release();
|
|
}
|
|
if (FAILED(hr)) break;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSegment::GetStartPoint(
|
|
MUSIC_TIME* pmtStart // @parm Returns the Segment's start point.
|
|
)
|
|
{
|
|
V_INAME(IDirectMusicSegment::GetStartPoint);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetStartPoint after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
*pmtStart = m_mtStart;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::SetStartPoint(
|
|
MUSIC_TIME mtStart // @parm The start point at which to begin playing the
|
|
// Segment. If it is less than zero or greater than the
|
|
// length of the Segment, the start point will be set
|
|
// to zero.
|
|
)
|
|
{
|
|
if( (mtStart < 0) || (mtStart >= m_mtLength) )
|
|
{
|
|
Trace(1,"Error: Unable to set start point %ld because not within the range of the segment, which is %ld.\n",
|
|
mtStart,m_mtLength);
|
|
return DMUS_E_OUT_OF_RANGE;
|
|
}
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetStartPoint after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
m_mtStart = mtStart;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::GetLoopPoints(
|
|
MUSIC_TIME* pmtStart, // @parm Returns the start point of the loop.
|
|
MUSIC_TIME* pmtEnd // @parm Returns the end point of the loop. A value of
|
|
// 0 indicates that the entire Segment will loop.
|
|
)
|
|
{
|
|
V_INAME(IDirectMusicSegment::GetLoopPoints);
|
|
V_PTR_WRITE(pmtStart, MUSIC_TIME);
|
|
V_PTR_WRITE(pmtEnd, MUSIC_TIME);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetLoopPoints after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
*pmtStart = m_mtLoopStart;
|
|
*pmtEnd = m_mtLoopEnd;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::SetLoopPoints(
|
|
MUSIC_TIME mtStart, // @parm The start point at which to begin the loop.
|
|
MUSIC_TIME mtEnd // @parm The end point at which to begin the loop. Set
|
|
// <p mtStart> and <p mtEnd> to 0
|
|
// to loop the entire Segment.
|
|
)
|
|
{
|
|
if( (mtStart < 0) || (mtEnd > m_mtLength) || (mtStart > mtEnd) )
|
|
{
|
|
Trace(1,"Error: Unable to set loop points %ld, %ld because they are not within the range of the segment, which is %ld.\n",
|
|
mtStart,mtStart,mtEnd,m_mtLength);
|
|
return DMUS_E_OUT_OF_RANGE;
|
|
}
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetLoopPoints after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
m_mtLoopStart = mtStart;
|
|
m_mtLoopEnd = mtEnd;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::SetPChannelsUsed(
|
|
DWORD dwNumPChannels, // @parm The number of PChannels to set. This must be equal
|
|
// to the number of members in the array pointed to by
|
|
// <p paPChannels>.
|
|
DWORD* paPChannels // @parm Points to an array of PChannels. The array should
|
|
// have the same number of elements as specified by <p dwNumPChannels>.
|
|
)
|
|
{
|
|
V_INAME(IDirectMusicSegment::SetPChannelsUsed);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetPChannelsUsed after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
if( dwNumPChannels )
|
|
{
|
|
if( NULL == paPChannels )
|
|
{
|
|
Trace(1,"Error: Bad call to SetPChannelsUsed, pointer to PChannel array is NULL.\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
V_BUFPTR_READ(paPChannels, sizeof(DWORD)*dwNumPChannels);
|
|
|
|
DWORD* padwTemp = new DWORD[dwNumPChannels]; // temp array
|
|
DWORD dwTotalNum = 0;
|
|
if( NULL == padwTemp )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
// count the number of unique PChannels are in the array. That is, the ones
|
|
// that we don't already have stored.
|
|
DWORD dwCount;
|
|
for( dwCount = 0; dwCount < dwNumPChannels; dwCount++ )
|
|
{
|
|
DWORD dwCurrent;
|
|
for( dwCurrent = 0; dwCurrent < m_dwNumPChannels; dwCurrent++ )
|
|
{
|
|
if( m_paPChannels[dwCurrent] == paPChannels[dwCount] )
|
|
{
|
|
// we already track this one
|
|
break;
|
|
}
|
|
}
|
|
if( dwCurrent >= m_dwNumPChannels )
|
|
{
|
|
// we're not already tracking this one
|
|
padwTemp[dwTotalNum] = paPChannels[dwCount];
|
|
dwTotalNum++;
|
|
}
|
|
}
|
|
// dwTotalNum equals the total number of new PChannels, and they are indexed
|
|
// inside adwTemp.
|
|
DWORD* paNewPChannels = new DWORD[m_dwNumPChannels + dwTotalNum];
|
|
if( NULL == paNewPChannels )
|
|
{
|
|
delete [] padwTemp;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
if( m_paPChannels )
|
|
{
|
|
memcpy( paNewPChannels, m_paPChannels, sizeof(DWORD) * m_dwNumPChannels );
|
|
delete [] m_paPChannels;
|
|
}
|
|
memcpy( &paNewPChannels[m_dwNumPChannels], padwTemp, sizeof(DWORD) * dwTotalNum );
|
|
delete [] padwTemp;
|
|
m_dwNumPChannels += dwTotalNum;
|
|
m_paPChannels = paNewPChannels;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// IDirectMusicSegmentObject (private)
|
|
HRESULT CSegment::GetPChannels(
|
|
DWORD* pdwNumPChannels, // returns the number of pchannels
|
|
DWORD** ppaPChannels) // returns a pointer to the array of pchannels. Don't free this
|
|
// memory or keep it, as it is owned by the Segment.
|
|
{
|
|
ASSERT(pdwNumPChannels && ppaPChannels);
|
|
*pdwNumPChannels = m_dwNumPChannels;
|
|
*ppaPChannels = m_paPChannels;
|
|
return S_OK;
|
|
}
|
|
|
|
// return S_OK if the notification is active, S_FALSE if not.
|
|
HRESULT CSegment::CheckNotification( REFGUID rguid )
|
|
{
|
|
if( NULL == FindNotification( rguid ) )
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// IPersist
|
|
|
|
HRESULT CSegment::GetClassID( CLSID* pClassID )
|
|
{
|
|
V_INAME(CSegment::GetClassID);
|
|
V_PTR_WRITE(pClassID, CLSID);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetClassID after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
*pClassID = CLSID_DirectMusicSegment;
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// IPersistStream functions
|
|
|
|
HRESULT CSegment::IsDirty()
|
|
{
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::IsDirty after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
#define DMUS_FOURCC_RMID_FORM mmioFOURCC('R','M','I','D')
|
|
#define DMUS_FOURCC_data_FORM mmioFOURCC('d','a','t','a')
|
|
#define DMUS_FOURCC_DLS_FORM mmioFOURCC('D','L','S',' ')
|
|
#define FOURCC_SECTION_FORM mmioFOURCC('A','A','S','E')
|
|
|
|
HRESULT CSegment::Load( IStream* pIStream )
|
|
{
|
|
V_INAME(CSegment::Load);
|
|
V_INTERFACE(pIStream);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::Load after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
// Save stream's current position
|
|
LARGE_INTEGER li;
|
|
ULARGE_INTEGER ul;
|
|
|
|
li.HighPart = 0;
|
|
li.LowPart = 0;
|
|
|
|
HRESULT hr = pIStream->Seek(li, STREAM_SEEK_CUR, &ul);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
Clear(false);
|
|
|
|
DWORD dwSavedPos = ul.LowPart;
|
|
|
|
// Read first 4 bytes to determine what type of stream we
|
|
// have been passed
|
|
|
|
FOURCC type;
|
|
DWORD dwRead;
|
|
hr = pIStream->Read(&type, sizeof(FOURCC), &dwRead);
|
|
|
|
if(SUCCEEDED(hr) && dwRead == sizeof(FOURCC))
|
|
{
|
|
// Check for a RIFF file
|
|
if(type == mmioFOURCC( 'R', 'I', 'F', 'F' ))
|
|
{
|
|
long lFileLength = 0;
|
|
pIStream->Read(&lFileLength, sizeof(long), &dwRead);
|
|
// Check to see if what type of RIFF file we have
|
|
hr = pIStream->Read(&type, sizeof(FOURCC), &dwRead);
|
|
|
|
if(SUCCEEDED(hr) && dwRead == sizeof(FOURCC))
|
|
{
|
|
if(type == DMUS_FOURCC_SEGMENT_FORM) // We have a DirectMusic segment
|
|
{
|
|
// Since we now know what type of stream we need to
|
|
// seek back to saved position
|
|
li.HighPart = 0;
|
|
li.LowPart = dwSavedPos;
|
|
hr = pIStream->Seek(li, STREAM_SEEK_SET, NULL);
|
|
|
|
hr = LoadDirectMusicSegment(pIStream);
|
|
}
|
|
else if(type == FOURCC_SECTION_FORM) // We have section
|
|
{
|
|
// Since we now know what type of stream we need to seek back to saved position
|
|
li.HighPart = 0;
|
|
li.LowPart = dwSavedPos;
|
|
hr = pIStream->Seek(li, STREAM_SEEK_SET, NULL);
|
|
|
|
// Create Section
|
|
IDMSection* pSection;
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = ::CoCreateInstance(CLSID_DMSection,
|
|
NULL,
|
|
CLSCTX_INPROC,
|
|
IID_IDMSection,
|
|
(void**)&pSection);
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
// Load Section
|
|
IPersistStream* pIPersistStream;
|
|
hr = pSection->QueryInterface(IID_IPersistStream, (void **)&pIPersistStream);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = pIPersistStream->Load(pIStream);
|
|
pIPersistStream->Release();
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
HRESULT hrTemp = pSection->CreateSegment(static_cast<IDirectMusicSegment*>(this));
|
|
if (hrTemp != S_OK)
|
|
{
|
|
hr = hrTemp;
|
|
}
|
|
}
|
|
|
|
pSection->Release();
|
|
}
|
|
}
|
|
else if(type == DMUS_FOURCC_RMID_FORM) // We have an RMID MIDI file
|
|
{
|
|
IDirectMusicCollection *pCollection = NULL;
|
|
BOOL fLoadedMIDI = FALSE;
|
|
// Since it's a RIFF file, it could have more than one top level chunk.
|
|
while (SUCCEEDED(hr) && (lFileLength > 8))
|
|
{
|
|
FOURCC dwType = 0;
|
|
DWORD dwLength;
|
|
pIStream->Read(&dwType, sizeof(FOURCC), &dwRead);
|
|
hr = pIStream->Read(&dwLength, sizeof(DWORD), &dwRead);
|
|
lFileLength -= 8;
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
ULARGE_INTEGER ulPosition; // Memorize start of chunk.
|
|
LARGE_INTEGER liStart;
|
|
liStart.QuadPart = 0;
|
|
hr = pIStream->Seek(liStart, STREAM_SEEK_CUR, &ulPosition);
|
|
liStart.QuadPart = ulPosition.QuadPart;
|
|
if (dwType == DMUS_FOURCC_data_FORM)
|
|
{ // Get MIDI file header.
|
|
hr = pIStream->Read(&dwType, sizeof(FOURCC), &dwRead);
|
|
if(SUCCEEDED(hr) && (dwType == mmioFOURCC( 'M', 'T', 'h', 'd' )))
|
|
{
|
|
// Since we now know what type of stream we need to seek back to saved position
|
|
hr = pIStream->Seek(liStart, STREAM_SEEK_SET, NULL);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = CreateSegmentFromMIDIStream(pIStream,
|
|
static_cast<IDirectMusicSegment*>(this));
|
|
}
|
|
if (SUCCEEDED(hr)) fLoadedMIDI = TRUE;
|
|
}
|
|
}
|
|
else if ((dwType == mmioFOURCC( 'R', 'I', 'F', 'F' ) ||
|
|
(dwType == mmioFOURCC( 'L', 'I', 'S', 'T' ))))
|
|
{
|
|
pIStream->Read(&dwType, sizeof(FOURCC), &dwRead);
|
|
if (dwType == DMUS_FOURCC_DLS_FORM)
|
|
{
|
|
hr = CoCreateInstance(CLSID_DirectMusicCollection,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IDirectMusicCollection,
|
|
(void**)&pCollection);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IPersistStream* pIPS;
|
|
hr = pCollection->QueryInterface( IID_IPersistStream, (void**)&pIPS );
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// We need to seek back to start of chunk
|
|
liStart.QuadPart -= 8;
|
|
pIStream->Seek(liStart, STREAM_SEEK_SET, NULL);
|
|
|
|
hr = pIPS->Load( pIStream );
|
|
pIPS->Release();
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
pCollection->Release();
|
|
pCollection = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwLength & 1) ++dwLength;
|
|
ulPosition.QuadPart += dwLength; // Point to start of next chunk.
|
|
liStart.QuadPart = ulPosition.QuadPart;
|
|
hr = pIStream->Seek(liStart, STREAM_SEEK_SET, NULL);
|
|
lFileLength -= dwLength; // Decrement amount left in file.
|
|
}
|
|
}
|
|
if (pCollection)
|
|
{
|
|
if (fLoadedMIDI)
|
|
{
|
|
SetParam(GUID_ConnectToDLSCollection,-1,0,0,(void *) pCollection);
|
|
}
|
|
pCollection->Release();
|
|
}
|
|
}
|
|
else if (type == mmioFOURCC('W','A','V','E')) // we have a wave file
|
|
{
|
|
IDirectSoundWave* pWave = NULL;
|
|
// Seek back to saved position
|
|
li.HighPart = 0;
|
|
li.LowPart = dwSavedPos;
|
|
hr = pIStream->Seek(li, STREAM_SEEK_SET, NULL);
|
|
|
|
// Check to see if this wave is embedded
|
|
if (dwSavedPos == 0)
|
|
{
|
|
// CoCreate the wave and load it from the stream
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CoCreateInstance(CLSID_DirectSoundWave,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IDirectSoundWave,
|
|
(void**)&pWave);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IPersistStream* pIPS = NULL;
|
|
|
|
hr = pWave->QueryInterface(IID_IPersistStream, (void**)&pIPS);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pIPS->Load( pIStream );
|
|
pIPS->Release();
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
pWave->Release();
|
|
pWave = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Have the loader load the wave object from the stream
|
|
DMUS_OBJECTDESC descWave;
|
|
ZeroMemory(&descWave, sizeof(descWave));
|
|
descWave.dwSize = sizeof(descWave);
|
|
descWave.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_STREAM;
|
|
descWave.guidClass = CLSID_DirectSoundWave;
|
|
descWave.pStream = pIStream;
|
|
IDirectMusicLoader *pLoader = NULL;
|
|
IDirectMusicGetLoader *pGetLoader = NULL;
|
|
hr = pIStream->QueryInterface(IID_IDirectMusicGetLoader,(void **)&pGetLoader);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (SUCCEEDED(pGetLoader->GetLoader(&pLoader)))
|
|
{
|
|
hr = pLoader->GetObject(&descWave, IID_IDirectSoundWave, (void **)&pWave);
|
|
descWave.pStream = NULL;
|
|
descWave.dwValidData &= ~DMUS_OBJ_STREAM;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IDirectMusicObject* pObject = NULL;
|
|
hr = pWave->QueryInterface(IID_IDirectMusicObject, (void **)&pObject);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// set this object to be a segment with the same GUID
|
|
pObject->GetDescriptor(&descWave);
|
|
descWave.guidClass = CLSID_DirectMusicSegment;
|
|
SetDescriptor(&descWave);
|
|
pObject->Release();
|
|
}
|
|
}
|
|
pLoader->Release();
|
|
}
|
|
pGetLoader->Release();
|
|
}
|
|
}
|
|
|
|
if(pWave)
|
|
{
|
|
|
|
// CoCreate a wave track
|
|
IDirectMusicTrack* pWaveTrack = NULL;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ::CoCreateInstance(CLSID_DirectMusicWaveTrack,
|
|
NULL,
|
|
CLSCTX_INPROC,
|
|
IID_IDirectMusicTrack,
|
|
(void**)&pWaveTrack);
|
|
}
|
|
|
|
// Add the wave object to the wave track, and insert the track in the segment.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IPrivateWaveTrack* pPrivateWave = NULL;
|
|
hr = pWaveTrack->QueryInterface(IID_IPrivateWaveTrack, (void**)&pPrivateWave);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
REFERENCE_TIME rt = 0;
|
|
hr = pPrivateWave->AddWave(pWave, 0, 0, 0, &rt);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SetClockTimeDuration(rt * REF_PER_MIL);
|
|
SetFlags(DMUS_SEGIOF_REFLENGTH);
|
|
}
|
|
InsertTrack(pWaveTrack, 1);
|
|
SetTrackConfig(CLSID_DirectMusicWaveTrack, 1, 0, DMUS_TRACKCONFIG_DEFAULT | DMUS_TRACKCONFIG_PLAY_CLOCKTIME,0);
|
|
pPrivateWave->Release();
|
|
}
|
|
}
|
|
|
|
// Clean up anything that's still hanging around
|
|
if (pWaveTrack) pWaveTrack->Release();
|
|
if (pWave) pWave->Release();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = DMUS_E_CANNOTREAD;
|
|
}
|
|
}
|
|
// Check for a template file
|
|
else if(type == mmioFOURCC('L', 'P', 'T', 's'))
|
|
{
|
|
// Since we now know what type of stream we need to seek back to saved position
|
|
li.HighPart = 0;
|
|
li.LowPart = dwSavedPos;
|
|
hr = pIStream->Seek(li, STREAM_SEEK_SET, NULL);
|
|
|
|
// Create Template
|
|
IDMTempl* pTemplate;
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = ::CoCreateInstance(CLSID_DMTempl,
|
|
NULL,
|
|
CLSCTX_INPROC,
|
|
IID_IDMTempl,
|
|
(void**)&pTemplate);
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
// Load Template
|
|
IPersistStream* pIPersistStream;
|
|
hr = pTemplate->QueryInterface(IID_IPersistStream, (void **)&pIPersistStream);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = pIPersistStream->Load(pIStream);
|
|
pIPersistStream->Release();
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = pTemplate->CreateSegment(static_cast<IDirectMusicSegment*>(this));
|
|
}
|
|
|
|
pTemplate->Release();
|
|
}
|
|
}
|
|
// Check for normal MIDI file
|
|
else if(type == mmioFOURCC('M', 'T', 'h', 'd'))
|
|
{
|
|
// Since we now know what type of stream we need to seek back to saved position
|
|
li.HighPart = 0;
|
|
li.LowPart = dwSavedPos;
|
|
hr = pIStream->Seek(li, STREAM_SEEK_SET, NULL);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = CreateSegmentFromMIDIStream(pIStream,
|
|
static_cast<IDirectMusicSegment*>(this));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not a DirectMusic Segment file, MIDI file or section or
|
|
// template; unsupported
|
|
Trace(1,"Error: Segment unable to parse file. Must be segment, midi, wave, or rmi file format.\n");
|
|
hr = DMUS_E_UNSUPPORTED_STREAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = DMUS_E_CANNOTREAD;
|
|
}
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
m_dwValidData |= DMUS_OBJ_LOADED;
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CSegment::Save( IStream* pIStream, BOOL fClearDirty )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CSegment::GetSizeMax( ULARGE_INTEGER FAR* pcbSize )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CSegment::LoadDirectMusicSegment(IStream* pIStream)
|
|
{
|
|
// Argument validation
|
|
assert(pIStream);
|
|
CRiffParser Parser(pIStream);
|
|
RIFFIO ckMain;
|
|
HRESULT hr = S_OK;
|
|
Parser.EnterList(&ckMain);
|
|
if (Parser.NextChunk(&hr))
|
|
{
|
|
if (ckMain.fccType == DMUS_FOURCC_SEGMENT_FORM)
|
|
{
|
|
RIFFIO ckNext; // Descends into the next chunk.
|
|
RIFFIO ckChild; // For scanning through children lists.
|
|
IDirectMusicContainer *pContainer = NULL; // For handling embedded container with linked objects.
|
|
Parser.EnterList(&ckNext);
|
|
while(Parser.NextChunk(&hr))
|
|
{
|
|
switch(ckNext.ckid)
|
|
{
|
|
case DMUS_FOURCC_SEGMENT_CHUNK:
|
|
DMUS_IO_SEGMENT_HEADER ioSegHdr;
|
|
ioSegHdr.rtLength = 0;
|
|
ioSegHdr.dwFlags = 0;
|
|
hr = Parser.Read(&ioSegHdr, sizeof(DMUS_IO_SEGMENT_HEADER));
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
m_dwResolution = ioSegHdr.dwResolution;
|
|
m_mtLength = ioSegHdr.mtLength;
|
|
m_mtStart = ioSegHdr.mtPlayStart;
|
|
m_mtLoopStart = ioSegHdr.mtLoopStart;
|
|
m_mtLoopEnd = ioSegHdr.mtLoopEnd;
|
|
m_dwRepeats = ioSegHdr.dwRepeats;
|
|
m_dwSegFlags = ioSegHdr.dwFlags;
|
|
if (m_dwSegFlags & DMUS_SEGIOF_REFLENGTH)
|
|
{
|
|
m_rtLength = ioSegHdr.rtLength;
|
|
}
|
|
else
|
|
{
|
|
m_rtLength = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DMUS_FOURCC_GUID_CHUNK:
|
|
if( ckNext.cksize == sizeof(GUID) )
|
|
{
|
|
hr = Parser.Read(&m_guidObject, sizeof(GUID));
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
m_dwValidData |= DMUS_OBJ_OBJECT;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DMUS_FOURCC_VERSION_CHUNK:
|
|
hr = Parser.Read(&m_vVersion, sizeof(DMUS_VERSION) );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
m_dwValidData |= DMUS_OBJ_VERSION;
|
|
}
|
|
break;
|
|
|
|
case DMUS_FOURCC_CATEGORY_CHUNK:
|
|
hr = Parser.Read( m_wszCategory, sizeof(WCHAR)*DMUS_MAX_CATEGORY );
|
|
m_wszCategory[DMUS_MAX_CATEGORY-1] = '\0';
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
m_dwValidData |= DMUS_OBJ_CATEGORY;
|
|
}
|
|
break;
|
|
|
|
case DMUS_FOURCC_DATE_CHUNK:
|
|
if( sizeof(FILETIME) == ckNext.cksize )
|
|
{
|
|
hr = Parser.Read( &m_ftDate, sizeof(FILETIME));
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
m_dwValidData |= DMUS_OBJ_DATE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FOURCC_LIST:
|
|
case FOURCC_RIFF:
|
|
switch(ckNext.fccType)
|
|
{
|
|
case DMUS_FOURCC_UNFO_LIST:
|
|
Parser.EnterList(&ckChild);
|
|
while (Parser.NextChunk(&hr))
|
|
{
|
|
switch( ckChild.ckid )
|
|
{
|
|
case DMUS_FOURCC_UNAM_CHUNK:
|
|
{
|
|
hr = Parser.Read(&m_wszName, sizeof(m_wszName));
|
|
m_wszName[DMUS_MAX_NAME-1] = '\0';
|
|
if(SUCCEEDED(hr) )
|
|
{
|
|
m_dwValidData |= DMUS_OBJ_NAME;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
Parser.LeaveList();
|
|
break;
|
|
case DMUS_FOURCC_CONTAINER_FORM:
|
|
// An embedded container RIFF chunk which includes a bunch
|
|
// of objects referenced by the segment. This should precede the
|
|
// tracks and gets loaded prior to the tracks. Loading this
|
|
// causes all of its objects to get SetObject'd in the loader,
|
|
// so they later get pulled in as requested by the tracks.
|
|
// After the tracks are loaded, the loader references are
|
|
// released by a call to release the IDirectMusicContainer.
|
|
{
|
|
DMUS_OBJECTDESC Desc;
|
|
IDirectMusicLoader *pLoader;
|
|
IDirectMusicGetLoader *pGetLoader;
|
|
HRESULT hrTemp = pIStream->QueryInterface(IID_IDirectMusicGetLoader,(void **) &pGetLoader);
|
|
if (SUCCEEDED(hrTemp))
|
|
{
|
|
if (SUCCEEDED(pGetLoader->GetLoader(&pLoader)))
|
|
{
|
|
// Move back stream's current position
|
|
Parser.SeekBack();
|
|
Desc.dwSize = sizeof(Desc);
|
|
Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_STREAM;
|
|
Desc.guidClass = CLSID_DirectMusicContainer;
|
|
Desc.pStream = pIStream;
|
|
pLoader->GetObject(&Desc,IID_IDirectMusicContainer,(void **) &pContainer);
|
|
if (pContainer)
|
|
{
|
|
// Don't cache the container object! We want it and the
|
|
// objects it references to go away when the segment is done loading.
|
|
IDirectMusicObject *pObject = NULL;
|
|
pContainer->QueryInterface(IID_IDirectMusicObject,(void **)&pObject);
|
|
if (pObject)
|
|
{
|
|
pLoader->ReleaseObject(pObject);
|
|
pObject->Release();
|
|
}
|
|
}
|
|
// Now, seek to the end of this chunk.
|
|
Parser.SeekForward();
|
|
pLoader->Release();
|
|
}
|
|
pGetLoader->Release();
|
|
}
|
|
}
|
|
break;
|
|
case DMUS_FOURCC_TRACK_LIST:
|
|
Parser.EnterList(&ckChild);
|
|
while(Parser.NextChunk(&hr))
|
|
{
|
|
if ((ckChild.ckid == FOURCC_RIFF) && (ckChild.fccType == DMUS_FOURCC_TRACK_FORM))
|
|
{
|
|
hr = LoadTrack(&Parser);
|
|
}
|
|
}
|
|
Parser.LeaveList();
|
|
break;
|
|
case DMUS_FOURCC_TOOLGRAPH_FORM:
|
|
hr = LoadGraph(&Parser,&m_pGraph);
|
|
break;
|
|
case DMUS_FOURCC_AUDIOPATH_FORM:
|
|
// Move back to start of this chunk.
|
|
Parser.SeekBack();
|
|
hr = LoadAudioPath(pIStream);
|
|
// Now, seek to the end of this chunk.
|
|
Parser.SeekForward();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
Parser.LeaveList();
|
|
if (pContainer)
|
|
{
|
|
pContainer->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Trace(1,"Error: Unknown file format.\n");
|
|
hr = DMUS_E_DESCEND_CHUNK_FAIL;
|
|
}
|
|
}
|
|
Parser.LeaveList();
|
|
if (SUCCEEDED(hr) && Parser.ComponentFailed())
|
|
{
|
|
Trace(1,"Warning: Segment successfully loaded but one or more tracks within it did not.\n");
|
|
hr = DMUS_S_PARTIALLOAD;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSegment::LoadTrack(CRiffParser *pParser)
|
|
{
|
|
BOOL fHeaderRead = FALSE;
|
|
|
|
DMUS_IO_TRACK_HEADER ioTrackHdr;
|
|
DMUS_IO_TRACK_EXTRAS_HEADER ioTrackExtrasHdr;
|
|
ioTrackExtrasHdr.dwPriority = 0;
|
|
ioTrackExtrasHdr.dwFlags = DMUS_TRACKCONFIG_DEFAULT;
|
|
ioTrackHdr.ckid = 0;
|
|
ioTrackHdr.fccType = 0;
|
|
ioTrackHdr.dwPosition = 0;
|
|
|
|
RIFFIO ckNext;
|
|
HRESULT hr = S_OK;
|
|
pParser->EnterList(&ckNext);
|
|
while(pParser->NextChunk(&hr))
|
|
{
|
|
if (ckNext.ckid == DMUS_FOURCC_TRACK_CHUNK)
|
|
{
|
|
fHeaderRead = TRUE;
|
|
hr = pParser->Read(&ioTrackHdr, sizeof(DMUS_IO_TRACK_HEADER));
|
|
if(ioTrackHdr.ckid == 0 && ioTrackHdr.fccType == NULL)
|
|
{
|
|
Trace(1,"Error: Invalid track header in Segment.\n");
|
|
hr = DMUS_E_INVALID_TRACK_HDR;
|
|
}
|
|
}
|
|
else if (ckNext.ckid == DMUS_FOURCC_TRACK_EXTRAS_CHUNK)
|
|
{
|
|
hr = pParser->Read(&ioTrackExtrasHdr, sizeof(DMUS_IO_TRACK_EXTRAS_HEADER));
|
|
}
|
|
else if((((ckNext.ckid == FOURCC_LIST) || (ckNext.ckid == FOURCC_RIFF))
|
|
&& ckNext.fccType == ioTrackHdr.fccType) ||
|
|
(ckNext.ckid == ioTrackHdr.ckid))
|
|
{
|
|
if (fHeaderRead)
|
|
{
|
|
// Okay, this is the chunk we are looking for.
|
|
// Seek back to start of chunk.
|
|
pParser->SeekBack();
|
|
// Let the parser know it's okay to fail this.
|
|
pParser->EnteringComponent();
|
|
hr = CreateTrack(ioTrackHdr, ioTrackExtrasHdr.dwFlags, ioTrackExtrasHdr.dwPriority, pParser->GetStream());
|
|
// Now, make sure we are at the end of the chunk.
|
|
pParser->SeekForward();
|
|
}
|
|
else
|
|
{
|
|
Trace(1,"Error: Invalid track in Segment - track header is not before track data.\n");
|
|
hr = DMUS_E_TRACK_HDR_NOT_FIRST_CK;
|
|
}
|
|
|
|
}
|
|
}
|
|
pParser->LeaveList();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSegment::CreateTrack(DMUS_IO_TRACK_HEADER& ioTrackHdr, DWORD dwFlags, DWORD dwPriority, IStream *pStream)
|
|
{
|
|
assert(pStream);
|
|
|
|
IDirectMusicTrack* pDMTrack = NULL;
|
|
HRESULT hrTrack = S_OK;
|
|
HRESULT hr = CoCreateInstance(ioTrackHdr.guidClassID,
|
|
NULL,
|
|
CLSCTX_INPROC,
|
|
IID_IDirectMusicTrack,
|
|
(void**)&pDMTrack);
|
|
|
|
IPersistStream *pIPersistStream = NULL;
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = pDMTrack->QueryInterface(IID_IPersistStream, (void **)&pIPersistStream);
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = hrTrack = pIPersistStream->Load(pStream);
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = InsertTrack(pDMTrack, ioTrackHdr.dwGroup, dwFlags, dwPriority, ioTrackHdr.dwPosition);
|
|
}
|
|
|
|
if(pIPersistStream)
|
|
{
|
|
pIPersistStream->Release();
|
|
}
|
|
|
|
if(pDMTrack)
|
|
{
|
|
pDMTrack->Release();
|
|
}
|
|
|
|
if (hr == S_OK && hrTrack != S_OK)
|
|
{
|
|
hr = hrTrack;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSegment::LoadGraph(CRiffParser *pParser,CGraph **ppGraph)
|
|
{
|
|
CGraph *pGraph = new CGraph;
|
|
if (pGraph == NULL) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT hr = pGraph->Load(pParser);
|
|
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
if(*ppGraph)
|
|
{
|
|
(*ppGraph)->Release();
|
|
}
|
|
*ppGraph = pGraph;
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSegment::LoadAudioPath(IStream *pStream)
|
|
{
|
|
assert(pStream);
|
|
|
|
CAudioPathConfig *pPath = new CAudioPathConfig;
|
|
if (pPath == NULL) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT hr = pPath->Load(pStream);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Trace(1,"Segment failed loading embedded AudioPath Configuration\n");
|
|
}
|
|
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
if(m_pAudioPathConfig)
|
|
{
|
|
m_pAudioPathConfig->Release();
|
|
}
|
|
m_pAudioPathConfig = pPath;
|
|
if (m_dwVersion < 8) m_dwVersion = 8;
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// IDirectMusicObject
|
|
|
|
STDMETHODIMP CSegment::GetDescriptor(LPDMUS_OBJECTDESC pDesc)
|
|
{
|
|
// Argument validation
|
|
V_INAME(CSegment::GetDescriptor);
|
|
V_PTR_WRITE(pDesc, DMUS_OBJECTDESC);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::GetDescriptor after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
if (pDesc->dwSize)
|
|
{
|
|
V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC);
|
|
}
|
|
else
|
|
{
|
|
pDesc->dwSize = sizeof(DMUS_OBJECTDESC);
|
|
}
|
|
|
|
pDesc->guidClass = CLSID_DirectMusicSegment;
|
|
pDesc->guidObject = m_guidObject;
|
|
pDesc->ftDate = m_ftDate;
|
|
pDesc->vVersion = m_vVersion;
|
|
StringCchCopyW( pDesc->wszName, DMUS_MAX_NAME, m_wszName);
|
|
StringCchCopyW( pDesc->wszCategory, DMUS_MAX_CATEGORY, m_wszCategory);
|
|
StringCchCopyW( pDesc->wszFileName, DMUS_MAX_FILENAME, m_wszFileName);
|
|
pDesc->dwValidData = ( m_dwValidData | DMUS_OBJ_CLASS );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::SetDescriptor(LPDMUS_OBJECTDESC pDesc)
|
|
{
|
|
// Argument validation
|
|
V_INAME(CSegment::SetDescriptor);
|
|
V_PTR_READ(pDesc, DMUS_OBJECTDESC);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::SetDescriptor after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
if (pDesc->dwSize)
|
|
{
|
|
V_STRUCTPTR_READ(pDesc, DMUS_OBJECTDESC);
|
|
}
|
|
|
|
HRESULT hr = E_INVALIDARG;
|
|
DWORD dw = 0;
|
|
|
|
if( pDesc->dwSize >= sizeof(DMUS_OBJECTDESC) )
|
|
{
|
|
if(pDesc->dwValidData & DMUS_OBJ_CLASS)
|
|
{
|
|
dw |= DMUS_OBJ_CLASS;
|
|
}
|
|
|
|
if(pDesc->dwValidData & DMUS_OBJ_LOADED)
|
|
{
|
|
dw |= DMUS_OBJ_LOADED;
|
|
}
|
|
|
|
if( pDesc->dwValidData & DMUS_OBJ_OBJECT )
|
|
{
|
|
m_guidObject = pDesc->guidObject;
|
|
dw |= DMUS_OBJ_OBJECT;
|
|
}
|
|
if( pDesc->dwValidData & DMUS_OBJ_NAME )
|
|
{
|
|
StringCchCopyW( m_wszName, DMUS_MAX_NAME, pDesc->wszName);
|
|
dw |= DMUS_OBJ_NAME;
|
|
}
|
|
if( pDesc->dwValidData & DMUS_OBJ_CATEGORY )
|
|
{
|
|
StringCchCopyW( m_wszCategory, DMUS_MAX_CATEGORY, pDesc->wszCategory);
|
|
dw |= DMUS_OBJ_CATEGORY;
|
|
}
|
|
if( ( pDesc->dwValidData & DMUS_OBJ_FILENAME ) ||
|
|
( pDesc->dwValidData & DMUS_OBJ_FULLPATH ) )
|
|
{
|
|
StringCchCopyW( m_wszFileName, DMUS_MAX_FILENAME, pDesc->wszFileName);
|
|
dw |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH));
|
|
}
|
|
if( pDesc->dwValidData & DMUS_OBJ_VERSION )
|
|
{
|
|
m_vVersion = pDesc->vVersion;
|
|
dw |= DMUS_OBJ_VERSION;
|
|
}
|
|
if( pDesc->dwValidData & DMUS_OBJ_DATE )
|
|
{
|
|
m_ftDate = pDesc->ftDate;
|
|
dw |= DMUS_OBJ_DATE;
|
|
}
|
|
m_dwValidData |= dw;
|
|
if( pDesc->dwValidData & (~dw) )
|
|
{
|
|
Trace(2,"Warning: Segment::SetDescriptor was not able to handle all passed fields, dwValidData bits %lx.\n",pDesc->dwValidData & (~dw));
|
|
hr = S_FALSE; // there were extra fields we didn't parse;
|
|
pDesc->dwValidData = dw;
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Trace(1,"Error: Unable to set segment descriptor, size field is too small.\n");
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CSegment::ParseDescriptor(LPSTREAM pStream, LPDMUS_OBJECTDESC pDesc)
|
|
{
|
|
V_INAME(CSegment::ParseDescriptor);
|
|
V_INTERFACE(pStream);
|
|
V_PTR_WRITE(pDesc, DMUS_OBJECTDESC);
|
|
if (pDesc->dwSize)
|
|
{
|
|
V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC);
|
|
}
|
|
else
|
|
{
|
|
pDesc->dwSize = sizeof(DMUS_OBJECTDESC);
|
|
}
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(2, "Warning: Call of IDirectMusicSegment::ParseDescriptor after the segment has been garbage collected.\n");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
HRESULT hret = E_FAIL;
|
|
// Save stream's current position
|
|
LARGE_INTEGER li;
|
|
ULARGE_INTEGER ul;
|
|
|
|
li.HighPart = 0;
|
|
li.LowPart = 0;
|
|
|
|
HRESULT hr = pStream->Seek(li, STREAM_SEEK_CUR, &ul);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
pDesc->dwValidData = 0;
|
|
DWORD dwSavedPos = ul.LowPart;
|
|
|
|
// Read first 4 bytes to determine what type of stream we
|
|
// have been passed
|
|
|
|
FOURCC type;
|
|
DWORD dwRead;
|
|
hr = pStream->Read(&type, sizeof(FOURCC), &dwRead);
|
|
|
|
if(SUCCEEDED(hr) && dwRead == sizeof(FOURCC))
|
|
{
|
|
// Check for a RIFF file
|
|
if(type == mmioFOURCC( 'R', 'I', 'F', 'F' ))
|
|
{
|
|
// Check to see if what type of RIFF file we have
|
|
li.HighPart = 0;
|
|
li.LowPart = dwSavedPos + 8; // Length needed to seek to form type of RIFF chunk
|
|
|
|
hr = pStream->Seek(li, STREAM_SEEK_SET, NULL);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = pStream->Read(&type, sizeof(FOURCC), &dwRead);
|
|
}
|
|
|
|
if(SUCCEEDED(hr) && dwRead == sizeof(FOURCC))
|
|
{
|
|
if(type == DMUS_FOURCC_SEGMENT_FORM) // We have a DirectMusic segment
|
|
{
|
|
// Since we now know what type of stream we need to
|
|
// seek back to saved position
|
|
li.HighPart = 0;
|
|
li.LowPart = dwSavedPos;
|
|
hr = pStream->Seek(li, STREAM_SEEK_SET, NULL);
|
|
if( SUCCEEDED(hr) ) // should always succeed.
|
|
{
|
|
hret = ParseSegment(pStream, pDesc);
|
|
}
|
|
}
|
|
else if(type == FOURCC_SECTION_FORM) // We have section
|
|
{
|
|
long lTemp;
|
|
hr = pStream->Read(&lTemp, sizeof(long), &dwRead);
|
|
if( lTemp == mmioFOURCC('s','e','c','n') )
|
|
{
|
|
hr = pStream->Read(&lTemp, sizeof(long), &dwRead); // length
|
|
hr = pStream->Read(&lTemp, sizeof(long), &dwRead); // time
|
|
if( SUCCEEDED(hr) && (dwRead == sizeof(long) ))
|
|
{
|
|
hr = pStream->Read(&pDesc->wszName, sizeof(WCHAR)*16, &dwRead);
|
|
pDesc->wszName[16-1] = '\0';
|
|
if(SUCCEEDED(hr) && (dwRead == sizeof(WCHAR)*16))
|
|
{
|
|
pDesc->dwValidData |= DMUS_OBJ_NAME;
|
|
}
|
|
}
|
|
hret = S_OK;
|
|
}
|
|
}
|
|
else if (type == mmioFOURCC('W','A','V','E')) // we have a wave file
|
|
{
|
|
// Create a wave object and have it parse the file.
|
|
IDirectMusicObject *pObject;
|
|
hret = CoCreateInstance(CLSID_DirectSoundWave,NULL,CLSCTX_INPROC_SERVER,
|
|
IID_IDirectMusicObject,(void **) &pObject);
|
|
if(SUCCEEDED(hret))
|
|
{
|
|
// seek back to saved position
|
|
li.HighPart = 0;
|
|
li.LowPart = dwSavedPos;
|
|
hret = pStream->Seek(li, STREAM_SEEK_SET, NULL);
|
|
if (SUCCEEDED(hret))
|
|
{
|
|
hret = pObject->ParseDescriptor(pStream,pDesc);
|
|
}
|
|
pObject->Release();
|
|
}
|
|
}
|
|
// Check to see if we have a MIDI file
|
|
else
|
|
{
|
|
li.HighPart = 0;
|
|
li.LowPart = dwSavedPos + 20; // Length needed to seek to start of normal MIDI file
|
|
// contained within the Riff chunk
|
|
|
|
hr = pStream->Seek(li, STREAM_SEEK_SET, NULL);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = pStream->Read(&type, sizeof(FOURCC), &dwRead);
|
|
}
|
|
|
|
if(SUCCEEDED(hr) && dwRead == sizeof(FOURCC))
|
|
{
|
|
if(type == mmioFOURCC( 'M', 'T', 'h', 'd' ))
|
|
{
|
|
hret = S_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Check for a template file
|
|
else if(type == mmioFOURCC('L', 'P', 'T', 's'))
|
|
{
|
|
hret = S_OK;
|
|
}
|
|
// Check for normal MIDI file
|
|
else if(type == mmioFOURCC('M', 'T', 'h', 'd'))
|
|
{
|
|
hret = S_OK;
|
|
}
|
|
}
|
|
if (SUCCEEDED(hret))
|
|
{
|
|
pDesc->dwValidData |= DMUS_OBJ_CLASS;
|
|
pDesc->guidClass = CLSID_DirectMusicSegment;
|
|
}
|
|
#ifdef DBG
|
|
if (hret == E_FAIL)
|
|
{
|
|
Trace(1,"Error: Segment unable to parse file - unknown format.\n");
|
|
}
|
|
#endif
|
|
return hret;
|
|
}
|
|
|
|
HRESULT CSegment::ParseSegment(IStream* pIStream, LPDMUS_OBJECTDESC pDesc)
|
|
{
|
|
CRiffParser Parser(pIStream);
|
|
RIFFIO ckMain;
|
|
RIFFIO ckNext;
|
|
RIFFIO ckUNFO;
|
|
HRESULT hr = S_OK;
|
|
|
|
Parser.EnterList(&ckMain);
|
|
if (Parser.NextChunk(&hr) && (ckMain.fccType == DMUS_FOURCC_SEGMENT_FORM))
|
|
{
|
|
Parser.EnterList(&ckNext);
|
|
while(Parser.NextChunk(&hr))
|
|
{
|
|
switch(ckNext.ckid)
|
|
{
|
|
case DMUS_FOURCC_GUID_CHUNK:
|
|
hr = Parser.Read( &pDesc->guidObject, sizeof(GUID) );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
pDesc->dwValidData |= DMUS_OBJ_OBJECT;
|
|
}
|
|
break;
|
|
case DMUS_FOURCC_VERSION_CHUNK:
|
|
hr = Parser.Read( &pDesc->vVersion, sizeof(DMUS_VERSION) );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
pDesc->dwValidData |= DMUS_OBJ_VERSION;
|
|
}
|
|
break;
|
|
|
|
case DMUS_FOURCC_CATEGORY_CHUNK:
|
|
hr = Parser.Read( &pDesc->wszCategory, sizeof(pDesc->wszCategory) );
|
|
pDesc->wszCategory[DMUS_MAX_CATEGORY-1] = '\0';
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
pDesc->dwValidData |= DMUS_OBJ_CATEGORY;
|
|
}
|
|
break;
|
|
|
|
case DMUS_FOURCC_DATE_CHUNK:
|
|
hr = Parser.Read( &pDesc->ftDate, sizeof(FILETIME) );
|
|
if( SUCCEEDED(hr))
|
|
{
|
|
pDesc->dwValidData |= DMUS_OBJ_DATE;
|
|
}
|
|
break;
|
|
case FOURCC_LIST:
|
|
switch(ckNext.fccType)
|
|
{
|
|
case DMUS_FOURCC_UNFO_LIST:
|
|
Parser.EnterList(&ckUNFO);
|
|
while (Parser.NextChunk(&hr))
|
|
{
|
|
switch( ckUNFO.ckid )
|
|
{
|
|
case DMUS_FOURCC_UNAM_CHUNK:
|
|
{
|
|
hr = Parser.Read(&pDesc->wszName, sizeof(pDesc->wszName));
|
|
pDesc->wszName[DMUS_MAX_NAME-1] = '\0';
|
|
if(SUCCEEDED(hr) )
|
|
{
|
|
pDesc->dwValidData |= DMUS_OBJ_NAME;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
Parser.LeaveList();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
}
|
|
Parser.LeaveList();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
void CSegmentList::Clear()
|
|
{
|
|
CSegment *pSeg;
|
|
while (pSeg = RemoveHead())
|
|
{
|
|
pSeg->SetNext(NULL);
|
|
pSeg->m_pSong = NULL;
|
|
pSeg->Release();
|
|
}
|
|
}
|
|
|
|
inline REFERENCE_TIME ConvertToReference(MUSIC_TIME mtSpan, double dblTempo)
|
|
{
|
|
REFERENCE_TIME rtTemp = mtSpan;
|
|
rtTemp *= 600000000;
|
|
rtTemp += (DMUS_PPQ / 2);
|
|
rtTemp /= DMUS_PPQ;
|
|
rtTemp = (REFERENCE_TIME)(rtTemp / dblTempo);
|
|
return rtTemp;
|
|
}
|
|
|
|
inline MUSIC_TIME ConvertToMusic(REFERENCE_TIME rtSpan, double dblTempo)
|
|
{
|
|
rtSpan *= DMUS_PPQ;
|
|
rtSpan = (REFERENCE_TIME)(rtSpan * dblTempo);
|
|
rtSpan += 300000000;
|
|
rtSpan /= 600000000;
|
|
#ifdef DBG
|
|
if ( rtSpan & 0xFFFFFFFF00000000 )
|
|
{
|
|
Trace(1,"Error: Invalid Reference to Music time conversion resulted in overflow.\n");
|
|
}
|
|
#endif
|
|
return (MUSIC_TIME) (rtSpan & 0xFFFFFFFF);
|
|
}
|
|
|
|
HRESULT CSegment::MusicToReferenceTime(MUSIC_TIME mtTime, REFERENCE_TIME *prtTime)
|
|
{
|
|
double dbl = 120;
|
|
MUSIC_TIME mtTempo = 0;
|
|
REFERENCE_TIME rtTempo = 0;
|
|
MUSIC_TIME mtNext = 0;
|
|
PrivateTempo Tempo;
|
|
HRESULT hr;
|
|
|
|
do
|
|
{
|
|
hr = GetParam(GUID_PrivateTempoParam, -1, 0, mtTempo, &mtNext,(void *)&Tempo );
|
|
if (hr == S_OK)
|
|
{
|
|
dbl = Tempo.dblTempo;
|
|
if (Tempo.fLast || mtTempo + mtNext >= mtTime) break;
|
|
rtTempo += ConvertToReference(mtNext, dbl);
|
|
mtTempo += mtNext;
|
|
}
|
|
|
|
} while (hr == S_OK);
|
|
|
|
*prtTime = rtTempo + ConvertToReference(mtTime - mtTempo, dbl);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CSegment::ReferenceToMusicTime(REFERENCE_TIME rtTime, MUSIC_TIME *pmtTime)
|
|
{
|
|
double dbl = 120;
|
|
MUSIC_TIME mtTempo = 0;
|
|
REFERENCE_TIME rtTempo = 0;
|
|
MUSIC_TIME mtNext = 0;
|
|
PrivateTempo Tempo;
|
|
HRESULT hr;
|
|
|
|
do
|
|
{
|
|
hr = GetParam(GUID_PrivateTempoParam, -1, 0, mtTempo, &mtNext,(void *)&Tempo );
|
|
if (hr == S_OK)
|
|
{
|
|
REFERENCE_TIME rtNext = rtTempo + ConvertToReference(mtNext, dbl);
|
|
dbl = Tempo.dblTempo;
|
|
if (Tempo.fLast || rtNext >= rtTime) break;
|
|
rtTempo = rtNext;
|
|
mtTempo += mtNext;
|
|
}
|
|
|
|
} while (hr == S_OK);
|
|
|
|
*pmtTime = mtTempo + ConvertToMusic(rtTime - rtTempo, dbl);
|
|
return S_OK;
|
|
}
|
|
|