557 lines
13 KiB
C
557 lines
13 KiB
C
/*
|
|
* Implements WAVE/AU/AIFF Parser.
|
|
*
|
|
* hidenori@a2.ctktv.ne.jp
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wingdi.h"
|
|
#include "winuser.h"
|
|
#include "mmsystem.h"
|
|
#include "mmreg.h"
|
|
#include "winerror.h"
|
|
#include "strmif.h"
|
|
#include "control.h"
|
|
#include "vfwmsgs.h"
|
|
#include "uuids.h"
|
|
|
|
#include "debugtools.h"
|
|
DEFAULT_DEBUG_CHANNEL(quartz);
|
|
|
|
#include "quartz_private.h"
|
|
#include "audioutl.h"
|
|
#include "parser.h"
|
|
|
|
|
|
static const WCHAR QUARTZ_WaveParser_Name[] =
|
|
{ 'W','a','v','e',' ','P','a','r','s','e','r',0 };
|
|
static const WCHAR QUARTZ_WaveParserInPin_Name[] =
|
|
{ 'I','n',0 };
|
|
static const WCHAR QUARTZ_WaveParserOutPin_Name[] =
|
|
{ 'O','u','t',0 };
|
|
|
|
|
|
/****************************************************************************/
|
|
|
|
/* S_OK = found, S_FALSE = not found */
|
|
HRESULT RIFF_GetNext(
|
|
CParserImpl* pImpl, LONGLONG llOfs,
|
|
DWORD* pdwCode, DWORD* pdwLength )
|
|
{
|
|
BYTE bTemp[8];
|
|
HRESULT hr;
|
|
|
|
hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, 8, bTemp );
|
|
if ( hr == S_OK )
|
|
{
|
|
*pdwCode = mmioFOURCC(bTemp[0],bTemp[1],bTemp[2],bTemp[3]);
|
|
*pdwLength = PARSER_LE_UINT32(&bTemp[4]);
|
|
}
|
|
else
|
|
{
|
|
*pdwCode = 0;
|
|
*pdwLength = 0;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/* S_OK = found, S_FALSE = not found */
|
|
HRESULT RIFF_SearchChunk(
|
|
CParserImpl* pImpl,
|
|
DWORD dwSearchLengthMax,
|
|
LONGLONG llOfs, DWORD dwChunk,
|
|
LONGLONG* pllOfs, DWORD* pdwChunkLength )
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwCurCode;
|
|
DWORD dwCurLen;
|
|
LONGLONG llCurLen;
|
|
|
|
while ( 1 )
|
|
{
|
|
hr = RIFF_GetNext( pImpl, llOfs, &dwCurCode, &dwCurLen );
|
|
if ( hr != S_OK )
|
|
break;
|
|
TRACE("%c%c%c%c len %lu\n",
|
|
(int)(dwCurCode>> 0)&0xff,
|
|
(int)(dwCurCode>> 8)&0xff,
|
|
(int)(dwCurCode>>16)&0xff,
|
|
(int)(dwCurCode>>24)&0xff,
|
|
(unsigned long)dwCurLen);
|
|
if ( dwChunk == dwCurCode )
|
|
break;
|
|
llCurLen = 8 + (LONGLONG)((dwCurLen+1)&(~1));
|
|
llOfs += llCurLen;
|
|
if ( (LONGLONG)dwSearchLengthMax <= llCurLen )
|
|
return S_FALSE;
|
|
if ( dwSearchLengthMax != (DWORD)0xffffffff )
|
|
dwSearchLengthMax -= (DWORD)llCurLen;
|
|
}
|
|
|
|
*pllOfs = llOfs + 8;
|
|
*pdwChunkLength = dwCurLen;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/* S_OK = found, S_FALSE = not found */
|
|
HRESULT RIFF_SearchList(
|
|
CParserImpl* pImpl,
|
|
DWORD dwSearchLengthMax,
|
|
LONGLONG llOfs, DWORD dwListChunk,
|
|
LONGLONG* pllOfs, DWORD* pdwChunkLength )
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwCurLen;
|
|
LONGLONG llCurLen;
|
|
BYTE bTemp[4];
|
|
|
|
while ( 1 )
|
|
{
|
|
hr = RIFF_SearchChunk(
|
|
pImpl, dwSearchLengthMax,
|
|
llOfs, PARSER_LIST,
|
|
&llOfs, &dwCurLen );
|
|
if ( hr != S_OK )
|
|
break;
|
|
|
|
hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, 4, bTemp );
|
|
if ( hr != S_OK )
|
|
break;
|
|
|
|
if ( mmioFOURCC(bTemp[0],bTemp[1],bTemp[2],bTemp[3]) == dwListChunk )
|
|
break;
|
|
|
|
llCurLen = (LONGLONG)((dwCurLen+1)&(~1));
|
|
llOfs += llCurLen;
|
|
if ( (LONGLONG)dwSearchLengthMax <= (llCurLen+8) )
|
|
return S_FALSE;
|
|
if ( dwSearchLengthMax != (DWORD)0xffffffff )
|
|
dwSearchLengthMax -= (DWORD)(llCurLen+8);
|
|
}
|
|
|
|
if ( dwCurLen < 12 )
|
|
return E_FAIL;
|
|
|
|
*pllOfs = llOfs+4;
|
|
*pdwChunkLength = dwCurLen-4;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* CWavParseImpl
|
|
*/
|
|
|
|
typedef enum WavParseFmtType
|
|
{
|
|
WaveParse_Native,
|
|
WaveParse_Signed8,
|
|
WaveParse_Signed16BE,
|
|
WaveParse_Unsigned16LE,
|
|
WaveParse_Unsigned16BE,
|
|
} WavParseFmtType;
|
|
|
|
typedef struct CWavParseImpl
|
|
{
|
|
DWORD cbFmt;
|
|
WAVEFORMATEX* pFmt;
|
|
DWORD dwBlockSize;
|
|
LONGLONG llDataStart;
|
|
LONGLONG llBytesTotal;
|
|
LONGLONG llBytesProcessed;
|
|
WavParseFmtType iFmtType;
|
|
} CWavParseImpl;
|
|
|
|
|
|
static HRESULT CWavParseImpl_InitWAV( CParserImpl* pImpl, CWavParseImpl* This )
|
|
{
|
|
HRESULT hr;
|
|
LONGLONG llOfs;
|
|
DWORD dwChunkLength;
|
|
|
|
hr = RIFF_SearchChunk(
|
|
pImpl, (DWORD)0xffffffff,
|
|
PARSER_RIFF_OfsFirst, PARSER_fmt,
|
|
&llOfs, &dwChunkLength );
|
|
if ( FAILED(hr) )
|
|
return hr;
|
|
if ( hr != S_OK || ( dwChunkLength < (sizeof(WAVEFORMATEX)-2) ) )
|
|
return E_FAIL;
|
|
|
|
This->cbFmt = dwChunkLength;
|
|
if ( dwChunkLength < sizeof(WAVEFORMATEX) )
|
|
This->cbFmt = sizeof(WAVEFORMATEX);
|
|
This->pFmt = (WAVEFORMATEX*)QUARTZ_AllocMem( dwChunkLength );
|
|
if ( This->pFmt == NULL )
|
|
return E_OUTOFMEMORY;
|
|
ZeroMemory( This->pFmt, This->cbFmt );
|
|
|
|
hr = IAsyncReader_SyncRead(
|
|
pImpl->m_pReader, llOfs, dwChunkLength, (BYTE*)This->pFmt );
|
|
if ( hr != S_OK )
|
|
{
|
|
if ( SUCCEEDED(hr) )
|
|
hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
|
|
|
|
hr = RIFF_SearchChunk(
|
|
pImpl, (DWORD)0xffffffff,
|
|
PARSER_RIFF_OfsFirst, PARSER_data,
|
|
&llOfs, &dwChunkLength );
|
|
if ( FAILED(hr) )
|
|
return hr;
|
|
if ( hr != S_OK || dwChunkLength == 0 )
|
|
return E_FAIL;
|
|
|
|
This->llDataStart = llOfs;
|
|
This->llBytesTotal = (LONGLONG)dwChunkLength;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
static HRESULT CWavParseImpl_InitAU( CParserImpl* pImpl, CWavParseImpl* This )
|
|
{
|
|
BYTE au_hdr[24];
|
|
DWORD dataofs;
|
|
DWORD datalen;
|
|
DWORD datafmt;
|
|
DWORD datarate;
|
|
DWORD datachannels;
|
|
HRESULT hr;
|
|
WAVEFORMATEX wfx;
|
|
|
|
hr = IAsyncReader_SyncRead( pImpl->m_pReader, 0, 24, au_hdr );
|
|
if ( FAILED(hr) )
|
|
return hr;
|
|
|
|
dataofs = PARSER_BE_UINT32(&au_hdr[4]);
|
|
datalen = PARSER_BE_UINT32(&au_hdr[8]);
|
|
datafmt = PARSER_BE_UINT32(&au_hdr[12]);
|
|
datarate = PARSER_BE_UINT32(&au_hdr[16]);
|
|
datachannels = PARSER_BE_UINT32(&au_hdr[20]);
|
|
|
|
if ( dataofs < 24U || datalen == 0U )
|
|
return E_FAIL;
|
|
if ( datachannels != 1 && datachannels != 2 )
|
|
return E_FAIL;
|
|
|
|
ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
|
|
wfx.nChannels = datachannels;
|
|
wfx.nSamplesPerSec = datarate;
|
|
|
|
switch ( datafmt )
|
|
{
|
|
case 1:
|
|
wfx.wFormatTag = WAVE_FORMAT_MULAW;
|
|
wfx.nBlockAlign = datachannels;
|
|
wfx.wBitsPerSample = 8;
|
|
break;
|
|
case 2:
|
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
|
wfx.nBlockAlign = datachannels;
|
|
wfx.wBitsPerSample = 8;
|
|
This->iFmtType = WaveParse_Signed8;
|
|
break;
|
|
case 3:
|
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
|
wfx.nBlockAlign = datachannels;
|
|
wfx.wBitsPerSample = 16;
|
|
This->iFmtType = WaveParse_Signed16BE;
|
|
break;
|
|
default:
|
|
FIXME("audio/basic - unknown format %lu\n", datafmt );
|
|
return E_FAIL;
|
|
}
|
|
wfx.nAvgBytesPerSec = (datarate * datachannels * (DWORD)wfx.wBitsPerSample) >> 3;
|
|
|
|
This->cbFmt = sizeof(WAVEFORMATEX);
|
|
This->pFmt = (WAVEFORMATEX*)QUARTZ_AllocMem( sizeof(WAVEFORMATEX) );
|
|
if ( This->pFmt == NULL )
|
|
return E_OUTOFMEMORY;
|
|
memcpy( This->pFmt, &wfx, sizeof(WAVEFORMATEX) );
|
|
|
|
This->llDataStart = dataofs;
|
|
This->llBytesTotal = datalen;
|
|
|
|
TRACE("offset %lu, length %lu\n",dataofs,datalen);
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
static HRESULT CWavParseImpl_InitAIFF( CParserImpl* pImpl, CWavParseImpl* This )
|
|
{
|
|
FIXME( "AIFF is not supported now.\n" );
|
|
return E_FAIL;
|
|
}
|
|
|
|
static HRESULT CWavParseImpl_InitParser( CParserImpl* pImpl, ULONG* pcStreams )
|
|
{
|
|
CWavParseImpl* This = NULL;
|
|
HRESULT hr;
|
|
BYTE header[12];
|
|
|
|
TRACE("(%p,%p)\n",pImpl,pcStreams);
|
|
|
|
if ( pImpl->m_pReader == NULL )
|
|
return E_UNEXPECTED;
|
|
|
|
This = (CWavParseImpl*)QUARTZ_AllocMem( sizeof(CWavParseImpl) );
|
|
if ( This == NULL )
|
|
return E_OUTOFMEMORY;
|
|
pImpl->m_pUserData = This;
|
|
|
|
/* construct */
|
|
This->cbFmt = 0;
|
|
This->pFmt = NULL;
|
|
This->dwBlockSize = 0;
|
|
This->llDataStart = 0;
|
|
This->llBytesTotal = 0;
|
|
This->llBytesProcessed = 0;
|
|
This->iFmtType = WaveParse_Native;
|
|
|
|
hr = IAsyncReader_SyncRead( pImpl->m_pReader, 0, 12, header );
|
|
if ( FAILED(hr) )
|
|
return hr;
|
|
if ( hr != S_OK )
|
|
return E_FAIL;
|
|
|
|
if ( !memcmp( &header[0], "RIFF", 4 ) &&
|
|
!memcmp( &header[8], "WAVE", 4 ) )
|
|
{
|
|
TRACE( "(%p) - it's audio/wav.\n", pImpl );
|
|
hr = CWavParseImpl_InitWAV( pImpl, This );
|
|
}
|
|
else
|
|
if ( !memcmp( &header[0], ".snd", 4 ) )
|
|
{
|
|
TRACE( "(%p) - it's audio/basic.\n", pImpl );
|
|
hr = CWavParseImpl_InitAU( pImpl, This );
|
|
}
|
|
else
|
|
if ( !memcmp( &header[0], "FORM", 4 ) &&
|
|
!memcmp( &header[8], "AIFF", 4 ) )
|
|
{
|
|
TRACE( "(%p) - it's audio/aiff.\n", pImpl );
|
|
hr = CWavParseImpl_InitAIFF( pImpl, This );
|
|
}
|
|
else
|
|
{
|
|
FIXME( "(%p) - unknown format.\n", pImpl );
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
/* initialized successfully. */
|
|
*pcStreams = 1;
|
|
|
|
This->dwBlockSize = (This->pFmt->nAvgBytesPerSec + (DWORD)This->pFmt->nBlockAlign - 1U) / (DWORD)This->pFmt->nBlockAlign;
|
|
|
|
TRACE( "(%p) returned successfully.\n", pImpl );
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
static HRESULT CWavParseImpl_UninitParser( CParserImpl* pImpl )
|
|
{
|
|
CWavParseImpl* This = (CWavParseImpl*)pImpl->m_pUserData;
|
|
|
|
TRACE("(%p)\n",This);
|
|
|
|
if ( This == NULL )
|
|
return NOERROR;
|
|
|
|
/* destruct */
|
|
if ( This->pFmt != NULL ) QUARTZ_FreeMem(This->pFmt);
|
|
|
|
QUARTZ_FreeMem( This );
|
|
pImpl->m_pUserData = NULL;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
static LPCWSTR CWavParseImpl_GetOutPinName( CParserImpl* pImpl, ULONG nStreamIndex )
|
|
{
|
|
CWavParseImpl* This = (CWavParseImpl*)pImpl->m_pUserData;
|
|
|
|
TRACE("(%p)\n",This);
|
|
|
|
return QUARTZ_WaveParserOutPin_Name;
|
|
}
|
|
|
|
static HRESULT CWavParseImpl_GetStreamType( CParserImpl* pImpl, ULONG nStreamIndex, AM_MEDIA_TYPE* pmt )
|
|
{
|
|
CWavParseImpl* This = (CWavParseImpl*)pImpl->m_pUserData;
|
|
|
|
TRACE("(%p)\n",This);
|
|
|
|
if ( This == NULL || This->pFmt == NULL )
|
|
return E_UNEXPECTED;
|
|
|
|
ZeroMemory( pmt, sizeof(AM_MEDIA_TYPE) );
|
|
memcpy( &pmt->majortype, &MEDIATYPE_Audio, sizeof(GUID) );
|
|
QUARTZ_MediaSubType_FromFourCC( &pmt->subtype, (DWORD)This->pFmt->wFormatTag );
|
|
pmt->bFixedSizeSamples = 1;
|
|
pmt->bTemporalCompression = 0;
|
|
pmt->lSampleSize = This->pFmt->nBlockAlign;
|
|
memcpy( &pmt->formattype, &FORMAT_WaveFormatEx, sizeof(GUID) );
|
|
pmt->pUnk = NULL;
|
|
|
|
pmt->pbFormat = (BYTE*)CoTaskMemAlloc( This->cbFmt );
|
|
if ( pmt->pbFormat == NULL )
|
|
return E_OUTOFMEMORY;
|
|
pmt->cbFormat = This->cbFmt;
|
|
memcpy( pmt->pbFormat, This->pFmt, This->cbFmt );
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
static HRESULT CWavParseImpl_CheckStreamType( CParserImpl* pImpl, ULONG nStreamIndex, const AM_MEDIA_TYPE* pmt )
|
|
{
|
|
if ( !IsEqualGUID( &pmt->majortype, &MEDIATYPE_Audio ) ||
|
|
!IsEqualGUID( &pmt->formattype, &FORMAT_WaveFormatEx ) )
|
|
return E_FAIL;
|
|
if ( pmt->pbFormat == NULL || pmt->cbFormat < sizeof(WAVEFORMATEX) )
|
|
return E_FAIL;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
static HRESULT CWavParseImpl_GetAllocProp( CParserImpl* pImpl, ALLOCATOR_PROPERTIES* pReqProp )
|
|
{
|
|
CWavParseImpl* This = (CWavParseImpl*)pImpl->m_pUserData;
|
|
|
|
TRACE("(%p)\n",This);
|
|
|
|
if ( This == NULL || This->pFmt == NULL )
|
|
return E_UNEXPECTED;
|
|
|
|
ZeroMemory( pReqProp, sizeof(ALLOCATOR_PROPERTIES) );
|
|
pReqProp->cBuffers = 1;
|
|
pReqProp->cbBuffer = This->dwBlockSize;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
static HRESULT CWavParseImpl_GetNextRequest( CParserImpl* pImpl, ULONG* pnStreamIndex, LONGLONG* pllStart, LONG* plLength, REFERENCE_TIME* prtStart, REFERENCE_TIME* prtStop )
|
|
{
|
|
CWavParseImpl* This = (CWavParseImpl*)pImpl->m_pUserData;
|
|
LONGLONG llAvail;
|
|
LONGLONG llStart;
|
|
LONGLONG llEnd;
|
|
|
|
TRACE("(%p)\n",This);
|
|
|
|
if ( This == NULL || This->pFmt == NULL )
|
|
return E_UNEXPECTED;
|
|
|
|
llAvail = This->llBytesTotal - This->llBytesProcessed;
|
|
if ( llAvail > (LONGLONG)This->dwBlockSize )
|
|
llAvail = (LONGLONG)This->dwBlockSize;
|
|
llStart = This->llDataStart + This->llBytesProcessed;
|
|
llEnd = llStart + llAvail;
|
|
This->llBytesProcessed = llEnd;
|
|
|
|
*pllStart = This->llBytesProcessed;
|
|
*plLength = (LONG)llAvail;
|
|
*prtStart = llStart * QUARTZ_TIMEUNITS / (LONGLONG)This->pFmt->nAvgBytesPerSec;
|
|
*prtStop = llEnd * QUARTZ_TIMEUNITS / (LONGLONG)This->pFmt->nAvgBytesPerSec;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
static HRESULT CWavParseImpl_ProcessSample( CParserImpl* pImpl, ULONG nStreamIndex, LONGLONG llStart, LONG lLength, IMediaSample* pSample )
|
|
{
|
|
CWavParseImpl* This = (CWavParseImpl*)pImpl->m_pUserData;
|
|
BYTE* pData;
|
|
LONG lActLen;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)\n",This);
|
|
|
|
hr = IMediaSample_GetPointer(pSample,&pData);
|
|
if ( FAILED(hr) )
|
|
return hr;
|
|
lActLen = (LONG)IMediaSample_GetActualDataLength(pSample);
|
|
if ( lActLen != lLength )
|
|
return E_FAIL;
|
|
|
|
switch ( This->iFmtType )
|
|
{
|
|
case WaveParse_Native:
|
|
break;
|
|
case WaveParse_Signed8:
|
|
AUDIOUTL_ChangeSign8(pData,lActLen);
|
|
break;
|
|
case WaveParse_Signed16BE:
|
|
AUDIOUTL_ByteSwap(pData,lActLen);
|
|
break;
|
|
case WaveParse_Unsigned16LE:
|
|
AUDIOUTL_ChangeSign16LE(pData,lActLen);
|
|
break;
|
|
case WaveParse_Unsigned16BE:
|
|
AUDIOUTL_ChangeSign16BE(pData,lActLen);
|
|
AUDIOUTL_ByteSwap(pData,lActLen);
|
|
break;
|
|
default:
|
|
FIXME("(%p) - %d not implemented\n", This, This->iFmtType );
|
|
return E_FAIL;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
static const struct ParserHandlers CWavParseImpl_Handlers =
|
|
{
|
|
CWavParseImpl_InitParser,
|
|
CWavParseImpl_UninitParser,
|
|
CWavParseImpl_GetOutPinName,
|
|
CWavParseImpl_GetStreamType,
|
|
CWavParseImpl_CheckStreamType,
|
|
CWavParseImpl_GetAllocProp,
|
|
CWavParseImpl_GetNextRequest,
|
|
CWavParseImpl_ProcessSample,
|
|
|
|
/* for IQualityControl */
|
|
NULL, /* pQualityNotify */
|
|
|
|
/* for seeking */
|
|
NULL, /* pGetSeekingCaps */
|
|
NULL, /* pIsTimeFormatSupported */
|
|
NULL, /* pGetCurPos */
|
|
NULL, /* pSetCurPos */
|
|
NULL, /* pGetDuration */
|
|
NULL, /* pSetDuration */
|
|
NULL, /* pGetStopPos */
|
|
NULL, /* pSetStopPos */
|
|
NULL, /* pGetPreroll */
|
|
NULL, /* pSetPreroll */
|
|
};
|
|
|
|
HRESULT QUARTZ_CreateWaveParser(IUnknown* punkOuter,void** ppobj)
|
|
{
|
|
return QUARTZ_CreateParser(
|
|
punkOuter,ppobj,
|
|
&CLSID_quartzWaveParser,
|
|
QUARTZ_WaveParser_Name,
|
|
QUARTZ_WaveParserInPin_Name,
|
|
&CWavParseImpl_Handlers );
|
|
}
|
|
|
|
|