// // dmband.cpp // // Copyright (c) 1997-1999 Microsoft Corporation // #define INITGUID #include #include "debug.h" #include "dmksctrl.h" #include "dmusicc.h" #include "dmusici.h" #include "..\shared\dmstrm.h" #include "dmbandp.h" #include "bandtrk.h" #include "debug.h" #define MAX_LEGACY_BAND_NAME 20 #define MAX_LEGACY_COLLECTION_NAME 32 extern long g_cComponent; static GUID nullGUID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; ////////////////////////////////////////////////////////////////////// // Class CBand ////////////////////////////////////////////////////////////////////// // CBand::CBand CBand::CBand() { m_lTimeLogical = 0; m_lTimePhysical = 0; m_dwFlags = 0; m_dwGroupBits = 0xffffffff; m_dwMidiMode = 0; m_cRef = 1; m_fCSInitialized = FALSE; InterlockedIncrement(&g_cComponent); // Do this first since it can throw an exception // InitializeCriticalSection(&m_CriticalSection); m_fCSInitialized = TRUE; m_dwValidData = 0; } ////////////////////////////////////////////////////////////////////// // CBand::~CBand CBand::~CBand() { if (m_fCSInitialized) { m_BandInstrumentList.Clear(); DeleteCriticalSection(&m_CriticalSection); } InterlockedDecrement(&g_cComponent); } void CBandInstrumentList::Clear() { CBandInstrument* pBandInstrument; while(pBandInstrument = RemoveHead()) { delete pBandInstrument; } } ////////////////////////////////////////////////////////////////////// // IUnknown ////////////////////////////////////////////////////////////////////// // CBand::QueryInterface STDMETHODIMP CBand::QueryInterface(const IID &iid, void **ppv) { V_INAME(CBand::QueryInterface); V_PTRPTR_WRITE(ppv); V_REFGUID(iid); *ppv = NULL; if(iid == IID_IUnknown || iid == IID_IDirectMusicBand) { *ppv = static_cast(this); } else if(iid == IID_IDirectMusicBandP) { *ppv = static_cast(this); } else if(iid == IID_IDirectMusicBandPrivate) { *ppv = static_cast(this); } else if(iid == IID_IDirectMusicObject) { *ppv = static_cast(this); } else if(iid == IID_IPersistStream) { *ppv = static_cast(this); } else if(iid == IID_IPersist) { *ppv = static_cast(this); } if (*ppv == NULL) return E_NOINTERFACE; reinterpret_cast(this)->AddRef(); return S_OK; } ////////////////////////////////////////////////////////////////////// // CBand::AddRef STDMETHODIMP_(ULONG) CBand::AddRef() { return InterlockedIncrement(&m_cRef); } ////////////////////////////////////////////////////////////////////// // CBand::Release STDMETHODIMP_(ULONG) CBand::Release() { if (!InterlockedDecrement(&m_cRef)) { m_cRef = 100; // artificial reference count to prevent reentrency due to COM aggregation delete this; return 0; } return m_cRef; } ////////////////////////////////////////////////////////////////////// // IPersistStream ////////////////////////////////////////////////////////////////////// // CBand::Load STDMETHODIMP CBand::Load(IStream* pStream) { V_INAME(CBand::Load); V_PTR_READ(pStream, IStream); // Get the loader from stream if it has one // so we can open required collections IDirectMusicLoader* pIDMLoader = NULL; IDirectMusicGetLoader *pIDMGetLoader = NULL; if (SUCCEEDED(pStream->QueryInterface(IID_IDirectMusicGetLoader,(void **)&pIDMGetLoader))) { pIDMGetLoader->GetLoader(&pIDMLoader); pIDMGetLoader->Release(); } else { Trace(1,"Error: Band unable to reference DLS Collections because IStream does not support Loader.\n"); return DMUS_E_UNSUPPORTED_STREAM; } EnterCriticalSection(&m_CriticalSection); // If we have been previously loaded, clean up instruments if(m_dwFlags & DMB_LOADED) { m_dwValidData = 0; m_BandInstrumentList.Clear(); m_lTimeLogical = 0; m_lTimePhysical = 0; m_dwFlags = 0; } RIFFIO ckMain; HRESULT hr = S_OK; CRiffParser Parser(pStream); Parser.EnterList(&ckMain); if (Parser.NextChunk(&hr)) { if (ckMain.fccType == FOURCC_BAND_FORM) { hr = LoadLegacyBand(&Parser, pIDMLoader); } else if(ckMain.fccType == DMUS_FOURCC_BAND_FORM) { hr = LoadDirectMusicBand(&Parser, pIDMLoader); } else { Trace(1,"Error: Failure Parsing Band - invalid chunk ID.\n"); hr = DMUS_E_INVALID_BAND; } } if(FAILED(hr)) { m_BandInstrumentList.Clear(); } if(pIDMLoader) { pIDMLoader->Release(); } LeaveCriticalSection(&m_CriticalSection); return hr; } ////////////////////////////////////////////////////////////////////// // IDirectMusicBand ////////////////////////////////////////////////////////////////////// // CBand::CreateSegment STDMETHODIMP CBand::CreateSegment(IDirectMusicSegment** ppSegment) { V_INAME(IDirectMusicBand::CreateSegment); V_PTRPTR_WRITE(ppSegment); HRESULT hr = CoCreateInstance(CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC, IID_IDirectMusicSegment, (void**)ppSegment); if(SUCCEEDED(hr)) { IDirectMusicTrack* pDMTrack = NULL; CBandTrk *pBandTrack; // Create Band track pBandTrack = new CBandTrk; if (pBandTrack) { pBandTrack->QueryInterface(IID_IDirectMusicTrack,(void**)&pDMTrack); // Add band to track m_lTimePhysical--; // Subtract one from the time when creating the segment. This is somewhat arbitrary. (See NT5 bug 226848.) hr = pBandTrack->AddBand(static_cast(this)); m_lTimePhysical++; // add the one back in // Set Auto-download to off pBandTrack->m_bAutoDownload = false; pBandTrack->m_fLockAutoDownload = true; // Insert track into segment hr = (*ppSegment)->InsertTrack(pDMTrack, 1); pDMTrack->Release(); pBandTrack->Release(); // We don't need the original count created by the constructor. } else { hr = E_OUTOFMEMORY; } } if(FAILED(hr)) { if(*ppSegment) { (*ppSegment)->Release(); *ppSegment = NULL; } } return hr; } HRESULT CBandInstrument::Download(IDirectMusicPerformanceP *pPerformance, IDirectMusicAudioPath *pPath, DWORD dwMidiMode) { DWORD dwPChannel; HRESULT hr = S_OK; // First, if there is an audiopath, convert the band's pchannel to performance pchannel. if (pPath) { hr = pPath->ConvertPChannel(m_dwPChannel,&dwPChannel); if (FAILED(hr)) { Trace(1,"Error: Couldn't download to Audiopath because pchannel %ld is out of bounds\n",m_dwPChannel); // Not a valid pchannel on this audiopath. return hr; } } else { dwPChannel = m_dwPChannel; } // We need to get the port we will be downloading to. IDirectMusicPort *pPort = NULL; DWORD dwGMFlags; BOOL fDownload = TRUE; hr = pPerformance->GetPortAndFlags(dwPChannel,&pPort,&dwGMFlags); // Once we know the port, we can find out whether a download has // already occured. And, if not, we'll use that to do the download. if (SUCCEEDED(hr)) { CDownloadedInstrument* pDLInstrument = m_DownloadList.GetHead(); for(; pDLInstrument; pDLInstrument = pDLInstrument->GetNext()) { if (pDLInstrument->m_pPort == pPort) { // Increment reference counter and leave. pDLInstrument->m_cRef++; pPort->Release(); return S_OK; } } // Okay, didn't find it, so we need to create a download record and download it. if(m_fNotInFile && !m_fGMOnly) { // Unless we've set the GMOnly flag, don't download an instrument // that was automatically generated from the midi // parsing to give a patchless channel an instrument. fDownload = FALSE; } else if (m_pIDMCollection == NULL) { // Can not download this instrument but still want to add a record and continue with others. // If instrument is a GM and GS instrument it may still play if GM or GS is supported in hardware. fDownload = FALSE; Trace(2,"Warning: No collection, unable to download instrument %lx on PChannel %ld\n",m_dwPatch,m_dwPChannel); } if (m_dwFlags & DMUS_IO_INST_GS) { // If this is a GS instrument, determine whether it needs to be downloaded. if ((dwGMFlags & DM_PORTFLAGS_GM) && (m_dwFlags & DMUS_IO_INST_GM)) { // The synth has a GM set in ROM, and this is a GM instrument, // and the instrument does not specifically requests that it use the // DLS version in gm.dls. if (!(m_dwFlags & DMUS_IO_INST_USE_DEFAULT_GM_SET) ) { fDownload = FALSE; } } else if (dwGMFlags & DM_PORTFLAGS_GS) { // If the synth has a GS set, the problem is simpler, since it is going to be very similar to our // gm.dls set, so it is okay to use it. fDownload = FALSE; } } if( dwMidiMode ) // if this is anything, it indicates we were loaded from a midi file { // if we're not an XG file, make sure channel 9 is drums if( (dwMidiMode != DMUS_MIDIMODEF_XG) && (m_dwPChannel == 9) ) { m_dwPatch |= 0x80000000; } } // Okay, ready to download... if (fDownload) { hr = DownloadAddRecord(pPort); // Use fallbacks for XG mode if( FAILED(hr) && dwMidiMode == DMUS_MIDIMODEF_XG ) { DWORD dwOldPatch = m_dwPatch; DWORD dwOldFlags = m_dwFlags; DWORD dwOldAssignPatch = m_dwAssignPatch; // If this band failed, try clearing the MSB. If it was an XG or GS instrument, // and the collection doesn't have the instrument, clearing the MSB is a // good fallback. If that doesn't work, try clearing the LSB. // Also, if this band is XG see if it is on the drum channel. If so, // try setting the drum bit. if( (m_dwPatch & 0x00ff0000) == 0x007f0000 ) { // XG drums. Try GM drums instead, but keep program change m_dwPatch &= 0xff0000ff; // clear MSB and LSB m_dwPatch |= 0x80000000; // set drum bit m_dwFlags |= DMUS_IO_INST_ASSIGN_PATCH; m_dwAssignPatch = dwOldPatch & 0x00ffffff; hr = DownloadAddRecord(pPort); if( FAILED(hr) ) { // If that didn't work, try unsetting the program change m_dwPatch = 0x80000000; hr = DownloadAddRecord(pPort); } } else { if( (m_dwPatch & 0x00ff0000) != 0x007e0000 ) { m_dwPatch &= 0xffff00ff; // clear LSB hr = DownloadAddRecord(pPort); if( FAILED(hr) ) { if( m_dwPatch & 0x0000ff00 ) { m_dwPatch &= 0xff0000ff; // clear MSB & LSB hr = DownloadAddRecord(pPort); } } } } if (FAILED(hr)) { // Revert back to original values m_dwPatch = dwOldPatch; m_dwFlags = dwOldFlags; m_dwAssignPatch = dwOldAssignPatch; } } } pPort->Release(); } else { Trace(1,"Error: Unable to download to Performance because pchannel %ld is not initialized on the performance.\n",m_dwPChannel); } return hr; } ////////////////////////////////////////////////////////////////////// // CBand::DownloadEx STDMETHODIMP CBand::DownloadEx(IUnknown *pAudioPath) { V_INAME(CBand::DownloadEx); V_PTR_READ(pAudioPath, IUnknown); BOOL fGotOneDown = FALSE; BOOL fNoInit = FALSE; IDirectMusicPerformance *pPerformance = NULL; IDirectMusicAudioPath *pPath = NULL; // If the band doesn't have any instruments, return immediately with S_FALSE. if (m_BandInstrumentList.IsEmpty()) { Trace(2,"Warning: Trying to download an empty band\n"); return S_FALSE; } HRESULT hr = pAudioPath->QueryInterface(IID_IDirectMusicAudioPath,(void **)&pPath); if (SUCCEEDED(hr)) { hr = pPath->GetObjectInPath(0,DMUS_PATH_PERFORMANCE,0,CLSID_DirectMusicPerformance,0,IID_IDirectMusicPerformance,(void **)&pPerformance); } else { hr = pAudioPath->QueryInterface(IID_IDirectMusicPerformance,(void **)&pPerformance); } if (SUCCEEDED(hr)) { EnterCriticalSection(&m_CriticalSection); IDirectMusicPerformanceP *pPerfp; hr = pPerformance->QueryInterface(IID_IDirectMusicPerformanceP, (void **)&pPerfp); if (SUCCEEDED(hr)) { DWORD dwSuccess = 0; HRESULT hrTemp = S_OK; CBandInstrument* pBandInstrument = m_BandInstrumentList.GetHead(); for( ; SUCCEEDED(hr) && pBandInstrument != NULL; pBandInstrument = pBandInstrument->GetNext()) { hr = pBandInstrument->Download(pPerfp,pPath,m_dwMidiMode); if (FAILED(hr)) { if (hr == DMUS_E_NOT_INIT) { Trace(1,"Error: Performance is not initialized - Band download terminated.\n"); // Performance is not initialized. Leave now. break; } hrTemp = hr; hr = S_FALSE; } else { // At least one succeeded. dwSuccess++; } } // If we had a failure but it was not performance not initialized and we did have at least one // successful download, return a partial download success code. if (FAILED(hrTemp)) { // Was this a partial download? if ((hr != DMUS_E_NOT_INIT) && dwSuccess) { hr = DMUS_S_PARTIALDOWNLOAD; } // Otherwise, make sure we don't return S_FALSE for hr! else { hr = hrTemp; } } pPerfp->Release(); } LeaveCriticalSection(&m_CriticalSection); } if (pPath) pPath->Release(); if (pPerformance) pPerformance->Release(); return hr; } STDMETHODIMP CBand::Download( IDirectMusicPerformance* pPerformance) // @parm Performance to download instruments // to. The performance manages the mapping // of PChannels to DirectMusic ports. { V_INAME(CBand::Download); V_PTR_READ(pPerformance, IDirectMusicPerformance); return DownloadEx(pPerformance); } HRESULT CBandInstrument::Unload(IDirectMusicPerformanceP *pPerformance, IDirectMusicAudioPath *pPath) { DWORD dwPChannel; HRESULT hr = S_OK; // First, if there is an audiopath, convert the band's pchannel to performance pchannel. if (pPath) { hr = pPath->ConvertPChannel(m_dwPChannel,&dwPChannel); if (FAILED(hr)) { Trace(1,"Error: Couldn't download to Audiopath because pchannel %ld is out of bounds\n",m_dwPChannel); // Not a valid pchannel on this audiopath. return hr; } } else { dwPChannel = m_dwPChannel; } // We need to get the port we will be unloading from. IDirectMusicPort *pPort = NULL; DWORD dwGMFlags; hr = pPerformance->GetPortAndFlags(dwPChannel,&pPort,&dwGMFlags); if (SUCCEEDED(hr)) { hr = S_FALSE; // Just in case we don't find the download record. CDownloadedInstrument* pDLInstrument = m_DownloadList.GetHead(); for(; pDLInstrument; pDLInstrument = pDLInstrument->GetNext()) { if (pDLInstrument->m_pPort == pPort) { pDLInstrument->m_cRef--; if(!pDLInstrument->m_cRef) { m_DownloadList.Remove(pDLInstrument); if (FAILED(pPort->UnloadInstrument(pDLInstrument->m_pDLInstrument))) { Trace(1, "Error: UnloadInstrument %ld failed\n",m_dwPatch); } pDLInstrument->m_pDLInstrument->Release(); pDLInstrument->m_pDLInstrument = NULL; delete pDLInstrument; } hr = S_OK; break; } } pPort->Release(); } else if (!pPath && m_DownloadList.GetCount() == 1) { CDownloadedInstrument* pDLInstrument = m_DownloadList.GetHead(); pDLInstrument->m_cRef--; if (!pDLInstrument->m_cRef) { m_DownloadList.Remove(pDLInstrument); if (FAILED(pDLInstrument->m_pPort->UnloadInstrument(pDLInstrument->m_pDLInstrument))) { Trace(1, "Error: UnloadInstrument %ld failed\n",m_dwPatch); } pDLInstrument->m_pDLInstrument->Release(); pDLInstrument->m_pDLInstrument = NULL; delete pDLInstrument; } hr = S_OK; } return hr; } ////////////////////////////////////////////////////////////////////// // CBand::UnloadEx STDMETHODIMP CBand::UnloadEx(IUnknown *pAudioPath) { V_INAME(CBand::UnloadEx); V_PTR_READ(pAudioPath, IUnknown); IDirectMusicPerformance *pPerformance = NULL; IDirectMusicAudioPath *pPath = NULL; HRESULT hr = pAudioPath->QueryInterface(IID_IDirectMusicAudioPath,(void **)&pPath); if (SUCCEEDED(hr)) { hr = pPath->GetObjectInPath(0,DMUS_PATH_PERFORMANCE,0,CLSID_DirectMusicPerformance,0,IID_IDirectMusicPerformance,(void **)&pPerformance); } else { hr = pAudioPath->QueryInterface(IID_IDirectMusicPerformance,(void **)&pPerformance); } if (SUCCEEDED(hr)) { hr = S_FALSE; // Returns this for empty band. EnterCriticalSection(&m_CriticalSection); IDirectMusicPerformanceP *pPerfp; hr = pPerformance->QueryInterface(IID_IDirectMusicPerformanceP, (void **)&pPerfp); if (SUCCEEDED(hr)) { CBandInstrument* pBandInstrument = m_BandInstrumentList.GetHead(); for( ; pBandInstrument != NULL; pBandInstrument = pBandInstrument->GetNext()) { hr = pBandInstrument->Unload(pPerfp,pPath); } pPerfp->Release(); } LeaveCriticalSection(&m_CriticalSection); } if (pPath) pPath->Release(); if (pPerformance) pPerformance->Release(); return hr; } STDMETHODIMP CBand::Unload( IDirectMusicPerformance* pPerformance) // @parm Performance to unload instruments // from. The performance manages the mapping // of PChannels to DirectMusic ports. { V_INAME(CBand::Unload); V_PTR_READ(pPerformance, IDirectMusicPerformance); return UnloadEx(pPerformance); } ////////////////////////////////////////////////////////////////////// // IDirectMusicObject ////////////////////////////////////////////////////////////////////// // CBand::GetDescriptor STDMETHODIMP CBand::GetDescriptor(LPDMUS_OBJECTDESC pDesc) { V_INAME(CBand::GetDescriptor); V_PTR_WRITE(pDesc, DMUS_OBJECTDESC); if (pDesc->dwSize) { V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC); } else { pDesc->dwSize = sizeof(DMUS_OBJECTDESC); } pDesc->guidClass = CLSID_DirectMusicBand; pDesc->guidObject = m_guidObject; pDesc->ftDate = m_ftDate; pDesc->vVersion = m_vVersion; memcpy( pDesc->wszName, m_wszName, sizeof(m_wszName) ); memcpy( pDesc->wszCategory, m_wszCategory, sizeof(m_wszCategory) ); memcpy( pDesc->wszFileName, m_wszFileName, sizeof(m_wszFileName) ); pDesc->dwValidData = ( m_dwValidData | DMUS_OBJ_CLASS ); return S_OK; } ////////////////////////////////////////////////////////////////////// // CBand::SetDescriptor STDMETHODIMP CBand::SetDescriptor(LPDMUS_OBJECTDESC pDesc) { // Argument validation V_INAME(CBand::SetDescriptor); V_PTR_READ(pDesc, DMUS_OBJECTDESC); if (pDesc->dwSize) { V_STRUCTPTR_READ(pDesc, DMUS_OBJECTDESC); } HRESULT hr = E_INVALIDARG; DWORD dw = 0; if( pDesc->dwValidData & DMUS_OBJ_OBJECT ) { m_guidObject = pDesc->guidObject; dw |= DMUS_OBJ_OBJECT; } if( pDesc->dwValidData & DMUS_OBJ_NAME ) { memcpy( m_wszName, pDesc->wszName, sizeof(WCHAR)*DMUS_MAX_NAME ); dw |= DMUS_OBJ_NAME; } if( pDesc->dwValidData & DMUS_OBJ_CATEGORY ) { memcpy( m_wszCategory, pDesc->wszCategory, sizeof(WCHAR)*DMUS_MAX_CATEGORY ); dw |= DMUS_OBJ_CATEGORY; } if( ( pDesc->dwValidData & DMUS_OBJ_FILENAME ) || ( pDesc->dwValidData & DMUS_OBJ_FULLPATH ) ) { memcpy( m_wszFileName, pDesc->wszFileName, sizeof(WCHAR)*DMUS_MAX_FILENAME ); dw |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)); } if( pDesc->dwValidData & DMUS_OBJ_VERSION ) { m_vVersion = pDesc->vVersion; dw |= DMUS_OBJ_VERSION; } if( pDesc->dwValidData & DMUS_OBJ_DATE ) { m_ftDate = pDesc->ftDate; dw |= DMUS_OBJ_DATE; } m_dwValidData |= dw; if( pDesc->dwValidData & (~dw) ) { hr = S_FALSE; // there were extra fields we didn't parse; pDesc->dwValidData = dw; } else { hr = S_OK; } return hr; } ////////////////////////////////////////////////////////////////////// // CBand::ParseDescriptor STDMETHODIMP CBand::ParseDescriptor(LPSTREAM pStream, LPDMUS_OBJECTDESC pDesc) { V_INAME(CBand::ParseDescriptor); V_PTR_READ(pStream, IStream); V_PTR_WRITE(pDesc, DMUS_OBJECTDESC); if (pDesc->dwSize) { V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC); } else { pDesc->dwSize = sizeof(DMUS_OBJECTDESC); } RIFFIO ckMain; HRESULT hr = S_OK; CRiffParser Parser(pStream); Parser.EnterList(&ckMain); if (Parser.NextChunk(&hr)) { pDesc->guidClass = CLSID_DirectMusicBand; pDesc->dwValidData |= DMUS_OBJ_CLASS; if (ckMain.fccType == FOURCC_BAND_FORM) { hr = ParseLegacyDescriptor(&Parser, pDesc); } else if(ckMain.fccType == DMUS_FOURCC_BAND_FORM) { hr = ParseDirectMusicDescriptor(&Parser, pDesc); } else { Trace(2,"Warning: ParseDescriptor failed because this is not a Band file.\n"); hr = DMUS_E_INVALID_BAND; } } return hr; } ////////////////////////////////////////////////////////////////////// // Internal ////////////////////////////////////////////////////////////////////// // CBand::ParseLegacyDescriptor HRESULT CBand::ParseLegacyDescriptor(CRiffParser *pParser, LPDMUS_OBJECTDESC pDesc) { RIFFIO ckNext; HRESULT hr = S_OK; pParser->EnterList(&ckNext); while(pParser->NextChunk(&hr)) { if (ckNext.ckid == FOURCC_BAND) { ioBandLegacy Band; hr = pParser->Read( &Band, sizeof(Band) ); if( SUCCEEDED(hr) ) { pDesc->dwValidData |= DMUS_OBJ_NAME; wcsncpy(pDesc->wszName, Band.wstrName, MAX_LEGACY_BAND_NAME); pDesc->wszName[MAX_LEGACY_BAND_NAME - 1] = 0; } } } return hr; } ////////////////////////////////////////////////////////////////////// // CBand::ParseDirectMusicDescriptor HRESULT CBand::ParseDirectMusicDescriptor(CRiffParser *pParser, LPDMUS_OBJECTDESC pDesc) { RIFFIO ckNext; RIFFIO ckUNFO; HRESULT hr = S_OK; DWORD dwValidData = pDesc->dwValidData; pParser->EnterList(&ckNext); while(pParser->NextChunk(&hr)) { switch(ckNext.ckid) { case DMUS_FOURCC_GUID_CHUNK: hr = pParser->Read( &pDesc->guidObject, sizeof(GUID) ); dwValidData |= DMUS_OBJ_OBJECT; break; case DMUS_FOURCC_VERSION_CHUNK: hr = pParser->Read( &pDesc->vVersion, sizeof(DMUS_VERSION) ); dwValidData |= DMUS_OBJ_VERSION; break; case DMUS_FOURCC_CATEGORY_CHUNK: hr = pParser->Read( &pDesc->wszCategory, sizeof(pDesc->wszCategory) ); pDesc->wszCategory[DMUS_MAX_CATEGORY - 1] = 0; dwValidData |= DMUS_OBJ_CATEGORY; case DMUS_FOURCC_DATE_CHUNK: hr = pParser->Read( &pDesc->ftDate, sizeof(FILETIME) ); dwValidData |= DMUS_OBJ_DATE; break; case FOURCC_LIST: switch(ckNext.fccType) { case DMUS_FOURCC_UNFO_LIST: pParser->EnterList(&ckUNFO); while (pParser->NextChunk(&hr)) { if (( ckUNFO.ckid == DMUS_FOURCC_UNAM_CHUNK ) || (ckUNFO.ckid == mmioFOURCC('I','N','A','M'))) { hr = pParser->Read(&pDesc->wszName, sizeof(pDesc->wszName)); pDesc->wszName[DMUS_MAX_NAME - 1] = 0; dwValidData |= DMUS_OBJ_NAME; } } pParser->LeaveList(); break; } break; } } if (SUCCEEDED(hr)) { pDesc->dwValidData = dwValidData; } return hr; } ////////////////////////////////////////////////////////////////////// // CBand::LoadLegacyBand HRESULT CBand::LoadLegacyBand(CRiffParser *pParser, IDirectMusicLoader* pIDMLoader) { RIFFIO ckNext; HRESULT hr = S_OK; pParser->EnterList(&ckNext); while(pParser->NextChunk(&hr)) { if (ckNext.ckid == FOURCC_BAND) { ioBandLegacy Band; hr = pParser->Read( &Band, sizeof(Band) ); if( SUCCEEDED(hr) ) { wcsncpy(m_wszName, Band.wstrName, MAX_LEGACY_BAND_NAME); m_wszName[MAX_LEGACY_BAND_NAME - 1] = 0; m_dwValidData |= DMUS_OBJ_NAME; hr = BuildLegacyInstrumentList(Band, pIDMLoader); if (SUCCEEDED(hr)) { m_dwFlags |= DMB_LOADED; if(Band.fDefault) { m_dwFlags |= DMB_DEFAULT; } } } } } return hr; } ////////////////////////////////////////////////////////////////////// // CBand::LoadDirectMusicBand HRESULT CBand::LoadDirectMusicBand(CRiffParser *pParser, IDirectMusicLoader *pIDMLoader) { HRESULT hrDLS = S_OK; RIFFIO ckNext; RIFFIO ckChild; HRESULT hr = S_OK; pParser->EnterList(&ckNext); while(pParser->NextChunk(&hr)) { switch(ckNext.ckid) { case DMUS_FOURCC_GUID_CHUNK: hr = pParser->Read( &m_guidObject, sizeof(GUID) ); m_dwValidData |= DMUS_OBJ_OBJECT; break; case DMUS_FOURCC_VERSION_CHUNK: hr = pParser->Read( &m_vVersion, sizeof(DMUS_VERSION) ); m_dwValidData |= DMUS_OBJ_VERSION; break; case DMUS_FOURCC_CATEGORY_CHUNK: hr = pParser->Read( &m_wszCategory, sizeof(WCHAR)*DMUS_MAX_CATEGORY ); m_wszCategory[DMUS_MAX_CATEGORY - 1] = 0; m_dwValidData |= DMUS_OBJ_CATEGORY; break; case DMUS_FOURCC_DATE_CHUNK: hr = pParser->Read( &m_ftDate, sizeof(FILETIME) ); m_dwValidData |= DMUS_OBJ_DATE; break; case FOURCC_LIST: switch(ckNext.fccType) { case DMUS_FOURCC_UNFO_LIST: pParser->EnterList(&ckChild); while (pParser->NextChunk(&hr)) { if (( ckChild.ckid == DMUS_FOURCC_UNAM_CHUNK ) || (ckChild.ckid == mmioFOURCC('I','N','A','M'))) { hr = pParser->Read(&m_wszName, sizeof(m_wszName)); m_wszName[DMUS_MAX_NAME - 1] = 0; m_dwValidData |= DMUS_OBJ_NAME; } } pParser->LeaveList(); break; case DMUS_FOURCC_INSTRUMENTS_LIST: pParser->EnterList(&ckChild); while (pParser->NextChunk(&hr)) { if ((ckChild.ckid == FOURCC_LIST) && (ckChild.fccType == DMUS_FOURCC_INSTRUMENT_LIST)) { hr = ExtractBandInstrument(pParser, pIDMLoader); if (hr != S_OK) { hrDLS = hr; } } } pParser->LeaveList(); break; } } } pParser->LeaveList(); if (hr == S_OK && hrDLS != S_OK) { hr = hrDLS; } return hr; } ////////////////////////////////////////////////////////////////////// // CBand::BuildLegacyInstrumentList HRESULT CBand::BuildLegacyInstrumentList(const ioBandLegacy& iob, IDirectMusicLoader* pIDMLoader) { // Legacy band channel to pchannel translation table static char sj_translation_table[] = { -1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3 }; HRESULT hrGM = S_OK; HRESULT hr = S_OK; EnterCriticalSection(&m_CriticalSection); char szCollection[DM_LEGACY_BAND_COLLECTION_NAME_LEN]; for(DWORD i = 0; SUCCEEDED(hr) && i < DMBAND_NUM_LEGACY_INSTRUMENTS; i++) { CBandInstrument* pBandInstrument = new CBandInstrument(); if(pBandInstrument) { if(iob.awDLSBank[i] & 0x8000) { // We have a plain old GM collection where MSB & LSB are both zero pBandInstrument->m_dwPatch = 0; pBandInstrument->m_dwPatch |= (iob.abPatch[i] & 0x7F); pBandInstrument->m_dwFlags |= (DMUS_IO_INST_GM | DMUS_IO_INST_GS); } else { if(iob.awDLSBank[i] & 0x4000) { // We has a GS collection with valid MSB and LSB numbers pBandInstrument->m_dwPatch = 0; pBandInstrument->m_dwPatch |= (iob.abDLSPatch[i] & 0x7F); pBandInstrument->m_dwPatch |= (iob.awDLSBank[i] & 0x7F) << 8; // Set LSB pBandInstrument->m_dwPatch |= ((iob.awDLSBank[i] >> 7) & 0x7F) << 16; // Set MSB pBandInstrument->m_dwFlags |= (DMUS_IO_INST_BANKSELECT | DMUS_IO_INST_GS | DMUS_IO_INST_GM); } else { if(iob.szCollection[0] == '\0') { // We have no unique DLS file so we will assume GM pBandInstrument->m_dwPatch = 0; pBandInstrument->m_dwPatch |= (iob.abPatch[i] & 0x7F); pBandInstrument->m_dwFlags |= (DMUS_IO_INST_GM | DMUS_IO_INST_GS); } else { // We have a unique DLS file pBandInstrument->m_dwPatch = 0; pBandInstrument->m_dwPatch |= (iob.abDLSPatch[i] & 0x7F); pBandInstrument->m_dwPatch |= (iob.awDLSBank[i] & 0x7F) << 8; // Set LSB pBandInstrument->m_dwPatch |= ((iob.awDLSBank[i] >> 7) & 0x7F) << 16; // Set MSB pBandInstrument->m_dwFlags |= (DMUS_IO_INST_BANKSELECT); lstrcpyn(szCollection, iob.szCollection, MAX_LEGACY_COLLECTION_NAME); szCollection[MAX_LEGACY_COLLECTION_NAME - 1] = '\0'; } } } pBandInstrument->m_dwFlags |= (DMUS_IO_INST_TRANSPOSE | DMUS_IO_INST_PAN | DMUS_IO_INST_VOLUME | DMUS_IO_INST_PATCH); pBandInstrument->m_bPan = iob.abPan[i]; pBandInstrument->m_bVolume = iob.abVolume[i]; pBandInstrument->m_dwPChannel = sj_translation_table[i + 1]; // Set drum-kit bit if a drum-kit if(pBandInstrument->m_dwPChannel % 16 == 9) { pBandInstrument->m_dwPatch |= 0x80000000; } pBandInstrument->m_nTranspose = iob.achOctave[i]; pBandInstrument->m_pIDMCollection = NULL; // We will try to load the collection but if we can not we will continure // and use the default GM on the card if(pIDMLoader && (pBandInstrument->m_dwFlags & DMUS_IO_INST_GM || pBandInstrument->m_dwFlags & DMUS_IO_INST_GS)) { HRESULT hrTemp = LoadCollection(&(pBandInstrument->m_pIDMCollection), NULL, pIDMLoader); if (FAILED(hrTemp)) { hrGM = hrTemp; } } else if(pIDMLoader) { HRESULT hrTemp = LoadCollection(&(pBandInstrument->m_pIDMCollection), szCollection, pIDMLoader); if (FAILED(hrTemp)) { hrGM = hrTemp; } } m_BandInstrumentList.AddHead(pBandInstrument); } else { hr = E_OUTOFMEMORY; } } LeaveCriticalSection(&m_CriticalSection); // This function expects the caller to cleanup m_BandInstrumentList on any errors if (SUCCEEDED(hrGM) || hr != S_OK) { return hr; } else { return DMUS_S_PARTIALLOAD; } } ////////////////////////////////////////////////////////////////////// // CBand::ExtractBandInstrument HRESULT CBand::ExtractBandInstrument(CRiffParser *pParser, IDirectMusicLoader* pIDMLoader) { CBandInstrument* pBandInstrument = new CBandInstrument(); if(pBandInstrument == NULL) { return E_OUTOFMEMORY; } RIFFIO ckNext; HRESULT hrGM = S_OK; HRESULT hr = S_OK; pParser->EnterList(&ckNext); while (pParser->NextChunk(&hr)) { switch(ckNext.ckid) { case DMUS_FOURCC_INSTRUMENT_CHUNK: { DMUS_IO_INSTRUMENT ioDMInst; ZeroMemory( &ioDMInst, sizeof(DMUS_IO_INSTRUMENT) ); hr = pParser->Read(&ioDMInst, sizeof(DMUS_IO_INSTRUMENT)); if(SUCCEEDED(hr)) { pBandInstrument->m_dwPatch = ioDMInst.dwPatch; pBandInstrument->m_dwAssignPatch = ioDMInst.dwAssignPatch; pBandInstrument->m_bPan = ioDMInst.bPan; pBandInstrument->m_bVolume = ioDMInst.bVolume; pBandInstrument->m_dwPChannel = ioDMInst.dwPChannel; pBandInstrument->m_nTranspose = ioDMInst.nTranspose; pBandInstrument->m_dwFlags = ioDMInst.dwFlags; pBandInstrument->m_dwChannelPriority = ioDMInst.dwChannelPriority; pBandInstrument->m_nPitchBendRange = ioDMInst.nPitchBendRange; CopyMemory(&(pBandInstrument->m_dwNoteRanges[0]), &(ioDMInst.dwNoteRanges[0]), (sizeof(DWORD) * 4)); pBandInstrument->m_pIDMCollection = NULL; } } break; case FOURCC_LIST : switch(ckNext.fccType) { case DMUS_FOURCC_REF_LIST: // We can only load if we have a valid loader if(pIDMLoader) { HRESULT hrTemp = GetCollectionRefAndLoad(pParser,pIDMLoader,pBandInstrument); if (FAILED(hrTemp)) { hrGM = hrTemp; } } break; } break; } } pParser->LeaveList(); if(SUCCEEDED(hr)) { if(pIDMLoader && pBandInstrument->m_pIDMCollection == NULL && (pBandInstrument->m_dwFlags & (DMUS_IO_INST_GM | DMUS_IO_INST_GS | DMUS_IO_INST_XG)) ) { HRESULT hrTemp = LoadCollection(&(pBandInstrument->m_pIDMCollection), NULL, pIDMLoader); if (FAILED(hrTemp)) { hrGM = hrTemp; } } m_BandInstrumentList.AddHead(pBandInstrument); } else { delete pBandInstrument; } if (SUCCEEDED(hrGM) || hr != S_OK) { return hr; } else { return DMUS_S_PARTIALLOAD; } } ////////////////////////////////////////////////////////////////////// // CBand::GetCollectionRefAndLoad HRESULT CBand::GetCollectionRefAndLoad(CRiffParser *pParser, IDirectMusicLoader *pIDMLoader, CBandInstrument *pBandInstrument) { DMUS_OBJECTDESC desc; desc.dwValidData = 0; desc.dwSize = sizeof(desc); RIFFIO ckNext; HRESULT hr = S_OK; pParser->EnterList(&ckNext); while (pParser->NextChunk(&hr)) { switch(ckNext.ckid) { case DMUS_FOURCC_REF_CHUNK: DMUS_IO_REFERENCE ioDMRef; hr = pParser->Read(&ioDMRef, sizeof(DMUS_IO_REFERENCE)); desc.guidClass = ioDMRef.guidClassID; desc.dwValidData |= ioDMRef.dwValidData; desc.dwValidData |= DMUS_OBJ_CLASS; break; case DMUS_FOURCC_GUID_CHUNK: hr = pParser->Read(&(desc.guidObject), sizeof(GUID)); desc.dwValidData |= DMUS_OBJ_OBJECT; break; case DMUS_FOURCC_DATE_CHUNK: hr = pParser->Read(&(desc.ftDate), sizeof(FILETIME)); desc.dwValidData |= DMUS_OBJ_DATE; break; case DMUS_FOURCC_NAME_CHUNK: hr = pParser->Read(desc.wszName, sizeof(desc.wszName)); desc.wszName[DMUS_MAX_NAME - 1] = 0; desc.dwValidData |= DMUS_OBJ_NAME; break; case DMUS_FOURCC_FILE_CHUNK: hr = pParser->Read(desc.wszFileName, sizeof(desc.wszFileName)); desc.wszFileName[DMUS_MAX_FILENAME - 1] = 0; desc.dwValidData |= DMUS_OBJ_FILENAME; break; case DMUS_FOURCC_CATEGORY_CHUNK: hr = pParser->Read(desc.wszCategory, sizeof(desc.wszCategory)); desc.wszCategory[DMUS_MAX_CATEGORY - 1] = 0; desc.dwValidData |= DMUS_OBJ_CATEGORY; break; case DMUS_FOURCC_VERSION_CHUNK: DMUS_IO_VERSION dmioVer; hr = pParser->Read(&dmioVer, sizeof(DMUS_IO_VERSION)); desc.vVersion.dwVersionMS = dmioVer.dwVersionMS; desc.vVersion.dwVersionLS = dmioVer.dwVersionLS; desc.dwValidData |= DMUS_OBJ_VERSION; break; } } pParser->LeaveList(); if(SUCCEEDED(hr)) { hr = pIDMLoader->GetObject(&desc,IID_IDirectMusicCollection, (void**)&(pBandInstrument->m_pIDMCollection)); } #ifdef DBG if (FAILED(hr)) { if (desc.dwValidData & DMUS_OBJ_FILENAME) { Trace(1,"Error: Unable to load DLS Collection from file %ls for instrument %lx\n", desc.wszFileName, pBandInstrument->m_dwPatch); } else if (desc.dwValidData & DMUS_OBJ_NAME) { Trace(1,"Error: Unable to load DLS Collection %ls for instrument %lx\n", desc.wszName, pBandInstrument->m_dwPatch); } else { Trace(1,"Error: Unable to load DLS Collection for instrument %lx\n", pBandInstrument->m_dwPatch); } } #endif return hr; } ////////////////////////////////////////////////////////////////////// // CBand::Load HRESULT CBand::Load(DMUS_IO_PATCH_ITEM& rPatchEvent) { // This method is used to load PatchEvents generated by the parsing of a MIDI file. // Each PatchEvent represents a program change and possibly a bank select. Using // this information this method will generate a band with one instrument. HRESULT hr = S_OK; EnterCriticalSection(&m_CriticalSection); CBandInstrument* pBandInstrument = NULL; pBandInstrument = new CBandInstrument(); if(pBandInstrument) { pBandInstrument->m_dwFlags |= rPatchEvent.dwFlags; if(pBandInstrument->m_dwFlags & DMUS_IO_INST_PATCH) { pBandInstrument->m_dwPatch |= (rPatchEvent.byPChange & 0x7F); // Program change } if(pBandInstrument->m_dwFlags & DMUS_IO_INST_BANKSELECT) { pBandInstrument->m_dwPatch |= (rPatchEvent.byLSB & 0x7F) << 8; // Set LSB pBandInstrument->m_dwPatch |= (rPatchEvent.byMSB & 0x7F) << 16; // Set MSB } if(IsGS(rPatchEvent)) { pBandInstrument->m_dwFlags |= DMUS_IO_INST_GS; if( (rPatchEvent.byLSB == 0) && (rPatchEvent.byMSB == 0) ) { pBandInstrument->m_dwFlags |= DMUS_IO_INST_GM; } } pBandInstrument->m_dwPChannel = (rPatchEvent.byStatus & 0xF); pBandInstrument->m_pIDMCollection = rPatchEvent.pIDMCollection; pBandInstrument->m_fNotInFile = rPatchEvent.fNotInFile; if(pBandInstrument->m_pIDMCollection) { (pBandInstrument->m_pIDMCollection)->AddRef(); } // Set the time for the band. Since this band will have only one instrument in // it we use the time for PatchEvent as the time for the band m_lTimeLogical = rPatchEvent.lTime; m_lTimePhysical = m_lTimeLogical; m_BandInstrumentList.AddHead(pBandInstrument); } else { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { m_dwFlags |= DMB_LOADED; } else { if(pBandInstrument) { delete pBandInstrument; } } LeaveCriticalSection(&m_CriticalSection); return hr; } ////////////////////////////////////////////////////////////////////// // CBand::Load HRESULT CBand::Load(CBandInstrument* pInstrument) { HRESULT hr = S_OK; EnterCriticalSection(&m_CriticalSection); CBandInstrument* pBandInstrument = NULL; pBandInstrument = new CBandInstrument(); if(pBandInstrument) { pBandInstrument->m_dwPatch = pInstrument->m_dwPatch; pBandInstrument->m_dwAssignPatch = pInstrument->m_dwAssignPatch; pBandInstrument->m_dwPChannel = pInstrument->m_dwPChannel; pBandInstrument->m_dwFlags = pInstrument->m_dwFlags; pBandInstrument->m_bPan = pInstrument->m_bPan; pBandInstrument->m_bVolume = pInstrument->m_bVolume; pBandInstrument->m_nTranspose = pInstrument->m_nTranspose; pBandInstrument->m_pIDMCollection = pInstrument->m_pIDMCollection; CopyMemory(pBandInstrument->m_dwNoteRanges, pInstrument->m_dwNoteRanges, sizeof(pInstrument->m_dwNoteRanges)); if(pBandInstrument->m_pIDMCollection) { (pBandInstrument->m_pIDMCollection)->AddRef(); } m_BandInstrumentList.AddHead(pBandInstrument); } else { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { m_dwFlags |= DMB_LOADED; } else { if(pBandInstrument) { delete pBandInstrument; } } LeaveCriticalSection(&m_CriticalSection); return hr; } ////////////////////////////////////////////////////////////////////// // CBand::SendMessages HRESULT CBand::SendMessages(CBandTrkStateData* pBTStateData, MUSIC_TIME mtOffset, REFERENCE_TIME rtOffset, bool fClockTime) { if(pBTStateData == NULL) { return E_POINTER; } HRESULT hr = S_OK; EnterCriticalSection(&m_CriticalSection); CBandInstrument* pInstrument = m_BandInstrumentList.GetHead(); for( ; pInstrument && SUCCEEDED(hr); pInstrument = pInstrument->GetNext()) { if( pInstrument->m_fNotInFile && !pInstrument->m_fGMOnly ) { // don't send program changes for instruments that were automatically // generated by midi file parsing, unless we've set GMOnly. continue; } hr = SendInstrumentAtTime(pInstrument, pBTStateData, m_lTimePhysical, mtOffset, rtOffset, fClockTime); } LeaveCriticalSection(&m_CriticalSection); return hr; } HRESULT CBand::AllocPMsgFromGenericTemplate( DWORD dwType, IDirectMusicPerformance *pPerformance, DMUS_PMSG **ppMsg, ULONG cb, DMUS_PMSG *pMsgGenericFields) { HRESULT hr = pPerformance->AllocPMsg(cb, ppMsg); if (SUCCEEDED(hr)) { DWORD dwSize = (*ppMsg)->dwSize; // Remember the size. assert(dwSize == cb); ZeroMemory(*ppMsg, cb); // Clear it - ensures we zero the non-DMUS_PMSG_PART fields. CopyMemory(*ppMsg, pMsgGenericFields, sizeof(*pMsgGenericFields)); // Copy the DMUS_PMSG_PART fields. // Fill in the correct size and type (*ppMsg)->dwSize = dwSize; (*ppMsg)->dwType = dwType; } return hr; } HRESULT CBand::StampSendFreePMsg( IDirectMusicPerformance *pPerformance, IDirectMusicGraph *pGraph, DMUS_PMSG *pMsg) { // Let the graph set the delivery parameters. HRESULT hr = pGraph->StampPMsg(pMsg); if (SUCCEEDED(hr)) hr = pPerformance->SendPMsg(pMsg); if (FAILED(hr)) hr = pPerformance->FreePMsg(pMsg); return hr; } HRESULT CBand::SendInstrumentAtTime(CBandInstrument* pInstrument, CBandTrkStateData* pBTStateData, MUSIC_TIME mtTimeToPlay, MUSIC_TIME mtOffset, REFERENCE_TIME rtOffset, bool fClockTime) { if(pInstrument == NULL || pBTStateData == NULL) { return E_POINTER; } IDirectMusicGraph *pGraph = NULL; IDirectMusicPerformance *pPerformance = pBTStateData->m_pPerformance; DWORD dwVirtualTrackID = pBTStateData->m_dwVirtualTrackID; DWORD dwPatch = 0; BOOL fMute; DWORD dwPChannel; // Get the mute/pchannel reassignment. MUSIC_TIME mtParam = ( m_lTimeLogical < 0 ) ? 0 : m_lTimeLogical; m_PChMap.GetInfo( pInstrument->m_dwPChannel, mtParam, mtOffset, m_dwGroupBits, pPerformance, &fMute, &dwPChannel, fClockTime ); if( fMute ) return S_OK; HRESULT hr = pBTStateData->m_pSegmentState->QueryInterface(IID_IDirectMusicGraph, reinterpret_cast(&pGraph)); if(FAILED(hr)) return hr; EnterCriticalSection(&m_CriticalSection); DMUS_PMSG pmsgGeneric; // template for stamping out the common fields in the various specific kinds of messages ZeroMemory(&pmsgGeneric, sizeof(pmsgGeneric)); if (fClockTime) { pmsgGeneric.rtTime = mtTimeToPlay * REF_PER_MIL + rtOffset; pmsgGeneric.dwFlags |= DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME; } else { pmsgGeneric.mtTime = mtTimeToPlay + mtOffset; pmsgGeneric.dwFlags |= DMUS_PMSGF_MUSICTIME; } pmsgGeneric.dwPChannel = dwPChannel; pmsgGeneric.dwVirtualTrackID = dwVirtualTrackID; pmsgGeneric.dwGroupID = m_dwGroupBits; if(pInstrument->m_dwFlags & DMUS_IO_INST_PATCH) { if(pInstrument->m_dwFlags & DMUS_IO_INST_BANKSELECT) { if(pInstrument->m_dwFlags & DMUS_IO_INST_ASSIGN_PATCH) { dwPatch = pInstrument->m_dwAssignPatch & 0x007F7F00; } else { dwPatch = pInstrument->m_dwPatch & 0x007F7F00; // if the m_fGMOnly flag is set, and either we're GS or we're XG and // the instument's port supports XG, use the full patch if (pInstrument->m_fGMOnly || (pInstrument->m_dwFlags & DMUS_IO_INST_XG)) { bool fXG = XGInHardware(pPerformance,pBTStateData->m_pSegmentState,pInstrument->m_dwPChannel); if(pInstrument->m_fGMOnly) { if ( m_dwMidiMode & DMUS_MIDIMODEF_GS ) { dwPatch = pInstrument->m_dwFullPatch & 0x007F7F00; } if ( (m_dwMidiMode & DMUS_MIDIMODEF_XG) && fXG ) { dwPatch = pInstrument->m_dwFullPatch & 0x007F7F00; } } // If the instrument is an XG instrument and the hardware doesn't support // XG, strip off the bank selects. if ( (pInstrument->m_dwFlags & DMUS_IO_INST_XG) && !fXG) { dwPatch = 0; } } } } // Now, get the program change. if(pInstrument->m_dwFlags & DMUS_IO_INST_ASSIGN_PATCH) { dwPatch |= pInstrument->m_dwAssignPatch & 0x7f; } else { dwPatch |= pInstrument->m_dwPatch & 0x7f; } DMUS_PATCH_PMSG *pMsg = NULL; hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_PATCH, pPerformance, reinterpret_cast(&pMsg), sizeof(*pMsg), &pmsgGeneric); if(SUCCEEDED(hr)) { // DMUS_PATCH_PMSG members that need to be initialized pMsg->byInstrument = (BYTE) dwPatch & 0x7F; pMsg->byMSB = (BYTE) ((dwPatch >> 16) & 0x7F); pMsg->byLSB = (BYTE) ((dwPatch >> 8) & 0x7F); hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast(pMsg)); } } if(pInstrument->m_dwFlags & DMUS_IO_INST_TRANSPOSE) { DMUS_TRANSPOSE_PMSG *pMsg = NULL; hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_TRANSPOSE, pPerformance, reinterpret_cast(&pMsg), sizeof(*pMsg), &pmsgGeneric); if(SUCCEEDED(hr)) { // DMUS_TRANSPOSE_PMSG members that need to be initialized pMsg->nTranspose = pInstrument->m_nTranspose; hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast(pMsg)); } } if(pInstrument->m_dwFlags & DMUS_IO_INST_VOLUME) { // Set Volume DMUS_MIDI_PMSG* pMsg = NULL; hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_MIDI, pPerformance, reinterpret_cast(&pMsg), sizeof(*pMsg), &pmsgGeneric); if(SUCCEEDED(hr)) { // DMUS_MIDI_PMSG members that need to be initialized pMsg->bStatus = MIDI_CONTROL_CHANGE; pMsg->bByte1 = MIDI_CC_VOLUME; pMsg->bByte2 = pInstrument->m_bVolume; hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast(pMsg)); } } if(pInstrument->m_dwFlags & DMUS_IO_INST_PAN) { // Set Pan DMUS_MIDI_PMSG* pMsg = NULL; hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_MIDI, pPerformance, reinterpret_cast(&pMsg), sizeof(*pMsg), &pmsgGeneric); if(SUCCEEDED(hr)) { // DMUS_MIDI_PMSG members that need to be initialized pMsg->bStatus = MIDI_CONTROL_CHANGE; pMsg->bByte1 = MIDI_CC_PAN; pMsg->bByte2 = pInstrument->m_bPan; hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast(pMsg)); } } if(pInstrument->m_dwFlags & DMUS_IO_INST_CHANNEL_PRIORITY) { DMUS_CHANNEL_PRIORITY_PMSG *pMsg = NULL; hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_CHANNEL_PRIORITY, pPerformance, reinterpret_cast(&pMsg), sizeof(*pMsg), &pmsgGeneric); if(SUCCEEDED(hr)) { // DMUS_CHANNEL_PRIORITY_PMSG members that need to be initialized pMsg->dwChannelPriority = pInstrument->m_dwChannelPriority; hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast(pMsg)); } } if(pInstrument->m_dwFlags & DMUS_IO_INST_PITCHBENDRANGE) { DMUS_CURVE_PMSG *pMsg = NULL; hr = AllocPMsgFromGenericTemplate(DMUS_PMSGT_CURVE, pPerformance, reinterpret_cast(&pMsg), sizeof(*pMsg), &pmsgGeneric); if(SUCCEEDED(hr)) { pMsg->dwFlags |= DMUS_PMSGF_DX8; // pitch band is a DX8-only flag // DMUS_CURVE_PMSG members that need to be initialized pMsg->nEndValue = pInstrument->m_nPitchBendRange << 7; pMsg->nOffset = static_cast(m_lTimePhysical - m_lTimeLogical); pMsg->bType = DMUS_CURVET_RPNCURVE; pMsg->bCurveShape = DMUS_CURVES_INSTANT; pMsg->wParamType = RPN_PITCHBEND; // Leave as zero: mtDuration, mtOriginalStart, mtResetDuration, nStartValue, nResetValue, // wMeasure, bBeat, bGrid, wMergeIndex hr = StampSendFreePMsg(pPerformance, pGraph, reinterpret_cast(pMsg)); } } pGraph->Release(); LeaveCriticalSection(&m_CriticalSection); return hr; } ////////////////////////////////////////////////////////////////////// // CBand::LoadCollection HRESULT CBand::LoadCollection(IDirectMusicCollection** ppIDMCollection, char* pszCollection, IDirectMusicLoader* pIDMLoader) { // Any changes made to this function should also be made to LoadCollection // in dmime.dll assert(ppIDMCollection); assert(pIDMLoader); DMUS_OBJECTDESC desc; ZeroMemory(&desc, sizeof(desc)); desc.dwSize = sizeof(desc); desc.guidClass = CLSID_DirectMusicCollection; if(pszCollection == NULL) { desc.guidObject = GUID_DefaultGMCollection; desc.dwValidData |= (DMUS_OBJ_CLASS | DMUS_OBJ_OBJECT); } else { MultiByteToWideChar(CP_ACP, 0, pszCollection, -1, desc.wszName, DMUS_MAX_NAME); desc.dwValidData |= (DMUS_OBJ_CLASS | DMUS_OBJ_NAME); } HRESULT hr = pIDMLoader->GetObject(&desc,IID_IDirectMusicCollection, (void**)ppIDMCollection); return hr; } ////////////////////////////////////////////////////////////////////// // CBand::GetPChannelCount DWORD CBand::GetPChannelCount() { EnterCriticalSection(&m_CriticalSection); DWORD dwCount = 0; CBandInstrument* pInstrument = m_BandInstrumentList.GetHead(); for( ; pInstrument; pInstrument = pInstrument->GetNext()) { dwCount++; } LeaveCriticalSection(&m_CriticalSection); return dwCount; } ////////////////////////////////////////////////////////////////////// // CBand::GetPChannels HRESULT CBand::GetPChannels(DWORD *pdwPChannels, DWORD *pdwNumWritten) { assert(pdwPChannels); assert(pdwNumWritten); EnterCriticalSection(&m_CriticalSection); *pdwNumWritten = 0; CBandInstrument* pInstrument = m_BandInstrumentList.GetHead(); for(; pInstrument; pInstrument = pInstrument->GetNext()) { *pdwPChannels++ = pInstrument->m_dwPChannel; (*pdwNumWritten)++; } LeaveCriticalSection(&m_CriticalSection); return S_OK; } HRESULT CBandInstrument::BuildNoteRangeArray(DWORD *pNoteRangeMap, DMUS_NOTERANGE **ppNoteRanges, DWORD *pdwNumNoteRanges) { const int c_iIn = 0; const int c_iOut = 1; HRESULT hr = S_OK; // Count the number of DMUS_NOTERANGE structures we need to allocate DWORD dwNRNum = 0; int nState = c_iOut; for(int i = 0; i < 4; i++) { DWORD dwTemp = pNoteRangeMap[i]; DWORD dwBitPos = 0; while(dwBitPos < 32) { if(dwTemp & 0x1ul) { if(nState == c_iOut) { nState = c_iIn; dwNRNum++; } } else { nState = c_iOut; } dwTemp = dwTemp >> 1; dwBitPos++; } } // If the NoteRangeMap is empty or full we do nothing // since this will cause NULL to be returned which means we // want to download the complete instrument if(dwNRNum && dwNRNum < 128) { *ppNoteRanges = new DMUS_NOTERANGE[dwNRNum]; if(*ppNoteRanges) { DWORD dwNRIdx = 0; for(dwNRIdx = 0; dwNRIdx < dwNRNum; dwNRIdx++) { (*ppNoteRanges)[dwNRIdx].dwLowNote = 0; (*ppNoteRanges)[dwNRIdx].dwHighNote = 127; } dwNRIdx = 0; nState = c_iOut; for(int i = 0; i < 4; i++) { DWORD dwTemp = pNoteRangeMap[i]; DWORD dwBitPos = 0; while(dwBitPos < 32) { if(dwTemp & 0x1ul) { if(nState == c_iOut) { nState = c_iIn; (*ppNoteRanges)[dwNRIdx].dwLowNote = dwBitPos + (i * 32); } } else if(nState == c_iIn) { (*ppNoteRanges)[dwNRIdx].dwHighNote = dwBitPos + (i * 32) - 1; nState = c_iOut; dwNRIdx++; } dwTemp = dwTemp >> 1; dwBitPos++; } } assert(nState == c_iIn ? dwNRIdx == dwNRNum - 1 : dwNRIdx == dwNRNum); *pdwNumNoteRanges = dwNRNum; } else { hr = E_OUTOFMEMORY; } } else { *ppNoteRanges = NULL; *pdwNumNoteRanges = 0; } return hr; } ////////////////////////////////////////////////////////////////////// // CBand::IsGS bool CBand::IsGS(DMUS_IO_PATCH_ITEM& rPatchEvent) { BYTE bMSB = 0; BYTE bPatch = 0; if( rPatchEvent.dwFlags & DMUS_IO_INST_BANKSELECT ) { if( rPatchEvent.byLSB != 0 ) return FALSE; // LSB must be 0 for GS bMSB = rPatchEvent.byMSB; } if( rPatchEvent.dwFlags & DMUS_IO_INST_PATCH ) { bPatch = rPatchEvent.byPChange & 0x7F; } if( bMSB == 0) { // If this is a drum kit (on MIDI channel 10) if( (rPatchEvent.byStatus & 0xF) == 10 ) { if ((bPatch == 0x0) || (bPatch == 0x08) || (bPatch == 0x10) || (bPatch == 0x18) || (bPatch == 0x19) || (bPatch == 0x20) || (bPatch == 0x28) || (bPatch == 0x30) || (bPatch == 0x38) ) { return true; } else return false; } else return true;//is GM } // check for GS switch (bMSB) { case 6: case 7: if (bPatch == 0x7D) return true; break; case 24: if ((bPatch == 0x04) || (bPatch == 0x06)) return true; break; case 9: if ((bPatch == 0x0E) || (bPatch == 0x76) || (bPatch == 0x7D)) return true; break; case 2: if ( (bPatch == 0x66) || (bPatch == 0x78) || ((bPatch > 0x79)&&(bPatch < 0x80) )) return true; break; case 3: if ((bPatch > 0x79) && (bPatch < 0x80)) return true; break; case 4: case 5: if ( (bPatch == 0x7A) || ((bPatch > 0x7B)&&(bPatch < 0x7F) )) return true; break; case 32: if ((bPatch == 0x10) || (bPatch == 0x11) || (bPatch == 0x18) || (bPatch == 0x34) ) return true; break; case 1: if ((bPatch == 0x26) || (bPatch == 0x39) || (bPatch == 0x3C) || (bPatch == 0x50) || (bPatch == 0x51) || (bPatch == 0x62) || (bPatch == 0x66) || (bPatch == 0x68) || ((bPatch > 0x77) && (bPatch < 0x80))) return true; break; case 16: switch (bPatch) { case 0x00: return true; break; case 0x04: return true; break; case 0x05: return true; break; case 0x06: return true; break; case 0x10: return true; break; case 0x13: return true; break; case 0x18: return true; break; case 0x19: return true; break; case 0x1C: return true; break; case 0x27: return true; break; case 0x3E: return true; break; case 0x3F: return true; break; default: return false; } break; case 8: if ((bPatch < 0x07) || ((bPatch == 0x7D))) { return true; } else if ((bPatch > 0x3F) && (bPatch < 0x50)) { return false; } else if ((bPatch > 0x4F) && (bPatch < 0x72) ) { if ((bPatch == 0x50) || (bPatch == 0x51) || (bPatch == 0x6B)) { return true; } return false; } else if ((bPatch > 0x1F) && (bPatch < 0x40)) { if ((bPatch > 0x25) && (bPatch < 0x29) || (bPatch > 0x3C) || (bPatch == 0x30) || (bPatch == 0x32) ) { return true; } return false; } else if ((bPatch > 0x0A) && (bPatch < 0x12) && (bPatch != 0x0D) && (bPatch != 0x0F)) { return true; } else if ((bPatch > 0x0F) && (bPatch < 0x20)) { if (bPatch > 0x17) { return true; } else if ( (bPatch == 0x13) || (bPatch == 0x15) ) return true; else return false; } break; default: return false; } return false; } HRESULT CBandInstrument::DownloadAddRecord(IDirectMusicPort *pPort) { IDirectMusicInstrument* pInstrument = NULL; HRESULT hr = m_pIDMCollection->GetInstrument(m_dwPatch, &pInstrument); if (FAILED(hr) && (m_dwFlags & (DMUS_IO_INST_GM | DMUS_IO_INST_GS | DMUS_IO_INST_XG))) { // If drums, set to standard drums. if (m_dwPatch & 0x80000000) { m_dwPatch = 0; } // Else make this a GM melodic instrument. else { m_dwPatch &= 0x7F; } hr = m_pIDMCollection->GetInstrument(m_dwPatch, &pInstrument); } if(SUCCEEDED(hr) && m_dwFlags & DMUS_IO_INST_ASSIGN_PATCH) { hr = pInstrument->SetPatch(m_dwAssignPatch); } if(SUCCEEDED(hr)) { CDownloadedInstrument* pDLInstrument = new CDownloadedInstrument; if(pDLInstrument) { pDLInstrument->m_pPort = pPort; pPort->AddRef(); pDLInstrument->m_cRef = 1; DMUS_NOTERANGE *pNoteRanges = NULL; DWORD dwNumNoteRanges = 0; if(m_dwFlags & DMUS_IO_INST_NOTERANGES) { BuildNoteRangeArray(m_dwNoteRanges, &pNoteRanges, &dwNumNoteRanges); } hr = pPort->DownloadInstrument( pInstrument, &pDLInstrument->m_pDLInstrument, pNoteRanges, dwNumNoteRanges ); if (pNoteRanges) { delete [] pNoteRanges; } if(SUCCEEDED(hr)) { m_DownloadList.AddHead(pDLInstrument); } else { delete pDLInstrument; #ifdef DBG if (hr == DMUS_E_NOT_INIT) { Trace(0,"Error: Download failed because performance not initialized\n"); } else { Trace(1,"Error: Unable to download instrument %lx to PChannel %ld\n", m_dwPatch,m_dwPChannel); } #endif } } else { hr = E_OUTOFMEMORY; Trace(0,"Error: Memory allocation failure - Unable to download instrument\n"); } } else { Trace(1,"Error: Unable to download instrument %lx; not in dls collection\n",m_dwPatch); } if (pInstrument) { pInstrument->Release(); } return hr; } ////////////////////////////////////////////////////////////////////// // CBand::XGInHardware bool CBand::XGInHardware( IDirectMusicPerformance *pPerformance, IDirectMusicSegmentState *pSegState, DWORD dwPChannel) { DWORD dwGMFlags = 0; // If this is playing via an audiopath, we need to access the audiopath to // convert the pchannels so we can use them to access the right port. IDirectMusicSegmentState8 *pState8; if (SUCCEEDED(pSegState->QueryInterface(IID_IDirectMusicSegmentState8,(void **) &pState8))) { IDirectMusicAudioPath *pAudioPath; if (SUCCEEDED(pState8->GetObjectInPath(DMUS_PCHANNEL_ALL,DMUS_PATH_AUDIOPATH,0, GUID_All_Objects,0,IID_IDirectMusicAudioPath,(void **) &pAudioPath))) { pAudioPath->ConvertPChannel(dwPChannel, &dwPChannel); pAudioPath->Release(); } pState8->Release(); } // Now, use the PChannel and the performance to read the flags. IDirectMusicPort *pPort = NULL; IDirectMusicPerformanceP *pPerfp; if (SUCCEEDED(pPerformance->QueryInterface(IID_IDirectMusicPerformanceP, (void **)&pPerfp))) { if (SUCCEEDED(pPerfp->GetPortAndFlags(dwPChannel,&pPort,&dwGMFlags))) { pPort->Release(); } pPerfp->Release(); } return ((dwGMFlags & DM_PORTFLAGS_XG) && TRUE); } ////////////////////////////////////////////////////////////////////// // CBand::MakeGMOnly HRESULT CBand::MakeGMOnly() { EnterCriticalSection(&m_CriticalSection); CBandInstrument* pBandInstrument = m_BandInstrumentList.GetHead(); for( ; pBandInstrument != NULL; pBandInstrument = pBandInstrument->GetNext()) { pBandInstrument->m_fGMOnly = true; pBandInstrument->m_dwFullPatch = pBandInstrument->m_dwPatch; DWORD dwTemp = pBandInstrument->m_dwPatch; pBandInstrument->m_dwPatch = (dwTemp & 0x7F); // If a drum kit set drum kit flag if( m_dwMidiMode == DMUS_MIDIMODEF_XG ) { if( (dwTemp & 0x00ff0000) == 0x007f0000 ) { // XG drums. Keep this msb, as it is taken care of in the :Download function. pBandInstrument->m_dwPatch |= 0x007f0000; } } if(dwTemp & 0x80000000) { pBandInstrument->m_dwPatch |= 0x80000000; } } LeaveCriticalSection(&m_CriticalSection); return S_OK; } ////////////////////////////////////////////////////////////////////// // CBand::ConnectToDLSCollection HRESULT CBand::ConnectToDLSCollection(IDirectMusicCollection *pCollection) { assert(pCollection); EnterCriticalSection(&m_CriticalSection); CBandInstrument* pBandInstrument = m_BandInstrumentList.GetHead(); for( ; pBandInstrument != NULL; pBandInstrument = pBandInstrument->GetNext()) { if(pBandInstrument->m_pIDMCollection == NULL) { pCollection->AddRef(); pBandInstrument->m_pIDMCollection = pCollection; } else { if( m_dwMidiMode ) // if this is anything, it indicates we were loaded from a midi file { // if we're not an XG file, make sure channel 9 is drums if( (m_dwMidiMode != DMUS_MIDIMODEF_XG) && (pBandInstrument->m_dwPChannel == 9) ) { pBandInstrument->m_dwPatch |= 0x80000000; } } // if we get an instrument from this collection, set the band's collection // pointer to it instead. IDirectMusicInstrument* pInstrument = NULL; if( SUCCEEDED( pCollection->GetInstrument(pBandInstrument->m_dwPatch, &pInstrument))) { pBandInstrument->m_pIDMCollection->Release(); pBandInstrument->m_pIDMCollection = pCollection; pCollection->AddRef(); pInstrument->Release(); } } } LeaveCriticalSection(&m_CriticalSection); return S_OK; }