// // bandtrk.cpp // // Copyright (c) 1997-2001 Microsoft Corporation // #include "debug.h" #include "dmusicc.h" #include "dmusici.h" #include "dmusicf.h" #include "..\shared\dmstrm.h" #include "..\shared\validate.h" #include "bandtrk.h" extern long g_cComponent; ////////////////////////////////////////////////////////////////////// // Class CBandTrk ////////////////////////////////////////////////////////////////////// // CBandTrk::CBandTrk CBandTrk::CBandTrk() : m_dwValidate(0), m_bAutoDownload(false), m_fLockAutoDownload(false), m_dwFlags(0), m_cRef(1), m_fCSInitialized(FALSE) { InterlockedIncrement(&g_cComponent); InitializeCriticalSection(&m_CriticalSection); // Note: on pre-Blackcomb OS's, this call can raise an exception; if it // ever pops in stress, we can add an exception handler and retry loop. // (Not all calls to 'new CBandTrk' are protected in handlers.) m_fCSInitialized = TRUE; } ////////////////////////////////////////////////////////////////////// // CBandTrk::~CBandTrk CBandTrk::~CBandTrk() { if (m_fCSInitialized) { m_MidiModeList.CleanUp(); while(!BandList.IsEmpty()) { CBand* pBand = BandList.RemoveHead(); pBand->Release(); } DeleteCriticalSection(&m_CriticalSection); } InterlockedDecrement(&g_cComponent); } ////////////////////////////////////////////////////////////////////// // IUnknown ////////////////////////////////////////////////////////////////////// // CBandTrk::QueryInterface STDMETHODIMP CBandTrk::QueryInterface(const IID &iid, void **ppv) { V_INAME(CBandTrk::QueryInterface); V_PTRPTR_WRITE(ppv); V_REFGUID(iid); if(iid == IID_IUnknown || iid == IID_IDirectMusicTrack || iid == IID_IDirectMusicTrack8) { *ppv = static_cast(this); } else if(iid == IID_IDirectMusicBandTrk) { *ppv = static_cast(this); } else if(iid == IID_IPersistStream) { *ppv = static_cast(this); } else if(iid == IID_IPersist) { *ppv = static_cast(this); } else { Trace(4,"Warning: Request to query unknown interface on Band Track object\n"); *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast(this)->AddRef(); return S_OK; } ////////////////////////////////////////////////////////////////////// // CBandTrk::AddRef STDMETHODIMP_(ULONG) CBandTrk::AddRef() { return InterlockedIncrement(&m_cRef); } ////////////////////////////////////////////////////////////////////// // CBandTrk::Release STDMETHODIMP_(ULONG) CBandTrk::Release() { if(!InterlockedDecrement(&m_cRef)) { delete this; return 0; } return m_cRef; } ///////////////////////////////////////////////////////////////////////////// // IPersist HRESULT CBandTrk::GetClassID( CLSID* pClassID ) { V_INAME(CBandTrk::GetClassID); V_PTR_WRITE(pClassID, CLSID); *pClassID = CLSID_DirectMusicBandTrack; return S_OK; } ////////////////////////////////////////////////////////////////////// // IPersistStream ////////////////////////////////////////////////////////////////////// // CBandTrk::Load STDMETHODIMP CBandTrk::Load(IStream* pIStream) { V_INAME(CBandTrk::Load); V_PTR_READ(pIStream, IStream); HRESULT hrDLS = S_OK; EnterCriticalSection(&m_CriticalSection); m_MidiModeList.CleanUp(); // If we have been previously loaded, cleanup bands if(!BandList.IsEmpty()) { m_bAutoDownload = true; while(!BandList.IsEmpty()) { CBand* pBand = BandList.RemoveHead(); pBand->Release(); } ++m_dwValidate; } CRiffParser Parser(pIStream); RIFFIO ckMain; HRESULT hr = S_OK; Parser.EnterList(&ckMain); if (Parser.NextChunk(&hr)) { if ((ckMain.ckid == FOURCC_RIFF) && (ckMain.fccType == DMUS_FOURCC_BANDTRACK_FORM)) { RIFFIO ckNext; // Descends into the children chunks. Parser.EnterList(&ckNext); while (Parser.NextChunk(&hr)) { switch(ckNext.ckid) { case DMUS_FOURCC_BANDTRACK_CHUNK: DMUS_IO_BAND_TRACK_HEADER ioDMBndTrkHdr; hr = Parser.Read(&ioDMBndTrkHdr, sizeof(DMUS_IO_BAND_TRACK_HEADER)); if(SUCCEEDED(hr)) { m_bAutoDownload = ioDMBndTrkHdr.bAutoDownload ? true : false; m_fLockAutoDownload = true; } break; case FOURCC_LIST: switch(ckNext.fccType) { case DMUS_FOURCC_BANDS_LIST: hr = BuildDirectMusicBandList(&Parser); if (hr != S_OK) { hrDLS = hr; } break; } } } Parser.LeaveList(); } } Parser.LeaveList(); LeaveCriticalSection(&m_CriticalSection); if (hr == S_OK && hrDLS != S_OK) { hr = hrDLS; } return hr; } ////////////////////////////////////////////////////////////////////// // IDirectMusicTrack ////////////////////////////////////////////////////////////////////// // CBandTrk::Init STDMETHODIMP CBandTrk::Init(IDirectMusicSegment* pSegment) { V_INAME(CBandTrk::Init); V_INTERFACE(pSegment); HRESULT hr = S_OK; DWORD dwNumPChannels = 0; DWORD *pdwPChannels = NULL; EnterCriticalSection(&m_CriticalSection); CBand* pBand = BandList.GetHead(); for(; pBand; pBand = pBand->GetNext()) { dwNumPChannels += pBand->GetPChannelCount(); } if(dwNumPChannels > 0) { pdwPChannels = new DWORD[dwNumPChannels]; if(pdwPChannels) { pBand = BandList.GetHead(); for(DWORD dwPos = 0; pBand; pBand = pBand->GetNext()) { DWORD dwNumWritten; pBand->GetPChannels(pdwPChannels + dwPos, &dwNumWritten); dwPos += dwNumWritten; } } else { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { hr = pSegment->SetPChannelsUsed(dwNumPChannels, pdwPChannels); } delete [] pdwPChannels; } LeaveCriticalSection(&m_CriticalSection); return hr; } ////////////////////////////////////////////////////////////////////// // CBandTrk::InitPlay STDMETHODIMP CBandTrk::InitPlay(IDirectMusicSegmentState* pSegmentState, IDirectMusicPerformance* pPerformance, void** ppStateData, DWORD dwVirtualTrackID, DWORD dwFlags) { V_INAME(CBandTrk::InitPlay); V_INTERFACE(pSegmentState); V_INTERFACE(pPerformance); assert(ppStateData); EnterCriticalSection(&m_CriticalSection); CBandTrkStateData* pBandTrkStateData = new CBandTrkStateData; // If we can not allocate the memory we need to set ppStateData to NULL // and return S_OK since the caller always expects S_OK; *ppStateData = pBandTrkStateData; if(pBandTrkStateData == NULL) { LeaveCriticalSection(&m_CriticalSection); return E_OUTOFMEMORY; } // Need to save State Data pBandTrkStateData->m_pSegmentState = pSegmentState; pBandTrkStateData->m_pPerformance = pPerformance; pBandTrkStateData->m_dwVirtualTrackID = dwVirtualTrackID; // Determines instance of Band Track CBand* pBand = BandList.GetHead(); pBandTrkStateData->m_pNextBandToSPE = pBand; BOOL fGlobal; // if the performance has been set with an autodownload preference, // use that. otherwise, assume autodownloading is off, unless it has // been locked (i.e. specified on the band track.) if( SUCCEEDED( pPerformance->GetGlobalParam( GUID_PerfAutoDownload, &fGlobal, sizeof(BOOL) ))) { if( !m_fLockAutoDownload ) { // it might seem like we can just assign m_bAutoDownload = fGlobal, // but that's bitten me before, so I'm being paranoid today. (markburt) if( fGlobal ) { m_bAutoDownload = true; } else { m_bAutoDownload = false; } } } else if( !m_fLockAutoDownload ) { m_bAutoDownload = false; } // Call SetParam to download all instruments used by the track's bands // This is the auto-download feature that can be turned off with a call to SetParam if(m_bAutoDownload) { IDirectMusicAudioPath *pPath = NULL; IDirectMusicSegmentState8 *pState8; if (SUCCEEDED(pSegmentState->QueryInterface(IID_IDirectMusicSegmentState8,(void **)&pState8))) { pState8->GetObjectInPath(0,DMUS_PATH_AUDIOPATH,0,GUID_NULL,0, IID_IDirectMusicAudioPath,(void **) &pPath); pState8->Release(); } if (pPath) { SetParam(GUID_DownloadToAudioPath,0,(void *)pPath); pPath->Release(); } else { SetParam(GUID_DownloadToAudioPath, 0, (void *)pPerformance); } } LeaveCriticalSection(&m_CriticalSection); return S_OK; } ////////////////////////////////////////////////////////////////////// // CBandTrk::EndPlay STDMETHODIMP CBandTrk::EndPlay(void* pStateData) { assert(pStateData); EnterCriticalSection(&m_CriticalSection); // Call SetParam to unload all instruments used by the track's bands // This is the auto-unload feature that can be turned off with a call to SetParam if(m_bAutoDownload) { IDirectMusicPerformance *pPerformance = ((CBandTrkStateData *)pStateData)->m_pPerformance; IDirectMusicSegmentState *pSegmentState = ((CBandTrkStateData *)pStateData)->m_pSegmentState; IDirectMusicAudioPath *pPath = NULL; IDirectMusicSegmentState8 *pState8; if (SUCCEEDED(pSegmentState->QueryInterface(IID_IDirectMusicSegmentState8,(void **)&pState8))) { pState8->GetObjectInPath(0,DMUS_PATH_AUDIOPATH,0,GUID_NULL,0, IID_IDirectMusicAudioPath,(void **) &pPath); pState8->Release(); } if (pPath) { SetParam(GUID_UnloadFromAudioPath,0,(void *)pPath); pPath->Release(); } else { SetParam(GUID_UnloadFromAudioPath, 0, (void *)pPerformance); } } if(pStateData) { delete ((CBandTrkStateData *)pStateData); } LeaveCriticalSection(&m_CriticalSection); return S_OK; } ////////////////////////////////////////////////////////////////////// // CBandTrk::PlayEx STDMETHODIMP CBandTrk::PlayEx(void* pStateData,REFERENCE_TIME rtStart, REFERENCE_TIME rtEnd,REFERENCE_TIME rtOffset, DWORD dwFlags,IDirectMusicPerformance* pPerf, IDirectMusicSegmentState* pSegSt,DWORD dwVirtualID) { HRESULT hr; EnterCriticalSection(&m_CriticalSection); if (dwFlags & DMUS_TRACKF_CLOCK) { // Convert all reference times to millisecond times. Then, just use same MUSIC_TIME // variables. hr = PlayMusicOrClock(pStateData,(MUSIC_TIME)(rtStart / REF_PER_MIL),(MUSIC_TIME)(rtEnd / REF_PER_MIL), (MUSIC_TIME)(rtOffset / REF_PER_MIL),rtOffset,dwFlags,pPerf,pSegSt,dwVirtualID,TRUE); } else { hr = PlayMusicOrClock(pStateData,(MUSIC_TIME)rtStart,(MUSIC_TIME)rtEnd, (MUSIC_TIME)rtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE); } LeaveCriticalSection(&m_CriticalSection); return hr; } ////////////////////////////////////////////////////////////////////// // CBandTrk::Play STDMETHODIMP CBandTrk::Play( void *pStateData, MUSIC_TIME mtStart, MUSIC_TIME mtEnd, MUSIC_TIME mtOffset, DWORD dwFlags, IDirectMusicPerformance* pPerf, IDirectMusicSegmentState* pSegSt, DWORD dwVirtualID) { EnterCriticalSection(&m_CriticalSection); HRESULT hr = PlayMusicOrClock(pStateData,mtStart,mtEnd,mtOffset,0,dwFlags,pPerf,pSegSt,dwVirtualID,FALSE); LeaveCriticalSection(&m_CriticalSection); return hr; } HRESULT CBandTrk::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) { assert(pPerf); assert(pSegSt); assert(pStateData); // Caller expects S_OK or S_END. Since we have no state info we can not do anything if(pStateData == NULL) { return DMUS_S_END; } EnterCriticalSection(&m_CriticalSection); if( dwFlags & (DMUS_TRACKF_SEEK | DMUS_TRACKF_FLUSH | DMUS_TRACKF_DIRTY | DMUS_TRACKF_LOOP) ) { // need to reset the PChannel Map in case of any of these flags. CBand* pBand = BandList.GetHead(); DWORD dwGroupBits = 0xffffffff; IDirectMusicSegment* pSeg; if( SUCCEEDED(pSegSt->GetSegment(&pSeg))) { pSeg->GetTrackGroup(this, &dwGroupBits); pSeg->Release(); } for(; pBand; pBand = pBand->GetNext()) { pBand->m_PChMap.Reset(); pBand->m_dwGroupBits = dwGroupBits; } } CBandTrkStateData* pBandTrkStateData = (CBandTrkStateData *)pStateData; // Seek if we're starting, looping, or if we've been reloaded if ((dwFlags & DMUS_TRACKF_LOOP) || (dwFlags & DMUS_TRACKF_START) || (pBandTrkStateData->dwValidate != m_dwValidate)) { // When we start playing a segment, we need to catch up with all the band changes // that happened before the start point. The instruments that sound when we start // playing in the middle of a segment should sound the same as if we had played the // segment to that point from the beginning. pBandTrkStateData->m_fPlayPreviousInSeek = !!(dwFlags & DMUS_TRACKF_START); Seek(pBandTrkStateData, mtStart, mtOffset, rtOffset, fClockTime); pBandTrkStateData->dwValidate = m_dwValidate; // if we were reloading, we're now adjusted } // Send all Patch changes between mtStart & mtEnd // If any fail try next one CBand* pBand = (CBand *)(pBandTrkStateData->m_pNextBandToSPE); for( ; pBand && pBand->m_lTimeLogical < mtEnd; pBand = pBand->GetNext()) { pBand->SendMessages(pBandTrkStateData, mtOffset, rtOffset, fClockTime); } // Save position for next time pBandTrkStateData->m_pNextBandToSPE = pBand; LeaveCriticalSection(&m_CriticalSection); return pBand == NULL ? DMUS_S_END : S_OK; } ////////////////////////////////////////////////////////////////////// // CBandTrk::GetParam STDMETHODIMP CBandTrk::GetParam(REFGUID rguidDataType, MUSIC_TIME mtTime, MUSIC_TIME* pmtNext, void* pData) { V_INAME(CBandTrk::GetParam); V_PTR_WRITE_OPT(pmtNext,MUSIC_TIME); V_PTR_WRITE(pData,1); V_REFGUID(rguidDataType); HRESULT hr = S_OK; EnterCriticalSection( &m_CriticalSection ); if (rguidDataType == GUID_BandParam) { CBand* pScan = BandList.GetHead(); if (pScan) { CBand* pBand = pScan; for (pScan = pScan->GetNext(); pScan; pScan = pScan->GetNext()) { if (mtTime < pScan->m_lTimeLogical) break; pBand = pScan; } // make a copy of the band found CBand *pNewBand = new CBand; if (pNewBand) { CBandInstrument* pBandInstrument = pBand->m_BandInstrumentList.GetHead(); for(; pBandInstrument && SUCCEEDED(hr); pBandInstrument = pBandInstrument->GetNext()) { hr = pNewBand->Load(pBandInstrument); } if (FAILED(hr)) { // Don't leak. delete pNewBand; } else { pNewBand->m_lTimeLogical = pBand->m_lTimeLogical; pNewBand->m_lTimePhysical = pBand->m_lTimePhysical; pNewBand->m_dwFlags |= DMB_LOADED; pNewBand->m_dwMidiMode = pBand->m_dwMidiMode; } } else { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { IDirectMusicBand* pIDMBand = NULL; pNewBand->QueryInterface(IID_IDirectMusicBand, (void**)&pIDMBand); // The constructor initialized the ref countto 1, so release the QI pNewBand->Release(); DMUS_BAND_PARAM *pBandParam = reinterpret_cast(pData); pBandParam->pBand = pIDMBand; pBandParam->mtTimePhysical = pBand->m_lTimePhysical; if (pmtNext) { *pmtNext = (pScan != NULL) ? pScan->m_lTimeLogical : 0; } hr = S_OK; } } else { Trace(4,"Warning: Band Track unable to find Band for GetParam call.\n"); hr = DMUS_E_NOT_FOUND; } } else { hr = DMUS_E_GET_UNSUPPORTED; } LeaveCriticalSection( &m_CriticalSection ); return hr; } ////////////////////////////////////////////////////////////////////// // CBandTrk::SetParam STDMETHODIMP CBandTrk::SetParam(REFGUID rguidDataType, MUSIC_TIME mtTime, void* pData) { V_INAME(CBandTrk::SetParam); V_REFGUID(rguidDataType); HRESULT hr = S_OK; if((pData == NULL) && (rguidDataType != GUID_Enable_Auto_Download) && (rguidDataType != GUID_Disable_Auto_Download) && (rguidDataType != GUID_Clear_All_Bands) && (rguidDataType != GUID_IgnoreBankSelectForGM)) { Trace(1,"Error: Invalid NULL pointer passed to Band Track for SetParam call.\n"); return E_POINTER; } EnterCriticalSection(&m_CriticalSection); if(rguidDataType == GUID_DownloadToAudioPath) { IDirectMusicAudioPath* pPath = (IDirectMusicAudioPath*)pData; V_INTERFACE(pPath); HRESULT hrFail = S_OK; DWORD dwSuccess = 0; CBand* pBand = BandList.GetHead(); for(; pBand; pBand = pBand->GetNext()) { if (FAILED(hr = pBand->DownloadEx(pPath))) // If not S_OK, download is only partial. { hrFail = hr; } else { dwSuccess++; } } // If we had a failure, return it if we had no successes. // Else return S_FALSE for partial success. if (FAILED(hrFail) && dwSuccess) { hr = S_FALSE; } } else if(rguidDataType == GUID_UnloadFromAudioPath) { IDirectMusicAudioPath* pPath = (IDirectMusicAudioPath*)pData; V_INTERFACE(pPath); CBand* pBand = BandList.GetHead(); for(; pBand; pBand = pBand->GetNext()) { pBand->UnloadEx(pPath); } } else if(rguidDataType == GUID_Download) { IDirectMusicPerformance* pPerf = (IDirectMusicPerformance*)pData; V_INTERFACE(pPerf); CBand* pBand = BandList.GetHead(); for(; pBand; pBand = pBand->GetNext()) { if (pBand->DownloadEx(pPerf) != S_OK) // If not S_OK, download is only partial. { hr = S_FALSE; } } } else if(rguidDataType == GUID_Unload) { IDirectMusicPerformance* pPerf = (IDirectMusicPerformance*)pData; V_INTERFACE(pPerf); CBand* pBand = BandList.GetHead(); for(; pBand; pBand = pBand->GetNext()) { pBand->UnloadEx(pPerf); } } else if(rguidDataType == GUID_Enable_Auto_Download) { m_bAutoDownload = true; m_fLockAutoDownload = true; } else if(rguidDataType == GUID_Disable_Auto_Download) { m_bAutoDownload = false; m_fLockAutoDownload = true; } else if(rguidDataType == GUID_Clear_All_Bands) { while(!BandList.IsEmpty()) { CBand* pBand = BandList.RemoveHead(); pBand->Release(); } } else if(rguidDataType == GUID_BandParam) { DMUS_BAND_PARAM *pBandParam = reinterpret_cast(pData); IDirectMusicBand *pBand = pBandParam->pBand; V_INTERFACE(pBand); // If you can QI pData for private interface IDirectMusicBandPrivate // pBand is of type CBand. IDirectMusicBandPrivate *pBandPrivate = NULL; hr = pBand->QueryInterface(IID_IDirectMusicBandPrivate, (void **)&pBandPrivate); if(FAILED(hr)) { LeaveCriticalSection(&m_CriticalSection); return hr; } pBandPrivate->Release(); CBand *pBandObject = static_cast(pBand); pBandObject->m_lTimeLogical = mtTime; pBandObject->m_lTimePhysical = pBandParam->mtTimePhysical; hr = AddBand(pBand); } else if(rguidDataType == GUID_IDirectMusicBand) { IDirectMusicBand *pBand = (IDirectMusicBand *)pData; V_INTERFACE(pBand); // If you can QI pData for private interface IDirectMusicBandPrivate // pData is of type CBand. IDirectMusicBandPrivate *pBandPrivate = NULL; hr = pBand->QueryInterface(IID_IDirectMusicBandPrivate, (void **)&pBandPrivate); if(FAILED(hr)) { LeaveCriticalSection(&m_CriticalSection); return hr; } pBandPrivate->Release(); CBand *pBandObject = static_cast(pBand); pBandObject->m_lTimeLogical = mtTime; pBandObject->m_lTimePhysical = pBandObject->m_lTimeLogical; hr = AddBand(pBand); } else if(rguidDataType == GUID_IgnoreBankSelectForGM) { CBand* pBand = BandList.GetHead(); for(; pBand; pBand = pBand->GetNext()) { pBand->MakeGMOnly(); } } else if(rguidDataType == GUID_ConnectToDLSCollection) { IDirectMusicCollection* pCollect = (IDirectMusicCollection*)pData; V_INTERFACE(pData); CBand* pBand = BandList.GetHead(); for(; pBand; pBand = pBand->GetNext()) { pBand->ConnectToDLSCollection(pCollect); } } else { Trace(3,"Warning: Invalid SetParam call on Band Track, GUID is unknown.\n"); hr = DMUS_E_TYPE_UNSUPPORTED; } LeaveCriticalSection(&m_CriticalSection); return hr; } ////////////////////////////////////////////////////////////////////// // CBandTrk::GetParamEx STDMETHODIMP CBandTrk::GetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime, REFERENCE_TIME* prtNext,void* pParam,void * pStateData, DWORD dwFlags) { HRESULT hr; MUSIC_TIME mtNext; if (dwFlags & DMUS_TRACK_PARAMF_CLOCK) { hr = GetParam(rguidType,(MUSIC_TIME) (rtTime / REF_PER_MIL), &mtNext, pParam); if (prtNext) { *prtNext = mtNext * REF_PER_MIL; } } else { hr = GetParam(rguidType,(MUSIC_TIME) rtTime, &mtNext, pParam); if (prtNext) { *prtNext = mtNext; } } return hr; } ////////////////////////////////////////////////////////////////////// // CBandTrk::SetParamEx STDMETHODIMP CBandTrk::SetParamEx(REFGUID rguidType,REFERENCE_TIME rtTime, void* pParam, void * pStateData, DWORD dwFlags) { if (dwFlags & DMUS_TRACK_PARAMF_CLOCK) { rtTime /= REF_PER_MIL; } return SetParam(rguidType, (MUSIC_TIME) rtTime, pParam); } ////////////////////////////////////////////////////////////////////// // CBandTrk::IsParamSupported STDMETHODIMP CBandTrk::IsParamSupported(REFGUID rguidDataType) { V_INAME(CBandTrk::IsParamSupported); V_REFGUID(rguidDataType); // Return S_OK if the object supports the GUID and S_FALSE otherwise if(rguidDataType == GUID_Download || rguidDataType == GUID_Unload || rguidDataType == GUID_DownloadToAudioPath || rguidDataType == GUID_UnloadFromAudioPath || rguidDataType == GUID_Enable_Auto_Download || rguidDataType == GUID_Disable_Auto_Download || rguidDataType == GUID_Clear_All_Bands || rguidDataType == GUID_IDirectMusicBand || rguidDataType == GUID_BandParam || rguidDataType == GUID_IgnoreBankSelectForGM || rguidDataType == GUID_ConnectToDLSCollection) { return S_OK; } else { return DMUS_E_TYPE_UNSUPPORTED; } } ////////////////////////////////////////////////////////////////////// // CBandTrk::AddNotificationType STDMETHODIMP CBandTrk::AddNotificationType(REFGUID rguidNotify) { return E_NOTIMPL; } ////////////////////////////////////////////////////////////////////// // CBandTrk::RemoveNotificationType STDMETHODIMP CBandTrk::RemoveNotificationType(REFGUID rguidNotify) { return E_NOTIMPL; } ////////////////////////////////////////////////////////////////////// // CBandTrk::Clone STDMETHODIMP CBandTrk::Clone(MUSIC_TIME mtStart, MUSIC_TIME mtEnd, IDirectMusicTrack** ppTrack) { V_INAME(CBandTrk::Clone); V_PTRPTR_WRITE(ppTrack); if ((mtStart < 0 ) || (mtStart > mtEnd)) { Trace(1,"Error: Invalid range %ld to %ld sent to Band Track Clone command.\n",mtStart,mtEnd); return E_INVALIDARG; } HRESULT hr = E_OUTOFMEMORY; IDirectMusicBandTrk *pBandTrack = NULL; CBandTrk *pNew = new CBandTrk; if (pNew) { hr = pNew->QueryInterface(IID_IDirectMusicBandTrk,(void**)&pBandTrack); if(SUCCEEDED(hr)) { hr = LoadClone(pBandTrack, mtStart, mtEnd); if(SUCCEEDED(hr)) { hr = pBandTrack->QueryInterface(IID_IDirectMusicTrack, (void **)ppTrack); if (SUCCEEDED(hr)) { pBandTrack->Release(); } } pBandTrack->Release(); } if (FAILED(hr)) { delete pNew; } } return hr; } ////////////////////////////////////////////////////////////////////// // IDirectMusicCommon ////////////////////////////////////////////////////////////////////// // CBandTrk::GetName STDMETHODIMP CBandTrk::GetName(BSTR* pbstrName) { return E_NOTIMPL; } ////////////////////////////////////////////////////////////////////// // IDirectMusicBandTrk ////////////////////////////////////////////////////////////////////// // CBandTrk::AddBand STDMETHODIMP CBandTrk::AddBand(DMUS_IO_PATCH_ITEM* pPatchEvent) { if(pPatchEvent == NULL) { return E_POINTER; } CBand *pNewBand = new CBand; HRESULT hr; if(pNewBand == NULL) { hr = E_OUTOFMEMORY; } else { hr = pNewBand->Load(*pPatchEvent); } if(SUCCEEDED(hr)) { hr = InsertBand(pNewBand); } if(FAILED(hr) && pNewBand) { delete pNewBand; } return hr; } ////////////////////////////////////////////////////////////////////// // CBandTrk::AddBand HRESULT CBandTrk::AddBand(IDirectMusicBand* pIDMBand) { if(pIDMBand == NULL) { return E_POINTER; } // If you can QI pIDMBand for private interface IDirectMusicBandPrivate // pIDMBand is of type CBand. IDirectMusicBandPrivate* pIDMBandP = NULL; HRESULT hr = pIDMBand->QueryInterface(IID_IDirectMusicBandPrivate, (void **)&pIDMBandP); if(SUCCEEDED(hr)) { pIDMBandP->Release(); CBand *pNewBand = (CBand *) pIDMBand; pNewBand->AddRef(); hr = InsertBand(pNewBand); if(FAILED(hr)) { pNewBand->Release(); } } return hr; } ////////////////////////////////////////////////////////////////////// // Internal ////////////////////////////////////////////////////////////////////// // CBandTrk::BuildDirectMusicBandList // This method loads all of the bands. HRESULT CBandTrk::BuildDirectMusicBandList(CRiffParser *pParser) { RIFFIO ckNext; HRESULT hrDLS = S_OK; HRESULT hr = S_OK; pParser->EnterList(&ckNext); while (pParser->NextChunk(&hr)) { switch(ckNext.ckid) { case FOURCC_LIST : switch(ckNext.fccType) { case DMUS_FOURCC_BAND_LIST: hr = ExtractBand(pParser); if (hr != S_OK) { hrDLS = hr; } break; } break; } } pParser->LeaveList(); if (hr == S_OK && hrDLS != S_OK) { hr = hrDLS; } return hr; } ////////////////////////////////////////////////////////////////////// // CBandTrk::ExtractBand HRESULT CBandTrk::ExtractBand(CRiffParser *pParser) { HRESULT hrDLS = S_OK; RIFFIO ckNext; CBand *pBand = new CBand; if(pBand == NULL) { return E_OUTOFMEMORY; } HRESULT hr = S_OK; bool fFoundChunk2 = false; pParser->EnterList(&ckNext); while (pParser->NextChunk(&hr)) { switch(ckNext.ckid) { case DMUS_FOURCC_BANDITEM_CHUNK2: fFoundChunk2 = true; DMUS_IO_BAND_ITEM_HEADER2 ioDMBndItemHdr2; hr = pParser->Read(&ioDMBndItemHdr2, sizeof(DMUS_IO_BAND_ITEM_HEADER2)); if(SUCCEEDED(hr)) { pBand->m_lTimeLogical = ioDMBndItemHdr2.lBandTimeLogical; pBand->m_lTimePhysical = ioDMBndItemHdr2.lBandTimePhysical; } break; case DMUS_FOURCC_BANDITEM_CHUNK: // if there is both a CHUNK and a CHUNK2, use the info from CHUNK2 if (fFoundChunk2) break; DMUS_IO_BAND_ITEM_HEADER ioDMBndItemHdr; hr = pParser->Read(&ioDMBndItemHdr, sizeof(DMUS_IO_BAND_ITEM_HEADER)); if(SUCCEEDED(hr)) { pBand->m_lTimeLogical = ioDMBndItemHdr.lBandTime; pBand->m_lTimePhysical = pBand->m_lTimeLogical; } break; case FOURCC_RIFF: switch(ckNext.fccType) { case DMUS_FOURCC_BAND_FORM: pParser->SeekBack(); hr = LoadBand(pParser->GetStream(), pBand); pParser->SeekForward(); if (hr != S_OK) { hrDLS = hr; } break; } break; default: break; } } pParser->LeaveList(); if(SUCCEEDED(hr)) { hr = AddBand(pBand); } pBand->Release(); if (hr == S_OK && hrDLS != S_OK) { hr = hrDLS; } return hr; } ////////////////////////////////////////////////////////////////////// // CBandTrk::LoadBand HRESULT CBandTrk::LoadBand(IStream *pIStream, CBand* pBand) { assert(pIStream); assert(pBand); IPersistStream *pIPersistStream = NULL; HRESULT hr = pBand->QueryInterface(IID_IPersistStream, (void **)&pIPersistStream); if(SUCCEEDED(hr)) { hr = pIPersistStream->Load(pIStream); pIPersistStream->Release(); } return hr; } ////////////////////////////////////////////////////////////////////// // CBandTrk::LoadClone HRESULT CBandTrk::LoadClone(IDirectMusicBandTrk* pBandTrack, MUSIC_TIME mtStart, MUSIC_TIME mtEnd) { assert(pBandTrack); assert(mtStart <= mtEnd); EnterCriticalSection(&m_CriticalSection); HRESULT hr = S_OK; if (mtStart > 0) { // We will take all the bands before the start time and create a single new band // at logical time zero, physical time either -1 or one tick before the physical time // of the first band after the start time, that accumulates all the instrument changes // from the earlier bands. TList SEList; // Build a list of all the instrument changes for the new band DWORD dwLastMidiMode = 0; // Keep track of the MIDI mode of the last band we encounter for( CBand* pBand = BandList.GetHead(); pBand && pBand->m_lTimeLogical < mtStart; pBand = pBand->GetNext()) { for(CBandInstrument* pInstrument = (pBand->m_BandInstrumentList).GetHead(); pInstrument && SUCCEEDED(hr); pInstrument = pInstrument->GetNext()) { // replace if we already have an entry on that channel hr = FindSEReplaceInstr(SEList, pInstrument->m_dwPChannel, pInstrument); // otherwise add an entry if(hr == S_FALSE) { TListItem* pSEListItem = new TListItem; if(pSEListItem) { SeekEvent& rSeekEvent = pSEListItem->GetItemValue(); rSeekEvent.m_dwPChannel = pInstrument->m_dwPChannel; rSeekEvent.m_pInstrument = pInstrument; rSeekEvent.m_pParentBand = pBand; dwLastMidiMode = pBand->m_dwMidiMode; SEList.AddHead(pSEListItem); } else { hr = E_OUTOFMEMORY; } } } } // Make sure the physical time of the new band is less than any bands being cloned. MUSIC_TIME mtNewPhysicalTime = -1; if (pBand && pBand->m_lTimePhysical <= mtStart) { mtNewPhysicalTime = (pBand->m_lTimePhysical - mtStart) - 1; } // Create the new band from the instrument list TListItem* pSEListItem = SEList.GetHead(); if(SUCCEEDED(hr) && pSEListItem) { CBand *pNewBand = new CBand; if(pNewBand) { for(; pSEListItem && SUCCEEDED(hr); pSEListItem = pSEListItem->GetNext()) { SeekEvent& rSeekEvent = pSEListItem->GetItemValue(); hr = pNewBand->Load(rSeekEvent.m_pInstrument); } pNewBand->m_lTimeLogical = 0; pNewBand->m_lTimePhysical = mtNewPhysicalTime; pNewBand->m_dwFlags |= DMB_LOADED; pNewBand->m_dwMidiMode = dwLastMidiMode; if(SUCCEEDED(hr)) { hr = pBandTrack->AddBand(pNewBand); } pNewBand->Release(); } else { hr = E_OUTOFMEMORY; } } } // Copy all the bands between the start and the end time if(SUCCEEDED(hr)) { for(CBand* pBand = BandList.GetHead(); pBand && SUCCEEDED(hr); pBand = pBand->GetNext()) { // If mtStart is 0, accept bands with negative times. if ((!mtStart || (pBand->m_lTimeLogical >= mtStart)) && pBand->m_lTimeLogical < mtEnd) { CBand *pNewBand = new CBand; if (pNewBand) { CBandInstrument* pBandInstrument = pBand->m_BandInstrumentList.GetHead(); for(; pBandInstrument && SUCCEEDED(hr); pBandInstrument = pBandInstrument->GetNext()) { hr = pNewBand->Load(pBandInstrument); } pNewBand->m_lTimeLogical = pBand->m_lTimeLogical - mtStart; pNewBand->m_lTimePhysical = pBand->m_lTimePhysical - mtStart; pNewBand->m_dwFlags |= DMB_LOADED; pNewBand->m_dwMidiMode = pBand->m_dwMidiMode; if(SUCCEEDED(hr)) { hr = pBandTrack->AddBand(pNewBand); } pNewBand->Release(); } else { hr = E_OUTOFMEMORY; } } } } LeaveCriticalSection(&m_CriticalSection); return hr; } ////////////////////////////////////////////////////////////////////// // CBandTrk::Seek HRESULT CBandTrk::Seek(CBandTrkStateData* pBandTrkStateData, MUSIC_TIME mtStart, MUSIC_TIME mtOffset, REFERENCE_TIME rtOffset, bool fClockTime) { assert(pBandTrkStateData); EnterCriticalSection(&m_CriticalSection); HRESULT hr = S_OK; CBand *pBand; int iPrevBandCount = 0; // count how many bands before mtStart for (pBand = BandList.GetHead(); pBand && pBand->m_lTimeLogical < mtStart; pBand = pBand->GetNext()) { ++iPrevBandCount; } // pBand now holds the first band >= mtStart (or NULL if none) // This is the next band that will be played. assert(!pBand || pBand->m_lTimeLogical >= mtStart); if (pBandTrkStateData->m_fPlayPreviousInSeek) { // When this flag is on not only do we need to find the first band, but we also // need to play all the bands before the start point and schedule them to play // in the correct order just beforehand. // (Note that we're going to order them according to their logical times. If // two bands's logical/physical times cross each other we'll play them in // incorrect order in terms of physical time. That's OK because giving // band A with a logical time before band B, yet giving A a physical time // after B is considered an authoring inconsistency. We'll play band A first.) // We will line up the bands just before the following time... MUSIC_TIME mtPrevBandQueueStart = (pBand && pBand->m_lTimePhysical < mtStart) ? pBand->m_lTimePhysical // put previous bands before next band to play if (due to anticipation) its physical time precedes the start time we're seeking : mtStart; // otherwise put them just before the start time for (pBand = BandList.GetHead(); pBand && pBand->m_lTimeLogical < mtStart; pBand = pBand->GetNext()) { CBandInstrument* pInstrument = (pBand->m_BandInstrumentList).GetHead(); for (; pInstrument && SUCCEEDED(hr); pInstrument = pInstrument->GetNext()) { pBand->SendInstrumentAtTime(pInstrument, pBandTrkStateData, mtPrevBandQueueStart - iPrevBandCount, mtOffset, rtOffset, fClockTime); } --iPrevBandCount; } assert(iPrevBandCount == 0); } if(SUCCEEDED(hr)) { // Set the state data to the next band to play assert(!pBand || pBand->m_lTimeLogical >= mtStart); pBandTrkStateData->m_pNextBandToSPE = pBand; } LeaveCriticalSection(&m_CriticalSection); return hr; } ////////////////////////////////////////////////////////////////////// // CBandTrk::FindSEReplaceInstr // If SEList contains an entry on channel dwPChannel, replace the instrument with pInstrument and return S_OK // Otherwise return S_FALSE HRESULT CBandTrk::FindSEReplaceInstr(TList& SEList, DWORD dwPChannel, CBandInstrument* pInstrument) { assert(pInstrument); EnterCriticalSection(&m_CriticalSection); TListItem* pSEListItem = SEList.GetHead(); for( ; pSEListItem; pSEListItem = pSEListItem->GetNext()) { SeekEvent& rSeekEvent = pSEListItem->GetItemValue(); if(rSeekEvent.m_dwPChannel == dwPChannel) { rSeekEvent.m_pInstrument = pInstrument; LeaveCriticalSection(&m_CriticalSection); return S_OK; } } LeaveCriticalSection(&m_CriticalSection); return S_FALSE; } ////////////////////////////////////////////////////////////////////// // CBandTrk::InsertBand HRESULT CBandTrk::InsertBand(CBand* pNewBand) { if (!pNewBand) return E_POINTER; EnterCriticalSection(&m_CriticalSection); TListItem* pPair = m_MidiModeList.GetHead(); for ( ; pPair; pPair = pPair->GetNext() ) { StampedGMGSXG& rPair = pPair->GetItemValue(); if (rPair.mtTime > pNewBand->m_lTimeLogical) { break; } pNewBand->SetGMGSXGMode(rPair.dwMidiMode); } CBand* pBand = BandList.GetHead(); CBand* pPrevBand = NULL; if(pBand == NULL) { // Handles case where there is no band in the list BandList.AddHead(pNewBand); } else { while(pBand != NULL && pNewBand->m_lTimeLogical > pBand->m_lTimeLogical) { pPrevBand = pBand; pBand = pBand->GetNext(); } if(pPrevBand) { // Handles the cases of inserting a band in the middle of list // and at the end CBand* pTemp = pPrevBand->GetNext(); pPrevBand->SetNext(pNewBand); pNewBand->SetNext(pTemp); } else { // Handles case where pNewBand->m_lTimeLogical < all pBand->m_lTimeLogical in list BandList.AddHead(pNewBand); } } LeaveCriticalSection(&m_CriticalSection); return S_OK; } STDMETHODIMP CBandTrk::Compose( IUnknown* pContext, DWORD dwTrackGroup, IDirectMusicTrack** ppResultTrack) { return E_NOTIMPL; } STDMETHODIMP CBandTrk::Join( IDirectMusicTrack* pNewTrack, MUSIC_TIME mtJoin, IUnknown* pContext, DWORD dwTrackGroup, IDirectMusicTrack** ppResultTrack) { V_INAME(IDirectMusicTrack::Join); V_INTERFACE(pNewTrack); V_INTERFACE_OPT(pContext); V_PTRPTR_WRITE_OPT(ppResultTrack); HRESULT hr = S_OK; EnterCriticalSection(&m_CriticalSection); if (ppResultTrack) { hr = Clone(0, mtJoin, ppResultTrack); if (SUCCEEDED(hr)) { hr = ((CBandTrk*)*ppResultTrack)->JoinInternal(pNewTrack, mtJoin, dwTrackGroup); } } else { hr = JoinInternal(pNewTrack, mtJoin, dwTrackGroup); } LeaveCriticalSection(&m_CriticalSection); return hr; } HRESULT CBandTrk::JoinInternal( IDirectMusicTrack* pNewTrack, MUSIC_TIME mtJoin, DWORD dwTrackGroup) { HRESULT hr = S_OK; CBandTrk* pOtherTrack = (CBandTrk*)pNewTrack; for(CBand* pBand = pOtherTrack->BandList.GetHead(); pBand && SUCCEEDED(hr); pBand = pBand->GetNext()) { CBand *pNewBand = new CBand; if (pNewBand) { CBandInstrument* pBandInstrument = pBand->m_BandInstrumentList.GetHead(); for(; pBandInstrument && SUCCEEDED(hr); pBandInstrument = pBandInstrument->GetNext()) { hr = pNewBand->Load(pBandInstrument); } pNewBand->m_lTimeLogical = pBand->m_lTimeLogical + mtJoin; pNewBand->m_lTimePhysical = pBand->m_lTimePhysical + mtJoin; pNewBand->m_dwFlags |= DMB_LOADED; pNewBand->m_dwMidiMode = pBand->m_dwMidiMode; if(SUCCEEDED(hr)) { hr = AddBand(pNewBand); } pNewBand->Release(); } else { hr = E_OUTOFMEMORY; } } return hr; }