windows-nt/Source/XPSP1/NT/multimedia/directx/dmusic/shared/trackhelp.inl
2020-09-26 16:20:57 +08:00

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