// Copyright (c) 1999 Microsoft Corporation. All rights reserved. // // Base classes that implement aspects of a standard DirectMusic track. // Implementations for CPlayingTrack. // ////////////////////////////////////////////////////////////////////// // Creation template CPlayingTrack::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 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 STDMETHODIMP CPlayingTrack::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 scomLoader; if (m_fNeedsLoader) { IDirectMusicGetLoader *pIGetLoader; hr = pIStream->QueryInterface(IID_IDirectMusicGetLoader, reinterpret_cast(&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::EventCompare); return hr; } ////////////////////////////////////////////////////////////////////// // IDirectMusicTrack template STDMETHODIMP CPlayingTrack::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 STDMETHODIMP CPlayingTrack::EndPlay(void *pStateData) { V_INAME(CPlayingTrack::EndPlay); V_BUFPTR_WRITE(pStateData, sizeof(StateData)); SmartRef::CritSec CS(&m_CriticalSection); StateData *pSD = static_cast(pStateData); delete pSD; return S_OK; } template STDMETHODIMP CPlayingTrack::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 *pItem = m_EventList.GetHead(); pItem; pItem = pItem->GetNext()) { EventItem &ritem = pItem->GetItemValue(); if (ritem.lTriggerTime >= mtEnd) break; if (ritem.lTriggerTime < mtStart) continue; TListItem *pNewItem = new TListItem; 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 HRESULT CPlayingTrack::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(pStateData); TListItem *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 TListItem * CPlayingTrack::Seek(MUSIC_TIME mtStart) { TListItem *li; for (li = m_EventList.GetHead(); li && li->GetItemValue().lTriggerTime < mtStart; li = li->GetNext()) {} return li; }