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

1639 lines
57 KiB
C++

// Copyright (c) 1998-2001 Microsoft Corporation
// dmsstobj.cpp : Implementation of CSegState
#include "dmime.h"
#include "DMSStObj.h"
#include "dmsegobj.h"
#include "song.h"
#include "dmgraph.h"
#include "dmperf.h"
#include "dmusici.h"
#include "..\shared\Validate.h"
#include "debug.h"
#include "dmscriptautguids.h"
#include "paramtrk.h"
#define ASSERT assert
CSegState::CSegState()
{
InitializeCriticalSection(&m_CriticalSection);
InterlockedIncrement(&g_cComponent);
m_fDelayShutDown = false;
m_fInPlay = false;
m_cRef = 1;
m_dwPlayTrackFlags = DMUS_TRACKF_START | DMUS_TRACKF_SEEK;
m_dwFirstTrackID = 0;
m_dwLastTrackID = 0;
m_mtEndTime = 0;
m_mtAbortTime = 0;
m_mtOffset = 0;
m_rtOffset = 0;
m_rtEndTime = 0;
m_mtStartPoint = 0;
m_rtStartPoint = 0;
m_mtSeek = 0;
m_rtSeek = 0;
m_rtFirstLoopStart = 0;
m_rtCurLoopStart = 0;
m_rtCurLoopEnd = 0;
m_mtLength = 0;
m_rtLength = 0;
m_mtLoopStart = 0;
m_mtLoopEnd = 0;
m_dwRepeatsLeft = 0;
m_dwRepeats = 0;
m_dwVersion = 0; // Init to 6.1 behavior.
m_fPrepped = FALSE;
m_fCanStop = TRUE;
m_rtGivenStart = -1;
m_mtResolvedStart = -1;
m_mtLastPlayed = 0;
m_rtLastPlayed = 0;
m_mtStopTime = 0;
m_dwPlaySegFlags = 0;
m_dwSegFlags = 0;
m_fStartedPlay = FALSE;
m_pUnkDispatch = NULL;
m_pSegment = NULL;
m_pPerformance = NULL;
m_pAudioPath = NULL;
m_pGraph = NULL;
m_fSongMode = FALSE;
m_pSongSegState = NULL;
TraceI(2, "SegmentState %lx created\n", this );
}
CSegState::~CSegState()
{
if (m_pUnkDispatch)
m_pUnkDispatch->Release(); // free IDispatch implementation we may have borrowed
if (m_pAudioPath) m_pAudioPath->Release();
if (m_pGraph) m_pGraph->Release();
if (m_pSongSegState) m_pSongSegState->Release();
InterlockedDecrement(&g_cComponent);
DeleteCriticalSection(&m_CriticalSection);
TraceI(2, "SegmentState %lx destroyed with %ld releases outstanding\n", this, m_cRef );
}
STDMETHODIMP CSegState::QueryInterface(
const IID &iid, // @parm Interface to query for
void **ppv) // @parm The requested interface will be returned here
{
V_INAME(CSegState::QueryInterface);
V_PTRPTR_WRITE(ppv);
V_REFGUID(iid);
*ppv = NULL;
if (iid == IID_IUnknown || iid == IID_IDirectMusicSegmentState ||
iid == IID_IDirectMusicSegmentState8)
{
*ppv = static_cast<IDirectMusicSegmentState*>(this);
} else
if (iid == IID_CSegState)
{
*ppv = static_cast<CSegState*>(this);
} else
if (iid == IID_IDirectMusicGraph)
{
*ppv = static_cast<IDirectMusicGraph*>(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_AutDirectMusicSegmentState,
static_cast<IDirectMusicSegmentState*>(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: Request to query unknown interface on SegmentState object\n");
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(this)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) CSegState::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) CSegState::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;
}
/*
Private initialization function called by IDirectMusicSegment to set this
state object's parent segment and performance. Addref's the parent segment
but only retains a weak reference to the performance.
*/
HRESULT CSegState::PrivateInit(
CSegment *pParentSegment,
CPerformance *pPerformance)
{
HRESULT hr = S_OK;
ASSERT(pParentSegment);
ASSERT(pPerformance);
m_pSegment = pParentSegment;
pParentSegment->AddRef();
m_pPerformance = pPerformance; // retain only a weak reference
m_rtLength = pParentSegment->m_rtLength;
if (m_rtLength) // It's a ref time segment, so convert the length to music time
{
pParentSegment->ReferenceToMusicTime(m_rtLength, &m_mtLength);
}
else
{
m_mtLength = pParentSegment->m_mtLength;
}
m_mtStartPoint = pParentSegment->m_mtStart;
pParentSegment->MusicToReferenceTime(m_mtStartPoint, &m_rtStartPoint);
m_mtLoopStart = pParentSegment->m_mtLoopStart;
m_mtLoopEnd = pParentSegment->m_mtLoopEnd;
m_dwSegFlags = pParentSegment->m_dwSegFlags;
m_dwRepeats = pParentSegment->m_dwRepeats;
// Don't allow repeat count to overflow and cause mathematical errors.
// Make it so it can't create a segment length larger than 0x3FFFFFFF,
// which would last for 8 days at 120 bpm!
if (m_dwRepeats)
{
if ((m_mtLoopEnd == 0) && (m_mtLoopStart == 0))
{
// This happens when loading waves and MIDI files.
m_mtLoopEnd = m_mtLength;
}
// Make sure the loop is real.
if (m_mtLoopEnd > m_mtLoopStart)
{
// Take the maximum length, subtract out the full length, then divide by the loop size.
DWORD dwMax = (0x3FFFFFFF - m_mtLength) / (m_mtLoopEnd - m_mtLoopStart);
// dwMax is the maximum number of loops that can be done without overflowing the time.
if (m_dwRepeats > dwMax)
{
m_dwRepeats = dwMax;
}
}
else
{
m_dwRepeats = 0;
}
}
m_dwRepeatsLeft = m_dwRepeats;
if( m_mtLoopEnd == 0 )
{
m_mtLoopEnd = m_mtLength;
}
if( m_mtStartPoint >= m_mtLoopEnd )
{
// in this case, we're not doing any looping.
m_mtLoopEnd = m_mtLoopStart = 0;
m_dwRepeats = m_dwRepeatsLeft = 0;
}
return hr;
}
HRESULT CSegState::InitRoute(IDirectMusicAudioPath *pAudioPath)
{
HRESULT hr = E_INVALIDARG;
EnterCriticalSection(&m_CriticalSection);
if (pAudioPath)
{
if (m_dwVersion < 8) m_dwVersion = 8;
m_pAudioPath = (CAudioPath *) pAudioPath;
pAudioPath->AddRef();
hr = S_OK;
}
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
/*
This is called from the performance when it wants to release a
segmentstate. This ensures that the segstate is
no longer valid once outside the Performance.
*/
HRESULT CSegState::ShutDown(void)
{
if (this)
{
if (m_fInPlay)
{
m_fDelayShutDown = true;
return S_OK;
}
EnterCriticalSection(&m_CriticalSection);
m_TrackList.Clear();
if( m_pSegment )
{
m_pSegment->Release();
m_pSegment = NULL;
}
if( m_pAudioPath)
{
m_pAudioPath->Release();
m_pAudioPath = NULL;
}
if (m_pSongSegState)
{
m_pSongSegState->Release();
m_pSongSegState = NULL;
}
m_pPerformance = NULL;
LeaveCriticalSection(&m_CriticalSection);
if( int nCount = Release() )
{
TraceI( 2, "Warning! SegmentState %lx still referenced %d times after Performance has released it.\n", this, nCount );
}
return S_OK;
}
TraceI(0,"Attempting to delete a NULL SegmentState!\n");
return E_FAIL;
}
/*
Retrieve the internal track list. Used by IDirectMusicSegment.
*/
HRESULT CSegState::GetTrackList(
void** ppTrackList)
{
ASSERT(ppTrackList);
*ppTrackList = (void*)&m_TrackList;
return S_OK;
}
/*
Computes the length of the segmentstate using the internal length, loop points,
and repeat count. This is the length of the segstate that will actually play,
not necessarily the length if it played from the beginning.
*/
MUSIC_TIME CSegState::GetEndTime(MUSIC_TIME mtStartTime)
{
EnterCriticalSection(&m_CriticalSection);
if (m_rtLength && m_pPerformance)
{
// If there is a reference time length, convert it into Music Time.
// ALSO: convert m_mtLength and re-adjust loop points.
MUSIC_TIME mtOffset = m_mtResolvedStart;
REFERENCE_TIME rtOffset = 0;
m_pPerformance->MusicToReferenceTime(mtOffset, &rtOffset);
REFERENCE_TIME rtEndTime = (m_rtLength - m_rtStartPoint) + rtOffset; // Convert from length to actual end time.
m_pPerformance->ReferenceToMusicTime(rtEndTime, &m_mtEndTime);
MUSIC_TIME mtOldLength = m_mtLength;
m_mtLength = m_mtEndTime - mtOffset + m_mtStartPoint;
if (m_mtLoopEnd >= mtOldLength) // keep loop end equal to length
{
m_mtLoopEnd = m_mtLength;
}
if( m_mtLoopEnd > m_mtLength ) // shrink loop end to equal length
{
m_mtLoopEnd = m_mtLength;
if( m_mtStartPoint >= m_mtLoopEnd )
{
// in this case, we're not doing any looping.
m_mtLoopEnd = m_mtLoopStart = 0;
m_dwRepeats = m_dwRepeatsLeft = 0;
}
}
}
LeaveCriticalSection(&m_CriticalSection);
LONGLONG length;
length = m_mtLength + ((m_mtLoopEnd - m_mtLoopStart) * m_dwRepeats);
length -= m_mtStartPoint;
length += mtStartTime;
if(length > 0x7fffffff) length = 0x7fffffff;
return (MUSIC_TIME)length;
}
/*
Converts an absolute Performance time to the index into the SegmentState, using
the SegmentState's offset, internal length, loop points, and repeat count.
Also returns the offset and repeat count for that time.
*/
HRESULT CSegState::ConvertToSegTime(
MUSIC_TIME* pmtTime, MUSIC_TIME* pmtOffset, DWORD* pdwRepeat )
{
ASSERT( pmtTime );
ASSERT( pmtOffset );
ASSERT( pdwRepeat );
MUSIC_TIME mtPos = *pmtTime - m_mtResolvedStart + m_mtStartPoint;
MUSIC_TIME mtLoopLength = m_mtLoopEnd - m_mtLoopStart;
DWORD dwRepeat = 0;
DWORD mtOffset = m_mtResolvedStart - m_mtStartPoint;
while( mtPos >= m_mtLoopEnd )
{
if( dwRepeat >= m_dwRepeats ) break;
mtPos -= mtLoopLength;
mtOffset += mtLoopLength;
dwRepeat++;
}
*pmtTime = mtPos;
*pmtOffset = mtOffset;
*pdwRepeat = dwRepeat;
if( (mtPos >= 0) && (mtPos < m_mtLength) )
{
return S_OK; // time is in range of the Segment
}
else
{
return S_FALSE; // time is out of range of the Segment
}
}
void CSegState::GenerateNotification( DWORD dwNotification, MUSIC_TIME mtTime )
{
GUID guid;
HRESULT hr;
guid = GUID_NOTIFICATION_SEGMENT;
hr = m_pSegment->CheckNotification( guid );
if( S_FALSE != hr )
{
DMUS_NOTIFICATION_PMSG* pEvent = NULL;
if( SUCCEEDED( m_pPerformance->AllocPMsg( sizeof(DMUS_NOTIFICATION_PMSG),
(DMUS_PMSG**)&pEvent )))
{
pEvent->dwField1 = 0;
pEvent->dwField2 = 0;
pEvent->guidNotificationType = GUID_NOTIFICATION_SEGMENT;
pEvent->dwType = DMUS_PMSGT_NOTIFICATION;
pEvent->mtTime = mtTime;
pEvent->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_ATTIME;
pEvent->dwPChannel = 0;
pEvent->dwNotificationOption = dwNotification;
pEvent->dwGroupID = 0xffffffff;
pEvent->punkUser = (IUnknown*)(IDirectMusicSegmentState*)this;
AddRef();
StampPMsg((DMUS_PMSG*)pEvent);
if(FAILED(m_pPerformance->SendPMsg( (DMUS_PMSG*)pEvent )))
{
m_pPerformance->FreePMsg((DMUS_PMSG*) pEvent );
}
}
}
}
/*
Called to send the tools in the tool graph a dirty pmsg so they update any
cached GetParam() info.
*/
void CSegState::SendDirtyPMsg( MUSIC_TIME mtTime )
{
DMUS_PMSG* pEvent = NULL;
if (m_pPerformance)
{
if( SUCCEEDED( m_pPerformance->AllocPMsg( sizeof(DMUS_PMSG),
(DMUS_PMSG**)&pEvent )))
{
pEvent->mtTime = mtTime;
pEvent->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE;
pEvent->dwGroupID = 0xffffffff;
pEvent->dwType = DMUS_PMSGT_DIRTY;
StampPMsg((DMUS_PMSG*)pEvent);
if( FAILED( m_pPerformance->SendPMsg( pEvent )))
{
m_pPerformance->FreePMsg( pEvent );
}
}
}
}
/*
Called when the SegState is stopped prematurely, so we can send a SEGABORT
Notification.
Also, flushes all events that were sent after the stop time.
*/
HRESULT CSegState::AbortPlay( MUSIC_TIME mtTime, BOOL fLeaveNotesOn )
{
EnterCriticalSection(&m_CriticalSection);
if (m_pPerformance)
{
if( m_mtLastPlayed > mtTime )
{
// If we've played past the abort time, we need to flush messages.
// Note that if we were aborted by playing another segment that had
// the DMUS_SEGF_NOINVALIDATE flag set, don't truncate notes
// that are currently on.
CTrack* pTrack;
pTrack = m_TrackList.GetHead();
while( pTrack )
{
m_pPerformance->FlushVirtualTrack( pTrack->m_dwVirtualID, mtTime, fLeaveNotesOn );
pTrack = pTrack->GetNext();
}
m_mtLastPlayed = mtTime;
}
// Always fill in the updated value for lastplayed so the ShutDown or Done queue will flush this
// at the right time.
m_pPerformance->MusicToReferenceTime(mtTime,&m_rtLastPlayed);
}
LeaveCriticalSection(&m_CriticalSection);
// Always generate an abort for a segment that has not started playing yet.
if (m_fStartedPlay && (m_mtEndTime <= mtTime))
{
return S_FALSE; // Abort was too late to matter.
}
if (m_mtAbortTime) // Previous abort.
{
if (m_mtAbortTime <= mtTime) // Is this earlier?
{
return S_FALSE; // No, don't send abort message.
}
}
m_mtAbortTime = mtTime;
// Find all the parameter control tracks and invalidate any parameter envelopes
// that need invalidation.
CTrack* pTrack = m_TrackList.GetHead();
while( pTrack )
{
if (pTrack->m_guidClassID == CLSID_DirectMusicParamControlTrack)
{
CParamControlTrack* pParamTrack = NULL;
if (pTrack->m_pTrack &&
SUCCEEDED(pTrack->m_pTrack->QueryInterface(IID_CParamControlTrack, (void**)&pParamTrack)))
{
pParamTrack->OnSegmentEnd(m_rtLastPlayed, pTrack->m_pTrackState);
pParamTrack->Release();
}
}
pTrack = pTrack->GetNext();
}
GenerateNotification( DMUS_NOTIFICATION_SEGABORT, mtTime );
// if this is a primary or controlling segment, send a DMUS_PMSGT_DIRTY message
if( !(m_dwPlaySegFlags & DMUS_SEGF_SECONDARY) || (m_dwPlaySegFlags & DMUS_SEGF_CONTROL) )
{
TraceI(4, "Send Dirty PMsg [4] %d (%d)\n", m_mtSeek, m_mtOffset + m_mtSeek);
SendDirtyPMsg( m_mtOffset + m_mtSeek );
}
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// IDirectMusicSegmentState
//////////////////////////////////////////////////////////////////////
// IDirectMusicSegmentState::GetRepeats
/*
@method HRESULT | IDirectMusicSegmentState | GetRepeats |
Returns the number of times the SegmentState is set to repeat. A value of zero indicates
to play through only once (no repeats.) This value remains constant throughout the life
of the SegmentState.
@rvalue E_POINTER | if <p pdwRepeats> is NULL or invalid.
@rvalue S_OK | Success.
*/
HRESULT STDMETHODCALLTYPE CSegState::GetRepeats(
DWORD *pdwRepeats) // @parm Returns the repeat count.
{
V_INAME(IDirectMusicSegmentState::GetRepeats);
V_PTR_WRITE(pdwRepeats,DWORD);
*pdwRepeats = m_dwRepeats;
return S_OK;
}
///////////////////////////////////////////////////////////////////////
// IDirectMusicSegmentState::GetSegment
/*
@method HRESULT | IDirectMusicSegmentState | GetSegment |
Returns a pointer to the Segment that owns this SegmentState.
@rvalue E_POINTER | if ppSegment is NULL or invalid.
@rvalue S_OK | Success.
*/
HRESULT STDMETHODCALLTYPE CSegState::GetSegment(
IDirectMusicSegment **ppSegment) // @parm The Segment interface pointer to this
// SegmentState. Call Release() on this pointer when
// through.
{
V_INAME(IDirectMusicSegmentState::GetSegment);
V_PTRPTR_WRITE(ppSegment);
*ppSegment = (IDirectMusicSegment *) m_pSegment;
if( m_pSegment )
{
m_pSegment->AddRef();
}
else
{
Trace(1,"Error: Segmentstate doesn't have an associated segment.\n");
return DMUS_E_NOT_FOUND;
}
return S_OK;
}
///////////////////////////////////////////////////////////////////////
// IDirectMusicSegmentState::Play
/*
method (INTERNAL) HRESULT | IDirectMusicSegmentState | Play |
<om IDirectMusicSegmentState.Play> is called regularly by the Performance object,
usually every 200 ms or so, at a time ahead of playback that is set by
<om IDirectMusicPerformance.SetPerformTime>
.
parm MUSIC_TIME | mtAmount |
[in] The length of time to play, starting at the current Seek time.
The SegmentState updates its Seek time to be the current Seek time
plus mtAmount. Therefore, the SegmentState should play from the current
Seek time to Seek time plus mtAmount, not including the last clock.
comm
Play calls each Track's Play method in priority order, instructing the Track to
create events from the current Seek time up to, but not including the current Seek
time plus <p mtAmount.>
Since the Segment started at the point designated by m_mtOffset (set by
<im IDirectMusicSegmentState.SetOffset>
m_mtOffset sets the starting offset to add to the times of all events.
rvalue E_INVALIDARG | mtAmount <= 0
rvalue S_OK | Success.
*/
HRESULT STDMETHODCALLTYPE CSegState::Play(
/* [in] */ MUSIC_TIME mtAmount, MUSIC_TIME* pmtPlayed )
{
return E_FAIL; // We don't want to support this publicly!
}
HRESULT CSegState::Play( MUSIC_TIME mtAmount )
{
CTrack* pCTrack;
MUSIC_TIME mtMyAmount = mtAmount;
REFERENCE_TIME rtMyAmount;
HRESULT hr = DMUS_S_END;
BOOL fUseClockTime = FALSE;
if( mtAmount <= 0 )
return E_INVALIDARG;
EnterCriticalSection(&m_CriticalSection);
if (m_fInPlay)
{
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
m_fInPlay = true;
m_pPerformance->m_pGetParamSegmentState = (IDirectMusicSegmentState *) this;
// if this is the first call to play, we need to send a SegStart notification.
// We also need to check to see if we are supposed to start at the beginning,
// or at an offset.
if( m_dwPlayTrackFlags & DMUS_TRACKF_START )
{
// send a segment start notification
GenerateNotification( DMUS_NOTIFICATION_SEGSTART, m_mtOffset );
// if this is a primary or controlling segment, send a DMUS_PMSGT_DIRTY message
if( !(m_dwPlaySegFlags & DMUS_SEGF_SECONDARY) || (m_dwPlaySegFlags & DMUS_SEGF_CONTROL) )
{
TraceI(4, "Send Dirty PMsg [1] %d (%d)\n", m_mtSeek, m_mtOffset + m_mtSeek);
SendDirtyPMsg( m_mtOffset + m_mtSeek );
}
// set the current seek to the start point
m_mtSeek = m_mtStartPoint;
// convert current offset to ref time
m_pPerformance->MusicToReferenceTime(m_mtOffset,&m_rtOffset);
m_rtEndTime = m_rtOffset + m_rtLength;
// subtract the start points from the offsets
m_mtOffset -= m_mtStartPoint;
m_rtOffset -= m_rtStartPoint;
m_rtEndTime -= m_rtStartPoint;
m_rtSeek = m_rtLastPlayed - m_rtOffset;
m_rtFirstLoopStart = 0;
}
if (m_rtLength)
{
// If there is a reference time length, convert it into mtTime.
// Because there's always the danger of a tempo change, we do this every
// time. It doesn't require the tight precision that song time
// requires, so that's okay.
// ALSO: convert m_mtLength and re-adjust loop points. (RSW)
m_pPerformance->ReferenceToMusicTime(m_rtEndTime, &m_mtEndTime);
MUSIC_TIME mtOldLength = m_mtLength;
m_mtLength = m_mtEndTime - m_mtOffset;
if (m_mtLoopEnd >= mtOldLength) // keep loop end equal to length
{
m_mtLoopEnd = m_mtLength;
}
if( m_mtLoopEnd > m_mtLength )
{
m_mtLoopEnd = m_mtLength;
if( m_mtStartPoint >= m_mtLoopEnd )
{
// in this case, we're not doing any looping.
m_mtLoopEnd = m_mtLoopStart = 0;
m_dwRepeats = m_dwRepeatsLeft = 0;
}
}
//m_mtEndTime += (m_mtLoopEnd - m_mtLoopStart) * m_dwRepeats;
fUseClockTime = TRUE;
}
// if we need to do a loop or the end is near, restrict mtMyAmount
// ASSERT( m_mtLength ); // length is 0, this segment won't do anything
if( m_dwRepeatsLeft )
{
if( mtMyAmount > m_mtLoopEnd - m_mtSeek )
{
mtMyAmount = m_mtLoopEnd - m_mtSeek;
}
}
else
{
if (fUseClockTime)
{
if (mtMyAmount > (m_mtEndTime - (m_mtOffset + m_mtSeek)))
{
mtMyAmount = m_mtEndTime - (m_mtOffset + m_mtSeek);
}
}
else if( mtMyAmount > m_mtLength - m_mtSeek )
{
mtMyAmount = m_mtLength - m_mtSeek;
}
}
if (mtMyAmount <= 0)
{
hr = DMUS_S_END;
}
else
{
// check the primary segment queue for a segment that might begin
// before mtMyAmount is up
MUSIC_TIME mtNextPri;
if (S_OK == m_pPerformance->GetPriSegTime( m_mtOffset + m_mtSeek, &mtNextPri ))
{
if( m_mtOffset + m_mtSeek + mtMyAmount > mtNextPri )
{
mtMyAmount = mtNextPri - m_mtOffset - m_mtSeek;
}
}
TraceI(3, "SegState %ld Play from %ld to %ld at %ld = %ld - %ld\n", this, m_mtSeek, m_mtSeek + mtMyAmount, m_mtOffset, m_mtSeek + m_mtOffset, m_mtSeek + mtMyAmount + m_mtOffset );
// find out if there's a control segment interrupting this period of time.
MUSIC_TIME mtControlSeg;
if( S_OK == m_pPerformance->GetControlSegTime( m_mtOffset + m_mtSeek, &mtControlSeg ))
{
if( m_mtOffset + m_mtSeek == mtControlSeg )
{
// we're at the beginning of a new control seg, so tell the tracks
m_dwPlayTrackFlags |= DMUS_TRACKF_DIRTY;
}
else if( m_mtOffset + m_mtSeek + mtMyAmount > mtControlSeg )
{
mtMyAmount = mtControlSeg - m_mtOffset - m_mtSeek;
}
}
// Now that mtMyAmount is calculated for how far to play in music time,
// create the equivalent value in reference time.
m_pPerformance->MusicToReferenceTime(m_mtLastPlayed + mtMyAmount,&rtMyAmount);
rtMyAmount -= m_rtLastPlayed;
pCTrack = m_TrackList.GetHead();
while( pCTrack )
{
if( mtMyAmount )
{
m_pPerformance->m_fInTrackPlay = TRUE; // This causes the Pmsgs to be stamped with PRIV_FLAG_TRACK.
ASSERT( pCTrack->m_pTrack );
// If either notification or play are enabled, we need to call the play method and set the behavior
// with the DMUS_TRACKF_NOTIFY_OFF and DMUS_TRACKF_PLAY_OFF flags.
if (pCTrack->m_dwFlags & (DMUS_TRACKCONFIG_PLAY_ENABLED | DMUS_TRACKCONFIG_NOTIFICATION_ENABLED))
{
DWORD dwAdditionalFlags = 0;
if (!(pCTrack->m_dwFlags & DMUS_TRACKCONFIG_NOTIFICATION_ENABLED))
{
dwAdditionalFlags = DMUS_TRACKF_NOTIFY_OFF;
}
if (!(pCTrack->m_dwFlags & DMUS_TRACKCONFIG_PLAY_ENABLED))
{
dwAdditionalFlags |= DMUS_TRACKF_PLAY_OFF;
}
// If the track was authored to generate new data on start or loop, let it know.
if ( ((m_dwPlayTrackFlags & DMUS_TRACKF_START) && (pCTrack->m_dwFlags & DMUS_TRACKCONFIG_PLAY_COMPOSE)) ||
((m_dwPlayTrackFlags & DMUS_TRACKF_LOOP) && (pCTrack->m_dwFlags & DMUS_TRACKCONFIG_LOOP_COMPOSE)) )
{
dwAdditionalFlags |= DMUS_TRACKF_RECOMPOSE;
}
if (pCTrack->m_dwInternalFlags & CONTROL_PLAY_REFRESH)
{
dwAdditionalFlags |= DMUS_TRACKF_START;
pCTrack->m_dwInternalFlags &= ~CONTROL_PLAY_REFRESH;
}
// Let performance know what the priority should be in ensuing GetParam() calls from the track.
m_pPerformance->m_dwGetParamFlags = pCTrack->m_dwFlags;
// If track has DX8 interface, use it.
if (pCTrack->m_pTrack8)
{
// The track can call GetParam on the segment which locks the segment so
// we have to lock the segment before calling PlayEx or we'll deadlock
// with a thread that's calling PlayOneSegment which locks the segment
// before playing the tracks.
if (m_pSegment) {
EnterCriticalSection(&m_pSegment->m_CriticalSection);
}
// If track plays in clock time, set time variables appropriately.
if (pCTrack->m_dwFlags & DMUS_TRACKCONFIG_PLAY_CLOCKTIME)
{
if( ( S_OK == (pCTrack->m_pTrack8->PlayEx(pCTrack->m_pTrackState,
m_rtSeek,m_rtSeek + rtMyAmount, m_rtOffset, m_dwPlayTrackFlags | dwAdditionalFlags | DMUS_TRACKF_CLOCK,
m_pPerformance, this, pCTrack->m_dwVirtualID ))))
{
hr = S_OK; // if even one track isn't done playing,
// keep going
}
else
{
pCTrack->m_bDone = TRUE;
}
}
else
{
if( ( S_OK == (pCTrack->m_pTrack8->PlayEx(pCTrack->m_pTrackState,
m_mtSeek,m_mtSeek + mtMyAmount, m_mtOffset, m_dwPlayTrackFlags | dwAdditionalFlags,
m_pPerformance, this, pCTrack->m_dwVirtualID ))))
{
hr = S_OK; // if even one track isn't done playing,
// keep going
}
else
{
pCTrack->m_bDone = TRUE;
}
}
if (m_pSegment) {
LeaveCriticalSection(&m_pSegment->m_CriticalSection);
}
}
else
{
if( ( S_OK == ( pCTrack->m_pTrack->Play( pCTrack->m_pTrackState,
m_mtSeek, m_mtSeek + mtMyAmount, m_mtOffset, m_dwPlayTrackFlags | dwAdditionalFlags,
m_pPerformance, this, pCTrack->m_dwVirtualID ))))
{
hr = S_OK; // if even one track isn't done playing,
// keep going
}
else
{
pCTrack->m_bDone = TRUE;
}
}
}
m_pPerformance->m_fInTrackPlay = FALSE;
}
pCTrack = pCTrack->GetNext();
if( pCTrack == NULL )
{
// none of the play flags are persistent
m_dwPlayTrackFlags = 0;
m_mtLastPlayed += mtMyAmount; // increment play pointer
m_rtLastPlayed += rtMyAmount; // same in ref time
m_mtSeek += mtMyAmount; // increment seek pointer
m_rtSeek += rtMyAmount;
hr = S_OK;
// If we're looping....
// And if this is the first repeat
if(m_dwRepeats > 0 && m_dwRepeats == m_dwRepeatsLeft)
{
// If we're playing the loop start remember it's reftime value
if(m_mtSeek >= m_mtLoopStart && m_rtFirstLoopStart == 0)
{
m_pPerformance->MusicToReferenceTime(m_mtLoopStart + m_mtOffset + m_mtStartPoint, &m_rtFirstLoopStart);
m_rtFirstLoopStart -= m_rtStartPoint;
m_rtCurLoopStart = m_rtFirstLoopStart;
}
}
// take into account repeats if necessary
if( m_mtSeek >= m_mtLoopEnd )
{
// Remember the current loop end
m_pPerformance->MusicToReferenceTime(m_mtLoopEnd + m_mtOffset + m_mtStartPoint, &m_rtCurLoopEnd);
m_rtCurLoopEnd -= m_rtStartPoint;
if(m_dwRepeatsLeft)
{
m_dwPlayTrackFlags |= DMUS_TRACKF_LOOP | DMUS_TRACKF_SEEK;
m_dwRepeatsLeft--;
pCTrack = m_TrackList.GetHead();
while( pCTrack )
{
pCTrack->m_bDone = FALSE;
pCTrack = pCTrack->GetNext();
}
m_mtSeek = m_mtLoopStart;
m_mtOffset += ( m_mtLoopEnd - m_mtLoopStart);
m_rtOffset += (m_rtCurLoopEnd - m_rtCurLoopStart);
m_rtFirstLoopStart += (m_rtCurLoopEnd - m_rtCurLoopStart);
m_rtSeek = m_rtFirstLoopStart - m_rtOffset;
m_rtEndTime += (m_rtCurLoopEnd - m_rtCurLoopStart);
m_rtCurLoopStart = m_rtCurLoopEnd;
if( mtMyAmount < mtAmount )
{
pCTrack = m_TrackList.GetHead(); // cause outer while loop to start over
mtMyAmount = mtAmount - mtMyAmount;
mtAmount = mtMyAmount;
// if we need to do a loop or the end is near, restrict mtMyAmount
if( m_dwRepeatsLeft )
{
if( mtMyAmount > m_mtLoopEnd - m_mtSeek )
{
mtMyAmount = m_mtLoopEnd - m_mtSeek;
}
}
else
{
if (fUseClockTime)
{
if (mtMyAmount > (m_mtEndTime - (m_mtOffset + m_mtSeek)))
{
mtMyAmount = m_mtEndTime - (m_mtOffset + m_mtSeek);
}
}
else if( mtMyAmount > m_mtLength - m_mtSeek )
{
mtMyAmount = m_mtLength - m_mtSeek;
}
}
}
// send a segment looped notification
GenerateNotification( DMUS_NOTIFICATION_SEGLOOP, m_mtOffset + m_mtSeek );
// find out if there's a control segment interrupting this period of time
if( S_OK == m_pPerformance->GetControlSegTime( m_mtOffset + m_mtSeek, &mtControlSeg ))
{
if( m_mtOffset + m_mtSeek == mtControlSeg )
{
// we're at the beginning of a new control seg, so tell the tracks
m_dwPlayTrackFlags |= DMUS_TRACKF_DIRTY;
}
else if( m_mtOffset + m_mtSeek + mtMyAmount < mtControlSeg )
{
mtMyAmount = mtControlSeg - m_mtOffset - m_mtSeek;
}
}
m_pPerformance->MusicToReferenceTime(m_mtLastPlayed + mtMyAmount,&rtMyAmount);
rtMyAmount -= m_rtLastPlayed;
}
else if( m_mtSeek == m_mtLength )
{
// no more repeats.
hr = DMUS_S_END;
}
}
}
}
}
if (hr == DMUS_S_END)
{
// send a segment end notification
GenerateNotification( DMUS_NOTIFICATION_SEGEND, m_mtOffset + m_mtSeek );
// also queue the almost ended for now
MUSIC_TIME mtNow;
m_pPerformance->GetTime( NULL, &mtNow );
GenerateNotification( DMUS_NOTIFICATION_SEGALMOSTEND, mtNow );
// if this is a primary or controlling segment, send a DMUS_PMSGT_DIRTY message
if( !(m_dwPlaySegFlags & DMUS_SEGF_SECONDARY) || (m_dwPlaySegFlags & DMUS_SEGF_CONTROL) )
{
TraceI(4, "Send Dirty PMsg [2] %d (%d)\n", m_mtSeek, m_mtOffset + m_mtSeek);
SendDirtyPMsg( m_mtOffset + m_mtSeek );
}
// If this is part of a song, we need to queue the next segment.
if (m_fSongMode)
{
if (m_pSegment)
{
CSong *pSong = m_pSegment->m_pSong;
if (pSong)
{
// Get the next segment from the song.
CSegment *pSegment;
if (S_OK == pSong->GetPlaySegment(m_pSegment->m_dwNextPlayID,&pSegment))
{
// Now, play it.
// Unless DMUS_SEGF_USE_AUDIOPATH is set, play it on the same audiopath.
// And, make sure that it plays at the same level (control, secondary, or primary.)
CSegState *pCSegState = NULL;
CAudioPath *pPath = m_pAudioPath;
CAudioPath *pInternalPath = NULL;
DWORD dwFlags = m_dwPlaySegFlags & (DMUS_SEGF_CONTROL | DMUS_SEGF_SECONDARY);
dwFlags &= ~DMUS_SEGF_REFTIME;
if (dwFlags & DMUS_SEGF_USE_AUDIOPATH)
{
IUnknown *pConfig;
if (SUCCEEDED(pSegment->GetAudioPathConfig(&pConfig)))
{
IDirectMusicAudioPath *pNewPath;
if (SUCCEEDED(m_pPerformance->CreateAudioPath(pConfig,TRUE,&pNewPath)))
{
// Now, get the CAudioPath structure.
pConfig->QueryInterface(IID_CAudioPath,(void **) &pInternalPath);
pPath = pInternalPath;
}
pConfig->Release();
}
}
if (SUCCEEDED(m_pPerformance->PlayOneSegment((CSegment *)pSegment,dwFlags,m_mtEndTime,&pCSegState,pPath)))
{
if (m_pSongSegState)
{
// This is not the first, so transfer the segstate pointer.
pCSegState->m_pSongSegState = m_pSongSegState;
m_pSongSegState = NULL;
}
else
{
// This is the first, so have the next segstate point to this.
pCSegState->m_pSongSegState = this;
AddRef();
}
pCSegState->m_fSongMode = TRUE;
pCSegState->Release();
}
if (pInternalPath)
{
pInternalPath->Release();
}
pSegment->Release();
}
}
}
}
}
m_dwPlayTrackFlags &= ~DMUS_TRACKF_DIRTY;
m_pPerformance->m_dwGetParamFlags = 0;
m_pPerformance->m_pGetParamSegmentState = NULL;
m_fInPlay = false;
if (m_fDelayShutDown)
{
Shutdown();
m_fDelayShutDown = false;
}
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
CTrack * CSegState::GetTrackByParam( CTrack * pCTrack,
REFGUID rguidType,DWORD dwGroupBits,DWORD dwIndex)
{
// 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 ) &&
(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;
}
/* GetParam() is called by the performance in response to a GetParam() call
on the performance. This needs the performance pointer so it can handle
clock time to music time conversion and back, in case the source track is a
clock time track.
*/
HRESULT CSegState::GetParam(
CPerformance *pPerf,
REFGUID rguidType,
DWORD dwGroupBits,
DWORD dwIndex,
MUSIC_TIME mtTime,
MUSIC_TIME* pmtNext,
void* pParam)
{
HRESULT hr = DMUS_E_TRACK_NOT_FOUND;
BOOL fMultipleTry = FALSE;
if (dwIndex == DMUS_SEG_ANYTRACK)
{
dwIndex = 0;
// Even though DX7 didn't support this, this is always safe because an index this high could never happen.
fMultipleTry = TRUE;
}
CTrack * pTrack = GetTrackByParam( NULL, rguidType, dwGroupBits, dwIndex);
while (pTrack)
{
if (pTrack->m_pTrack8)
{
if (pTrack->m_dwFlags & DMUS_TRACKCONFIG_PLAY_CLOCKTIME)
{
REFERENCE_TIME rtTime, rtNext;
// Convert mtTime into reference time units:
pPerf->MusicToReferenceTime(m_mtOffset + mtTime,&rtTime);
rtTime -= m_rtOffset;
hr = pTrack->m_pTrack8->GetParamEx( rguidType, rtTime, &rtNext,
pParam, pTrack->m_pTrackState, DMUS_TRACK_PARAMF_CLOCK );
if (pmtNext)
{
if (rtNext == 0) *pmtNext = 0;
else
{
rtNext += m_rtOffset;
pPerf->ReferenceToMusicTime(rtNext,pmtNext);
*pmtNext -= m_mtOffset;
}
}
}
else
{
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 = pTrack->m_pTrack8->GetParamEx( rguidType, mtTime, prtNext, pParam,
pTrack->m_pTrackState, 0 );
if (pmtNext)
{
*pmtNext = (MUSIC_TIME) rtNext;
}
}
}
else
{
// This is a pre DX8 track...
hr = pTrack->m_pTrack->GetParam( rguidType, mtTime, pmtNext, pParam );
}
if (SUCCEEDED(hr))
{
if( pmtNext )
{
if(( *pmtNext == 0 ) || (*pmtNext > (m_mtLength - mtTime)))
{
// If no next was found OR it's greater than the end of the segment, set
// it to the end of the segment.
*pmtNext = m_mtLength - mtTime;
}
}
pTrack = NULL;
}
// If nothing was found and dwIndex was DMUS_SEG_ANYTRACK, try again...
else if (fMultipleTry && (hr == DMUS_E_NOT_FOUND))
{
pTrack = GetTrackByParam( pTrack, rguidType, dwGroupBits, 0);
}
else
{
pTrack = NULL;
}
}
#ifdef DBG
if (hr == DMUS_E_TRACK_NOT_FOUND)
{
Trace(4,"Warning: Segmentstate::GetParam failed, unable to find a track that supports the requested param.\n");
}
#endif
return hr;
}
CTrack *CSegState::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_All_Objects == rType) || (pCTrack->m_guidClassID == rType))
{
if( 0 == dwIndex )
{
break;
}
dwIndex--;
}
}
pCTrack = pCTrack->GetNext();
}
return pCTrack;
}
STDMETHODIMP CSegState::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;
}
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,dwIndex))
{
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: Segmentstate::SetTrackConfig failed, unable to find the requested track.\n");
}
#endif
return hr;
}
HRESULT CSegState::CheckPlay(
MUSIC_TIME mtAmount, MUSIC_TIME* pmtResult )
{
MUSIC_TIME mtMyAmount = mtAmount;
MUSIC_TIME mtSeek = m_mtSeek;
MUSIC_TIME mtOffset = m_mtOffset;
ASSERT(pmtResult);
// if this is the first call to play,
// We also need to check to see if we are supposed to start at the beginning,
// or at an offset.
if( m_dwPlayTrackFlags & DMUS_TRACKF_START )
{
// set the current seek to the start point
mtSeek = m_mtStartPoint;
}
// if we need to do a loop or the end is near, restrict mtMyAmount
ASSERT( m_mtLength ); // length is 0, this segment won't do anything
if( m_dwRepeatsLeft )
{
if( mtMyAmount > m_mtLoopEnd - mtSeek )
{
mtMyAmount = m_mtLoopEnd - mtSeek;
}
}
else if( mtMyAmount > m_mtLength - mtSeek )
{
mtMyAmount = m_mtLength - mtSeek;
}
// take into account repeats if necessary
*pmtResult = mtMyAmount;
return S_OK;
}
///////////////////////////////////////////////////////////////////////
// IDirectMusicSegmentState::GetStartTime
/*
@method HRESULT | IDirectMusicSegmentState | GetStartTime |
Gets the music time this SegmentState started playing.
@rvalue E_POINTER | <p pmtStart> is NULL or invalid.
@rvalue S_OK | Success.
@xref <om IDirectMusicPerformance.PlaySegment>
*/
HRESULT STDMETHODCALLTYPE CSegState::GetStartTime(
MUSIC_TIME *pmtStart) // @parm Returns the music time of the start of this SegmentState.
// This is the music time, in Performance time, that the SegmentState
// started or will start playing.
{
V_INAME(IDirectMusicSegmentState::GetStartTime);
V_PTR_WRITE(pmtStart,MUSIC_TIME);
*pmtStart = m_mtResolvedStart;
return S_OK;
}
///////////////////////////////////////////////////////////////////////
// IDirectMusicSegmentState::GetStartPoint
/*
@method HRESULT | IDirectMusicSegmentState | GetStartPoint |
Calling <om IDirectMusicSegment.SetStartPoint> causes the SegmentState to begin
playing from the middle instead of from the beginning. <om .GetStartPoint>
returns the amount of time from the beginning of the SegmentState that it
plays.
@rvalue E_POINTER | <p pmtStart> is NULL or invalid.
@rvalue S_OK | Success.
@xref <om IDirectMusicSegment.SetStartPoint>,
<om IDirectMusicPerformance.PlaySegment>
*/
HRESULT STDMETHODCALLTYPE CSegState::GetStartPoint(
MUSIC_TIME *pmtStart) // @parm Returns the music time offset from the start of the
// SegmentState at which the SegmentState initially plays.
{
V_INAME(IDirectMusicSegmentState::GetStartPoint);
V_PTR_WRITE(pmtStart,MUSIC_TIME);
*pmtStart = m_mtStartPoint;
return S_OK;
}
///////////////////////////////////////////////////////////////////////
// IDirectMusicSegmentState::SetSeek
/*
method (INTERNAL) HRESULT | IDirectMusicSegmentState | SetSeek |
Sets the music time Seek maintained by this SegmentState.
parm MUSIC_TIME | mtSeek |
[in] The music time Seek to store in this SegmentState.
comm The SegmentState passes this Seek value to <im IDirectMusicTrack.Play>
Note that newly created SegmentState's start with a Seek time of 0.
rvalue S_OK | Success.
*/
HRESULT CSegState::SetSeek(
MUSIC_TIME mtSeek, DWORD dwPlayFlags)
{
m_mtSeek = mtSeek;
m_dwPlayTrackFlags |= dwPlayFlags | DMUS_TRACKF_SEEK;
return S_OK;
}
/*
Called from IDirectMusicPerformance::Invalidate, this routine helps set
the current seek pointer. Done here instead of directly inside Performance
because it's easier to compute the repeats, etc. here.
*/
HRESULT CSegState::SetInvalidate(
MUSIC_TIME mtTime) // mtTime is in Performance time
{
MUSIC_TIME mtOffset;
DWORD dwRepeat;
DWORD dwFlags = DMUS_TRACKF_FLUSH | DMUS_TRACKF_SEEK;
HRESULT hr = ConvertToSegTime( &mtTime, &mtOffset, &dwRepeat );
if( hr != S_OK )
{
mtTime = 0;
m_dwRepeatsLeft = m_dwRepeats;
m_mtOffset = m_mtResolvedStart;
dwFlags |= DMUS_TRACKF_START;
}
else
{
m_dwRepeatsLeft = m_dwRepeats - dwRepeat;
m_mtOffset = mtOffset;
}
EnterCriticalSection(&m_CriticalSection);
CTrack* pCTrack = m_TrackList.GetHead();
while( pCTrack )
{
pCTrack->m_bDone = FALSE;
pCTrack = pCTrack->GetNext();
}
LeaveCriticalSection(&m_CriticalSection);
return SetSeek( mtTime, dwFlags );
}
///////////////////////////////////////////////////////////////////////
// IDirectMusicSegmentState::GetSeek
HRESULT STDMETHODCALLTYPE CSegState::GetSeek(
MUSIC_TIME *pmtSeek) // @parm Returns the current seek pointer, which indicates
// the next time that will be called inside <om IDirectMusicTrack.Play>.
{
V_INAME(IDirectMusicSegmentState::GetSeek);
V_PTR_WRITE(pmtSeek, MUSIC_TIME);
*pmtSeek = m_mtSeek;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CSegState::Flush(MUSIC_TIME mtTime) // The time on and after which to flush.
{
CTrack* pTrack;
EnterCriticalSection(&m_CriticalSection);
pTrack = m_TrackList.GetHead();
while( pTrack )
{
m_pPerformance->FlushVirtualTrack( pTrack->m_dwVirtualID, mtTime, FALSE );
pTrack = pTrack->GetNext();
}
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CSegState::Shutdown()
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CSegState::InsertTool(
IDirectMusicTool *pTool,
DWORD *pdwPChannels,
DWORD cPChannels,
LONG lIndex)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CSegState::GetTool(
DWORD dwIndex,
IDirectMusicTool** ppTool)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CSegState::RemoveTool(
IDirectMusicTool* pTool)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CSegState::StampPMsg(
/* [in */ DMUS_PMSG* pPMsg)
{
V_INAME(IDirectMusicSegmentState::StampPMsg);
if( m_dwVersion < 8)
{
V_BUFPTR_WRITE(pPMsg,sizeof(DMUS_PMSG));
}
else
{
#ifdef DBG
V_BUFPTR_WRITE(pPMsg,sizeof(DMUS_PMSG));
#else
if (!pPMsg)
{
return E_POINTER;
}
#endif
}
HRESULT hr = E_FAIL;
EnterCriticalSection(&m_CriticalSection);
if (m_pPerformance)
{
// First, check if the segmentstate has its own graph.
if (m_pGraph)
{
// Could return DMUS_S_LAST_TOOL, indicating end of graph.
// If so, we'll treat that as a failure and drop on through to the next graph...
if( S_OK == ( hr = m_pGraph->StampPMsg( pPMsg )))
{
if( pPMsg->pGraph != this ) // Make sure this is set to point to the segstate embedded graph so it will come here again.
{
if( pPMsg->pGraph )
{
pPMsg->pGraph->Release();
pPMsg->pGraph = NULL;
}
pPMsg->pGraph = this;
AddRef();
}
}
}
// If done with the graph, send to the audio path, if it exists,
// else the performance. Also, check for the special case of
// DMUS_PCHANNEL_BROADCAST_SEGMENT. If so, duplicate the pMsg
// and send all the copies with the appropriate pchannel values.
if( FAILED(hr) || (m_dwVersion && (hr == DMUS_S_LAST_TOOL)))
{
if (pPMsg->dwPChannel == DMUS_PCHANNEL_BROADCAST_SEGMENT)
{
CSegment *pSegment = m_pSegment;
EnterCriticalSection(&pSegment->m_CriticalSection);
DWORD dwIndex;
// Create new messages with new pchannels for all but one, which will
// be assigned to this message.
for (dwIndex = 1;dwIndex < pSegment->m_dwNumPChannels;dwIndex++)
{
DWORD dwNewChannel = pSegment->m_paPChannels[dwIndex];
// Don't broadcast any broadcast messages!
// And, if this is a transpose on the drum channel, don't send it.
if ((dwNewChannel < DMUS_PCHANNEL_BROADCAST_GROUPS) &&
((pPMsg->dwType != DMUS_PMSGT_TRANSPOSE) || ((dwNewChannel & 0xF) != 9)))
{
DMUS_PMSG *pNewMsg;
if (SUCCEEDED(m_pPerformance->ClonePMsg(pPMsg,&pNewMsg)))
{
HRESULT hrTemp;
pNewMsg->dwPChannel = dwNewChannel;
if (m_pAudioPath)
{
hrTemp = m_pAudioPath->StampPMsg(pNewMsg);
}
else
{
hrTemp = m_pPerformance->StampPMsg(pNewMsg);
}
if (SUCCEEDED(hrTemp))
{
m_pPerformance->SendPMsg(pNewMsg);
}
else
{
m_pPerformance->FreePMsg(pNewMsg);
}
}
}
}
// Now, set the pchannel for this one. First check that there are any
// pchannels. If none, mark the PMsg to be deleted by the SendPMsg routine.
// Also, mark it this way if the PMsg is a broadcast PMsg.
pPMsg->dwPChannel = DMUS_PCHANNEL_KILL_ME;
if (pSegment->m_dwNumPChannels)
{
if (pSegment->m_paPChannels[0] < DMUS_PCHANNEL_BROADCAST_GROUPS)
{
pPMsg->dwPChannel = pSegment->m_paPChannels[0];
}
}
LeaveCriticalSection(&pSegment->m_CriticalSection);
}
if (m_pAudioPath)
{
hr = m_pAudioPath->StampPMsg(pPMsg);
}
else
{
hr = m_pPerformance->StampPMsg(pPMsg);
}
}
}
else
{
hr = DMUS_E_NOT_INIT;
Trace(1,"Error: Segmentstate::StampPMsg failed because the segmentstate is not properly initialized.\n");
}
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
STDMETHODIMP CSegState::GetObjectInPath( DWORD dwPChannel,DWORD dwStage,DWORD dwBuffer, REFGUID guidObject,
DWORD dwIndex,REFGUID iidInterface, void ** ppObject)
{
V_INAME(IDirectMusicSegmentState::GetObjectInPath);
V_PTRPTR_WRITE(ppObject);
*ppObject = NULL;
if (dwBuffer && ((dwStage < DMUS_PATH_BUFFER) || (dwStage >= DMUS_PATH_PRIMARY_BUFFER)))
{
return DMUS_E_NOT_FOUND;
}
HRESULT hr = DMUS_E_NOT_FOUND;
EnterCriticalSection(&m_CriticalSection);
switch (dwStage)
{
case DMUS_PATH_SEGMENT:
if (m_pSegment && (dwIndex == 0) && (dwPChannel == 0))
{
hr = m_pSegment->QueryInterface(iidInterface,ppObject);
}
break;
case DMUS_PATH_SEGMENT_TRACK:
if (dwPChannel == 0)
{
CTrack * pCTrack = GetTrack(guidObject,-1,dwIndex);
if (pCTrack)
{
if (pCTrack->m_pTrack)
{
hr = pCTrack->m_pTrack->QueryInterface(iidInterface,ppObject);
}
}
}
break;
case DMUS_PATH_SEGMENT_GRAPH:
if ((dwIndex == 0) && (dwPChannel == 0))
{
if (!m_pGraph)
{
m_pGraph = new CGraph;
}
if (m_pGraph)
{
hr = m_pGraph->QueryInterface(iidInterface,ppObject);
}
else
{
hr = E_OUTOFMEMORY;
}
}
break;
case 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;
}
break;
case DMUS_PATH_PERFORMANCE:
if (m_pPerformance && (dwIndex == 0) && (dwPChannel == 0))
{
hr = m_pPerformance->QueryInterface(iidInterface,ppObject);
}
break;
case DMUS_PATH_PERFORMANCE_GRAPH:
if (m_pPerformance && (dwIndex == 0) && (dwPChannel == 0))
{
IDirectMusicGraph *pGraph;
if (SUCCEEDED(hr = m_pPerformance->GetGraphInternal(&pGraph)))
{
hr = pGraph->QueryInterface(iidInterface,ppObject);
pGraph->Release();
}
}
break;
case DMUS_PATH_PERFORMANCE_TOOL:
if (m_pPerformance)
{
IDirectMusicGraph *pGraph;
if (SUCCEEDED(hr = m_pPerformance->GetGraphInternal(&pGraph)))
{
CGraph *pCGraph = (CGraph *) pGraph;
hr = pCGraph->GetObjectInPath(dwPChannel,guidObject,dwIndex,iidInterface,ppObject);
pGraph->Release();
}
}
break;
default:
if (m_pAudioPath)
{
hr = m_pAudioPath->GetObjectInPath(dwPChannel,dwStage,dwBuffer,guidObject,dwIndex,iidInterface,ppObject);
}
else
{
Trace(1,"Error: Unable to access audiopath components of segmentstate.\n");
}
}
LeaveCriticalSection(&m_CriticalSection);
return hr;
}