wintab32: Store and use the physical device id to match device to cursor.
[wine] / dlls / quartz / waveparser.c
1 /*
2  * WAVE Parser Filter
3  *
4  * Copyright 2005 Christian Costa
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "quartz_private.h"
22 #include "control_private.h"
23 #include "pin.h"
24
25 #include "uuids.h"
26 #include "aviriff.h"
27 #include "vfwmsgs.h"
28 #include "mmsystem.h"
29
30 #include "fourcc.h"
31
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
34
35 #include <math.h>
36 #include <assert.h>
37
38 #include "parser.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
41
42 static const WCHAR wcsOutputPinName[] = {'o','u','t','p','u','t',' ','p','i','n',0};
43
44 typedef struct WAVEParserImpl
45 {
46     ParserImpl Parser;
47     IMediaSample * pCurrentSample;
48     LONGLONG StartOfFile; /* in media time */
49     LONGLONG EndOfFile;
50 } WAVEParserImpl;
51
52 static HRESULT WAVEParser_Sample(LPVOID iface, IMediaSample * pSample)
53 {
54     WAVEParserImpl *This = (WAVEParserImpl *)iface;
55     LPBYTE pbSrcStream = NULL;
56     long cbSrcStream = 0;
57     REFERENCE_TIME tStart, tStop;
58     HRESULT hr;
59     BOOL bMoreData = TRUE;
60     Parser_OutputPin * pOutputPin;
61     BYTE * pbDstStream;
62     long cbDstStream;
63     long chunk_remaining_bytes = 0;
64     long offset_src = 0;
65  
66     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
67
68     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
69
70     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
71
72     assert(BYTES_FROM_MEDIATIME(tStop - tStart) == cbSrcStream);
73
74     pOutputPin = (Parser_OutputPin *)This->Parser.ppPins[1];
75
76     if (tStop < This->StartOfFile)
77         return S_OK;
78
79     if (tStart < This->StartOfFile)
80         offset_src = BYTES_FROM_MEDIATIME(This->StartOfFile - tStart);
81
82     while (bMoreData)
83     {
84         if (!This->pCurrentSample)
85         {
86             /* cache media sample until it is ready to be despatched
87              * (i.e. we reach the end of the chunk) */
88             hr = OutputPin_GetDeliveryBuffer(&pOutputPin->pin, &This->pCurrentSample, NULL, NULL, 0);
89
90             if (SUCCEEDED(hr))
91             {
92                 hr = IMediaSample_SetActualDataLength(This->pCurrentSample, 0);
93                 assert(hr == S_OK);
94             }
95             else
96             {
97                 TRACE("Skipping sending sample due to error (%x)\n", hr);
98                 This->pCurrentSample = NULL;
99                 break;
100             }
101         }
102
103         hr = IMediaSample_GetPointer(This->pCurrentSample, &pbDstStream);
104
105         if (SUCCEEDED(hr))
106         {
107             cbDstStream = IMediaSample_GetSize(This->pCurrentSample);
108
109             chunk_remaining_bytes = cbDstStream - IMediaSample_GetActualDataLength(This->pCurrentSample);
110         
111             assert(chunk_remaining_bytes >= 0);
112             assert(chunk_remaining_bytes <= cbDstStream - IMediaSample_GetActualDataLength(This->pCurrentSample));
113         }
114
115         if (chunk_remaining_bytes <= cbSrcStream - offset_src)
116         {
117             if (SUCCEEDED(hr))
118             {
119                 memcpy(pbDstStream + IMediaSample_GetActualDataLength(This->pCurrentSample), pbSrcStream + offset_src, chunk_remaining_bytes);
120                 hr = IMediaSample_SetActualDataLength(This->pCurrentSample, chunk_remaining_bytes + IMediaSample_GetActualDataLength(This->pCurrentSample));
121                 assert(hr == S_OK);
122             }
123
124             if (SUCCEEDED(hr))
125             {
126                 REFERENCE_TIME tAviStart, tAviStop;
127
128                 /* FIXME: hack */
129                 if (pOutputPin->dwSamplesProcessed == 0) {
130                     IMediaSample_SetDiscontinuity(This->pCurrentSample, TRUE);
131                     IMediaSample_SetSyncPoint(This->pCurrentSample, TRUE);
132                 }
133
134                 pOutputPin->dwSamplesProcessed++;
135
136                 if (pOutputPin->dwSampleSize)
137                     tAviStart = (LONGLONG)ceil(10000000.0 * (float)(pOutputPin->dwSamplesProcessed - 1) * (float)IMediaSample_GetActualDataLength(This->pCurrentSample) / ((float)pOutputPin->dwSampleSize * pOutputPin->fSamplesPerSec));
138                 else
139                     tAviStart = (LONGLONG)ceil(10000000.0 * (float)(pOutputPin->dwSamplesProcessed - 1) / (float)pOutputPin->fSamplesPerSec);
140                 if (pOutputPin->dwSampleSize)
141                     tAviStop = (LONGLONG)ceil(10000000.0 * (float)pOutputPin->dwSamplesProcessed * (float)IMediaSample_GetActualDataLength(This->pCurrentSample) / ((float)pOutputPin->dwSampleSize * pOutputPin->fSamplesPerSec));
142                 else
143                     tAviStop = (LONGLONG)ceil(10000000.0 * (float)pOutputPin->dwSamplesProcessed / (float)pOutputPin->fSamplesPerSec);
144
145                 IMediaSample_SetTime(This->pCurrentSample, &tAviStart, &tAviStop);
146
147                 hr = OutputPin_SendSample(&pOutputPin->pin, This->pCurrentSample);
148                 if (hr != S_OK && hr != VFW_E_NOT_CONNECTED)
149                     ERR("Error sending sample (%x)\n", hr);
150             }
151
152             if (This->pCurrentSample)
153                 IMediaSample_Release(This->pCurrentSample);
154                     
155             This->pCurrentSample = NULL;
156         }
157         else
158         {
159             if (SUCCEEDED(hr))
160             {
161                 memcpy(pbDstStream + IMediaSample_GetActualDataLength(This->pCurrentSample), pbSrcStream + offset_src, cbSrcStream - offset_src);
162                 IMediaSample_SetActualDataLength(This->pCurrentSample, cbSrcStream - offset_src + IMediaSample_GetActualDataLength(This->pCurrentSample));
163             }
164             bMoreData = FALSE;
165         }
166         offset_src += chunk_remaining_bytes;
167     }
168
169     if (tStop >= This->EndOfFile)
170     {
171         int i;
172
173         TRACE("End of file reached\n");
174
175         for (i = 0; i < This->Parser.cStreams; i++)
176         {
177             IPin* ppin;
178             HRESULT hr;
179
180             TRACE("Send End Of Stream to output pin %d\n", i);
181
182             hr = IPin_ConnectedTo(This->Parser.ppPins[i+1], &ppin);
183             if (SUCCEEDED(hr))
184             {
185                 hr = IPin_EndOfStream(ppin);
186                 IPin_Release(ppin);
187             }
188             if (FAILED(hr))
189             {
190                 ERR("%x\n", hr);
191                 break;
192             }
193         }
194
195         /* Force the pullpin thread to stop */
196         hr = S_FALSE;
197     }
198
199     return hr;
200 }
201
202 static HRESULT WAVEParser_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
203 {
204     if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
205         return S_FALSE;
206     if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_WAVE))
207         return S_OK;
208     if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_AU) || IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_AIFF))
209         FIXME("AU and AIFF files not supported yet!\n");
210     return S_FALSE;
211 }
212
213 static HRESULT WAVEParser_InputPin_PreConnect(IPin * iface, IPin * pConnectPin)
214 {
215     PullPin *This = (PullPin *)iface;
216     HRESULT hr;
217     RIFFLIST list;
218     RIFFCHUNK chunk;
219     LONGLONG pos = 0; /* in bytes */
220     PIN_INFO piOutput;
221     ALLOCATOR_PROPERTIES props;
222     AM_MEDIA_TYPE amt;
223     float fSamplesPerSec = 0.0f;
224     DWORD dwSampleSize = 0;
225     DWORD dwLength = 0;
226     WAVEParserImpl * pWAVEParser = (WAVEParserImpl *)This->pin.pinInfo.pFilter;
227
228     piOutput.dir = PINDIR_OUTPUT;
229     piOutput.pFilter = (IBaseFilter *)This;
230     lstrcpynW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
231     
232     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
233     pos += sizeof(list);
234
235     if (list.fcc != ckidRIFF)
236     {
237         ERR("Input stream not a RIFF file\n");
238         return E_FAIL;
239     }
240     if (list.cb > 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
241     {
242         ERR("Input stream violates RIFF spec\n");
243         return E_FAIL;
244     }
245     if (list.fccListType != mmioFOURCC('W','A','V','E'))
246     {
247         ERR("Input stream not an WAVE RIFF file\n");
248         return E_FAIL;
249     }
250
251     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(chunk), (BYTE *)&chunk);
252     pos += sizeof(chunk);
253     if (chunk.fcc != mmioFOURCC('f','m','t',' '))
254     {
255         ERR("Expected 'fmt ' chunk, but got %.04s\n", (LPSTR)&chunk.fcc);
256         return E_FAIL;
257     }
258
259     memcpy(&amt.majortype, &MEDIATYPE_Audio, sizeof(GUID));
260     memcpy(&amt.formattype, &FORMAT_WaveFormatEx, sizeof(GUID));
261     amt.cbFormat = chunk.cb;
262     amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
263     amt.pUnk = NULL;
264     hr = IAsyncReader_SyncRead(This->pReader, pos, amt.cbFormat, amt.pbFormat);
265     memcpy(&amt.subtype, &MEDIATYPE_Audio, sizeof(GUID));
266     amt.subtype.Data1 = ((WAVEFORMATEX*)amt.pbFormat)->wFormatTag;
267
268     pos += chunk.cb;
269     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(chunk), (BYTE *)&chunk);
270     if (chunk.fcc == mmioFOURCC('f','a','c','t'))
271     {
272         FIXME("'fact' chunk not supported yet\n");
273         pos += sizeof(chunk) + chunk.cb;
274         hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(chunk), (BYTE *)&chunk);
275     }
276     if (chunk.fcc != mmioFOURCC('d','a','t','a'))
277     {
278         ERR("Expected 'data' chunk, but got %.04s\n", (LPSTR)&chunk.fcc);
279         return E_FAIL;
280     }
281
282     if (hr == S_OK)
283     {
284         pWAVEParser->StartOfFile = MEDIATIME_FROM_BYTES(pos + sizeof(RIFFCHUNK));
285         pWAVEParser->EndOfFile = MEDIATIME_FROM_BYTES(pos + chunk.cb + sizeof(RIFFCHUNK));
286     }
287
288     if (hr != S_OK)
289         return E_FAIL;
290
291     props.cbAlign = ((WAVEFORMATEX*)amt.pbFormat)->nBlockAlign;
292     props.cbPrefix = 0;
293     props.cbBuffer = 4096;
294     props.cBuffers = 2;
295     
296     hr = Parser_AddPin(&(pWAVEParser->Parser), &piOutput, &props, &amt, fSamplesPerSec, dwSampleSize, dwLength);
297     
298     TRACE("WAVE File ok\n");
299
300     return hr;
301 }
302
303 static HRESULT WAVEParser_Cleanup(LPVOID iface)
304 {
305     WAVEParserImpl *This = (WAVEParserImpl*)iface;
306
307     TRACE("(%p)->()\n", This);
308
309     if (This->pCurrentSample)
310         IMediaSample_Release(This->pCurrentSample);
311     This->pCurrentSample = NULL;
312
313     return S_OK;
314 }
315
316 HRESULT WAVEParser_create(IUnknown * pUnkOuter, LPVOID * ppv)
317 {
318     HRESULT hr;
319     WAVEParserImpl * This;
320
321     TRACE("(%p, %p)\n", pUnkOuter, ppv);
322
323     *ppv = NULL;
324
325     if (pUnkOuter)
326         return CLASS_E_NOAGGREGATION;
327
328     /* Note: This memory is managed by the transform filter once created */
329     This = CoTaskMemAlloc(sizeof(WAVEParserImpl));
330
331     This->pCurrentSample = NULL;
332
333     hr = Parser_Create(&(This->Parser), &CLSID_WAVEParser, WAVEParser_Sample, WAVEParser_QueryAccept, WAVEParser_InputPin_PreConnect, WAVEParser_Cleanup);
334
335     if (FAILED(hr))
336         return hr;
337
338     *ppv = (LPVOID)This;
339
340     return hr;
341 }