mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1330 lines
37 KiB
1330 lines
37 KiB
/****************************************************************************
|
|
* RecPlayAudio.cpp
|
|
* Implementation of the CRecPlayAudio device class
|
|
*
|
|
* Owner: robch
|
|
* Copyright (c) 2000 Microsoft Corporation All Rights Reserved.
|
|
*****************************************************************************/
|
|
|
|
//--- Includes --------------------------------------------------------------
|
|
#include "stdafx.h"
|
|
#include "RecPlayAudio.h"
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::CRecPlayAudio *
|
|
*------------------------------*
|
|
* Description:
|
|
* ctor
|
|
******************************************************************** robch */
|
|
CRecPlayAudio::CRecPlayAudio()
|
|
{
|
|
m_fIn = FALSE;
|
|
m_fOut = FALSE;
|
|
|
|
m_pszFileList = NULL;
|
|
m_ulBaseFileNextNum = 0;
|
|
m_ulBaseFileMaxNum = UINT_MAX - 1;
|
|
m_hStartReadingEvent = NULL;
|
|
m_hFinishedReadingEvent = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::FinalRelease *
|
|
*-----------------------------*
|
|
* Description:
|
|
* Called by ATL when our object is going away.
|
|
******************************************************************** robch */
|
|
void CRecPlayAudio::FinalRelease()
|
|
{
|
|
CloseHandle(m_hStartReadingEvent);
|
|
CloseHandle(m_hFinishedReadingEvent);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::SetObjectToken *
|
|
*-------------------------------*
|
|
* Description:
|
|
* ISpObjectToken::SetObjectToken implementation. Basically get ready
|
|
* to read from the files specified, or write to the file specified,
|
|
* in addition to delegating to the actual audio object.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::SetObjectToken(ISpObjectToken * pToken)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::SetObjectToken");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
// Set our token (this does param validation, etc)
|
|
hr = SpGenericSetObjectToken(pToken, m_cpToken);
|
|
|
|
// Get the name of this RecPlayAudioDevice.
|
|
CSpDynamicString dstrSRE, dstrFRE;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_cpToken->GetStringValue(L"", &dstrSRE);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_cpToken->GetStringValue(L"", &dstrFRE);
|
|
}
|
|
dstrSRE.Append(L"SRE");
|
|
dstrFRE.Append(L"FRE");
|
|
|
|
// Get the token id for the audio device
|
|
CSpDynamicString dstrTokenId;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_cpToken->GetStringValue(L"AudioTokenId", &dstrTokenId);
|
|
}
|
|
|
|
// Create the audio device
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SpCreateObjectFromTokenId(dstrTokenId, &m_cpAudio);
|
|
}
|
|
|
|
// Are we reading? Or writing?
|
|
CSpDynamicString dstrReadOrWrite;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_cpToken->GetStringValue(L"ReadOrWrite", &dstrReadOrWrite);
|
|
if (hr == SPERR_NOT_FOUND)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && dstrReadOrWrite)
|
|
{
|
|
if (wcsicmp(dstrReadOrWrite, L"Read") == 0)
|
|
{
|
|
m_fIn = TRUE;
|
|
}
|
|
else if (wcsicmp(dstrReadOrWrite, L"Write") == 0)
|
|
{
|
|
m_fOut = TRUE;
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
// Create unsignalled StartReadingEvent.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_hStartReadingEvent = g_Unicode.CreateEvent(NULL, TRUE, FALSE, dstrSRE);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_cpToken->SetStringValue(L"StartReadingEvent", dstrSRE);
|
|
}
|
|
|
|
// Create unsignalled FinishedReadingEvent.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_hFinishedReadingEvent = g_Unicode.CreateEvent(NULL, TRUE, FALSE, dstrFRE);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_cpToken->SetStringValue(L"FinishedReadingEvent", dstrFRE);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = InitFileList();
|
|
}
|
|
|
|
// We need input to be ready so we do proper format negotiation. Don't
|
|
// worry about output, it'll get ready in the audio state transition
|
|
// (after format negotiation).
|
|
if (SUCCEEDED(hr) && m_fIn)
|
|
{
|
|
hr = GetNextFileReady();
|
|
if (hr == SPERR_NO_MORE_ITEMS)
|
|
{
|
|
// This is now valid. RecPlayAudio will start feeding silence immediately.
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::InitFileList *
|
|
*-----------------------------*
|
|
* Description:
|
|
* Checks registry and updates file list information.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
***************************************************************** agarside */
|
|
HRESULT CRecPlayAudio::InitFileList(void)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::InitFiles");
|
|
HRESULT hr = S_OK;
|
|
|
|
// What directory?
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_dstrDirectory.Clear();
|
|
hr = m_cpToken->GetStringValue(L"Directory", &m_dstrDirectory);
|
|
if (hr == SPERR_NOT_FOUND)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
// Are we using a list of files
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_dstrFileList.Clear();
|
|
hr = m_cpToken->GetStringValue(L"FileList", &m_dstrFileList);
|
|
m_pszFileList = m_dstrFileList;
|
|
|
|
if (hr == SPERR_NOT_FOUND)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_dstrBaseFile.Clear();
|
|
hr = m_cpToken->GetStringValue(L"BaseFile", &m_dstrBaseFile);
|
|
if (hr == SPERR_NOT_FOUND)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_cpToken->GetDWORD(L"BaseFileNextNum", &m_ulBaseFileNextNum);
|
|
if (hr == SPERR_NOT_FOUND)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_cpToken->GetDWORD(L"BaseFileMaxNum", &m_ulBaseFileMaxNum);
|
|
if (hr == SPERR_NOT_FOUND)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
// Now check to make sure we're set up in a reasonable way
|
|
if (SUCCEEDED(hr) && (m_fIn || m_fOut))
|
|
{
|
|
// We better have audio, and we can't be both in and out
|
|
SPDBG_ASSERT(m_cpAudio != NULL);
|
|
SPDBG_ASSERT(m_fIn != m_fOut);
|
|
|
|
if (m_dstrFileList != NULL && m_dstrBaseFile != NULL)
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
else if (m_dstrFileList == NULL && m_dstrBaseFile == NULL)
|
|
{
|
|
m_dstrBaseFile = L"RecPlay";
|
|
}
|
|
if (m_dstrFileList && wcslen(m_dstrFileList) == 0)
|
|
{
|
|
// Set this to null - indicates no more files left.
|
|
m_pszFileList = NULL;
|
|
}
|
|
if (m_dstrBaseFile && wcslen(m_dstrBaseFile) == 0)
|
|
{
|
|
// Set this to null - indicates no more files left.
|
|
m_dstrBaseFile.Clear();
|
|
}
|
|
}
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::GetObjectToken *
|
|
*-------------------------------*
|
|
* Description:
|
|
* ISpObjectToken::GetObjectToken implementation.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::GetObjectToken(ISpObjectToken ** ppToken)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::GetObjectToken");
|
|
return SpGenericGetObjectToken(ppToken, m_cpToken);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::Read *
|
|
*---------------------*
|
|
* Description:
|
|
* ISequentialStream::Read implementation. Read data from the actual
|
|
* audio object, potentially replacing it with data from the files on
|
|
* disk, or potentially saving the data to a file on disk.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::Read(void * pv, ULONG cb, ULONG *pcbRead)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::Read");
|
|
HRESULT hr = S_OK;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
if (SPIsBadWritePtr(pv, cb) ||
|
|
SP_IS_BAD_OPTIONAL_WRITE_PTR(pcbRead))
|
|
{
|
|
hr = E_POINTER;
|
|
}
|
|
else if (m_cpAudio == NULL)
|
|
{
|
|
hr = SPERR_UNINITIALIZED;
|
|
}
|
|
|
|
// First read from the real device
|
|
ULONG cbReadFromDevice;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_cpAudio->Read(pv, cb, &cbReadFromDevice);
|
|
}
|
|
|
|
// Now, we might need to write that back out
|
|
if (SUCCEEDED(hr) && m_cpOutStream != NULL)
|
|
{
|
|
hr = m_cpOutStream->Write(pv, cbReadFromDevice, NULL);
|
|
}
|
|
|
|
// Might need to refresh file list if signalled via registry.
|
|
if (m_fIn && m_cpInStream == NULL)
|
|
{
|
|
hr = GetNextFileReady();
|
|
}
|
|
|
|
// Now, we might need to replace the input with something else
|
|
ULONG cbReadAndReplaced = 0;
|
|
BYTE *pb = static_cast<BYTE*>(pv);
|
|
while (SUCCEEDED(hr) &&
|
|
m_cpInStream != NULL &&
|
|
cbReadAndReplaced < cbReadFromDevice)
|
|
{
|
|
ULONG cbReadFromInStream;
|
|
hr = m_cpInStream->Read(
|
|
pb + cbReadAndReplaced,
|
|
cbReadFromDevice - cbReadAndReplaced,
|
|
&cbReadFromInStream);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// If we didn't read all that we wanted, from that
|
|
// stream, go to the next stream
|
|
if (cbReadFromInStream < cbReadFromDevice - cbReadAndReplaced)
|
|
{
|
|
m_cpInStream.Release();
|
|
hr = GetNextFileReady();
|
|
}
|
|
|
|
cbReadAndReplaced += cbReadFromInStream;
|
|
}
|
|
}
|
|
|
|
if (hr == SPERR_NO_MORE_ITEMS)
|
|
{
|
|
// Add silence to fill the requested buffer.
|
|
|
|
// First get audio format to determine silence value.
|
|
// 0x0000 for 16 bit
|
|
// 0x80 for 8 bit
|
|
GUID guidFormatId;
|
|
WAVEFORMATEX *pCoMemWaveFormatEx;
|
|
hr = m_cpAudio->GetFormat(&guidFormatId, &pCoMemWaveFormatEx);
|
|
if (SUCCEEDED(hr) &&
|
|
guidFormatId == SPDFID_WaveFormatEx &&
|
|
pCoMemWaveFormatEx->wFormatTag == WAVE_FORMAT_PCM )
|
|
{
|
|
if (pCoMemWaveFormatEx->wBitsPerSample == 8)
|
|
{
|
|
memset(pb + cbReadAndReplaced, 0x80, cbReadFromDevice - cbReadAndReplaced);
|
|
}
|
|
else
|
|
{
|
|
memset(pb + cbReadAndReplaced, 0, cbReadFromDevice - cbReadAndReplaced);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set to zero if this fails. Should never happen.
|
|
SPDBG_ASSERT(FALSE);
|
|
memset(pb + cbReadAndReplaced, 0, cbReadFromDevice - cbReadAndReplaced);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
::CoTaskMemFree(pCoMemWaveFormatEx);
|
|
}
|
|
cbReadAndReplaced = cbReadFromDevice;
|
|
hr = S_OK;
|
|
}
|
|
|
|
// We're done. Tell the caller how much we read. This should now always
|
|
// be the full amount as we artificially add flat-line silence.
|
|
// Except when the audio device has been closed in which case it will be less.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (pcbRead != NULL)
|
|
{
|
|
*pcbRead = cbReadFromDevice;
|
|
}
|
|
}
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::Write *
|
|
*----------------------*
|
|
* Description:
|
|
* ISequentialStream::Write implementation. Delegate to the actual
|
|
* audio device.
|
|
*
|
|
* NOTE: Currently, Recplay only replaces/records data for input. If
|
|
* we wanted similar functionality for output, we'd modifiy this
|
|
* function.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::Write(const void * pv, ULONG cb, ULONG *pcbWritten)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::Write");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->Write(pv, cb, pcbWritten);
|
|
|
|
return STG_E_ACCESSDENIED;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::Seek *
|
|
*---------------------*
|
|
* Description:
|
|
* IStream::Seek implementation. Delegate to the actual audio device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER * plibNewPosition)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::Seek");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
SPDBG_ASSERT(dwOrigin == STREAM_SEEK_CUR);
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->Seek(dlibMove, dwOrigin, plibNewPosition);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::SetSize *
|
|
*------------------------*
|
|
* Description:
|
|
* IStream::SetSize implementation. Delegate to the actual audio device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::SetSize(ULARGE_INTEGER libNewSize)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::SetSize");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->SetSize(libNewSize);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::CopyTo *
|
|
*-----------------------*
|
|
* Description:
|
|
* IStream::CopyTo implementation. Delegate to the actual audio device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::CopyTo");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->CopyTo(pstm, cb, pcbRead, pcbWritten);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::Commit *
|
|
*-----------------------*
|
|
* Description:
|
|
* IStream::Commit implementation. Delegate to the actual audio device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::Commit(DWORD grfCommitFlags)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::Commit");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->Commit(grfCommitFlags);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::Revert *
|
|
*-----------------------*
|
|
* Description:
|
|
* IStream::Revert implementation. Delegate to the actual audio device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::Revert(void)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::Revert");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->Revert();
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::LockRegion *
|
|
*---------------------------*
|
|
* Description:
|
|
* IStream::LockRegion implementation. Delegate to the actual audio
|
|
* device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::LockRegion");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->LockRegion(libOffset, cb, dwLockType);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::UnlockRegion *
|
|
*-----------------------------*
|
|
* Description:
|
|
* IStream::UnlockRegion implementation. Delegate to the actual audio
|
|
* device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::UnlockRegion");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->UnlockRegion(libOffset, cb, dwLockType);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::Stat *
|
|
*---------------------*
|
|
* Description:
|
|
* IStream::Stat implementation. Delegate to the actual audio device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::Stat");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->Stat(pstatstg, grfStatFlag);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::Clone *
|
|
*----------------------*
|
|
* Description:
|
|
* IStream::Clone implementation. Delegate to the actual audio device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::Clone(IStream **ppstm)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::Clone");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->Clone(ppstm);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::GetFormat *
|
|
*--------------------------*
|
|
* Description:
|
|
* ISpStreamFormat::GetFormat implementation. The format of this audio
|
|
* device, is either the format of the input files, or the format
|
|
* of the underlying audio device.
|
|
*
|
|
* Remember, RecPlay runs in one of three modes, if you will. It's
|
|
* either a pass through, and thus we just delegate to the contained
|
|
* audio device. Or it's reading from input files, and thus the format
|
|
* is precisely that of the input files. Or, it's outputting to a file
|
|
* on disk. In this mode, we still obtain the format via the audio
|
|
* device, because we really want to be in the format that the SR engine
|
|
* wants, so we just let default behavior do this for us.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::GetFormat(GUID * pguidFormatId, WAVEFORMATEX ** ppCoMemWaveFormatEx)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::GetFormat");
|
|
HRESULT hr = S_OK;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
if (m_cpAudio == NULL)
|
|
{
|
|
hr = SPERR_UNINITIALIZED;
|
|
}
|
|
else if (SP_IS_BAD_WRITE_PTR(pguidFormatId) ||
|
|
SP_IS_BAD_WRITE_PTR(ppCoMemWaveFormatEx))
|
|
{
|
|
hr = E_POINTER;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (m_cpInStream != NULL)
|
|
{
|
|
hr = m_cpInStream->GetFormat(pguidFormatId, ppCoMemWaveFormatEx);
|
|
}
|
|
else
|
|
{
|
|
hr = m_cpAudio->GetFormat(pguidFormatId, ppCoMemWaveFormatEx);
|
|
}
|
|
}
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::SetState *
|
|
*-------------------------*
|
|
* Description:
|
|
* ISpAudio::SetState implementation. Delegate to the actual audio
|
|
* device. If we're transitioning to SPAS_RUN and we're supposed to be
|
|
* writing an output, we need to create a new output file
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::SetState(SPAUDIOSTATE NewState, ULONGLONG ullReserved )
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::SetState");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->SetState(NewState, ullReserved);
|
|
|
|
if (SUCCEEDED(hr) && NewState == SPAS_RUN)
|
|
{
|
|
if (m_fOut)
|
|
{
|
|
hr = GetNextFileReady();
|
|
if (hr == SPERR_NO_MORE_ITEMS)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
// Make sure the formats all look fine
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = VerifyFormats();
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && NewState != SPAS_RUN && m_fOut)
|
|
{
|
|
m_cpOutStream.Release();
|
|
}
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::SetFormat *
|
|
*--------------------------*
|
|
* Description:
|
|
* ISpAudio::SetFormat implementation. We don't allow setting the format
|
|
* to anything other than the input format if we're reading from input
|
|
* files. We'll let the format converter do the right thing for us for
|
|
* the SR engine.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::SetFormat(REFGUID rguidFmtId, const WAVEFORMATEX * pWaveFormatEx)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::SetFormat");
|
|
HRESULT hr = S_OK;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
GUID guidFormat;
|
|
CSpCoTaskMemPtr<WAVEFORMATEX> pwfex = NULL;
|
|
|
|
if (m_cpAudio == NULL)
|
|
{
|
|
hr = SPERR_UNINITIALIZED;
|
|
}
|
|
else if (m_cpInStream != NULL)
|
|
{
|
|
hr = m_cpInStream->GetFormat(&guidFormat, &pwfex);
|
|
}
|
|
|
|
// Allow setting the format, only to the in stream format, or
|
|
// to anything if we have no in streams
|
|
|
|
if (SUCCEEDED(hr) && pwfex != NULL)
|
|
{
|
|
if (guidFormat != rguidFmtId ||
|
|
pwfex->cbSize != pWaveFormatEx->cbSize ||
|
|
memcmp(pWaveFormatEx, pwfex, sizeof(WAVEFORMATEX) + pwfex->cbSize) != 0)
|
|
{
|
|
hr = SPERR_UNSUPPORTED_FORMAT;
|
|
}
|
|
}
|
|
|
|
// If it's OK, delegate t the actual audio device
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_cpAudio->SetFormat(rguidFmtId, pWaveFormatEx);
|
|
}
|
|
|
|
if (hr != SPERR_UNSUPPORTED_FORMAT)
|
|
{
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::GetStatus *
|
|
*--------------------------*
|
|
* Description:
|
|
* ISpAudio::GetStatus implementation. Delegate to the actual audio
|
|
* device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::GetStatus(SPAUDIOSTATUS *pStatus)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::GetStatus");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->GetStatus(pStatus);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::SetBufferInfo *
|
|
*------------------------------*
|
|
* Description:
|
|
* ISpAudio::SetBufferInfo implementation. Delegate to the actual audio
|
|
* device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::SetBufferInfo(const SPAUDIOBUFFERINFO * pInfo)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::SetBufferInfo");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->SetBufferInfo(pInfo);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::GetBufferInfo *
|
|
*------------------------------*
|
|
* Description:
|
|
* ISpAudio::GetBufferInfo implementation. Delegate to the actual audio
|
|
* device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::GetBufferInfo(SPAUDIOBUFFERINFO * pInfo)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::GetBufferInfo");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->GetBufferInfo(pInfo);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::GetDefaultFormat *
|
|
*---------------------------------*
|
|
* Description:
|
|
* ISpAudio::GetDefaultFormat implementation. Our default format is
|
|
* either that of the actual audio device, or that of the input files.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::GetDefaultFormat(GUID * pFormatId, WAVEFORMATEX ** ppCoMemWaveFormatEx)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::GetDefaultFormat");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
// The default format is either the format of the in streams,
|
|
// or whatever the actual audio device is
|
|
|
|
if (m_cpAudio == NULL)
|
|
{
|
|
hr = SPERR_UNINITIALIZED;
|
|
}
|
|
else if (m_cpInStream != NULL)
|
|
{
|
|
hr = m_cpInStream->GetFormat(pFormatId, ppCoMemWaveFormatEx);
|
|
}
|
|
else
|
|
{
|
|
hr = m_cpAudio->GetDefaultFormat(pFormatId, ppCoMemWaveFormatEx);
|
|
}
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::EventHandle *
|
|
*----------------------------*
|
|
* Description:
|
|
* ISpAudio::EventHandle implementation. Delegate to the actual audio
|
|
* device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP_(HANDLE) CRecPlayAudio::EventHandle()
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::EventHandle");
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
return m_cpAudio == NULL
|
|
? NULL
|
|
: m_cpAudio->EventHandle();
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::GetVolumeLevel *
|
|
*-------------------------------*
|
|
* Description:
|
|
* ISpAudio:GetVolumeLevel implementation. Delegate to the actual audio
|
|
* device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::GetVolumeLevel(ULONG *pLevel)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::GetVolumeLevel");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->GetVolumeLevel(pLevel);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::SetVolumeLevel *
|
|
*-------------------------------*
|
|
* Description:
|
|
* ISpAudio::SetVolumeLevel implementation. Delegate to the actual audio
|
|
* device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::SetVolumeLevel(ULONG Level)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::SetVolumeLevel");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->SetVolumeLevel(Level);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::GetBufferNotifySize *
|
|
*------------------------------------*
|
|
* Description:
|
|
* ISpAudio::GetBufferNotifySize implementation. Delegate to the actual
|
|
* audio device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::GetBufferNotifySize(ULONG *pcbSize)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::GetBufferNotifySize");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->GetBufferNotifySize(pcbSize);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::SetBufferNotifySize *
|
|
*------------------------------------*
|
|
* Description:
|
|
* ISpAudio::SetBufferNotifySize implementation. Delegate to the actual
|
|
* audio device.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
STDMETHODIMP CRecPlayAudio::SetBufferNotifySize(ULONG cbSize)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::SetBufferNotifySize");
|
|
HRESULT hr;
|
|
|
|
SPAUTO_OBJ_LOCK;
|
|
|
|
hr = m_cpAudio == NULL
|
|
? SPERR_UNINITIALIZED
|
|
: m_cpAudio->SetBufferNotifySize(cbSize);
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::GetNextFileName *
|
|
*--------------------------------*
|
|
* Description:
|
|
* Get the next file name either from the file list or create it from
|
|
* the base file information
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
HRESULT CRecPlayAudio::GetNextFileName(WCHAR ** ppszFileName)
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::GetNextFileName");
|
|
HRESULT hr = S_OK;
|
|
|
|
CSpDynamicString dstrFileName;
|
|
dstrFileName = m_dstrDirectory;
|
|
if (dstrFileName.Length() >= 1 &&
|
|
dstrFileName[dstrFileName.Length() - 1] != '\\')
|
|
{
|
|
dstrFileName.Append(L"\\");
|
|
}
|
|
|
|
if (m_pszFileList != NULL)
|
|
{
|
|
// Skip leading space and semi-colons
|
|
while (iswspace(*m_pszFileList) || *m_pszFileList == ';')
|
|
{
|
|
m_pszFileList++;
|
|
}
|
|
|
|
// This is the beginning
|
|
WCHAR * pszBeginningOfFileName = m_pszFileList;
|
|
|
|
// Loop until we hit the end
|
|
while (*m_pszFileList && *m_pszFileList != ';')
|
|
{
|
|
m_pszFileList++;
|
|
}
|
|
|
|
// Copy the filename
|
|
CSpDynamicString dstrTemp;
|
|
dstrTemp = pszBeginningOfFileName;
|
|
dstrTemp.TrimToSize(ULONG(m_pszFileList - pszBeginningOfFileName));
|
|
|
|
// If it contains slashes, it's probably a fully qualified path,
|
|
// use that directly, otherwise append it to the already existing
|
|
// filename which has already been prep'd with the directory
|
|
if (wcschr(dstrTemp, L'\\') == NULL)
|
|
{
|
|
dstrFileName.Append(dstrTemp);
|
|
}
|
|
else
|
|
{
|
|
dstrFileName = dstrTemp;
|
|
}
|
|
|
|
// We're done, this will trigger no more files
|
|
if (*m_pszFileList == '\0')
|
|
{
|
|
m_pszFileList = NULL;
|
|
}
|
|
}
|
|
else if (m_dstrBaseFile != NULL &&
|
|
m_ulBaseFileNextNum <= m_ulBaseFileMaxNum)
|
|
{
|
|
TCHAR szNum[10];
|
|
wsprintf(szNum, _T("%03d"), m_ulBaseFileNextNum++);
|
|
|
|
USES_CONVERSION;
|
|
|
|
dstrFileName.Append2(m_dstrBaseFile, T2W(szNum));
|
|
dstrFileName.Append(L".wav");
|
|
|
|
// Now update the token with the new file number
|
|
// if we're writing
|
|
if (m_fOut)
|
|
{
|
|
hr = m_cpToken->SetDWORD(L"BaseFileNextNum", m_ulBaseFileNextNum);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = SPERR_NO_MORE_ITEMS;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppszFileName = dstrFileName.Detach();
|
|
}
|
|
|
|
if (hr != SPERR_NO_MORE_ITEMS)
|
|
{
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::GetNextFileReady *
|
|
*---------------------------------*
|
|
* Description:
|
|
* Get the next file ready, either input or output.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
HRESULT CRecPlayAudio::GetNextFileReady()
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::GetNextFileReady");
|
|
HRESULT hr = S_OK;
|
|
|
|
// If we're reading or writing
|
|
if (m_fIn || m_fOut)
|
|
{
|
|
m_cpInStream.Release();
|
|
m_cpOutStream.Release();
|
|
|
|
// Get the file name
|
|
CSpDynamicString dstrFileName;
|
|
hr = GetNextFileName(&dstrFileName);
|
|
|
|
if (hr == SPERR_NO_MORE_ITEMS)
|
|
{
|
|
// The file list has been fully used.
|
|
// Set finished reading event to 1.
|
|
HRESULT hr2 = S_OK;
|
|
|
|
// Time to check 'StartReadingEvent' to
|
|
// signal that we need to refresh our list of files.
|
|
if (WaitForSingleObject(m_hStartReadingEvent, 0) == WAIT_OBJECT_0)
|
|
{
|
|
// Reset event.
|
|
ResetEvent(m_hStartReadingEvent);
|
|
ResetEvent(m_hFinishedReadingEvent);
|
|
// Initialize with new file list.
|
|
hr2 = InitFileList();
|
|
SPDBG_ASSERT(SUCCEEDED(hr2));
|
|
hr = GetNextFileName(&dstrFileName);
|
|
// hr should now be S_OK
|
|
}
|
|
if (hr == SPERR_NO_MORE_ITEMS)
|
|
{
|
|
SetEvent(m_hFinishedReadingEvent);
|
|
// hr is still SPERR_NO_MORE_ITEMS
|
|
}
|
|
|
|
}
|
|
|
|
// Create the stream
|
|
CComPtr<ISpStream> cpStream;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = cpStream.CoCreateInstance(CLSID_SpStream);
|
|
}
|
|
|
|
// Get the actual audio device format so we can open
|
|
// our output to the correct format, or we can ensure
|
|
// that our new input file is of the correct format
|
|
GUID guidFormat;
|
|
CSpCoTaskMemPtr<WAVEFORMATEX> pwfex;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_cpAudio->GetFormat(&guidFormat, &pwfex);
|
|
}
|
|
|
|
// Bind the stream to the specific file
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = cpStream->BindToFile(
|
|
dstrFileName,
|
|
m_fIn
|
|
? SPFM_OPEN_READONLY
|
|
: SPFM_CREATE_ALWAYS,
|
|
m_fIn
|
|
? NULL
|
|
: &guidFormat,
|
|
m_fIn
|
|
? NULL
|
|
: pwfex,
|
|
0);
|
|
}
|
|
|
|
// Set up whichever stream we're supposed to
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (m_fIn)
|
|
{
|
|
m_cpInStream = cpStream;
|
|
}
|
|
else
|
|
{
|
|
m_cpOutStream = cpStream;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hr != SPERR_NO_MORE_ITEMS)
|
|
{
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* CRecPlayAudio::VerifyFormats *
|
|
*------------------------------*
|
|
* Description:
|
|
* Verify that the formats are in fact correct.
|
|
*
|
|
* Return:
|
|
* S_OK on success
|
|
* FAILED(hr) otherwise
|
|
******************************************************************** robch */
|
|
HRESULT CRecPlayAudio::VerifyFormats()
|
|
{
|
|
SPDBG_FUNC("CRecPlayAudio::VerifyFormats");
|
|
HRESULT hr;
|
|
|
|
GUID guidFormat;
|
|
CSpCoTaskMemPtr<WAVEFORMATEX> pwfex;
|
|
|
|
// See what the acutal device format is
|
|
SPDBG_ASSERT(m_cpAudio != NULL);
|
|
hr = m_cpAudio->GetFormat(&guidFormat, &pwfex);
|
|
|
|
// Make sure our input is the same
|
|
if (SUCCEEDED(hr) && m_cpInStream != NULL)
|
|
{
|
|
GUID guidFormatIn;
|
|
CSpCoTaskMemPtr<WAVEFORMATEX> pwfexIn;
|
|
hr = m_cpInStream->GetFormat(&guidFormatIn, &pwfexIn);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (guidFormat != guidFormatIn ||
|
|
pwfex->cbSize != pwfexIn->cbSize ||
|
|
memcmp(pwfex, pwfexIn, sizeof(WAVEFORMATEX) + pwfex->cbSize) != 0)
|
|
{
|
|
hr = SPERR_UNSUPPORTED_FORMAT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure out output is the same
|
|
if (SUCCEEDED(hr) && m_cpOutStream != NULL)
|
|
{
|
|
GUID guidFormatOut;
|
|
CSpCoTaskMemPtr<WAVEFORMATEX> pwfexOut;
|
|
hr = m_cpOutStream->GetFormat(&guidFormatOut, &pwfexOut);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (guidFormat != guidFormatOut ||
|
|
pwfex->cbSize != pwfexOut->cbSize ||
|
|
memcmp(pwfex, pwfexOut, sizeof(WAVEFORMATEX) + pwfex->cbSize) != 0)
|
|
{
|
|
hr = SPERR_UNSUPPORTED_FORMAT;
|
|
}
|
|
}
|
|
}
|
|
|
|
SPDBG_REPORT_ON_FAIL(hr);
|
|
return hr;
|
|
}
|