windows-nt/Source/XPSP1/NT/enduser/speech/sapi/sapi/wavstream.cpp
2020-09-26 16:20:57 +08:00

1621 lines
44 KiB
C++

// WavStream.cpp : Implementation of CWavStream
#include "stdafx.h"
#ifndef __sapi_h__
#include <sapi.h>
#endif
#include "WavStream.h"
#include "StreamHlp.h"
const FOURCC g_fourccFormat = mmioFOURCC('f','m','t',' ');
const FOURCC g_fourccEvents = mmioFOURCC('E', 'V', 'N', 'T');
const FOURCC g_fourccTranscript = mmioFOURCC('T','e','X','t');
const FOURCC g_fourccWave = mmioFOURCC('W','A','V','E');
const FOURCC g_fourccData = mmioFOURCC('d','a','t','a');
/////////////////////////////////////////////////////////////////////////////
// CWavStream
//
// Inline helpers convert mmioxxx functions into standard HRESULT based methods
//
/****************************************************************************
* MMIORESULT_TO_HRESULT *
*-----------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
inline HRESULT _MMIORESULT_TO_HRESULT(MMRESULT mm)
{
SPDBG_FUNC("_MMIORESULT_TO_HRESULT");
switch (mm)
{
case MMSYSERR_NOERROR:
return S_OK;
case MMSYSERR_NOMEM:
case MMIOERR_OUTOFMEMORY:
return E_OUTOFMEMORY;
case MMIOERR_CANNOTSEEK:
return STG_E_SEEKERROR;
case MMIOERR_CANNOTWRITE:
return STG_E_WRITEFAULT;
case MMIOERR_CANNOTREAD:
return STG_E_READFAULT;
case MMIOERR_PATHNOTFOUND:
return STG_E_PATHNOTFOUND;
case MMIOERR_FILENOTFOUND:
return STG_E_FILENOTFOUND;
case MMIOERR_ACCESSDENIED:
return STG_E_ACCESSDENIED;
case MMIOERR_SHARINGVIOLATION:
return STG_E_SHAREVIOLATION;
case MMIOERR_TOOMANYOPENFILES:
return STG_E_TOOMANYOPENFILES;
case MMIOERR_CANNOTCLOSE:
return STG_E_CANTSAVE;
case MMIOERR_INVALIDFILE:
case MMIOERR_CHUNKNOTFOUND: // Assume that a missing chunk is an invalid file
return SPERR_INVALID_WAV_FILE;
default:
// MMIOERR_CANNOTOPEN
// MMIOERR_CANNOTEXPAND
// MMIOERR_CHUNKNOTFOUND
// MMIOERR_UNBUFFERED
// MMIOERR_NETWORKERROR
// + any other unknown codes become...
return STG_E_UNKNOWN;
}
}
/****************************************************************************
* CWavStream::MMOpen *
*--------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
inline HRESULT CWavStream::MMOpen(const WCHAR * pszFileName, DWORD dwOpenFlags)
{
SPDBG_FUNC("CWavStream::MMOpen");
HRESULT hr = S_OK;
SPDBG_ASSERT(m_hFile == NULL);
MMIOINFO mmioinfo;
memset(&mmioinfo, 0, sizeof(mmioinfo));
m_hFile = g_Unicode.mmioOpen(pszFileName, &mmioinfo, dwOpenFlags);
if(m_hFile == NULL)
{
hr = _MMIORESULT_TO_HRESULT(mmioinfo.wErrorRet);
}
return hr;
}
inline HRESULT CWavStream::MMClose()
{
SPDBG_ASSERT(m_hFile);
HRESULT hr = _MMIORESULT_TO_HRESULT(::mmioClose(m_hFile, 0));
m_hFile = NULL;
return hr;
}
inline HRESULT CWavStream::MMSeek(LONG lOffset, int iOrigin, LONG * plNewPos)
{
HRESULT hr = S_OK;
*plNewPos = ::mmioSeek(m_hFile, lOffset, iOrigin);
if (*plNewPos == -1)
{
hr = STG_E_SEEKERROR;
}
return hr;
}
inline HRESULT CWavStream::MMRead(void * pv, LONG cb, LONG * plBytesRead)
{
HRESULT hr = S_OK;
*plBytesRead = ::mmioRead(m_hFile, (HPSTR)pv, cb);
if (*plBytesRead == -1)
{
*plBytesRead = 0;
hr = STG_E_READFAULT;
}
return hr;
}
inline HRESULT CWavStream::MMReadExact(void * pv, LONG cb)
{
LONG lReadSize;
HRESULT hr = MMRead(pv, cb, &lReadSize);
if (SUCCEEDED(hr) && lReadSize != cb)
{
hr = SPERR_INVALID_WAV_FILE;
}
return hr;
}
inline HRESULT CWavStream::MMWrite(const void * pv, LONG cb, LONG * plBytesWritten)
{
HRESULT hr = S_OK;
*plBytesWritten = ::mmioWrite(m_hFile, (const char *)pv, cb);
if (*plBytesWritten == -1)
{
*plBytesWritten = 0;
hr = STG_E_WRITEFAULT;
}
return hr;
}
inline HRESULT CWavStream::MMDescend(LPMMCKINFO lpck, const LPMMCKINFO lpckParent, UINT wFlags)
{
return _MMIORESULT_TO_HRESULT(::mmioDescend(m_hFile, lpck, lpckParent, wFlags));
}
inline HRESULT CWavStream::MMAscend(LPMMCKINFO lpck)
{
return _MMIORESULT_TO_HRESULT(::mmioAscend(m_hFile, lpck, 0));
}
inline HRESULT CWavStream::MMCreateChunk(LPMMCKINFO lpck, UINT wFlags)
{
return _MMIORESULT_TO_HRESULT(::mmioCreateChunk(m_hFile, lpck, wFlags));
}
/****************************************************************************
* CWavStream::FinalConstruct *
*----------------------------*
* Description:
* Initializes the WavStream object and obtains a pointer to the resource
* manager.
*
* Returns:
* Success code if object should be created
*
********************************************************************* RAL ***/
HRESULT CWavStream::FinalConstruct()
{
SPDBG_FUNC("CWavStream::FinalConstruct");
HRESULT hr = S_OK;
m_hFile = NULL;
m_hrStreamDefault = SPERR_UNINITIALIZED;
m_fEventSource = 0;
m_fEventSink = 0;
m_fTranscript = 0;
return hr;
}
/****************************************************************************
* CWavStream::FinalRelease *
*--------------------------*
* Description:
* This method is called when the object is released. It will unconditinally
* call Close() to close the file.
*
* Returns:
* void
*
********************************************************************* RAL ***/
void CWavStream::FinalRelease()
{
SPDBG_FUNC("CWavStream::FinalRelease");
Close();
}
/****************************************************************************
* CWavStream::QIExtendedInterfaces *
*----------------------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
HRESULT WINAPI CWavStream::QIExtendedInterfaces(void* pv, REFIID riid, void ** ppv, DWORD_PTR dw)
{
SPDBG_FUNC("CWavStream::QIExtendedInterfaces");
*ppv = NULL;
CWavStream * pThis = (CWavStream *)pv;
if (pThis->m_fEventSource && (riid == IID_ISpEventSource || riid == IID_ISpNotifySource))
{
*ppv = static_cast<ISpEventSource *>(pThis);
}
else if (pThis->m_fEventSink && riid == IID_ISpEventSink)
{
*ppv = static_cast<ISpEventSink *>(pThis);
}
else if (pThis->m_fTranscript && riid == IID_ISpTranscript)
{
*ppv = static_cast<ISpTranscript *>(pThis);
}
if (*ppv)
{
((IUnknown *)(*ppv))->AddRef();
return S_OK;
}
else
{
return S_FALSE; // Tells ATL to continue searching COM_INTERFACE_ENTRY list
}
}
/****************************************************************************
* CWavStream::Read *
*------------------*
* Description:
* Standard method of ISequentialStream interface. This method reads the
* specified number of bytes and returns the amount read.
*
* Returns:
* S_OK Success
* SPERR_UNINITIALIZED The object has not been initialzed with Open() or Create()
* STG_E_INVALIDPOINTER Invalid pointer (Defined by ISequentialStream)
* STG_E_READFAULT Generic error if mmioRead fails.
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::Read(void * pv, ULONG cb, ULONG *pcbRead)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::Read");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr))
{
if (m_cpBaseStream)
{
hr = m_cpBaseStream->Read(pv, cb, pcbRead);
}
else
{
if (SPIsBadWritePtr(pv, cb) || SP_IS_BAD_OPTIONAL_WRITE_PTR(pcbRead))
{
hr = STG_E_INVALIDPOINTER;
}
else
{
LONG lRead = 0;
hr = MMRead(pv, (m_cbSize - m_ulCurSeekPos >= cb) ? cb : (m_cbSize - m_ulCurSeekPos), &lRead);
m_ulCurSeekPos += lRead;
if (pcbRead)
{
*pcbRead = lRead;
}
}
}
}
return hr;
}
/****************************************************************************
* CWavStream::Write *
*-------------------*
* Description:
* Standard method of ISequentialStream interface. This method writes the
* specified number of bytes and returns the amount wriiten.
*
* Returns:
* S_OK Success
* SPERR_UNINITIALIZED The object has not been initialzed with Open() or Create()
* STG_E_INVALIDPOINTER Invalid pointer (Defined by ISequentialStream)
* STG_E_WRITEFAULT Generic error if mmioWrite fails.
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::Write(const void * pv, ULONG cb, ULONG *pcbWritten)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::Write");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr))
{
if (m_cpBaseStream)
{
hr = m_cpBaseStream->Write(pv, cb, pcbWritten);
}
else
{
if (SPIsBadReadPtr(pv, cb) || SP_IS_BAD_OPTIONAL_WRITE_PTR(pcbWritten))
{
hr = STG_E_INVALIDPOINTER;
}
else
{
LONG lWritten = 0;
if (m_hFile == NULL)
{
hr = SPERR_UNINITIALIZED;
}
else
{
if (!m_fWriteable)
{
hr = STG_E_ACCESSDENIED;
}
else
{
hr = MMWrite(pv, cb, &lWritten);
m_ulCurSeekPos += lWritten;
if(m_ulCurSeekPos > m_cbSize)
{
m_cbSize = m_ulCurSeekPos;
}
}
}
if (pcbWritten)
{
*pcbWritten = lWritten;
}
}
}
}
return hr;
}
/****************************************************************************
* CWavStream::Seek *
*------------------*
* Description:
* Standard method of IStream. This method seeks the stream within the data
* chunk of the wav file.
*
* Returns:
* S_OK Success
* SPERR_UNINITIALIZED The object has not been initialzed with Open() or Create()
* STG_E_INVALIDPOINTER Invalid pointer
* STG_E_INVALIDFUNCTION
*
********************************************************************* RAL ***/
//
// Currently this function will not allow seeks past the end of the file. This is an acceptable
// limitation since wav files should always be treated as linear data.
//
STDMETHODIMP CWavStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::Seek");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr))
{
if (m_cpBaseStream)
{
hr = m_cpBaseStream->Seek(dlibMove, dwOrigin, plibNewPosition);
}
else
{
if (SP_IS_BAD_OPTIONAL_WRITE_PTR(plibNewPosition))
{
hr = STG_E_INVALIDPOINTER;
}
else
{
if (dlibMove.HighPart != 0 && dlibMove.HighPart != 0xFFFFFFFF)
{
hr = STG_E_INVALIDFUNCTION;
}
else
{
LONG lDesiredPos;
switch (dwOrigin)
{
case SEEK_CUR:
lDesiredPos = ((LONG)m_ulCurSeekPos) + ((LONG)dlibMove.LowPart);
break;
case SEEK_SET:
lDesiredPos = (LONG)dlibMove.LowPart;
break;
case SEEK_END:
lDesiredPos = m_cbSize - (LONG)dlibMove.LowPart;
break;
default:
hr = STG_E_INVALIDFUNCTION;
}
if (SUCCEEDED(hr) && (ULONG)lDesiredPos != m_ulCurSeekPos)
{
if (lDesiredPos < 0 || (ULONG)lDesiredPos > m_cbSize)
{
hr = STG_E_INVALIDFUNCTION;
}
else
{
LONG lIgnored;
hr = MMSeek(lDesiredPos + m_lDataStart, SEEK_SET, &lIgnored);
if (SUCCEEDED(hr))
{
m_ulCurSeekPos = lDesiredPos;
}
}
}
if (plibNewPosition)
{
plibNewPosition->QuadPart = m_ulCurSeekPos;
}
}
}
}
}
return hr;
}
/****************************************************************************
* CWavStream::SetSize *
*---------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::SetSize(ULARGE_INTEGER libNewSize)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::SetSize");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr) && m_cpBaseStream)
{
hr = m_cpBaseStream->SetSize(libNewSize);
}
// Ignore this method for wav files.
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
/****************************************************************************
* CWavStream::CopyTo *
*--------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::CopyTo(IStream * pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::CopyTo");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr))
{
if (m_cpBaseStream)
{
hr = m_cpBaseStream->CopyTo(pstm, cb, pcbRead, pcbWritten);
}
else
{
hr = SpGenericCopyTo(this, pstm, cb, pcbRead, pcbWritten);
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
/****************************************************************************
* CWavStream::Revert *
*--------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::Revert()
{
SPDBG_FUNC("CWavStream::Revert");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr) && m_cpBaseStream)
{
hr = m_cpBaseStream->Revert();
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
/****************************************************************************
* CWavStream::LockRegion *
*------------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
SPDBG_FUNC("CWavStream::LockRegion");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr))
{
if (m_cpBaseStream)
{
hr = m_cpBaseStream->LockRegion(libOffset, cb, dwLockType);
}
else
{
hr = STG_E_INVALIDFUNCTION;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
/****************************************************************************
* CWavStream::UnlockRegion *
*--------------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
SPDBG_FUNC("CWavStream::UnlockRegion");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr))
{
if (m_cpBaseStream)
{
hr = m_cpBaseStream->UnlockRegion(libOffset, cb, dwLockType);
}
else
{
hr = STG_E_INVALIDFUNCTION;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
/****************************************************************************
* CWavStream::Commit *
*--------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::Commit(DWORD dwFlags)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::Commit");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr) && m_cpBaseStream)
{
hr = m_cpBaseStream->Commit(dwFlags);
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
/****************************************************************************
* CWavStream::Stat *
*------------------*
* Description:
* Standard method of IStream. This method returns information about the
* stream. The only information returned by this method is the size of the
* stream. It is acceptable to simple zero the STATSTG structure and only
* initialize the type and cbSize fields. This is the same behaviour as streams
* that were created using ::CreateStreamOnHGlobal().
*
* Returns:
* S_OK Success
* SPERR_UNINITIALIZED The object has not been initialzed with Open() or Create()
* STG_E_INVALIDPOINTER Invalid pointer
* STG_E_INVALIDFLAG Invalid flag in grfStatFlag
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
{
SPAUTO_OBJ_LOCK;;
SPDBG_FUNC("CWavStream::Stat");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr))
{
if (m_cpBaseStream)
{
hr = m_cpBaseStream->Stat(pstatstg, grfStatFlag);
}
else
{
if (SP_IS_BAD_WRITE_PTR(pstatstg))
{
hr = STG_E_INVALIDPOINTER;
}
else
{
if (grfStatFlag & (~STATFLAG_NONAME))
{
hr = STG_E_INVALIDFLAG;
}
else
{
ZeroMemory(pstatstg, sizeof(*pstatstg));
pstatstg->type = STGTY_STREAM;
pstatstg->cbSize.QuadPart = m_cbSize;
}
}
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
/****************************************************************************
* CWavStream::GetFormat *
*-----------------------*
* Description:
* This method returns the format GUID for the stream.
*
* Returns:
* S_OK
* E_POINTER
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::GetFormat(GUID * pFmtId, WAVEFORMATEX ** ppCoMemWaveFormatEx)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::GetFormat");
HRESULT hr = m_hrStreamDefault;
if(SUCCEEDED(hr) && m_cpBaseStreamFormat)
{
hr = m_StreamFormat.AssignFormat(m_cpBaseStreamFormat);
}
if(SUCCEEDED(hr))
{
hr = m_StreamFormat.ParamValidateCopyTo(pFmtId, ppCoMemWaveFormatEx);
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
#ifdef SAPI_AUTOMATION
/****************************************************************************
* CWavStream::SetFormat *
*-----------------------*
* Description:
* This method sets up the stream format without initializing the stream.
* Needed for automation because we allow format setting independently
* of when the basestream / file is set
*
* Returns:
********************************************************************* DAVEWOOD ***/
STDMETHODIMP CWavStream::SetFormat(REFGUID rguidFmtId, const WAVEFORMATEX * pWaveFormatEx)
{
HRESULT hr = S_OK;
if(m_hFile)
{
// Can't alter the format of an already created file.
hr = SPERR_ALREADY_INITIALIZED;
}
else if(m_cpBaseStreamAccess)
{
// Set the format on the stream under this one.
hr = m_cpBaseStreamAccess->SetFormat(rguidFmtId, pWaveFormatEx);
}
if(SUCCEEDED(hr))
{
hr = m_StreamFormat.ParamValidateAssignFormat(rguidFmtId, pWaveFormatEx);
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
/****************************************************************************
* CWavStream::_GetFormat *
*-----------------------*
* Description:
* Version of GetFormat that works when the stream isn't initialized. Needed by automation
*
* Returns:
********************************************************************* DAVEWOOD ***/
STDMETHODIMP CWavStream::_GetFormat(GUID * pFmtId, WAVEFORMATEX ** ppCoMemWaveFormatEx)
{
HRESULT hr = S_OK;
if(m_cpBaseStreamFormat)
{
hr = m_StreamFormat.AssignFormat(m_cpBaseStreamFormat);
}
if(SUCCEEDED(hr))
{
hr = m_StreamFormat.ParamValidateCopyTo(pFmtId, ppCoMemWaveFormatEx);
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
#endif
/****************************************************************************
* CWavStream::ReadFormatHeader *
*------------------------------*
* Description:
* This internal function is only used by the Open() method. The lpckParent
* must point to the WAVE chunk of the file. When this function returns, the
* current position of the file will be the point immediately past the 'fmt' chunk.
*
* Returns:
*
********************************************************************* RAL ***/
HRESULT CWavStream::ReadFormatHeader(const LPMMCKINFO lpckParent)
{
SPDBG_FUNC("CWavStream::ReadFormatHeader");
HRESULT hr = S_OK;
MMCKINFO mminfoFormat;
mminfoFormat.ckid = g_fourccFormat;
hr = MMDescend(&mminfoFormat, lpckParent, MMIO_FINDCHUNK);
if (SUCCEEDED(hr))
{
if (mminfoFormat.cksize < sizeof(WAVEFORMAT))
{
hr = SPERR_INVALID_WAV_FILE;
}
else
{
WAVEFORMATEX * pwfex = (WAVEFORMATEX *)_alloca(mminfoFormat.cksize >= sizeof(WAVEFORMATEX) ? mminfoFormat.cksize : sizeof(WAVEFORMATEX));
hr = MMReadExact(pwfex, mminfoFormat.cksize);
if (SUCCEEDED(hr))
{
if (mminfoFormat.cksize < sizeof(WAVEFORMATEX))
{
pwfex->cbSize = 0;
}
hr = m_StreamFormat.AssignFormat(pwfex);
}
}
HRESULT hrAscend = MMAscend(&mminfoFormat);
if (SUCCEEDED(hr))
{
hr = hrAscend;
}
}
return hr;
}
/****************************************************************************
* CWavStream::ReadEvents *
*------------------------*
* Description:
* This internal function is only used by the Open() method. The lpckParent
* must point to the WAVE chunk of the file. When this function returns, the
* file position will point past the end of the event block (if there is any).
*
* Returns:
* S_OK if no events or if read successfully
*
********************************************************************* RAL ***/
HRESULT CWavStream::ReadEvents(const LPMMCKINFO lpckParent)
{
SPDBG_FUNC("CWavStream::ReadEvents");
HRESULT hr = S_OK;
MMCKINFO mminfoEvent;
mminfoEvent.ckid = g_fourccEvents;
if (SUCCEEDED(MMDescend(&mminfoEvent, lpckParent, MMIO_FINDCHUNK)))
{
BYTE * pBuff = new BYTE[mminfoEvent.cksize];
if (pBuff)
{
CSpEvent Event;
hr = MMReadExact(pBuff, mminfoEvent.cksize);
for (ULONG iCur = 0; SUCCEEDED(hr) && iCur < mminfoEvent.cksize; )
{
ULONG cbUsed;
SPSERIALIZEDEVENT * pSerEvent = (SPSERIALIZEDEVENT *)(pBuff + iCur);
if (SUCCEEDED(Event.Deserialize(pSerEvent, &cbUsed)))
{
iCur += cbUsed;
hr = m_SpEventSource._AddEvent(Event);
}
else
{
SPDBG_ASSERT(FALSE); // Event did not deserialize properly
#ifndef _WIN32_WCE
iCur += SpSerializedEventSize(pSerEvent);
#else
iCur += SpSerializedEventSize(pSerEvent, sizeof(*pSerEvent));
#endif
}
}
delete[] pBuff;
if (SUCCEEDED(hr))
{
hr = m_SpEventSource._CompleteEvents();
}
}
else
{
hr = E_OUTOFMEMORY;
}
HRESULT hrAscend = MMAscend(&mminfoEvent);
if (SUCCEEDED(hr))
{
hr = hrAscend;
}
}
return hr;
}
/****************************************************************************
* CWavStream::ReadTranscript *
*----------------------------*
* Description:
* This internal function is only used by the Open() method. The lpckParent
* must point to the WAVE chunk of the file. When this function returns, the
* file position will point past the end of the transcript block (if there is any).
*
* Returns:
* S_OK if no transcript exists or if read successfully
*
********************************************************************* RAL ***/
HRESULT CWavStream::ReadTranscript(const LPMMCKINFO lpckParent)
{
SPDBG_FUNC("CWavStream::ReadTranscript");
HRESULT hr = S_OK;
MMCKINFO mminfoTranscript;
mminfoTranscript.ckid = g_fourccTranscript;
if (SUCCEEDED(MMDescend(&mminfoTranscript, lpckParent, MMIO_FINDCHUNK)))
{
if (m_dstrTranscript.ClearAndGrowTo(mminfoTranscript.cksize/sizeof(WCHAR)))
{
hr = MMReadExact(static_cast<WCHAR *>(m_dstrTranscript), mminfoTranscript.cksize);
}
else
{
hr = E_OUTOFMEMORY;
}
HRESULT hrAscend = MMAscend(&mminfoTranscript);
if (SUCCEEDED(hr))
{
hr = hrAscend;
}
}
return hr;
}
//
// NOTE: Something is quite goofy about mmio routines. When you create a chunk the chunk
// structure is actually used by the service to maintain state. For that reason, we have
// member variables m_ckFile and m_ckData so that when we close the file we can Ascend back
// out of these chunks. Unbelieveable.......
//
/****************************************************************************
* CWavStream::Open *
*------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
HRESULT CWavStream::OpenWav(const WCHAR *pszFileName, ULONGLONG ullEventInterest)
{
SPDBG_FUNC("CWavStream::OpenWav");
HRESULT hr = S_OK;
m_SpEventSource.m_ullEventInterest = m_SpEventSource.m_ullQueuedInterest = ullEventInterest;
hr = MMOpen(pszFileName, MMIO_READ | MMIO_ALLOCBUF);
if (SUCCEEDED(hr))
{
LONG lStartWaveChunk, lIgnored;
MMCKINFO mminfoChunk;
// search for wave type...
mminfoChunk.fccType = g_fourccWave;
hr = MMDescend(&mminfoChunk, NULL, MMIO_FINDRIFF);
if (SUCCEEDED(hr))
{
hr = MMSeek(0, SEEK_CUR, &lStartWaveChunk);
}
if (SUCCEEDED(hr))
{
hr = ReadFormatHeader(&mminfoChunk);
}
if (SUCCEEDED(hr))
{
hr = ReadTranscript(&mminfoChunk);
}
if (SUCCEEDED(hr))
{
hr = MMSeek(lStartWaveChunk, SEEK_SET, &lIgnored);
}
if (SUCCEEDED(hr))
{
hr = ReadEvents(&mminfoChunk);
}
if (SUCCEEDED(hr))
{
hr = MMSeek(lStartWaveChunk, SEEK_SET, &lIgnored);
}
if (SUCCEEDED(hr))
{
MMCKINFO mminfoData;
mminfoData.ckid = g_fourccData;
hr = MMDescend(&mminfoData, &mminfoChunk, MMIO_FINDCHUNK);
m_cbSize = mminfoData.cksize;
}
if (SUCCEEDED(hr))
{
hr = MMSeek(0, SEEK_CUR, &m_lDataStart);
}
if (SUCCEEDED(hr))
{
m_ulCurSeekPos = 0;
m_fWriteable = FALSE;
m_fEventSource = TRUE;
m_fTranscript = TRUE;
}
else
{
MMClose();
}
}
return hr;
}
/****************************************************************************
* CWavStream::Create *
*--------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
HRESULT CWavStream::CreateWav(const WCHAR *pszFileName, ULONGLONG ullEventInterest)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::Create");
HRESULT hr = S_OK;
if (m_StreamFormat.FormatId() != SPDFID_WaveFormatEx)
{
hr = SPERR_UNSUPPORTED_FORMAT;
}
else
{
m_SpEventSource.m_ullEventInterest = m_SpEventSource.m_ullQueuedInterest = ullEventInterest;
hr = MMOpen(pszFileName, MMIO_CREATE | MMIO_WRITE | MMIO_EXCLUSIVE | MMIO_ALLOCBUF );
if (SUCCEEDED(hr))
{
ZeroMemory(&m_ckFile, sizeof(m_ckFile));
m_ckFile.fccType = g_fourccWave;
hr = MMCreateChunk(&m_ckFile, MMIO_CREATERIFF);
if (SUCCEEDED(hr))
{
MMCKINFO ck;
ZeroMemory(&ck, sizeof(ck));
ck.ckid = g_fourccFormat;
hr = MMCreateChunk(&ck, 0);
if (SUCCEEDED(hr))
{
LONG lIgnored;
const WAVEFORMATEX * pwfex = m_StreamFormat.WaveFormatExPtr();
hr = MMWrite(pwfex, sizeof(*pwfex) + pwfex->cbSize, &lIgnored);
MMAscend(&ck);
}
if (SUCCEEDED(hr))
{
ZeroMemory(&m_ckData, sizeof(m_ckData));
m_ckData.ckid = g_fourccData;
hr = MMCreateChunk(&m_ckData, 0);
}
if (SUCCEEDED(hr))
{
hr = MMSeek(0, SEEK_CUR, &m_lDataStart);
}
}
if (SUCCEEDED(hr))
{
m_ulCurSeekPos = 0;
m_fWriteable = TRUE;
m_fEventSource = FALSE;
m_fEventSink = TRUE;
m_fTranscript = TRUE;
m_cbSize = 0;
}
else
{
MMClose();
}
}
}
return hr;
}
/****************************************************************************
* CWavStream::SerializeEvents *
*-----------------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
HRESULT CWavStream::SerializeEvents()
{
SPDBG_FUNC("CWavStream::SerializeEvents");
HRESULT hr = S_OK;
if (m_SpEventSource.m_PendingList.GetCount())
{
MMCKINFO ck;
ZeroMemory(&ck, sizeof(ck));
ck.ckid = g_fourccEvents;
hr = MMCreateChunk(&ck, 0);
if (SUCCEEDED(hr))
{
ULONG cbSerializeSize = 0;
CSpEventNode * pNode;
for (pNode = m_SpEventSource.m_PendingList.GetHead(); pNode; pNode = pNode->m_pNext)
{
// WCE compiler does not work propertly with template
#ifndef _WIN32_WCE
cbSerializeSize += pNode->SerializeSize<SPSERIALIZEDEVENT>();
#else
cbSerializeSize += SpEventSerializeSize(pNode, sizeof(SPSERIALIZEDEVENT));
#endif
}
BYTE * pBuff = new BYTE[cbSerializeSize];
if (pBuff)
{
BYTE * pCur = pBuff;
for (pNode = m_SpEventSource.m_PendingList.GetHead(); SUCCEEDED(hr) && pNode; pNode = pNode->m_pNext)
{
pNode->Serialize((UNALIGNED SPSERIALIZEDEVENT *)pCur);
// WCE compiler does not work propertly with template
#ifndef _WIN32_WCE
pCur += pNode->SerializeSize<SPSERIALIZEDEVENT>();
#else
pCur += SpEventSerializeSize(pNode, sizeof(SPSERIALIZEDEVENT));
#endif
}
LONG lIgnored;
hr = MMWrite(pBuff, cbSerializeSize, &lIgnored);
delete[] pBuff;
}
else
{
hr = E_OUTOFMEMORY;
}
MMAscend(&ck);
}
}
return hr;
}
/****************************************************************************
* CWavStream::SerializeTranscript *
*---------------------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
HRESULT CWavStream::SerializeTranscript()
{
SPDBG_FUNC("CWavStream::SerializeTranscript");
HRESULT hr = S_OK;
ULONG cch = m_dstrTranscript.Length();
if (cch)
{
MMCKINFO ck;
ZeroMemory(&ck, sizeof(ck));
ck.ckid = g_fourccTranscript;
hr = MMCreateChunk(&ck, 0);
if (SUCCEEDED(hr))
{
LONG lWritten;
hr = MMWrite(static_cast<WCHAR *>(m_dstrTranscript), (cch+1) * sizeof(WCHAR), &lWritten);
MMAscend(&ck);
}
}
return hr;
}
/****************************************************************************
* CWavStream::SetBaseStream *
*---------------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::SetBaseStream(IStream * pStream, REFGUID rguidFormat, const WAVEFORMATEX * pWaveFormatEx)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::SetBaseStream");
HRESULT hr = m_hrStreamDefault;
if (hr == SPERR_UNINITIALIZED)
{
if (SP_IS_BAD_INTERFACE_PTR(pStream))
{
hr = E_INVALIDARG;
}
else
{
hr = m_StreamFormat.ParamValidateAssignFormat(rguidFormat, pWaveFormatEx);
}
if (SUCCEEDED(hr))
{
if(pStream == this)
{
hr = E_INVALIDARG;
}
else
{
m_cpBaseStream = pStream;
m_cpBaseStreamFormat = pStream;
m_cpBaseStreamAccess = pStream;
}
if(SUCCEEDED(hr) && m_cpBaseStreamAccess && !m_cpBaseStreamFormat)
{
// Can't have StreamAccess without format.
hr = E_UNEXPECTED;
}
if(m_cpBaseStreamFormat)
{
// If this BaseStream implements ISpStreamFormat, we should get format info from it
hr = m_StreamFormat.AssignFormat(m_cpBaseStreamFormat);
}
if(SUCCEEDED(hr))
{
m_hrStreamDefault = S_OK;
hr = S_OK;
}
else
{
m_cpBaseStreamAccess.Release();
m_cpBaseStream.Release();
m_cpBaseStream.Release();
}
}
}
else
{
hr = SPERR_ALREADY_INITIALIZED;
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
/****************************************************************************
* CWavStream::GetBaseStream *
*---------------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
HRESULT CWavStream::GetBaseStream(IStream ** ppStream)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::GetBaseStream");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr))
{
if (SP_IS_BAD_WRITE_PTR(ppStream))
{
hr = E_POINTER;
}
else
{
*ppStream = m_cpBaseStream;
if (*ppStream)
{
(*ppStream)->AddRef();
}
else
{
hr = S_FALSE;
}
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
/****************************************************************************
* CWavStream::BindToFile *
*------------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::BindToFile(const WCHAR * pszFileName, SPFILEMODE eMode,
const GUID * pguidFormatId, const WAVEFORMATEX * pWaveFormatEx,
ULONGLONG ullEventInterest)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::BindToFile");
HRESULT hr = S_OK;
if (SP_IS_BAD_STRING_PTR(pszFileName) ||
eMode >= SPFM_NUM_MODES ||
SP_IS_BAD_OPTIONAL_READ_PTR(pguidFormatId))
{
hr = E_INVALIDARG;
}
else if (m_hrStreamDefault == S_OK)
{
hr = SPERR_ALREADY_INITIALIZED;
}
else if (pguidFormatId)
{
hr = m_StreamFormat.ParamValidateAssignFormat(*pguidFormatId, pWaveFormatEx);
}
if (SUCCEEDED(hr))
{
ULONG cchFileName = wcslen(pszFileName);
if (cchFileName > 4 && (_wcsicmp(pszFileName + cchFileName - 4, L".wav") == 0))
{
if( SUCCEEDED( hr ) )
{
if( eMode == SPFM_OPEN_READONLY )
{
hr = OpenWav( pszFileName, ullEventInterest );
}
else
{
if ( eMode == SPFM_CREATE_ALWAYS && m_StreamFormat.FormatId() == SPDFID_WaveFormatEx )
{
hr = CreateWav( pszFileName, ullEventInterest );
}
else
{
hr = E_INVALIDARG;
}
}
}
}
else //=== Generic binding for text files
{
//--- Init vars
m_StreamFormat.Clear();
m_fWriteable = TRUE;
m_fEventSource = FALSE;
m_fEventSink = FALSE;
m_fTranscript = FALSE;
if (eMode == SPFM_OPEN_READONLY)
{
m_fWriteable = FALSE;
hr = ::URLOpenBlockingStreamW(NULL, pszFileName, &m_cpBaseStream, 0, NULL);
}
else
{
DWORD dwCreateDisp;
switch (eMode)
{
case SPFM_OPEN_READWRITE:
dwCreateDisp = OPEN_EXISTING;
break;
case SPFM_CREATE:
dwCreateDisp = OPEN_ALWAYS;
break;
case SPFM_CREATE_ALWAYS:
dwCreateDisp = CREATE_ALWAYS;
break;
}
CSpFileStream * pNew = new CSpFileStream(&hr, pszFileName,
GENERIC_WRITE | GENERIC_READ, 0, dwCreateDisp);
if (pNew)
{
if (SUCCEEDED(hr))
{
m_cpBaseStream = pNew;
}
pNew->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
}
}
if (SUCCEEDED(hr))
{
m_hrStreamDefault = S_OK;
}
else
{
m_StreamFormat.Clear();
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
} /* SPBindToFile */
/****************************************************************************
* CWavStream::Close *
*-------------------*
* Description:
* This method is exposed as an interface so that clients can receive failure
* codes that would otherwise not be available if they simply released the stream.
* Upon release, streams are automatically closed (by calling this method from
* FinalConstruct()).
*
* Returns:
* S_OK if successful.
* SPERR_UNINITIALIZED if file is not opened
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::Close()
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::Close");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr))
{
m_cpBaseStream.Release();
if (m_hFile)
{
if (m_fWriteable)
{
hr = MMAscend(&m_ckData);
if (SUCCEEDED(hr))
{
hr = SerializeEvents();
}
if (SUCCEEDED(hr))
{
hr = SerializeTranscript();
}
if (SUCCEEDED(hr))
{
hr = MMAscend(&m_ckFile);
}
}
HRESULT hrClose = MMClose();
if (SUCCEEDED(hr))
{
hr = hrClose;
}
}
m_hrStreamDefault = SPERR_STREAM_CLOSED;
}
return hr;
}
/****************************************************************************
* CWavStream::AddEvents *
*-----------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::AddEvents(const SPEVENT* pEventArray, ULONG ulCount)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::AddEvents");
HRESULT hr = S_OK;
if (SPIsBadReadPtr(pEventArray, sizeof(*pEventArray)*ulCount))
{
hr = E_INVALIDARG;
}
else
{
hr = m_SpEventSource._AddEvents(pEventArray, ulCount);
}
return hr;
}
/****************************************************************************
* CWavStream::GetEventInterest *
*------------------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
HRESULT CWavStream::GetEventInterest(ULONGLONG * pullEventInterest)
{
SPDBG_FUNC("CWavStream::GetEventInterest");
HRESULT hr = S_OK;
if (SP_IS_BAD_WRITE_PTR(pullEventInterest))
{
hr = E_POINTER;
}
else
{
*pullEventInterest = m_SpEventSource.m_ullEventInterest;
}
return hr;
}
/****************************************************************************
* CWavStream::GetTranscript *
*---------------------------*
* Description:
*
* Returns:
* S_OK if *ppszTranscript contains a CoTaskMemAllocated string
* S_FALSE if object has no transcript
* E_POINTER if ppszTranscript is invalid
* SPERR_UNINITIALIZED if object has not been initialized
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::GetTranscript(WCHAR ** ppszTranscript)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::GetTranscription");
HRESULT hr = S_OK;
if (SP_IS_BAD_WRITE_PTR(ppszTranscript))
{
hr = E_POINTER;
}
else
{
if (m_dstrTranscript)
{
*ppszTranscript = m_dstrTranscript.Copy();
if (*ppszTranscript == NULL)
{
hr = E_OUTOFMEMORY;
}
}
else
{
*ppszTranscript = NULL;
hr = m_hFile ? S_FALSE : SPERR_UNINITIALIZED;
}
}
return hr;
}
/****************************************************************************
* CWavStream::AppendTranscript *
*------------------------------*
* Description:
* If pszTranscript is NULL then the current transcript is deleted,
* otherwise, the text is appended to the current transcript.
*
* Returns:
*
********************************************************************* RAL ***/
STDMETHODIMP CWavStream::AppendTranscript(const WCHAR * pszTranscript)
{
SPAUTO_OBJ_LOCK;
SPDBG_FUNC("CWavStream::SetTranscript");
HRESULT hr = m_hrStreamDefault;
if (SUCCEEDED(hr))
{
if (pszTranscript)
{
if (SP_IS_BAD_STRING_PTR(pszTranscript))
{
hr = E_INVALIDARG;
}
else if (wcslen(pszTranscript) == 0)
{
hr = S_FALSE;
}
else
{
m_dstrTranscript.Append(pszTranscript);
if (m_dstrTranscript == NULL)
{
hr = E_OUTOFMEMORY;
}
}
}
else
{
m_dstrTranscript.Clear();
}
}
return hr;
}