#include #include #include #include //#include "debug.h" #include "debug.h" #include "dmusicc.h" #include "dmusici.h" #include "dmusicf.h" #include "validate.h" #include "riff.h" #include "dswave.h" #include "riff.h" #include #include #include "waveutil.h" #include "dmstrm.h" // seeks to a 32-bit position in a stream. HRESULT __inline StreamSeek( LPSTREAM pStream, long lSeekTo, DWORD dwOrigin ) { LARGE_INTEGER li; if( lSeekTo < 0 ) { li.HighPart = -1; } else { li.HighPart = 0; } li.LowPart = lSeekTo; return pStream->Seek( li, dwOrigin, NULL ); } // returns the current 32-bit position in a stream. DWORD __inline StreamTell( LPSTREAM pStream ) { LARGE_INTEGER li; ULARGE_INTEGER ul; #ifdef DBG HRESULT hr; #endif li.HighPart = 0; li.LowPart = 0; #ifdef DBG hr = pStream->Seek( li, STREAM_SEEK_CUR, &ul ); if( FAILED( hr ) ) #else if( FAILED( pStream->Seek( li, STREAM_SEEK_CUR, &ul ) ) ) #endif { return 0; } return ul.LowPart; } CWave::CWave() : m_dwDecompressedStart(0) { V_INAME(CWave::CWave); InterlockedIncrement(&g_cComponent); InitializeCriticalSection(&m_CriticalSection); m_pwfx = NULL; m_pwfxDst = NULL; m_fdwFlags = 0; m_fdwOptions = 0; m_cSamples = 0L; m_pStream = NULL; m_rtReadAheadTime = 0; m_cRef = 1; } CWave::~CWave() { V_INAME(CWave::~CWave); if (NULL != m_pwfx) { GlobalFreePtr(m_pwfx); } if (NULL != m_pwfxDst) { GlobalFreePtr(m_pwfxDst); } if (m_pStream) m_pStream->Release(); DeleteCriticalSection(&m_CriticalSection); InterlockedDecrement(&g_cComponent); } STDMETHODIMP CWave::QueryInterface ( const IID &iid, void **ppv ) { V_INAME(CWave::QueryInterface); V_REFGUID(iid); V_PTRPTR_WRITE(ppv); if (iid == IID_IUnknown || iid == IID_IDirectSoundWave) { *ppv = static_cast(this); } else if (iid == IID_IPersistStream) { *ppv = static_cast(this); } else if (iid == IID_IDirectMusicObject) { *ppv = static_cast(this); } else if (iid == IID_IPrivateWave) { *ppv = static_cast(this); } else { *ppv = NULL; Trace(4,"Warning: Request to query unknown interface on Wave Object\n"); return E_NOINTERFACE; } reinterpret_cast(this)->AddRef(); return S_OK; } STDMETHODIMP_(ULONG) CWave::AddRef() { V_INAME(CWave::AddRef); return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CWave::Release() { V_INAME(CWave::Release); if (!InterlockedDecrement(&m_cRef)) { delete this; return 0; } return m_cRef; } static inline HRESULT MMRESULTToHRESULT( MMRESULT mmr) { switch (mmr) { case MMSYSERR_NOERROR: return S_OK; case MMSYSERR_ALLOCATED: return DSERR_ALLOCATED; case MMSYSERR_NOMEM: return E_OUTOFMEMORY; case MMSYSERR_INVALFLAG: return E_INVALIDARG; case MMSYSERR_INVALHANDLE: return E_INVALIDARG; case MMSYSERR_INVALPARAM: return E_INVALIDARG; } return E_FAIL; } STDMETHODIMP CWave::GetFormat ( LPWAVEFORMATEX pwfx, DWORD dwSizeAllocated, LPDWORD pdwSizeWritten ) { DWORD cbSize; LPWAVEFORMATEX pwfxTemp = NULL; V_INAME(CWave::GetFormat); if (!pwfx && !pdwSizeWritten) { Trace(1, "ERROR: GetFormat (Wave): Must request either the format or the required size\n."); return E_INVALIDARG; } if (!m_pwfx) { return DSERR_BADFORMAT; } if (WAVE_FORMAT_PCM == m_pwfx->wFormatTag) { pwfxTemp = m_pwfx; } else { pwfxTemp = m_pwfxDst; if (!pwfxTemp) { return DSERR_BADFORMAT; } } // Note: Assuming that the wave object fills the cbSize field even // on PCM formats... if (WAVE_FORMAT_PCM == pwfxTemp->wFormatTag) { cbSize = sizeof(PCMWAVEFORMAT); } else { cbSize = sizeof(WAVEFORMATEX) + pwfxTemp->cbSize; } if (cbSize > dwSizeAllocated || !pwfx) { if (pdwSizeWritten) { *pdwSizeWritten = cbSize; return S_OK; // What to return? } else { return DSERR_INVALIDPARAM; } } // We don't validate this parameter any earlier on the off chance // that they're doing a query... V_BUFPTR_WRITE(pwfx, dwSizeAllocated); CopyMemory(pwfx, pwfxTemp, cbSize); // Set the cbSize field in destination for PCM, IF WE HAVE ROOM... if (WAVE_FORMAT_PCM == pwfxTemp->wFormatTag) { if (sizeof(WAVEFORMATEX) <= dwSizeAllocated) { pwfx->cbSize = 0; } } // Return the numbers of bytes actually writted if (pdwSizeWritten) { *pdwSizeWritten = cbSize; } return S_OK; } STDMETHODIMP CWave::CreateSource ( IDirectSoundSource **ppSource, LPWAVEFORMATEX pwfx, DWORD fdwFlags ) { HRESULT hr = S_OK; CWaveViewPort* pVP; CREATEVIEWPORT cvp; V_INAME(CWave::CreateSource); V_PTRPTR_WRITE(ppSource); V_PWFX_READ(pwfx); DWORD dwCreateFlags = 0; if (fdwFlags == DMUS_DOWNLOADINFO_ONESHOTWAVE) { dwCreateFlags |= DSOUND_WAVEF_ONESHOT; } if (dwCreateFlags & (~DSOUND_WAVEF_CREATEMASK)) { Trace(1, "ERROR: CreateSource (Wave): Unknown flag.\n"); return (E_INVALIDARG); } TraceI(5, "CWave::CreateSource [%d samples]\n", m_cSamples); pVP = new CWaveViewPort; if (!pVP) { return E_OUTOFMEMORY; } cvp.pStream = m_pStream; cvp.cSamples = m_cSamples; cvp.dwDecompressedStart = m_dwDecompressedStart; cvp.cbStream = m_cbStream; cvp.pwfxSource = m_pwfx; cvp.pwfxTarget = pwfx; cvp.fdwOptions = dwCreateFlags; hr = pVP->Create(&cvp); if (SUCCEEDED(hr)) { hr = pVP->QueryInterface(IID_IDirectSoundSource, (void **)ppSource); } else { TraceI(5, "CWave::CreateSource 00\n"); } if (SUCCEEDED(hr)) { // The QI gave us one ref too many pVP->Release(); } else { TraceI(5, "CWave::CreateSource 01\n"); } return hr; } STDMETHODIMP CWave::GetStreamingParms ( LPDWORD pdwFlags, LPREFERENCE_TIME prtReadAhead ) { V_INAME(IDirectSoundWave::GetStreamingParms); V_PTR_WRITE(pdwFlags, DWORD); V_PTR_WRITE(prtReadAhead, REFERENCE_TIME); *pdwFlags = 0; if(!(m_fdwFlags & DSOUND_WAVEF_ONESHOT)) { *pdwFlags |= DMUS_WAVEF_STREAMING; } if(m_fdwFlags & DMUS_WAVEF_NOPREROLL) { *pdwFlags |= DMUS_WAVEF_NOPREROLL; } *prtReadAhead = m_rtReadAheadTime; return S_OK; } STDMETHODIMP CWave::GetClassID ( CLSID* pClsId ) { V_INAME(CWave::GetClassID); V_PTR_WRITE(pClsId, CLSID); *pClsId = CLSID_DirectSoundWave; return S_OK; } STDMETHODIMP CWave::IsDirty() { V_INAME(CWave::IsDirty); return S_FALSE; } BOOL CWave::ParseHeader ( IStream* pIStream, IRIFFStream* pIRiffStream, LPMMCKINFO pckMain ) { MMCKINFO ck; DWORD cb = 0; DWORD dwPos = 0; MMCKINFO ckINFO; HRESULT hr; ck.ckid = 0; ck.fccType = 0; V_INAME(CWave::ParseHeader); BOOL fFormat = FALSE; BOOL fData = FALSE; BOOL fHeader = FALSE; BOOL fReadDecompressionFmt = FALSE; DWORD dwSamplesFromFact = 0; while (0 == pIRiffStream->Descend(&ck, pckMain, MMIO_FINDCHUNK)) { switch (ck.ckid) { case mmioFOURCC('w','a','v','u') : { cb = 0; bool bRuntime = false; hr = pIStream->Read(&bRuntime, sizeof(bool), &cb); if(FAILED(hr) || cb != sizeof(bool)) { return FALSE; } cb = 0; bool bCompressed = false; hr = pIStream->Read(&bCompressed, sizeof(bool), &cb); if(FAILED(hr) || cb != sizeof(bool)) { return FALSE; } if(bRuntime && bCompressed) { // If we have already allocated m_pwfxDst, delete it first if (NULL != m_pwfxDst) { GlobalFreePtr(m_pwfxDst); } m_pwfxDst = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, sizeof(WAVEFORMATEX)); if (NULL == m_pwfxDst) { return FALSE; } cb = 0; hr = pIStream->Read(m_pwfxDst, sizeof(WAVEFORMATEX), &cb); if(FAILED(hr) || cb != sizeof(WAVEFORMATEX)) { GlobalFreePtr(m_pwfxDst); return FALSE; } // Read the actual start for the decompressed data if available // This is very important for MP3 and WMA codecs which insert silence in the beginning if(ck.cksize > 2 + sizeof(WAVEFORMATEX)) { cb = 0; hr = pIStream->Read(&m_dwDecompressedStart, sizeof(DWORD), &cb); if(FAILED(hr) || cb != sizeof(DWORD)) { GlobalFreePtr(m_pwfxDst); return FALSE; } } fReadDecompressionFmt = TRUE; } break; } case DMUS_FOURCC_WAVEHEADER_CHUNK: { m_fdwFlags = 0; fHeader = TRUE; DMUS_IO_WAVE_HEADER iWaveHeader; memset(&iWaveHeader, 0, sizeof(iWaveHeader)); hr = pIStream->Read(&iWaveHeader, sizeof(iWaveHeader), &cb); if (iWaveHeader.dwFlags & DMUS_WAVEF_STREAMING) { m_fdwFlags &= ~DSOUND_WAVEF_ONESHOT; m_rtReadAheadTime = iWaveHeader.rtReadAhead; } else { m_fdwFlags |= DSOUND_WAVEF_ONESHOT; m_rtReadAheadTime = 0; } if (iWaveHeader.dwFlags & DMUS_WAVEF_NOPREROLL) { m_fdwFlags |= DMUS_WAVEF_NOPREROLL; } else { m_fdwFlags &= ~DMUS_WAVEF_NOPREROLL; } break; } case DMUS_FOURCC_GUID_CHUNK: hr = pIStream->Read(&m_guid, sizeof(GUID), &cb); m_fdwOptions |= DMUS_OBJ_OBJECT; break; case DMUS_FOURCC_VERSION_CHUNK: hr = pIStream->Read( &m_vVersion, sizeof(DMUS_VERSION), &cb ); m_fdwOptions |= DMUS_OBJ_VERSION; break; case FOURCC_LIST: switch(ck.fccType) { case DMUS_FOURCC_INFO_LIST: while( pIRiffStream->Descend( &ckINFO, &ck, 0 ) == 0 ) { switch(ckINFO.ckid) { case mmioFOURCC('I','N','A','M'): { DWORD cbSize; cbSize = min(ckINFO.cksize, DMUS_MAX_NAME); char szName[DMUS_MAX_NAME]; hr = pIStream->Read((BYTE*)szName, cbSize, &cb); if(SUCCEEDED(hr)) { MultiByteToWideChar(CP_ACP, 0, szName, -1, m_wszFilename, DMUS_MAX_NAME); m_fdwOptions |= DMUS_OBJ_NAME; } break; } } pIRiffStream->Ascend( &ckINFO, 0 ); } break; } break; case mmioFOURCC('f','m','t',' '): { // If we have already allocated m_pwfx, delete it first if (NULL != m_pwfx) { GlobalFreePtr(m_pwfx); } m_pwfx = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, max (sizeof(WAVEFORMATEX), ck.cksize)); if (NULL == m_pwfx) { return FALSE; } hr = pIStream->Read(m_pwfx, ck.cksize, &cb); if (S_OK != hr) { return FALSE; } if (m_pwfx && WAVE_FORMAT_PCM != m_pwfx->wFormatTag) { if(fReadDecompressionFmt == FALSE) { // If we have already allocated m_pwfxDst, delete it first if (NULL != m_pwfxDst) { GlobalFreePtr(m_pwfxDst); } m_pwfxDst = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, sizeof(WAVEFORMATEX)); if (NULL == m_pwfxDst) { return FALSE; } m_pwfxDst->wFormatTag = WAVE_FORMAT_PCM; MMRESULT mmr = acmFormatSuggest(NULL, m_pwfx, m_pwfxDst, sizeof(WAVEFORMATEX), ACM_FORMATSUGGESTF_WFORMATTAG); if(MMSYSERR_NOERROR != mmr) { GlobalFreePtr(m_pwfxDst); m_pwfxDst = NULL; return FALSE; } } } fFormat = TRUE; TraceI(5, "Format [%d:%d%c%02d]\n", m_pwfx->wFormatTag, m_pwfx->nSamplesPerSec/1000, ((2==m_pwfx->nChannels)?'S':'M'), m_pwfx->wBitsPerSample); break; } case mmioFOURCC('f','a','c','t'): hr = pIStream->Read(&dwSamplesFromFact, sizeof(DWORD), &cb); TraceI(5, "Stream is %d samples\n", dwSamplesFromFact); break; case mmioFOURCC('d','a','t','a'): TraceI(5, "Data chunk %d bytes\n", ck.cksize); m_cbStream = ck.cksize; fData = TRUE; if (!fHeader) FallbackStreamingBehavior(); // save stream position so we can seek back later dwPos = StreamTell( pIStream ); break; default: break; } pIRiffStream->Ascend(&ck, 0); ck.ckid = 0; ck.fccType = 0; } if (m_pwfx && WAVE_FORMAT_PCM != m_pwfx->wFormatTag && dwSamplesFromFact) { m_cSamples = dwSamplesFromFact; } if (!fHeader) FallbackStreamingBehavior(); // Seek to beginning of data if (fData) { StreamSeek(pIStream, dwPos, STREAM_SEEK_SET); } return fFormat && fData; } STDMETHODIMP CWave::Load ( IStream* pIStream ) { IRIFFStream* pIRiffStream = NULL; HRESULT hr = S_OK; V_INAME(CWave::Load); if (NULL == pIStream) { Trace(1, "ERROR: Load (Wave): Attempt to load from null stream.\n"); return E_INVALIDARG; } EnterCriticalSection( &m_CriticalSection ); if (SUCCEEDED(AllocRIFFStream(pIStream, &pIRiffStream))) { MMCKINFO ckMain; ckMain.fccType = mmioFOURCC('W','A','V','E'); if (0 != pIRiffStream->Descend(&ckMain, NULL, MMIO_FINDRIFF)) { Trace(1, "ERROR: Load (Wave): Stream does not contain a wave chunk.\n"); hr = E_INVALIDARG; goto ON_END; } // Parses the header information and seeks to the beginning // of the data in the data chunk. if (0 == ParseHeader(pIStream, pIRiffStream, &ckMain)) { Trace(1, "ERROR: Load (Wave): Attempt to read wave's header information failed.\n"); hr = E_INVALIDARG; goto ON_END; } if (0 == m_cSamples) { if (m_pwfx && WAVE_FORMAT_PCM == m_pwfx->wFormatTag) { m_cSamples = m_cbStream / m_pwfx->nBlockAlign; } else // wave format not supported { hr = DSERR_BADFORMAT; goto ON_END; } } } pIStream->AddRef(); if (m_pStream) { m_pStream->Release(); } m_pStream = pIStream; ON_END: if (pIRiffStream) pIRiffStream->Release(); LeaveCriticalSection( &m_CriticalSection ); TraceI(5, "CWave::Load01\n"); return hr; } STDMETHODIMP CWave::Save ( IStream* pIStream, BOOL fClearDirty ) { V_INAME(CWave::Save); return E_NOTIMPL; } STDMETHODIMP CWave::GetSizeMax ( ULARGE_INTEGER FAR* pcbSize ) { V_INAME(CWave::GetSizeMax); return E_NOTIMPL; } STDMETHODIMP CWave::GetDescriptor ( LPDMUS_OBJECTDESC pDesc ) { // Parameter validation... V_INAME(CWave::GetDescriptor); V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC) ZeroMemory(pDesc, sizeof(DMUS_OBJECTDESC)); pDesc->dwSize = sizeof(DMUS_OBJECTDESC); pDesc->dwValidData = DMUS_OBJ_CLASS; pDesc->guidClass = CLSID_DirectSoundWave; if (NULL != m_pwfx) { pDesc->dwValidData |= DMUS_OBJ_LOADED; } if (m_fdwOptions & DMUS_OBJ_OBJECT) { pDesc->guidObject = m_guid; pDesc->dwValidData |= DMUS_OBJ_OBJECT; } if (m_fdwOptions & DMUS_OBJ_NAME) { memcpy( pDesc->wszName, m_wszFilename, sizeof(m_wszFilename) ); pDesc->dwValidData |= DMUS_OBJ_NAME; } if (m_fdwOptions & DMUS_OBJ_VERSION) { pDesc->vVersion.dwVersionMS = m_vVersion.dwVersionMS; pDesc->vVersion.dwVersionLS = m_vVersion.dwVersionLS; pDesc->dwValidData |= DMUS_OBJ_VERSION; } return S_OK; } STDMETHODIMP CWave::SetDescriptor ( LPDMUS_OBJECTDESC pDesc ) { HRESULT hr = E_INVALIDARG; DWORD dw = 0; // Parameter validation... V_INAME(CWave::SetDescriptor); V_PTR_READ(pDesc, DMUS_OBJECTDESC) if (pDesc->dwSize >= sizeof(DMUS_OBJECTDESC)) { if(pDesc->dwValidData & DMUS_OBJ_CLASS) { dw |= DMUS_OBJ_CLASS; } if(pDesc->dwValidData & DMUS_OBJ_LOADED) { dw |= DMUS_OBJ_LOADED; } if (pDesc->dwValidData & DMUS_OBJ_OBJECT) { m_guid = pDesc->guidObject; dw |= DMUS_OBJ_OBJECT; m_fdwOptions |= DMUS_OBJ_OBJECT; } if (pDesc->dwValidData & DMUS_OBJ_NAME) { memcpy( m_wszFilename, pDesc->wszName, sizeof(WCHAR)*DMUS_MAX_NAME ); dw |= DMUS_OBJ_NAME; m_fdwOptions |= DMUS_OBJ_NAME; } if (pDesc->dwValidData & DMUS_OBJ_VERSION) { m_vVersion.dwVersionMS = pDesc->vVersion.dwVersionMS; m_vVersion.dwVersionLS = pDesc->vVersion.dwVersionLS; dw |= DMUS_OBJ_VERSION; m_fdwOptions |= DMUS_OBJ_VERSION; } if (pDesc->dwValidData & (~dw)) { Trace(2, "WARNING: SetDescriptor (Wave): Descriptor contains fields that were not set.\n"); hr = S_FALSE; pDesc->dwValidData = dw; } else { hr = S_OK; } } return hr; } STDMETHODIMP CWave::ParseDescriptor ( LPSTREAM pStream, LPDMUS_OBJECTDESC pDesc ) { V_INAME(CWave::ParseDescriptor); V_PTR_READ(pDesc, DMUS_OBJECTDESC) CRiffParser Parser(pStream); RIFFIO ckMain; RIFFIO ckNext; RIFFIO ckINFO; HRESULT hr = S_OK; Parser.EnterList(&ckMain); if (Parser.NextChunk(&hr) && (ckMain.fccType == mmioFOURCC('W','A','V','E'))) { Parser.EnterList(&ckNext); while(Parser.NextChunk(&hr)) { switch(ckNext.ckid) { case DMUS_FOURCC_GUID_CHUNK: hr = Parser.Read( &pDesc->guidObject, sizeof(GUID) ); if( SUCCEEDED(hr) ) { pDesc->dwValidData |= DMUS_OBJ_OBJECT; } break; case DMUS_FOURCC_VERSION_CHUNK: hr = Parser.Read( &pDesc->vVersion, sizeof(DMUS_VERSION) ); if( SUCCEEDED(hr) ) { pDesc->dwValidData |= DMUS_OBJ_VERSION; } break; case FOURCC_LIST: switch(ckNext.fccType) { case DMUS_FOURCC_INFO_LIST: Parser.EnterList(&ckINFO); while (Parser.NextChunk(&hr)) { switch( ckINFO.ckid ) { case mmioFOURCC('I','N','A','M'): { DWORD cbSize; cbSize = min(ckINFO.cksize, DMUS_MAX_NAME); char szName[DMUS_MAX_NAME]; hr = Parser.Read(&szName, sizeof(szName)); if(SUCCEEDED(hr)) { MultiByteToWideChar(CP_ACP, 0, szName, -1, pDesc->wszName, DMUS_MAX_NAME); pDesc->dwValidData |= DMUS_OBJ_NAME; } break; } default: break; } } Parser.LeaveList(); break; } break; default: break; } } Parser.LeaveList(); } else { Trace(2, "WARNING: ParseDescriptor (Wave): The stream does not contain a Wave chunk.\n"); hr = DMUS_E_CHUNKNOTFOUND; } return hr; } STDMETHODIMP CWave::GetLength(REFERENCE_TIME *prtLength) { HRESULT hr = S_OK; if (0 == m_cSamples) { if (m_pwfx && WAVE_FORMAT_PCM == m_pwfx->wFormatTag) { m_cSamples = m_cbStream / m_pwfx->nBlockAlign; } } if (m_cSamples && m_pwfx && m_pwfx->nSamplesPerSec) { if(m_dwDecompressedStart > 0) { assert(m_dwDecompressedStart < m_cSamples); *prtLength = 1000 * (REFERENCE_TIME)(m_cSamples - m_dwDecompressedStart) / m_pwfx->nSamplesPerSec; } else { *prtLength = 1000 * (REFERENCE_TIME)m_cSamples / m_pwfx->nSamplesPerSec; } } else { Trace(2, "WARNING: Couldn't get a length for a Wave.\n"); hr = DMUS_E_BADWAVE; } return hr; }