252 lines
6.3 KiB
C++
252 lines
6.3 KiB
C++
// Copyright (c) 1999 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Base classes that implement aspects of a standard DirectMusic track.
|
|
// Implementations for CPlayingTrack.
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Creation
|
|
|
|
template<class T, class EventItem, class StateData>
|
|
CPlayingTrack<T, EventItem, StateData>::CPlayingTrack(
|
|
long *plModuleLockCounter,
|
|
const CLSID &rclsid,
|
|
bool fNeedsLoader,
|
|
bool fPlayInvalidations)
|
|
: m_dwValidate(0),
|
|
m_fNeedsLoader(fNeedsLoader),
|
|
m_fPlayInvalidations(fPlayInvalidations),
|
|
CBasicTrack(plModuleLockCounter, rclsid)
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// IPersistStream
|
|
|
|
// Function used to sort the event list according to trigger time.
|
|
template<class EventItem>
|
|
struct CmpStruct // §§ shouldn't need this, but I had trouble getting a straight templated function to match the function pointer with the NT compiler. try again later with the new one.
|
|
{
|
|
static BOOL EventCompare(EventItem &ri1, EventItem &ri2)
|
|
{
|
|
return ri1.lTriggerTime < ri2.lTriggerTime;
|
|
}
|
|
};
|
|
|
|
template<class T, class EventItem, class StateData>
|
|
STDMETHODIMP
|
|
CPlayingTrack<T, EventItem, StateData>::Load(IStream* pIStream)
|
|
{
|
|
V_INAME(CPlayingTrack::Load);
|
|
V_INTERFACE(pIStream);
|
|
HRESULT hr = S_OK;
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
// Clear the event list in case we're being reloaded.
|
|
m_EventList.CleanUp();
|
|
// Increment counter so the next play will update state data with the new list.
|
|
++m_dwValidate;
|
|
|
|
// Get the loader if requested in constructor
|
|
SmartRef::ComPtr<IDirectMusicLoader> scomLoader;
|
|
if (m_fNeedsLoader)
|
|
{
|
|
IDirectMusicGetLoader *pIGetLoader;
|
|
hr = pIStream->QueryInterface(IID_IDirectMusicGetLoader, reinterpret_cast<void**>(&pIGetLoader));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
hr = pIGetLoader->GetLoader(&scomLoader);
|
|
pIGetLoader->Release();
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
SmartRef::RiffIter ri(pIStream);
|
|
if (!ri)
|
|
return ri.hr();
|
|
|
|
hr = this->LoadRiff(ri, scomLoader);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
m_EventList.MergeSort(CmpStruct<EventItem>::EventCompare);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// IDirectMusicTrack
|
|
|
|
template<class T, class EventItem, class StateData>
|
|
STDMETHODIMP
|
|
CPlayingTrack<T, EventItem, StateData>::InitPlay(
|
|
IDirectMusicSegmentState *pSegmentState,
|
|
IDirectMusicPerformance *pPerformance,
|
|
void **ppStateData,
|
|
DWORD dwTrackID,
|
|
DWORD dwFlags)
|
|
{
|
|
V_INAME(CPlayingTrack::InitPlay);
|
|
V_PTRPTR_WRITE(ppStateData);
|
|
V_INTERFACE(pSegmentState);
|
|
V_INTERFACE(pPerformance);
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
// Set up state data
|
|
StateData *pStateData = new StateData;
|
|
if (!pStateData)
|
|
return E_OUTOFMEMORY;
|
|
|
|
*ppStateData = pStateData;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
template<class T, class EventItem, class StateData>
|
|
STDMETHODIMP
|
|
CPlayingTrack<T, EventItem, StateData>::EndPlay(void *pStateData)
|
|
{
|
|
V_INAME(CPlayingTrack::EndPlay);
|
|
V_BUFPTR_WRITE(pStateData, sizeof(StateData));
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
StateData *pSD = static_cast<StateData *>(pStateData);
|
|
delete pSD;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
template<class T, class EventItem, class StateData>
|
|
STDMETHODIMP
|
|
CPlayingTrack<T, EventItem, StateData>::Clone(
|
|
MUSIC_TIME mtStart,
|
|
MUSIC_TIME mtEnd,
|
|
IDirectMusicTrack** ppTrack)
|
|
{
|
|
V_INAME(CPlayingTrack::Clone);
|
|
V_PTRPTR_WRITE(ppTrack);
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
T *pTrack = new T(&hr);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
*ppTrack = pTrack;
|
|
if (!pTrack)
|
|
return E_OUTOFMEMORY;
|
|
|
|
pTrack->AddRef();
|
|
for (TListItem<EventItem> *pItem = m_EventList.GetHead();
|
|
pItem;
|
|
pItem = pItem->GetNext())
|
|
{
|
|
EventItem &ritem = pItem->GetItemValue();
|
|
if (ritem.lTriggerTime >= mtEnd)
|
|
break;
|
|
if (ritem.lTriggerTime < mtStart)
|
|
continue;
|
|
|
|
TListItem<EventItem> *pNewItem = new TListItem<EventItem>;
|
|
if (!pNewItem)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto End;
|
|
}
|
|
|
|
EventItem &rnew = pNewItem->GetItemValue();
|
|
hr = rnew.Clone(ritem, mtStart);
|
|
if (FAILED(hr))
|
|
{
|
|
delete pNewItem;
|
|
goto End;
|
|
}
|
|
pTrack->m_EventList.AddHead(pNewItem);
|
|
}
|
|
pTrack->m_EventList.Reverse();
|
|
++pTrack->m_dwValidate;
|
|
|
|
End:
|
|
if (FAILED(hr))
|
|
pTrack->Release();
|
|
return hr;
|
|
}
|
|
|
|
template<class T, class EventItem, class StateData>
|
|
HRESULT
|
|
CPlayingTrack<T, EventItem, StateData>::PlayMusicOrClock(
|
|
void *pStateData,
|
|
MUSIC_TIME mtStart,
|
|
MUSIC_TIME mtEnd,
|
|
MUSIC_TIME mtOffset,
|
|
REFERENCE_TIME rtOffset,
|
|
DWORD dwFlags,
|
|
IDirectMusicPerformance* pPerf,
|
|
IDirectMusicSegmentState* pSegSt,
|
|
DWORD dwVirtualID,
|
|
bool fClockTime)
|
|
{
|
|
V_INAME(CPlayingTrack::Play);
|
|
V_BUFPTR_WRITE( pStateData, sizeof(StateData));
|
|
V_INTERFACE(pPerf);
|
|
V_INTERFACE(pSegSt);
|
|
|
|
if (dwFlags & DMUS_TRACKF_PLAY_OFF)
|
|
return S_OK;
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
StateData *pSD = static_cast<StateData *>(pStateData);
|
|
TListItem<EventItem> *li = pSD->pCurrentEvent;
|
|
|
|
// Seek through the event list to find the proper first event if
|
|
// the event list pointed to by the state data has been reloaded
|
|
// or if playback has made a jump to a different position in the track.
|
|
if (m_dwValidate != pSD->dwValidate ||
|
|
dwFlags & (DMUS_TRACKF_SEEK | DMUS_TRACKF_LOOP | DMUS_TRACKF_FLUSH))
|
|
{
|
|
assert(m_dwValidate != pSD->dwValidate || dwFlags & DMUS_TRACKF_SEEK); // by contract SEEK should be set whenever the other dwFlags are
|
|
li = this->Seek(mtStart);
|
|
pSD->dwValidate = m_dwValidate;
|
|
}
|
|
|
|
if (m_fPlayInvalidations || !(dwFlags & DMUS_TRACKF_FLUSH))
|
|
{
|
|
for (; li; li = li->GetNext())
|
|
{
|
|
EventItem &rinfo = li->GetItemValue();
|
|
if (rinfo.lTriggerTime < mtStart) // this can happen if DMUS_TRACKF_PLAY_OFF was set and the seek pointer remains at events from the past
|
|
continue;
|
|
if (rinfo.lTriggerTime >= mtEnd)
|
|
break;
|
|
if (FAILED(this->PlayItem(rinfo, *pSD, pPerf, pSegSt, dwVirtualID, mtOffset, rtOffset, fClockTime)))
|
|
{
|
|
// Returning an error from Play is not allowed. Just ignore it and assert
|
|
// so we would detect this while testing.
|
|
assert(false);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
pSD->pCurrentEvent = li;
|
|
return li ? S_OK : DMUS_S_END;
|
|
}
|
|
|
|
template<class T, class EventItem, class StateData>
|
|
TListItem<EventItem> *
|
|
CPlayingTrack<T, EventItem, StateData>::Seek(MUSIC_TIME mtStart)
|
|
{
|
|
TListItem<EventItem> *li;
|
|
for (li = m_EventList.GetHead();
|
|
li && li->GetItemValue().lTriggerTime < mtStart;
|
|
li = li->GetNext())
|
|
{}
|
|
|
|
return li;
|
|
}
|