Added *Filter* functions.
[wine] / dlls / msacm / stream.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  *      MSACM32 library
5  *
6  *      Copyright 1998  Patrik Stridvall
7  *                1999  Eric Pouech
8  */
9
10 /* TODO
11  *      + asynchronous conversion is not implemented
12  *      + callback/notification
13  *      * acmStreamMessage
14  *      + properly close ACM streams
15  */
16
17 #include "winbase.h"
18 #include "winerror.h"
19 #include "windef.h"
20 #include "debugtools.h"
21 #include "mmsystem.h"
22 #include "msacm.h"
23 #include "msacmdrv.h"
24 #include "wineacm.h"
25
26 DEFAULT_DEBUG_CHANNEL(msacm)
27     
28 static PWINE_ACMSTREAM  ACM_GetStream(HACMSTREAM has)
29 {
30     return (PWINE_ACMSTREAM)has;
31 }
32
33 /***********************************************************************
34  *           acmStreamClose (MSACM32.37)
35  */
36 MMRESULT WINAPI acmStreamClose(HACMSTREAM has, DWORD fdwClose)
37 {
38     PWINE_ACMSTREAM     was;
39     MMRESULT            ret;
40                 
41     TRACE("(0x%08x, %ld)\n", has, fdwClose);
42     
43     if ((was = ACM_GetStream(has)) == NULL) {
44         return MMSYSERR_INVALHANDLE;
45     }
46     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CLOSE, (DWORD)&was->drvInst, 0);
47     if (ret == MMSYSERR_NOERROR) {
48         if (was->hAcmDriver)
49             acmDriverClose(was->hAcmDriver, 0L);        
50         HeapFree(MSACM_hHeap, 0, was);
51     }
52     TRACE("=> (%d)\n", ret);
53     return ret;
54 }
55
56 /***********************************************************************
57  *           acmStreamConvert (MSACM32.38)
58  */
59 MMRESULT WINAPI acmStreamConvert(HACMSTREAM has, PACMSTREAMHEADER pash, 
60                                  DWORD fdwConvert)
61 {
62     PWINE_ACMSTREAM     was;
63     MMRESULT            ret = MMSYSERR_NOERROR;
64     PACMDRVSTREAMHEADER padsh;
65
66     TRACE("(0x%08x, %p, %ld)\n", has, pash, fdwConvert);
67     
68     if ((was = ACM_GetStream(has)) == NULL)
69         return MMSYSERR_INVALHANDLE;
70     if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER))
71         return MMSYSERR_INVALPARAM;
72
73     if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED))
74         return ACMERR_UNPREPARED;
75
76     /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
77      * size. some fields are private to msacm internals, and are exposed
78      * in ACMSTREAMHEADER in the dwReservedDriver array
79      */
80     padsh = (PACMDRVSTREAMHEADER)pash;
81
82     /* check that pointers have not been modified */
83     if (padsh->pbPreparedSrc != padsh->pbSrc ||
84         padsh->cbPreparedSrcLength < padsh->cbSrcLength ||
85         padsh->pbPreparedDst != padsh->pbDst ||
86         padsh->cbPreparedDstLength < padsh->cbDstLength) {
87         return MMSYSERR_INVALPARAM;
88     }   
89
90     padsh->fdwConvert = fdwConvert;
91
92     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CONVERT, (DWORD)&was->drvInst, (DWORD)padsh);
93     if (ret == MMSYSERR_NOERROR) {
94         padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_DONE;
95     }
96     TRACE("=> (%d)\n", ret);
97     return ret;
98 }
99
100 /***********************************************************************
101  *           acmStreamMessage (MSACM32.39)
102  */
103 MMRESULT WINAPI acmStreamMessage(HACMSTREAM has, UINT uMsg, LPARAM lParam1, 
104                                  LPARAM lParam2)
105 {
106     FIXME("(0x%08x, %u, %ld, %ld): stub\n", has, uMsg, lParam1, lParam2);
107     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
108     return MMSYSERR_ERROR;
109 }
110
111 /***********************************************************************
112  *           acmStreamOpen (MSACM32.40)
113  */
114 MMRESULT WINAPI acmStreamOpen(PHACMSTREAM phas, HACMDRIVER had, PWAVEFORMATEX pwfxSrc,
115                               PWAVEFORMATEX pwfxDst, PWAVEFILTER pwfltr, DWORD dwCallback,
116                               DWORD dwInstance, DWORD fdwOpen)
117 {
118     PWINE_ACMSTREAM     was;
119     PWINE_ACMDRIVER     wad;
120     MMRESULT            ret;
121     int                 wfxSrcSize;
122     int                 wfxDstSize;
123     
124     TRACE("(%p, 0x%08x, %p, %p, %p, %ld, %ld, %ld)\n",
125           phas, had, pwfxSrc, pwfxDst, pwfltr, dwCallback, dwInstance, fdwOpen);
126
127     TRACE("src [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n", 
128           pwfxSrc->wFormatTag, pwfxSrc->nChannels, pwfxSrc->nSamplesPerSec, pwfxSrc->nAvgBytesPerSec, 
129           pwfxSrc->nBlockAlign, pwfxSrc->wBitsPerSample, pwfxSrc->cbSize);
130
131     TRACE("dst [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n", 
132           pwfxDst->wFormatTag, pwfxDst->nChannels, pwfxDst->nSamplesPerSec, pwfxDst->nAvgBytesPerSec, 
133           pwfxDst->nBlockAlign, pwfxDst->wBitsPerSample, pwfxDst->cbSize);
134
135     if ((fdwOpen & ACM_STREAMOPENF_QUERY) && phas) return MMSYSERR_INVALPARAM;
136     if (pwfltr && (pwfxSrc->wFormatTag != pwfxDst->wFormatTag)) return MMSYSERR_INVALPARAM;
137
138     wfxSrcSize = wfxDstSize = sizeof(WAVEFORMATEX);
139     if (pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) wfxSrcSize += pwfxSrc->cbSize;
140     if (pwfxDst->wFormatTag != WAVE_FORMAT_PCM) wfxDstSize += pwfxDst->cbSize;
141
142     was = HeapAlloc(MSACM_hHeap, 0, sizeof(*was) + wfxSrcSize + wfxDstSize + ((pwfltr) ? sizeof(WAVEFILTER) : 0));
143     if (was == NULL)
144         return MMSYSERR_NOMEM;
145     
146     was->drvInst.cbStruct = sizeof(was->drvInst);
147     was->drvInst.pwfxSrc = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was));
148     memcpy(was->drvInst.pwfxSrc, pwfxSrc, wfxSrcSize);
149     was->drvInst.pwfxDst = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was) + wfxSrcSize);
150     memcpy(was->drvInst.pwfxDst, pwfxDst, wfxDstSize);
151     if (pwfltr) {
152         was->drvInst.pwfltr = (PWAVEFILTER)((LPSTR)was + sizeof(*was) + wfxSrcSize + wfxDstSize);
153         memcpy(was->drvInst.pwfltr, pwfltr, sizeof(WAVEFILTER));
154     } else {
155         was->drvInst.pwfltr = NULL;
156     }
157     was->drvInst.dwCallback = dwCallback;    
158     was->drvInst.dwInstance = dwInstance;
159     was->drvInst.fdwOpen = fdwOpen;
160     was->drvInst.fdwDriver = 0L;  
161     was->drvInst.dwDriver = 0L;     
162     was->drvInst.has = (HACMSTREAM)was;
163     
164     if (had) {
165         if (!(wad = MSACM_GetDriver(had))) {
166             ret = MMSYSERR_INVALPARAM;
167             goto errCleanUp;
168         }
169         
170         was->obj.dwType = WINE_ACMOBJ_STREAM;
171         was->obj.pACMDriverID = wad->obj.pACMDriverID;
172         was->pDrv = wad;
173         was->hAcmDriver = 0; /* not to close it in acmStreamClose */
174
175         ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L);
176         if (ret != MMSYSERR_NOERROR)
177             goto errCleanUp;
178     } else {
179         PWINE_ACMDRIVERID wadi;
180         
181         ret = ACMERR_NOTPOSSIBLE;
182         for (wadi = MSACM_pFirstACMDriverID; wadi; wadi = wadi->pNextACMDriverID) {
183             ret = acmDriverOpen(&had, (HACMDRIVERID)wadi, 0L);
184             if (ret == MMSYSERR_NOERROR) {
185                 if ((wad = MSACM_GetDriver(had)) != 0) {
186                     was->obj.dwType = WINE_ACMOBJ_STREAM;
187                     was->obj.pACMDriverID = wad->obj.pACMDriverID;
188                     was->pDrv = wad;
189                     was->hAcmDriver = had;
190                     
191                     ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L);
192                     if (ret == MMSYSERR_NOERROR) {
193                         if (fdwOpen & ACM_STREAMOPENF_QUERY) {
194                             acmDriverClose(had, 0L);
195                         }
196                         break;
197                     }
198                 }
199                 /* no match, close this acm driver and try next one */
200                 acmDriverClose(had, 0L);
201             }
202         }
203         if (ret != MMSYSERR_NOERROR) {
204             ret = ACMERR_NOTPOSSIBLE;
205             goto errCleanUp;
206         }
207     }
208     ret = MMSYSERR_NOERROR;
209     if (!(fdwOpen & ACM_STREAMOPENF_QUERY)) {
210         if (phas)
211             *phas = (HACMSTREAM)was;
212         TRACE("=> (%d)\n", ret);
213         return ret;
214     }
215 errCleanUp:             
216     if (phas)
217         *phas = (HACMSTREAM)0;
218     HeapFree(MSACM_hHeap, 0, was);
219     TRACE("=> (%d)\n", ret);
220     return ret;
221 }
222
223
224 /***********************************************************************
225  *           acmStreamPrepareHeader (MSACM32.41)
226  */
227 MMRESULT WINAPI acmStreamPrepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash, 
228                                        DWORD fdwPrepare)
229 {
230     PWINE_ACMSTREAM     was;
231     MMRESULT            ret = MMSYSERR_NOERROR;
232     PACMDRVSTREAMHEADER padsh;
233
234     TRACE("(0x%08x, %p, %ld)\n", has, pash, fdwPrepare);
235     
236     if ((was = ACM_GetStream(has)) == NULL)
237         return MMSYSERR_INVALHANDLE;
238     if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER))
239         return MMSYSERR_INVALPARAM;
240     if (fdwPrepare)
241         ret = MMSYSERR_INVALFLAG;
242
243     if (pash->fdwStatus & ACMSTREAMHEADER_STATUSF_DONE)
244         return MMSYSERR_NOERROR;
245
246     /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
247      * size. some fields are private to msacm internals, and are exposed
248      * in ACMSTREAMHEADER in the dwReservedDriver array
249      */
250     padsh = (PACMDRVSTREAMHEADER)pash;
251
252     padsh->fdwConvert = fdwPrepare;
253     padsh->padshNext = NULL;
254     padsh->fdwDriver = padsh->dwDriver = 0L;
255
256     padsh->fdwPrepared = 0;
257     padsh->dwPrepared = 0;
258     padsh->pbPreparedSrc = 0;
259     padsh->cbPreparedSrcLength = 0;
260     padsh->pbPreparedDst = 0;
261     padsh->cbPreparedDstLength = 0;
262
263     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_PREPARE, (DWORD)&was->drvInst, (DWORD)padsh);
264     if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
265         ret = MMSYSERR_NOERROR;
266         padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE);
267         padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_PREPARED;
268         padsh->fdwPrepared = padsh->fdwStatus;
269         padsh->dwPrepared = 0;
270         padsh->pbPreparedSrc = padsh->pbSrc;
271         padsh->cbPreparedSrcLength = padsh->cbSrcLength;
272         padsh->pbPreparedDst = padsh->pbDst;
273         padsh->cbPreparedDstLength = padsh->cbDstLength;
274     } else {
275         padsh->fdwPrepared = 0;
276         padsh->dwPrepared = 0;
277         padsh->pbPreparedSrc = 0;
278         padsh->cbPreparedSrcLength = 0;
279         padsh->pbPreparedDst = 0;
280         padsh->cbPreparedDstLength = 0;
281     }
282     TRACE("=> (%d)\n", ret);
283     return ret;
284 }
285
286 /***********************************************************************
287  *           acmStreamReset (MSACM32.42)
288  */
289 MMRESULT WINAPI acmStreamReset(HACMSTREAM has, DWORD fdwReset)
290 {
291     PWINE_ACMSTREAM     was;
292     MMRESULT            ret = MMSYSERR_NOERROR;
293
294     TRACE("(0x%08x, %ld)\n", has, fdwReset);
295
296     if (fdwReset) {
297         ret = MMSYSERR_INVALFLAG;
298     } else if ((was = ACM_GetStream(has)) == NULL) {
299         return MMSYSERR_INVALHANDLE;
300     } else if (was->drvInst.fdwOpen & ACM_STREAMOPENF_ASYNC) {
301         ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_RESET, (DWORD)&was->drvInst, 0);
302     }
303     TRACE("=> (%d)\n", ret);
304     return ret;
305 }
306
307 /***********************************************************************
308  *           acmStreamSize (MSACM32.43)
309  */
310 MMRESULT WINAPI acmStreamSize(HACMSTREAM has, DWORD cbInput, 
311                               LPDWORD pdwOutputBytes, DWORD fdwSize)
312 {
313     PWINE_ACMSTREAM     was;
314     ACMDRVSTREAMSIZE    adss;
315     MMRESULT            ret;
316     
317     TRACE("(0x%08x, %ld, %p, %ld)\n", has, cbInput, pdwOutputBytes, fdwSize);
318     
319     if ((was = ACM_GetStream(has)) == NULL) {
320         return MMSYSERR_INVALHANDLE;
321     }
322     if ((fdwSize & ~ACM_STREAMSIZEF_QUERYMASK) != 0) {
323         return MMSYSERR_INVALFLAG;
324     }
325
326     *pdwOutputBytes = 0L;
327     
328     switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
329     case ACM_STREAMSIZEF_DESTINATION:
330         adss.cbDstLength = cbInput;
331         adss.cbSrcLength = 0;
332         break;
333     case ACM_STREAMSIZEF_SOURCE:
334         adss.cbSrcLength = cbInput;
335         adss.cbDstLength = 0;
336         break;
337     default:    
338         return MMSYSERR_INVALFLAG;
339     }
340     
341     adss.cbStruct = sizeof(adss);
342     adss.fdwSize = fdwSize;
343     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_SIZE, 
344                             (DWORD)&was->drvInst, (DWORD)&adss);
345     if (ret == MMSYSERR_NOERROR) {
346         switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
347         case ACM_STREAMSIZEF_DESTINATION:
348             *pdwOutputBytes = adss.cbSrcLength;
349             break;
350         case ACM_STREAMSIZEF_SOURCE:
351             *pdwOutputBytes = adss.cbDstLength;
352             break;
353         }
354     }
355     TRACE("=> (%d) [%lu]\n", ret, *pdwOutputBytes);
356     return ret;
357 }
358
359 /***********************************************************************
360  *           acmStreamUnprepareHeader (MSACM32.44)
361  */
362 MMRESULT WINAPI acmStreamUnprepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash, 
363                                          DWORD fdwUnprepare)
364 {
365     PWINE_ACMSTREAM     was;
366     MMRESULT            ret = MMSYSERR_NOERROR;
367     PACMDRVSTREAMHEADER padsh;
368
369     TRACE("(0x%08x, %p, %ld)\n", has, pash, fdwUnprepare);
370     
371     if ((was = ACM_GetStream(has)) == NULL)
372         return MMSYSERR_INVALHANDLE;
373     if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER))
374         return MMSYSERR_INVALPARAM;
375
376     if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED))
377         return ACMERR_UNPREPARED;
378
379     /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
380      * size. some fields are private to msacm internals, and are exposed
381      * in ACMSTREAMHEADER in the dwReservedDriver array
382      */
383     padsh = (PACMDRVSTREAMHEADER)pash;
384
385     /* check that pointers have not been modified */
386     if (padsh->pbPreparedSrc != padsh->pbSrc ||
387         padsh->cbPreparedSrcLength < padsh->cbSrcLength ||
388         padsh->pbPreparedDst != padsh->pbDst ||
389         padsh->cbPreparedDstLength < padsh->cbDstLength) {
390         return MMSYSERR_INVALPARAM;
391     }   
392
393     padsh->fdwConvert = fdwUnprepare;
394
395     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_UNPREPARE, (DWORD)&was->drvInst, (DWORD)padsh);
396     if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
397         ret = MMSYSERR_NOERROR;
398         padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE|ACMSTREAMHEADER_STATUSF_PREPARED);
399     }
400     TRACE("=> (%d)\n", ret);
401     return ret;
402 }