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