|
|
#include <objbase.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <dsoundp.h>
//#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 <regstr.h>
#include <share.h>
#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<IDirectSoundWave*>(this); } else if (iid == IID_IPersistStream) { *ppv = static_cast<IPersistStream*>(this); } else if (iid == IID_IDirectMusicObject) { *ppv = static_cast<IDirectMusicObject*>(this); } else if (iid == IID_IPrivateWave) { *ppv = static_cast<IPrivateWave*>(this); } else { *ppv = NULL; Trace(4,"Warning: Request to query unknown interface on Wave Object\n"); return E_NOINTERFACE; } reinterpret_cast<IUnknown*>(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; }
|