Added SystemParametersInfo unit test.
[wine] / dlls / quartz / wavparse.c
1 /*
2  * Implements WAVE/AU/AIFF Parser.
3  *
4  * hidenori@a2.ctktv.ne.jp
5  */
6
7 #include "config.h"
8
9 #include "windef.h"
10 #include "winbase.h"
11 #include "wingdi.h"
12 #include "winuser.h"
13 #include "mmsystem.h"
14 #include "mmreg.h"
15 #include "winerror.h"
16 #include "strmif.h"
17 #include "control.h"
18 #include "vfwmsgs.h"
19 #include "uuids.h"
20
21 #include "debugtools.h"
22 DEFAULT_DEBUG_CHANNEL(quartz);
23
24 #include "quartz_private.h"
25 #include "audioutl.h"
26 #include "parser.h"
27
28
29 static const WCHAR QUARTZ_WaveParser_Name[] =
30 { 'W','a','v','e',' ','P','a','r','s','e','r',0 };
31 static const WCHAR QUARTZ_WaveParserInPin_Name[] =
32 { 'I','n',0 };
33 static const WCHAR QUARTZ_WaveParserOutPin_Name[] =
34 { 'O','u','t',0 };
35
36
37 /****************************************************************************/
38
39 /* S_OK = found, S_FALSE = not found */
40 HRESULT RIFF_GetNext(
41         CParserImpl* pImpl, LONGLONG llOfs,
42         DWORD* pdwCode, DWORD* pdwLength )
43 {
44         BYTE bTemp[8];
45         HRESULT hr;
46
47         hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, 8, bTemp );
48         if ( hr == S_OK )
49         {
50                 *pdwCode = mmioFOURCC(bTemp[0],bTemp[1],bTemp[2],bTemp[3]);
51                 *pdwLength = PARSER_LE_UINT32(&bTemp[4]);
52         }
53         else
54         {
55                 *pdwCode = 0;
56                 *pdwLength = 0;
57         }
58
59         return hr;
60 }
61
62 /* S_OK = found, S_FALSE = not found */
63 HRESULT RIFF_SearchChunk(
64         CParserImpl* pImpl,
65         DWORD dwSearchLengthMax,
66         LONGLONG llOfs, DWORD dwChunk,
67         LONGLONG* pllOfs, DWORD* pdwChunkLength )
68 {
69         HRESULT hr;
70         DWORD dwCurCode;
71         DWORD dwCurLen;
72         LONGLONG llCurLen;
73
74         while ( 1 )
75         {
76                 hr = RIFF_GetNext( pImpl, llOfs, &dwCurCode, &dwCurLen );
77                 if ( hr != S_OK )
78                         break;
79                 TRACE("%c%c%c%c len %lu\n",
80                         (int)(dwCurCode>> 0)&0xff,
81                         (int)(dwCurCode>> 8)&0xff,
82                         (int)(dwCurCode>>16)&0xff,
83                         (int)(dwCurCode>>24)&0xff,
84                         (unsigned long)dwCurLen);
85                 if ( dwChunk == dwCurCode )
86                         break;
87                 llCurLen = 8 + (LONGLONG)((dwCurLen+1)&(~1));
88                 llOfs += llCurLen;
89                 if ( (LONGLONG)dwSearchLengthMax <= llCurLen )
90                         return S_FALSE;
91                 if ( dwSearchLengthMax != (DWORD)0xffffffff )
92                         dwSearchLengthMax -= (DWORD)llCurLen;
93         }
94
95         *pllOfs = llOfs + 8;
96         *pdwChunkLength = dwCurLen;
97
98         return hr;
99 }
100
101 /* S_OK = found, S_FALSE = not found */
102 HRESULT RIFF_SearchList(
103         CParserImpl* pImpl,
104         DWORD dwSearchLengthMax,
105         LONGLONG llOfs, DWORD dwListChunk,
106         LONGLONG* pllOfs, DWORD* pdwChunkLength )
107 {
108         HRESULT hr;
109         DWORD dwCurLen;
110         LONGLONG llCurLen;
111         BYTE bTemp[4];
112
113         while ( 1 )
114         {
115                 hr = RIFF_SearchChunk(
116                         pImpl, dwSearchLengthMax,
117                         llOfs, PARSER_LIST,
118                         &llOfs, &dwCurLen );
119                 if ( hr != S_OK )
120                         break;
121
122                 hr = IAsyncReader_SyncRead( pImpl->m_pReader, llOfs, 4, bTemp );
123                 if ( hr != S_OK )
124                         break;
125
126                 if ( mmioFOURCC(bTemp[0],bTemp[1],bTemp[2],bTemp[3]) == dwListChunk )
127                         break;
128
129                 llCurLen = (LONGLONG)((dwCurLen+1)&(~1));
130                 llOfs += llCurLen;
131                 if ( (LONGLONG)dwSearchLengthMax <= (llCurLen+8) )
132                         return S_FALSE;
133                 if ( dwSearchLengthMax != (DWORD)0xffffffff )
134                         dwSearchLengthMax -= (DWORD)(llCurLen+8);
135         }
136
137         if ( dwCurLen < 12 )
138                 return E_FAIL;
139
140         *pllOfs = llOfs+4;
141         *pdwChunkLength = dwCurLen-4;
142
143         return hr;
144 }
145
146
147
148
149 /****************************************************************************
150  *
151  *      CWavParseImpl
152  */
153
154 typedef enum WavParseFmtType
155 {
156         WaveParse_Native,
157         WaveParse_Signed8,
158         WaveParse_Signed16BE,
159         WaveParse_Unsigned16LE,
160         WaveParse_Unsigned16BE,
161 } WavParseFmtType;
162
163 typedef struct CWavParseImpl
164 {
165         DWORD   cbFmt;
166         WAVEFORMATEX*   pFmt;
167         DWORD   dwBlockSize;
168         LONGLONG        llDataStart;
169         LONGLONG        llBytesTotal;
170         LONGLONG        llBytesProcessed;
171         WavParseFmtType iFmtType;
172 } CWavParseImpl;
173
174
175 static HRESULT CWavParseImpl_InitWAV( CParserImpl* pImpl, CWavParseImpl* This )
176 {
177         HRESULT hr;
178         LONGLONG        llOfs;
179         DWORD   dwChunkLength;
180
181         hr = RIFF_SearchChunk(
182                 pImpl, (DWORD)0xffffffff,
183                 PARSER_RIFF_OfsFirst, PARSER_fmt,
184                 &llOfs, &dwChunkLength );
185         if ( FAILED(hr) )
186                 return hr;
187         if ( hr != S_OK || ( dwChunkLength < (sizeof(WAVEFORMATEX)-2) ) )
188                 return E_FAIL;
189
190         This->cbFmt = dwChunkLength;
191         if ( dwChunkLength < sizeof(WAVEFORMATEX) )
192                 This->cbFmt = sizeof(WAVEFORMATEX);
193         This->pFmt = (WAVEFORMATEX*)QUARTZ_AllocMem( dwChunkLength );
194         if ( This->pFmt == NULL )
195                 return E_OUTOFMEMORY;
196         ZeroMemory( This->pFmt, This->cbFmt );
197
198         hr = IAsyncReader_SyncRead(
199                 pImpl->m_pReader, llOfs, dwChunkLength, (BYTE*)This->pFmt );
200         if ( hr != S_OK )
201         {
202                 if ( SUCCEEDED(hr) )
203                         hr = E_FAIL;
204                 return hr;
205         }
206
207
208         hr = RIFF_SearchChunk(
209                 pImpl, (DWORD)0xffffffff,
210                 PARSER_RIFF_OfsFirst, PARSER_data,
211                 &llOfs, &dwChunkLength );
212         if ( FAILED(hr) )
213                 return hr;
214         if ( hr != S_OK || dwChunkLength == 0 )
215                 return E_FAIL;
216
217         This->llDataStart = llOfs;
218         This->llBytesTotal = (LONGLONG)dwChunkLength;
219
220         return NOERROR;
221 }
222
223 static HRESULT CWavParseImpl_InitAU( CParserImpl* pImpl, CWavParseImpl* This )
224 {
225         BYTE    au_hdr[24];
226         DWORD   dataofs;
227         DWORD   datalen;
228         DWORD   datafmt;
229         DWORD   datarate;
230         DWORD   datachannels;
231         HRESULT hr;
232         WAVEFORMATEX    wfx;
233
234         hr = IAsyncReader_SyncRead( pImpl->m_pReader, 0, 24, au_hdr );
235         if ( FAILED(hr) )
236                 return hr;
237
238         dataofs = PARSER_BE_UINT32(&au_hdr[4]);
239         datalen = PARSER_BE_UINT32(&au_hdr[8]);
240         datafmt = PARSER_BE_UINT32(&au_hdr[12]);
241         datarate = PARSER_BE_UINT32(&au_hdr[16]);
242         datachannels = PARSER_BE_UINT32(&au_hdr[20]);
243
244         if ( dataofs < 24U || datalen == 0U )
245                 return E_FAIL;
246         if ( datachannels != 1 && datachannels != 2 )
247                 return E_FAIL;
248
249         ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
250         wfx.nChannels = datachannels;
251         wfx.nSamplesPerSec = datarate;
252
253         switch ( datafmt )
254         {
255         case 1:
256                 wfx.wFormatTag = WAVE_FORMAT_MULAW;
257                 wfx.nBlockAlign = datachannels;
258                 wfx.wBitsPerSample = 8;
259                 break;
260         case 2:
261                 wfx.wFormatTag = WAVE_FORMAT_PCM;
262                 wfx.nBlockAlign = datachannels;
263                 wfx.wBitsPerSample = 8;
264                 This->iFmtType = WaveParse_Signed8;
265                 break;
266         case 3:
267                 wfx.wFormatTag = WAVE_FORMAT_PCM;
268                 wfx.nBlockAlign = datachannels;
269                 wfx.wBitsPerSample = 16;
270                 This->iFmtType = WaveParse_Signed16BE;
271                 break;
272         default:
273                 FIXME("audio/basic - unknown format %lu\n", datafmt );
274                 return E_FAIL;
275         }
276         wfx.nAvgBytesPerSec = (datarate * datachannels * (DWORD)wfx.wBitsPerSample) >> 3;
277
278         This->cbFmt = sizeof(WAVEFORMATEX);
279         This->pFmt = (WAVEFORMATEX*)QUARTZ_AllocMem( sizeof(WAVEFORMATEX) );
280         if ( This->pFmt == NULL )
281                 return E_OUTOFMEMORY;
282         memcpy( This->pFmt, &wfx, sizeof(WAVEFORMATEX) );
283
284         This->llDataStart = dataofs;
285         This->llBytesTotal = datalen;
286
287         TRACE("offset %lu, length %lu\n",dataofs,datalen);
288
289         return NOERROR;
290 }
291
292 static HRESULT CWavParseImpl_InitAIFF( CParserImpl* pImpl, CWavParseImpl* This )
293 {
294         FIXME( "AIFF is not supported now.\n" );
295         return E_FAIL;
296 }
297
298 static HRESULT CWavParseImpl_InitParser( CParserImpl* pImpl, ULONG* pcStreams )
299 {
300         CWavParseImpl*  This = NULL;
301         HRESULT hr;
302         BYTE    header[12];
303
304         TRACE("(%p,%p)\n",pImpl,pcStreams);
305
306         if ( pImpl->m_pReader == NULL )
307                 return E_UNEXPECTED;
308
309         This = (CWavParseImpl*)QUARTZ_AllocMem( sizeof(CWavParseImpl) );
310         if ( This == NULL )
311                 return E_OUTOFMEMORY;
312         pImpl->m_pUserData = This;
313
314         /* construct */
315         This->cbFmt = 0;
316         This->pFmt = NULL;
317         This->dwBlockSize = 0;
318         This->llDataStart = 0;
319         This->llBytesTotal = 0;
320         This->llBytesProcessed = 0;
321         This->iFmtType = WaveParse_Native;
322
323         hr = IAsyncReader_SyncRead( pImpl->m_pReader, 0, 12, header );
324         if ( FAILED(hr) )
325                 return hr;
326         if ( hr != S_OK )
327                 return E_FAIL;
328
329         if ( !memcmp( &header[0], "RIFF", 4 ) &&
330                  !memcmp( &header[8], "WAVE", 4 ) )
331         {
332                 TRACE( "(%p) - it's audio/wav.\n", pImpl );
333                 hr = CWavParseImpl_InitWAV( pImpl, This );
334         }
335         else
336         if ( !memcmp( &header[0], ".snd", 4 ) )
337         {
338                 TRACE( "(%p) - it's audio/basic.\n", pImpl );
339                 hr = CWavParseImpl_InitAU( pImpl, This );
340         }
341         else
342         if ( !memcmp( &header[0], "FORM", 4 ) &&
343                  !memcmp( &header[8], "AIFF", 4 ) )
344         {
345                 TRACE( "(%p) - it's audio/aiff.\n", pImpl );
346                 hr = CWavParseImpl_InitAIFF( pImpl, This );
347         }
348         else
349         {
350                 FIXME( "(%p) - unknown format.\n", pImpl );
351                 hr = E_FAIL;
352         }
353
354         if ( FAILED(hr) )
355         {
356                 return hr;
357         }
358
359         /* initialized successfully. */
360         *pcStreams = 1;
361
362         This->dwBlockSize = (This->pFmt->nAvgBytesPerSec + (DWORD)This->pFmt->nBlockAlign - 1U) / (DWORD)This->pFmt->nBlockAlign;
363
364         TRACE( "(%p) returned successfully.\n", pImpl );
365
366         return NOERROR;
367 }
368
369 static HRESULT CWavParseImpl_UninitParser( CParserImpl* pImpl )
370 {
371         CWavParseImpl*  This = (CWavParseImpl*)pImpl->m_pUserData;
372
373         TRACE("(%p)\n",This);
374
375         if ( This == NULL )
376                 return NOERROR;
377
378         /* destruct */
379         if ( This->pFmt != NULL ) QUARTZ_FreeMem(This->pFmt);
380
381         QUARTZ_FreeMem( This );
382         pImpl->m_pUserData = NULL;
383
384         return NOERROR;
385 }
386
387 static LPCWSTR CWavParseImpl_GetOutPinName( CParserImpl* pImpl, ULONG nStreamIndex )
388 {
389         CWavParseImpl*  This = (CWavParseImpl*)pImpl->m_pUserData;
390
391         TRACE("(%p)\n",This);
392
393         return QUARTZ_WaveParserOutPin_Name;
394 }
395
396 static HRESULT CWavParseImpl_GetStreamType( CParserImpl* pImpl, ULONG nStreamIndex, AM_MEDIA_TYPE* pmt )
397 {
398         CWavParseImpl*  This = (CWavParseImpl*)pImpl->m_pUserData;
399
400         TRACE("(%p)\n",This);
401
402         if ( This == NULL || This->pFmt == NULL )
403                 return E_UNEXPECTED;
404
405         ZeroMemory( pmt, sizeof(AM_MEDIA_TYPE) );
406         memcpy( &pmt->majortype, &MEDIATYPE_Audio, sizeof(GUID) );
407         QUARTZ_MediaSubType_FromFourCC( &pmt->subtype, (DWORD)This->pFmt->wFormatTag );
408         pmt->bFixedSizeSamples = 1;
409         pmt->bTemporalCompression = 0;
410         pmt->lSampleSize = This->pFmt->nBlockAlign;
411         memcpy( &pmt->formattype, &FORMAT_WaveFormatEx, sizeof(GUID) );
412         pmt->pUnk = NULL;
413
414         pmt->pbFormat = (BYTE*)CoTaskMemAlloc( This->cbFmt );
415         if ( pmt->pbFormat == NULL )
416                 return E_OUTOFMEMORY;
417         pmt->cbFormat = This->cbFmt;
418         memcpy( pmt->pbFormat, This->pFmt, This->cbFmt );
419
420         return NOERROR;
421 }
422
423 static HRESULT CWavParseImpl_CheckStreamType( CParserImpl* pImpl, ULONG nStreamIndex, const AM_MEDIA_TYPE* pmt )
424 {
425         if ( !IsEqualGUID( &pmt->majortype, &MEDIATYPE_Audio ) ||
426                  !IsEqualGUID( &pmt->formattype, &FORMAT_WaveFormatEx ) )
427                 return E_FAIL;
428         if ( pmt->pbFormat == NULL || pmt->cbFormat < sizeof(WAVEFORMATEX) )
429                 return E_FAIL;
430
431         return NOERROR;
432 }
433
434 static HRESULT CWavParseImpl_GetAllocProp( CParserImpl* pImpl, ALLOCATOR_PROPERTIES* pReqProp )
435 {
436         CWavParseImpl*  This = (CWavParseImpl*)pImpl->m_pUserData;
437
438         TRACE("(%p)\n",This);
439
440         if ( This == NULL || This->pFmt == NULL )
441                 return E_UNEXPECTED;
442
443         ZeroMemory( pReqProp, sizeof(ALLOCATOR_PROPERTIES) );
444         pReqProp->cBuffers = 1;
445         pReqProp->cbBuffer = This->dwBlockSize;
446
447         return NOERROR;
448 }
449
450 static HRESULT CWavParseImpl_GetNextRequest( CParserImpl* pImpl, ULONG* pnStreamIndex, LONGLONG* pllStart, LONG* plLength, REFERENCE_TIME* prtStart, REFERENCE_TIME* prtStop )
451 {
452         CWavParseImpl*  This = (CWavParseImpl*)pImpl->m_pUserData;
453         LONGLONG        llAvail;
454         LONGLONG        llStart;
455         LONGLONG        llEnd;
456
457         TRACE("(%p)\n",This);
458
459         if ( This == NULL || This->pFmt == NULL )
460                 return E_UNEXPECTED;
461
462         llAvail = This->llBytesTotal - This->llBytesProcessed;
463         if ( llAvail > (LONGLONG)This->dwBlockSize )
464                 llAvail = (LONGLONG)This->dwBlockSize;
465         llStart = This->llDataStart + This->llBytesProcessed;
466         llEnd = llStart + llAvail;
467         This->llBytesProcessed = llEnd;
468
469         *pllStart = This->llBytesProcessed;
470         *plLength = (LONG)llAvail;
471         *prtStart = llStart * QUARTZ_TIMEUNITS / (LONGLONG)This->pFmt->nAvgBytesPerSec;
472         *prtStop = llEnd * QUARTZ_TIMEUNITS / (LONGLONG)This->pFmt->nAvgBytesPerSec;
473
474         return NOERROR;
475 }
476
477 static HRESULT CWavParseImpl_ProcessSample( CParserImpl* pImpl, ULONG nStreamIndex, LONGLONG llStart, LONG lLength, IMediaSample* pSample )
478 {
479         CWavParseImpl*  This = (CWavParseImpl*)pImpl->m_pUserData;
480         BYTE*   pData;
481         LONG    lActLen;
482         HRESULT hr;
483
484         TRACE("(%p)\n",This);
485
486         hr = IMediaSample_GetPointer(pSample,&pData);
487         if ( FAILED(hr) )
488                 return hr;
489         lActLen = (LONG)IMediaSample_GetActualDataLength(pSample);
490         if ( lActLen != lLength )
491                 return E_FAIL;
492
493         switch ( This->iFmtType )
494         {
495         case WaveParse_Native:
496                 break;
497         case WaveParse_Signed8:
498                 AUDIOUTL_ChangeSign8(pData,lActLen);
499                 break;
500         case WaveParse_Signed16BE:
501                 AUDIOUTL_ByteSwap(pData,lActLen);
502                 break;
503         case WaveParse_Unsigned16LE:
504                 AUDIOUTL_ChangeSign16LE(pData,lActLen);
505                 break;
506         case WaveParse_Unsigned16BE:
507                 AUDIOUTL_ChangeSign16BE(pData,lActLen);
508                 AUDIOUTL_ByteSwap(pData,lActLen);
509                 break;
510         default:
511                 FIXME("(%p) - %d not implemented\n", This, This->iFmtType );
512                 return E_FAIL;
513         }
514
515         return NOERROR;
516 }
517
518
519 static const struct ParserHandlers CWavParseImpl_Handlers =
520 {
521         CWavParseImpl_InitParser,
522         CWavParseImpl_UninitParser,
523         CWavParseImpl_GetOutPinName,
524         CWavParseImpl_GetStreamType,
525         CWavParseImpl_CheckStreamType,
526         CWavParseImpl_GetAllocProp,
527         CWavParseImpl_GetNextRequest,
528         CWavParseImpl_ProcessSample,
529
530         /* for IQualityControl */
531         NULL, /* pQualityNotify */
532
533         /* for seeking */
534         NULL, /* pGetSeekingCaps */
535         NULL, /* pIsTimeFormatSupported */
536         NULL, /* pGetCurPos */
537         NULL, /* pSetCurPos */
538         NULL, /* pGetDuration */
539         NULL, /* pSetDuration */
540         NULL, /* pGetStopPos */
541         NULL, /* pSetStopPos */
542         NULL, /* pGetPreroll */
543         NULL, /* pSetPreroll */
544 };
545
546 HRESULT QUARTZ_CreateWaveParser(IUnknown* punkOuter,void** ppobj)
547 {
548         return QUARTZ_CreateParser(
549                 punkOuter,ppobj,
550                 &CLSID_quartzWaveParser,
551                 QUARTZ_WaveParser_Name,
552                 QUARTZ_WaveParserInPin_Name,
553                 &CWavParseImpl_Handlers );
554 }
555
556