windows-nt/Source/XPSP1/NT/multimedia/directx/dmusic/dswave/viewport.cpp
2020-09-26 16:20:57 +08:00

964 lines
27 KiB
C++

#include <objbase.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <dsoundp.h>
#include "debug.h"
#include "dmusicc.h"
#include "dmusici.h"
#include "validate.h"
#include "riff.h"
#include "dswave.h"
#include "waveutil.h"
#include "riff.h"
#include <regstr.h>
#include <share.h>
// CWaveViewPort(IStream *pStream); // Constructor receives stream.
// ~CWaveViewPort(); // Destructor releases memory, streams, etc.
//
// STDMETHODIMP Init();
// STDMETHODIMP GetFormat(LPWAVEFORMATEX pWaveFormatEx, LPDWORD pdwWaveFormatExSize);
// STDMETHODIMP Seek(DWORD dwSample);
// STDMETHODIMP Read(LPVOID *ppvBuffer, DWORD cpvBuffer, LPDWORD pcb);
CWaveViewPort::CWaveViewPort() : m_dwDecompressedStart(0), m_dwDecompStartOffset(0), m_dwDecompStartOffsetPCM(0), m_dwDecompStartDelta(0)
{
V_INAME(CWaveViewPort::CWaveViewPort);
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.
m_cRef = 1;
// General stuff...
m_pStream = NULL;
m_cSamples = 0L;
m_cbStream = 0L;
m_dwStart = 0L;
// Viewport info...
m_pwfxTarget = NULL;
ZeroMemory(&m_ash, sizeof(ACMSTREAMHEADER));
m_hStream = NULL;
m_pDst = NULL;
m_pRaw = NULL;
m_fdwOptions = 0L;
m_dwPreCacheFilePos = 0;
m_dwFirstPCMSample = 0;
m_dwPCMSampleOut = 0;
return;
}
CWaveViewPort::~CWaveViewPort()
{
V_INAME(CWaveViewPort::~CWaveViewPort);
if (m_pStream) m_pStream->Release();
if (m_hStream)
{
acmStreamUnprepareHeader(m_hStream, &m_ash, 0L);
acmStreamClose(m_hStream, 0);
}
if (NULL != m_pwfxTarget)
{
GlobalFreePtr(m_pwfxTarget);
}
if (NULL != m_ash.pbDst)
{
GlobalFreePtr(m_ash.pbDst);
}
if (NULL != m_ash.pbSrc)
{
GlobalFreePtr(m_ash.pbSrc);
}
DeleteCriticalSection(&m_CriticalSection);
InterlockedDecrement(&g_cComponent);
return;
}
STDMETHODIMP CWaveViewPort::QueryInterface
(
const IID &iid,
void **ppv
)
{
V_INAME(CWaveViewPort::QueryInterface);
if (iid == IID_IUnknown || iid == IID_IDirectSoundSource)
{
*ppv = static_cast<IDirectSoundSource*>(this);
}
else
{
*ppv = NULL;
Trace(4,"Warning: Request to query unknown interface on Viewport\n");
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(this)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) CWaveViewPort::AddRef()
{
V_INAME(CWaveViewPort::AddRef);
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) CWaveViewPort::Release()
{
V_INAME(CWaveViewPort::Release);
if (!InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}
return m_cRef;
}
STDMETHODIMP CWaveViewPort::SetSink
(
IDirectSoundConnect *pSinkConnect
)
{
V_INAME(CWaveViewPort::Init);
return S_OK;
}
STDMETHODIMP CWaveViewPort::GetFormat
(
LPWAVEFORMATEX pwfx,
DWORD dwSizeAllocated,
LPDWORD pdwSizeWritten
)
{
DWORD cbSize;
V_INAME(CWaveViewPort::GetFormat);
if (!pwfx && !pdwSizeWritten)
{
Trace(1, "ERROR: GetFormat (Viewport): Must request either the format or the required size");
return E_INVALIDARG;
}
if (!m_pwfxTarget)
{
return DSERR_BADFORMAT;
}
// Note: Assuming that the wave object fills the cbSize field even
// on PCM formats...
if (WAVE_FORMAT_PCM == m_pwfxTarget->wFormatTag)
{
cbSize = sizeof(PCMWAVEFORMAT);
}
else
{
cbSize = sizeof(WAVEFORMATEX) + m_pwfxTarget->cbSize;
}
if (pdwSizeWritten)
{
V_PTR_WRITE(pdwSizeWritten, DWORD);
*pdwSizeWritten = cbSize;
}
if (pwfx)
{
V_BUFPTR_WRITE(pwfx, dwSizeAllocated);
if (dwSizeAllocated < cbSize)
{
return DSERR_INVALIDPARAM;
}
else
{
CopyMemory(pwfx, m_pwfxTarget, cbSize);
// Set the cbSize field in destination if we have room
if (WAVE_FORMAT_PCM == m_pwfxTarget->wFormatTag && dwSizeAllocated >= sizeof(WAVEFORMATEX))
{
pwfx->cbSize = 0;
}
}
}
return S_OK;
}
STDMETHODIMP CWaveViewPort::Seek
(
ULONGLONG ullPosition
)
{
LARGE_INTEGER li;
HRESULT hr;
MMRESULT mmr;
DWORD cbSize;
V_INAME(CWaveViewPort::Seek);
m_fdwOptions &= ~DSOUND_WVP_STREAMEND; // rsw clear this on seek: no longer at stream end
if (m_fdwOptions & DSOUND_WVP_NOCONVERT)
{
if ((DWORD) ullPosition >= m_cbStream)
{
// Seek past end of stream
//
m_fdwOptions |= DSOUND_WVP_STREAMEND;
m_dwOffset = m_cbStream;
return S_OK;
}
m_dwOffset = (DWORD) ullPosition; // rsw initialize offset to Seek position
if (0 != (ullPosition % m_pwfxTarget->nBlockAlign))
{
// Seek position not block aligned?
Trace(1, "ERROR: Seek (wave): Seek position is not block-aligned.\n");
return (DMUS_E_BADWAVE);
}
li.HighPart = 0;
li.LowPart = ((DWORD)ullPosition) + m_dwStartPos;
hr = m_pStream->Seek(li, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
{
Trace(1, "ERROR: Seek (Viewport): Seeking the vieport's stream failed.\n");
return (DMUS_E_BADWAVE);
}
}
else
{
// Estimating source stream position...
//
// Should we create lookup table?!
cbSize = (DWORD)ullPosition;
if (cbSize)
{
mmr = acmStreamSize(m_hStream, cbSize, &cbSize, ACM_STREAMSIZEF_DESTINATION);
if (MMSYSERR_NOERROR != mmr)
{
Trace(1, "ERROR: Seek (viewport): Could not convert target stream size to source format.\n");
return (DMUS_E_BADWAVE);
}
}
if (cbSize >= m_cbStream)
{
// Seek past end of stream
//
m_fdwOptions |= DSOUND_WVP_STREAMEND;
m_dwOffset = m_cbStream;
return S_OK;
}
// If this is a seek to the precache end we know where to start reading from
if((m_fdwOptions & DSOUND_WAVEF_ONESHOT) == 0)
{
// Go back to the block that was read for the end of the precached data
if(cbSize != 0 && m_dwPCMSampleOut == ullPosition)
{
m_dwOffset = m_dwPreCacheFilePos;
li.HighPart = 0;
li.LowPart = m_dwOffset + m_dwStartPos;
}
else
{
m_dwOffset = cbSize; // rsw initialize offset to Seek position
li.HighPart = 0;
li.LowPart = cbSize + m_dwStartPos;
}
hr = m_pStream->Seek(li, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
{
Trace(1, "ERROR: Seek (viewport): Seeking the viewport's stream failed.\n");
return (DMUS_E_BADWAVE);
}
// Since we're restarting, re-initialize.
m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
m_fdwOptions |= DSOUND_WVP_CONVERTSTATE_01;
m_ash.cbSrcLength = (DWORD)m_ash.dwSrcUser;
m_ash.cbSrcLengthUsed = m_ash.cbSrcLength;
m_ash.dwDstUser = 0L;
m_ash.cbDstLengthUsed = 0L;
}
/////////////////////////////////////////////////////////////////////////////////////
// If we're starting the wave, re-seek the stream (one-shots always need to re-seek).
// NOTE: The following assumes that compressed waves always seek from the beginning,
// since the value returned by acmStreamSize is pretty unreliable.
/////////////////////////////////////////////////////////////////////////////////////
else if ( cbSize == 0 || (m_fdwOptions & DSOUND_WAVEF_ONESHOT) )
{
m_dwOffset = cbSize; // rsw initialize offset to Seek position
li.HighPart = 0;
li.LowPart = cbSize + m_dwStartPos;
hr = m_pStream->Seek(li, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
{
Trace(1, "ERROR: Seek (viewport): Seeking the viewport's stream failed.\n");
return (DMUS_E_BADWAVE);
}
// Since we're restarting, re-initialize.
m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
m_fdwOptions |= DSOUND_WVP_CONVERTSTATE_01;
m_ash.cbSrcLength = (DWORD)m_ash.dwSrcUser;
m_ash.cbSrcLengthUsed = m_ash.cbSrcLength;
m_ash.dwDstUser = 0L;
m_ash.cbDstLengthUsed = 0L;
}
}
TraceI(5, "Seek (Viewport): Succeeded.\n");
return S_OK;
}
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;
}
return E_FAIL;
}
HRESULT CWaveViewPort::acmRead
(
void
)
{
DWORD cbSize;
DWORD dwOffset;
DWORD fdwConvert = 0;
MMRESULT mmr;
HRESULT hr;
V_INAME(CWaveViewPort::acmRead);
for (m_ash.cbDstLengthUsed = 0; 0 == m_ash.cbDstLengthUsed; )
{
// Did we use up the entire buffer?
if (m_ash.cbSrcLengthUsed == m_ash.cbSrcLength)
{
// Yep!
dwOffset = 0L;
cbSize = (DWORD)m_ash.dwSrcUser;
}
else
{
// Nope!
dwOffset = m_ash.cbSrcLength - m_ash.cbSrcLengthUsed;
cbSize = (DWORD)m_ash.dwSrcUser - dwOffset;
// Moving the remaining data from the end of buffer to the beginning
MoveMemory(
m_ash.pbSrc, // Base address
&(m_ash.pbSrc[m_ash.cbSrcLengthUsed]), // Address of unused bytes
dwOffset); // Number of unused bytes
}
// Are we at the end of the stream?
cbSize = min(cbSize, m_cbStream - m_dwOffset);
if (0 == cbSize)
{
if (dwOffset)
{
m_ash.cbSrcLength = dwOffset;
}
}
else
{
hr = m_pStream->Read(&(m_ash.pbSrc[dwOffset]), cbSize, &cbSize);
if (FAILED(hr))
{
Trace(1, "ERROR: Read (Viewport): Attempt to read source stream returned 0x%08lx\n", hr);
//>>>>>>>>>>>>>>>>>>>>
m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
m_fdwOptions |= DSOUND_WVP_STREAMEND;
return(DMUS_E_CANNOTREAD);
//>>>>>>>>>>>>>>>>>>>>
}
m_dwOffset += cbSize;
m_ash.cbSrcLength = cbSize + dwOffset;
}
switch (m_fdwOptions & DSOUND_WVP_CONVERTMASK)
{
case DSOUND_WVP_CONVERTSTATE_01:
fdwConvert = ACM_STREAMCONVERTF_BLOCKALIGN;
break;
case DSOUND_WVP_CONVERTSTATE_02:
fdwConvert = ACM_STREAMCONVERTF_BLOCKALIGN | ACM_STREAMCONVERTF_END;
break;
case DSOUND_WVP_CONVERTSTATE_03:
fdwConvert = ACM_STREAMCONVERTF_END;
break;
default:
TraceI(3, "CWaveViewPort::acmRead: Default case?!\n");
break;
}
mmr = acmStreamConvert(m_hStream, &m_ash, fdwConvert);
if (MMSYSERR_NOERROR != mmr)
{
Trace(1, "ERROR: Read (Viewport): Attempt to convert wave to PCM failed.\n");
return (MMRESULTToHRESULT(mmr));
}
if (0 != m_ash.cbDstLengthUsed)
{
m_ash.dwDstUser = 0L;
return (S_OK);
}
// No data returned?
switch (m_fdwOptions & DSOUND_WVP_CONVERTMASK)
{
case DSOUND_WVP_CONVERTSTATE_01:
if (0 == cbSize)
{
// We're at the end of the stream..
m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
m_fdwOptions |= DSOUND_WVP_CONVERTSTATE_02;
TraceI(5, "CWaveViewPort::acmRead: Moving to stage 2\n");
}
// Otherwise, continue converting data as normal.
break;
case DSOUND_WVP_CONVERTSTATE_02:
// We have hit the last partial block!
m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
m_fdwOptions |= DSOUND_WVP_CONVERTSTATE_03;
TraceI(5, "CWaveViewPort::acmRead: Moving to stage 3\n");
break;
case DSOUND_WVP_CONVERTSTATE_03:
// No more data after end flag, NO MORE DATA!!
m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
m_fdwOptions |= DSOUND_WVP_STREAMEND;
Trace(2, "WARNING: Read (Viewport): End of source stream.\n");
return (DMUS_E_BADWAVE);
default:
TraceI(3, "CWaveViewPort::acmRead: Default case?!\n");
break;
}
}
TraceI(3, "CWaveViewPort::acmRead: We should never get here!\n");
return (S_OK);
}
//////////////////////////////////////////////////////////////////////////////
//
// ppvBuffer[] contains cpvBuffer pointers-to-samples, each to be filled with
// *pcb bytes of data. On output *pcb will contain the number of bytes (per
// buffer) actually read.
//
// pdwBusIds and pdwFuncIds are used to specify the bus and functionality
// of each buffer, but these are ignored by the wave object.
//
STDMETHODIMP CWaveViewPort::Read
(
LPVOID *ppvBuffer,
LPDWORD pdwBusIds,
LPDWORD pdwFuncIds,
LPLONG plPitchShifts,
DWORD cpvBuffer,
ULONGLONG *pcb
)
{
HRESULT hr = S_OK;
DWORD cbRead;
DWORD dwOffset;
DWORD cbSize;
V_INAME(CWaveViewPort::Read);
V_BUFPTR_READ(ppvBuffer, (cpvBuffer * sizeof(LPVOID)));
V_BUFPTR_READ_OPT(pdwBusIds, (cpvBuffer * sizeof(LPDWORD)));
V_BUFPTR_READ_OPT(pdwFuncIds, (cpvBuffer * sizeof(LPDWORD)));
for (cbRead = cpvBuffer, cbSize = (DWORD)*pcb; cbRead; cbRead--)
{
V_BUFPTR_WRITE(ppvBuffer[cbRead - 1], cbSize);
}
if (m_fdwOptions & DSOUND_WVP_STREAMEND)
{
*pcb = 0;
Trace(2, "WARNING: Read (Viewport): Attempt to read at end of stream.\n");
return (S_FALSE);
}
LPVOID *ppvWriteBuffers = ppvBuffer;
DWORD dwWriteBufferCount = cpvBuffer;
if (m_fdwOptions & DSOUND_WVP_NOCONVERT)
{
// Total number of bytes to read... size of each buffer * number of buffers
cbRead = ((DWORD)*pcb) * dwWriteBufferCount;
dwOffset = 0;
TraceI(5, "CWaveViewPort::Read - No conversion [%d bytes]\n", cbRead);
do
{
// Calculate read size... It's going to be the size of:
// 1. Remaining bytes to read.
// 2. Size of the buffer.
// 3. Remaining bytes in the stream.
// Whichever happens to be the smallest.
cbSize = min(cbRead, m_ash.cbDstLength);
cbSize = min(cbSize, m_cbStream - m_dwOffset);
TraceI(5, "CWaveViewPort::Read - Trying to read %d bytes\n", cbSize);
DWORD _cbSize = cbSize; cbSize = 0; // Read may not set cbSize to zero
hr = m_pStream->Read(m_ash.pbDst, _cbSize, &cbSize);
TraceI(5, "CWaveViewPort::Read - Read %d bytes\n", cbSize);
if (FAILED(hr))
{
Trace(2, "WARNING: Read (Viewport): Attempt to read returned 0x%08lx.\n", hr);
break;
}
dwOffset = DeinterleaveBuffers(
m_pwfxTarget,
m_ash.pbDst,
(LPBYTE *)ppvWriteBuffers,
dwWriteBufferCount,
cbSize,
dwOffset);
cbRead -= cbSize;
m_dwOffset += cbSize;
if (m_dwOffset >= m_cbStream)
{
m_fdwOptions |= DSOUND_WVP_STREAMEND;
break;
}
}
while (0 != cbRead);
if (SUCCEEDED(hr))
{
*pcb = dwOffset;
}
}
else
{
// If this is the read for the precache then we should remember the fileposition,
// start sample for the decompressed block and the last sample passed back so we
// can accurately pick up from there when refilling buffers
// We use the LPLONG plPitchShifts in the read method as a boolean
// this is a HACK!! We need to change this...
// *plPitchShifts == 2 is to remember the precache offset
// *plPitchShifts == 1 is to read from there
bool fRememberPreCache = false;
if(plPitchShifts != NULL && *plPitchShifts == 2 && (m_fdwOptions & DSOUND_WAVEF_ONESHOT) == 0)
{
fRememberPreCache = true;
}
bool bRemoveSilence = false;
cbRead = ((DWORD)*pcb) * dwWriteBufferCount;
dwOffset = 0;
TraceI(5, "CWaveViewPort::Read - Conversion needed\n");
do
{
if(m_dwDecompressedStart > 0 && m_dwOffset <= m_dwDecompStartOffset)
{
bRemoveSilence = true;
}
// Is there any remnant data in destination buffer?
if (m_ash.dwDstUser >= m_ash.cbDstLengthUsed)
{
if(fRememberPreCache)
{
// Go back on block
m_dwPreCacheFilePos = m_dwOffset - m_ash.cbSrcLength;
m_dwFirstPCMSample = dwOffset * dwWriteBufferCount;
}
if(plPitchShifts != NULL && *plPitchShifts == 1)
{
// Seek to the right place first
Seek(m_dwPCMSampleOut);
// Read one block since we're starting one block behind
hr = acmRead();
if(FAILED(hr))
{
break;
}
}
hr = acmRead();
}
if (FAILED(hr))
{
// acmRead spews when it fails; no need to do it again here
break;
}
DWORD dwDstOffset = (ULONG)m_ash.dwDstUser;
if(bRemoveSilence)
{
// We have partial data to throw away
if(m_dwDecompStartOffset <= m_dwOffset)
{
if(dwDstOffset > 0)
{
dwDstOffset += m_dwDecompStartDelta;
}
else
{
// This is the first decompressed block so we go straight to the value we know
dwDstOffset += m_dwDecompStartOffsetPCM;
}
m_ash.dwDstUser = dwDstOffset;
bRemoveSilence = false;
}
else
{
// This is all throw away data
bRemoveSilence = false;
cbSize = min(cbRead, m_ash.cbDstLengthUsed - dwDstOffset);
m_ash.dwDstUser += cbSize;
continue;
}
}
// We use the LPLONG plPitchShifts in the read method as a boolean
// this is a HACK!! We need to change this...
if(plPitchShifts && *plPitchShifts == 1)
{
dwDstOffset = m_dwPCMSampleOut - m_dwFirstPCMSample;
m_ash.dwDstUser = dwDstOffset;
plPitchShifts = 0;
}
cbSize = min(cbRead, m_ash.cbDstLengthUsed - dwDstOffset);
dwOffset = DeinterleaveBuffers(
m_pwfxTarget,
&(m_ash.pbDst[dwDstOffset]),
(LPBYTE *)ppvWriteBuffers,
dwWriteBufferCount,
cbSize,
dwOffset);
cbRead -= cbSize;
m_ash.dwDstUser += cbSize;
if ((m_fdwOptions & DSOUND_WVP_STREAMEND) &&
(m_ash.dwDstUser >= m_ash.cbDstLengthUsed))
{
break;
}
}
while(0 != cbRead);
if(fRememberPreCache)
{
m_dwPCMSampleOut = dwOffset * dwWriteBufferCount;
}
if (SUCCEEDED(hr))
{
*pcb = dwOffset;
}
}
TraceI(5, "CWaveViewPort::Read returning %x (%d bytes)\n", hr, dwOffset);
return hr;
}
STDMETHODIMP CWaveViewPort::GetSize
(
ULONGLONG *pcb
)
{
V_INAME(CWaveViewPort::GetSize);
V_PTR_WRITE(pcb, ULONGLONG);
TraceI(5, "CWaveViewPort::GetSize [%d samples]\n", m_cSamples);
HRESULT hr = S_OK;
if (m_fdwOptions & DSOUND_WVP_NOCONVERT)
{
// No conversion. This is trivial
*pcb = (SAMPLE_TIME)(m_cbStream);
}
else if (!m_pwfxTarget)
{
hr = DSERR_UNINITIALIZED;
}
else
{
// Conversion required; let's hope target format is PCM
if (WAVE_FORMAT_PCM == m_pwfxTarget->wFormatTag)
{
// Cool. This is simply the number of samples X the block align
*pcb = (SAMPLE_TIME)((m_cSamples - m_dwDecompressedStart) * m_pwfxTarget->nBlockAlign);
}
else
{
Trace(1, "ERROR: GetSize (Viewport): Conversion required and target is not PCM.\n");
hr = DSERR_BADFORMAT;
}
}
return (hr);
}
HRESULT CWaveViewPort::Create
(
PCREATEVIEWPORT pCreate
)
{
DWORD cbSize;
MMRESULT mmr;
HRESULT hr;
LARGE_INTEGER li;
LPWAVEFORMATEX pwfxSrc = pCreate->pwfxSource;
LPWAVEFORMATEX pwfxDst = pCreate->pwfxTarget;
V_INAME(CWaveViewPort::Create);
TraceI(5, "CWaveViewPort::Create [%d samples]\n", pCreate->cSamples);
EnterCriticalSection(&m_CriticalSection);
// Clone source stream...
hr = pCreate->pStream->Clone(&m_pStream);
if (FAILED(hr))
{
LeaveCriticalSection(&m_CriticalSection);
return (hr);
}
// Misc assignments
m_cSamples = pCreate->cSamples;
m_cbStream = pCreate->cbStream;
m_dwOffset = 0L;
m_fdwOptions = pCreate->fdwOptions;
m_dwDecompressedStart = pCreate->dwDecompressedStart;
m_dwDecompStartOffset = 0L;
m_dwDecompStartOffsetPCM = 0L;
m_dwDecompStartDelta = 0L;
TraceI(5, "CWaveViewPort:: %d samples\n", m_cSamples);
// Allocate destination format
cbSize = SIZEOFFORMATEX(pwfxDst);
m_pwfxTarget = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, cbSize);
if (NULL == m_pwfxTarget)
{
LeaveCriticalSection(&m_CriticalSection);
TraceI(1, "OUT OF MEMORY: CWaveViewPort::Create - size: %d \n", cbSize);
return (E_OUTOFMEMORY);
}
// We don't own the buffer for pwfxDst, so we can't touch its cbSize.
// We have to set the size manually on PCM, we KNOW the buffer is
// large enough.
CopyFormat(m_pwfxTarget, pwfxDst);
if (WAVE_FORMAT_PCM == m_pwfxTarget->wFormatTag)
{
m_pwfxTarget->cbSize = 0;
}
// Calculating (block-aligned) size of destination buffer...
cbSize = (pwfxDst->nAvgBytesPerSec * CONVERTLENGTH) / 1000;
cbSize = BLOCKALIGN(cbSize, pwfxDst->nBlockAlign);
m_ash.pbDst = (LPBYTE)GlobalAllocPtr(GHND, cbSize);
if (NULL == m_ash.pbDst)
{
LeaveCriticalSection(&m_CriticalSection);
TraceI(1, "OUT OF MEMORY: CWaveViewPort::Create 01\n");
return (E_OUTOFMEMORY);
}
m_ash.cbDstLength = cbSize;
// Getting stream starting offset...
li.HighPart = 0;
li.LowPart = 0;
hr = m_pStream->Seek(li, STREAM_SEEK_CUR, (ULARGE_INTEGER *)(&li));
m_dwStartPos = li.LowPart;
// Do we need to use the ACM?
if (FormatCmp(pwfxSrc, pwfxDst))
{
// Formats compare!! All we need to do is to copy the data straight
// from the source stream. Way Cool!!
TraceI(5, "Source and Destination formats are similar!\n");
m_fdwOptions |= DSOUND_WVP_NOCONVERT;
}
else
{
// Source and destination formats are different...
TraceI(5, "CWaveViewPort:Create: Formats are different... Use ACM!\n");
m_fdwOptions |= DSOUND_WVP_CONVERTSTATE_01;
mmr = acmStreamOpen(&m_hStream, NULL, pwfxSrc, pwfxDst, NULL, 0, 0, 0);
if (MMSYSERR_NOERROR != mmr)
{
Trace(1, "ERROR: Create (Viewport): Attempt to open a conversion stream failed.\n");
LeaveCriticalSection(&m_CriticalSection);
return MMRESULTToHRESULT(mmr);
}
mmr = acmStreamSize(m_hStream, cbSize, &cbSize, ACM_STREAMSIZEF_DESTINATION);
if (MMSYSERR_NOERROR != mmr)
{
Trace(1, "ERROR: Create(Viewport): Could not convert target stream size to source format.\n");
LeaveCriticalSection(&m_CriticalSection);
return MMRESULTToHRESULT(mmr);
}
m_ash.cbSrcLength = cbSize;
m_ash.pbSrc = (LPBYTE)GlobalAllocPtr(GHND, cbSize);
if (NULL == m_ash.pbSrc)
{
TraceI(1, "OUT OF MEMORY: CWaveViewPort:Create: GlobalAlloc failed.\n");
LeaveCriticalSection(&m_CriticalSection);
return E_OUTOFMEMORY;
}
// Also get the position for the actual start for the decompressed data
if(m_dwDecompressedStart > 0)
{
m_dwDecompStartOffsetPCM = m_dwDecompressedStart * (pwfxDst->wBitsPerSample / 8) * pwfxDst->nChannels;
mmr = acmStreamSize(m_hStream, m_dwDecompStartOffsetPCM, &m_dwDecompStartOffset, ACM_STREAMSIZEF_DESTINATION);
DWORD dwDelta = 0;
mmr = acmStreamSize(m_hStream, m_dwDecompStartOffset, &dwDelta, ACM_STREAMSIZEF_SOURCE);
m_dwDecompStartDelta = m_dwDecompStartOffsetPCM - dwDelta;
m_dwDecompStartOffset += m_dwStartPos;
}
// For the source buffer, it is the full buffer size.
m_ash.dwSrcUser = m_ash.cbSrcLength;
m_ash.cbSrcLengthUsed = m_ash.cbSrcLength;
// For the destination buffer, it is the offset into the buffer
// where the data can be found.
m_ash.dwDstUser = 0L;
m_ash.cbDstLengthUsed = 0L;
m_ash.cbStruct = sizeof(ACMSTREAMHEADER);
mmr= acmStreamPrepareHeader(m_hStream, &m_ash, 0L);
if (MMSYSERR_NOERROR != mmr)
{
Trace(1, "ERROR: Create (Viewport): Attempt to prepare header for conversion stream failed.\n");
LeaveCriticalSection(&m_CriticalSection);
return MMRESULTToHRESULT(mmr);
}
}
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}