324 lines
9.2 KiB
C++
324 lines
9.2 KiB
C++
|
// Copyright (c) 1999 Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
// Declaration of CSegTriggerTrack.
|
||
|
//
|
||
|
|
||
|
#include "dmime.h"
|
||
|
#include "segtrtrk.h"
|
||
|
#include "..\shared\Validate.h"
|
||
|
#include "dmperf.h"
|
||
|
#include "miscutil.h"
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// SetParam
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CSegTriggerTrack::SetParam(REFGUID rguid, MUSIC_TIME mtTime, void *pData)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
// Allow a certain amount of recursion. If it gets to 10, something is obviously broken.
|
||
|
if (m_dwRecursionCount < 10)
|
||
|
{
|
||
|
m_dwRecursionCount++;
|
||
|
TListItem<TriggerInfo> *li = m_EventList.GetHead();
|
||
|
for (; li; li = li->GetNext())
|
||
|
{
|
||
|
TriggerInfo &rinfo = li->GetItemValue();
|
||
|
rinfo.pIDMSegment->SetParam(rguid, 0xFFFFFFFF, DMUS_SEG_ALLTRACKS, mtTime - rinfo.lTimePhysical, pData);
|
||
|
}
|
||
|
m_dwRecursionCount--;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CSegTriggerTrack::InitPlay(
|
||
|
IDirectMusicSegmentState *pSegmentState,
|
||
|
IDirectMusicPerformance *pPerformance,
|
||
|
void **ppStateData,
|
||
|
DWORD dwTrackID,
|
||
|
DWORD dwFlags)
|
||
|
{
|
||
|
// Call PlayingTrack base class, which sets up our state data.
|
||
|
HRESULT hr = CSegTriggerTrackBase::InitPlay(pSegmentState, pPerformance, ppStateData, dwTrackID, dwFlags);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// Get the audiopath being used by our segment state and save it in our state data.
|
||
|
assert(*ppStateData); // base class should have created state data
|
||
|
assert(pSegmentState); // base class should have returned E_POINTER if it wasn't given a segment state
|
||
|
|
||
|
CSegTriggerTrackState *pState = static_cast<CSegTriggerTrackState *>(*ppStateData);
|
||
|
|
||
|
IDirectMusicSegmentState8 *pSegSt8 = NULL;
|
||
|
hr = pSegmentState->QueryInterface(IID_IDirectMusicSegmentState8, reinterpret_cast<void**>(&pSegSt8));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pSegSt8->GetObjectInPath(
|
||
|
0, // pchannel doesn't apply
|
||
|
DMUS_PATH_AUDIOPATH, // get the audiopath
|
||
|
0, // buffer index doesn't apply
|
||
|
CLSID_NULL, // clsid doesn't apply
|
||
|
0, // there should be only one audiopath
|
||
|
IID_IDirectMusicAudioPath,
|
||
|
reinterpret_cast<void**>(&pState->pAudioPath));
|
||
|
|
||
|
pSegSt8->Release();
|
||
|
|
||
|
// If this doesn't find an audiopath that's OK. If we're not playing on an audiopath then
|
||
|
// pAudioPath stays NULL and we'll play our triggered segments on the general performance.
|
||
|
if (hr == DMUS_E_NOT_FOUND)
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// Load
|
||
|
|
||
|
// Helper used by the Load functions when we expected to find something
|
||
|
// but a RiffIter becomes false. In this case, if it has a success HR
|
||
|
// indicating there were no more items then we return DMUS_E_INVALID_SEGMENTTRIGGERTRACK
|
||
|
// because the stream didn't contain the data we expected. If it has a
|
||
|
// failure hr, it was unable to read from the stream and we return its HR.
|
||
|
HRESULT LoadHrFailOK(const SmartRef::RiffIter &ri)
|
||
|
{
|
||
|
HRESULT hr = ri.hr();
|
||
|
return SUCCEEDED(hr) ? DMUS_E_INVALID_SEGMENTTRIGGERTRACK : hr;
|
||
|
};
|
||
|
|
||
|
HRESULT
|
||
|
CSegTriggerTrack::LoadRiff(SmartRef::RiffIter &ri, IDirectMusicLoader *pIDMLoader)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
if (!ri.Find(SmartRef::RiffIter::List, DMUS_FOURCC_SEGTRACK_LIST))
|
||
|
{
|
||
|
#ifdef DBG
|
||
|
if (SUCCEEDED(ri.hr()))
|
||
|
{
|
||
|
Trace(1, "Error: Unable to load segment trigger track: List 'segt' not found.\n");
|
||
|
}
|
||
|
#endif
|
||
|
return LoadHrFailOK(ri);
|
||
|
}
|
||
|
|
||
|
SmartRef::RiffIter riTrackForm = ri.Descend();
|
||
|
if (!riTrackForm)
|
||
|
return riTrackForm.hr();
|
||
|
|
||
|
for ( ; riTrackForm; ++riTrackForm)
|
||
|
{
|
||
|
if (riTrackForm.type() == SmartRef::RiffIter::Chunk)
|
||
|
{
|
||
|
if (riTrackForm.id() == DMUS_FOURCC_SEGTRACK_CHUNK)
|
||
|
{
|
||
|
DMUS_IO_SEGMENT_TRACK_HEADER ioItem;
|
||
|
hr = SmartRef::RiffIterReadChunk(riTrackForm, &ioItem);
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
|
||
|
m_dwFlags = ioItem.dwFlags;
|
||
|
}
|
||
|
}
|
||
|
else if (riTrackForm.type() == SmartRef::RiffIter::List)
|
||
|
{
|
||
|
if (riTrackForm.id() == DMUS_FOURCC_SEGMENTS_LIST)
|
||
|
{
|
||
|
SmartRef::RiffIter riSegList = riTrackForm.Descend();
|
||
|
while (riSegList && riSegList.Find(SmartRef::RiffIter::List, DMUS_FOURCC_SEGMENT_LIST))
|
||
|
{
|
||
|
hr = LoadTrigger(riSegList.Descend(), pIDMLoader);
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
++riSegList;
|
||
|
}
|
||
|
hr = riSegList.hr();
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return riTrackForm.hr();
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// other methods
|
||
|
|
||
|
HRESULT
|
||
|
CSegTriggerTrack::PlayItem(
|
||
|
const TriggerInfo &item,
|
||
|
statedata &state,
|
||
|
IDirectMusicPerformance *pPerf,
|
||
|
IDirectMusicSegmentState* pSegSt,
|
||
|
DWORD dwVirtualID,
|
||
|
MUSIC_TIME mtOffset,
|
||
|
REFERENCE_TIME rtOffset,
|
||
|
bool fClockTime)
|
||
|
{
|
||
|
IDirectMusicPerformance8 *pPerf8 = NULL;
|
||
|
HRESULT hr = pPerf->QueryInterface(IID_IDirectMusicPerformance8, reinterpret_cast<void**>(&pPerf8));
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
|
||
|
hr = pPerf8->PlaySegmentEx(
|
||
|
item.pIDMSegment,
|
||
|
NULL, // not a song
|
||
|
NULL, // no transition
|
||
|
item.dwPlayFlags | (fClockTime ? DMUS_SEGF_REFTIME : 0), // flags
|
||
|
fClockTime
|
||
|
? item.lTimePhysical * REF_PER_MIL + rtOffset
|
||
|
: item.lTimePhysical + mtOffset, // time
|
||
|
NULL, // ignore returned segment state
|
||
|
NULL, // no replacement
|
||
|
state.pAudioPath // audio path to use (may be NULL indicating defualt)
|
||
|
);
|
||
|
pPerf8->Release();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
Trace(0,"Segment Trigger Track failed segment playback\n");
|
||
|
hr = S_OK; // Avoid an assert.
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CSegTriggerTrack::LoadTrigger(
|
||
|
SmartRef::RiffIter ri,
|
||
|
IDirectMusicLoader *pIDMLoader)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
if (!ri)
|
||
|
return ri.hr();
|
||
|
|
||
|
// Create an event
|
||
|
TListItem<TriggerInfo> *pItem = new TListItem<TriggerInfo>;
|
||
|
if (!pItem)
|
||
|
return E_OUTOFMEMORY;
|
||
|
TriggerInfo &rinfo = pItem->GetItemValue();
|
||
|
|
||
|
// find the item header (we can't interpret the other chunks until we've found it)
|
||
|
if (!ri.Find(SmartRef::RiffIter::Chunk, DMUS_FOURCC_SEGMENTITEM_CHUNK))
|
||
|
{
|
||
|
delete pItem;
|
||
|
#ifdef DBG
|
||
|
if (SUCCEEDED(ri.hr()))
|
||
|
{
|
||
|
Trace(1, "Error: Unable to load segment trigger track: Chunk 'sgih' not found.\n");
|
||
|
}
|
||
|
#endif
|
||
|
return LoadHrFailOK(ri);
|
||
|
}
|
||
|
|
||
|
// read the header
|
||
|
DMUS_IO_SEGMENT_ITEM_HEADER ioItem;
|
||
|
hr = SmartRef::RiffIterReadChunk(ri, &ioItem);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pItem;
|
||
|
return hr;
|
||
|
}
|
||
|
rinfo.lTriggerTime = ioItem.lTimeLogical;
|
||
|
rinfo.lTimePhysical = ioItem.lTimePhysical;
|
||
|
rinfo.dwPlayFlags = ioItem.dwPlayFlags;
|
||
|
rinfo.dwFlags = ioItem.dwFlags;
|
||
|
++ri;
|
||
|
if (!ri)
|
||
|
{
|
||
|
// If there's nothing more then this is an empty trigger we should ignore because the user hasn't specified
|
||
|
// the style or segment to play from.
|
||
|
delete pItem;
|
||
|
return ri.hr();
|
||
|
}
|
||
|
|
||
|
if (!(rinfo.dwFlags & DMUS_SEGMENTTRACKF_MOTIF))
|
||
|
{
|
||
|
// find the referenced segment
|
||
|
if (!ri.Find(SmartRef::RiffIter::List, DMUS_FOURCC_REF_LIST))
|
||
|
{
|
||
|
// If there's no DMRF then we should ignore this trigger because the user hasn't specified a segment.
|
||
|
delete pItem;
|
||
|
return ri.hr();
|
||
|
}
|
||
|
|
||
|
hr = ri.LoadReference(pIDMLoader, IID_IDirectMusicSegment, reinterpret_cast<void**>(&rinfo.pIDMSegment));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pItem;
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// find the segment from the referenced style and motif name
|
||
|
SmartRef::ComPtr<IDirectMusicStyle> scomStyle;
|
||
|
SmartRef::Buffer<WCHAR> wbufMotifName;
|
||
|
for ( ; ri; ++ri)
|
||
|
{
|
||
|
if (ri.type() == SmartRef::RiffIter::List)
|
||
|
{
|
||
|
if (ri.id() == DMUS_FOURCC_REF_LIST)
|
||
|
{
|
||
|
hr = ri.LoadReference(pIDMLoader, IID_IDirectMusicStyle, reinterpret_cast<void**>(&scomStyle));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pItem;
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (ri.type() == SmartRef::RiffIter::Chunk)
|
||
|
{
|
||
|
if (ri.id() == DMUS_FOURCC_SEGMENTITEMNAME_CHUNK)
|
||
|
{
|
||
|
hr = ri.ReadText(&wbufMotifName);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pItem;
|
||
|
#ifdef DBG
|
||
|
if (hr == E_FAIL)
|
||
|
{
|
||
|
Trace(1, "Error: Unable to load segment trigger track: Problem reading 'snam' chunk.\n");
|
||
|
}
|
||
|
#endif
|
||
|
return hr == E_FAIL ? DMUS_E_INVALID_SEGMENTTRIGGERTRACK : hr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
hr = ri.hr();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pItem;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
if (!(scomStyle && wbufMotifName))
|
||
|
{
|
||
|
// This happens if the track didn't contain a DMRF list or snam chunk. We allow
|
||
|
// this as a means of representing an empty trigger track item or where the
|
||
|
// motif to play hasn't been specified. When loading we'll simply ignore
|
||
|
// this item and continue reading the track.
|
||
|
delete pItem;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
hr = scomStyle->GetMotif(wbufMotifName, &rinfo.pIDMSegment);
|
||
|
if (hr == S_FALSE)
|
||
|
{
|
||
|
Trace(1, "Error: The segment trigger track couldn't load because the motif %S was not found in the style.\n", wbufMotifName);
|
||
|
hr = DMUS_E_NOT_FOUND;
|
||
|
}
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pItem;
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_EventList.AddHead(pItem);
|
||
|
return hr;
|
||
|
}
|