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

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;
}