Fixed improper acm streams closing.
[wine] / dlls / msacm32 / 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 #define SIZEOF_WFX(wfx) (sizeof(WAVEFORMATEX) + ((wfx->wFormatTag == WAVE_FORMAT_PCM) ? 0 : wfx->cbSize))
136     wfxSrcSize = SIZEOF_WFX(pwfxSrc);
137     wfxDstSize = SIZEOF_WFX(pwfxDst);
138 #undef SIZEOF_WFX
139
140     was = HeapAlloc(MSACM_hHeap, 0, sizeof(*was) + wfxSrcSize + wfxDstSize + ((pwfltr) ? sizeof(WAVEFILTER) : 0));
141     if (was == NULL)
142         return MMSYSERR_NOMEM;
143     
144     was->drvInst.cbStruct = sizeof(was->drvInst);
145     was->drvInst.pwfxSrc = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was));
146     memcpy(was->drvInst.pwfxSrc, pwfxSrc, wfxSrcSize);
147     was->drvInst.pwfxDst = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was) + wfxSrcSize);
148     memcpy(was->drvInst.pwfxDst, pwfxDst, wfxDstSize);
149     if (pwfltr) {
150         was->drvInst.pwfltr = (PWAVEFILTER)((LPSTR)was + sizeof(*was) + wfxSrcSize + wfxDstSize);
151         memcpy(was->drvInst.pwfltr, pwfltr, sizeof(WAVEFILTER));
152     } else {
153         was->drvInst.pwfltr = NULL;
154     }
155     was->drvInst.dwCallback = dwCallback;    
156     was->drvInst.dwInstance = dwInstance;
157     was->drvInst.fdwOpen = fdwOpen;
158     was->drvInst.fdwDriver = 0L;  
159     was->drvInst.dwDriver = 0L;     
160     was->drvInst.has = (HACMSTREAM)was;
161     
162     if (had) {
163         if (!(wad = MSACM_GetDriver(had))) {
164             ret = MMSYSERR_INVALPARAM;
165             goto errCleanUp;
166         }
167         
168         was->obj.pACMDriverID = wad->obj.pACMDriverID;
169         was->pDrv = wad;
170         was->hAcmDriver = 0; /* not to close it in acmStreamClose */
171
172         ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L);
173         if (ret != MMSYSERR_NOERROR)
174             goto errCleanUp;
175     } else {
176         PWINE_ACMDRIVERID wadi;
177         
178         ret = ACMERR_NOTPOSSIBLE;
179         for (wadi = MSACM_pFirstACMDriverID; wadi; wadi = wadi->pNextACMDriverID) {
180             ret = acmDriverOpen(&had, (HACMDRIVERID)wadi, 0L);
181             if (ret == MMSYSERR_NOERROR) {
182                 if ((wad = MSACM_GetDriver(had)) != 0) {
183                     was->obj.pACMDriverID = wad->obj.pACMDriverID;
184                     was->pDrv = wad;
185                     was->hAcmDriver = had;
186                     
187                     ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L);
188                     /* FIXME: when shall the acmDriver be Close():d ? */
189                     if (ret == MMSYSERR_NOERROR)
190                         break;
191                 }
192                 /* no match, close this acm driver and try next one */
193                 acmDriverClose(had, 0L);
194             }
195         }
196         if (ret != MMSYSERR_NOERROR) {
197             ret = ACMERR_NOTPOSSIBLE;
198             goto errCleanUp;
199         }
200     }
201     if (phas)
202         *phas = (HACMSTREAM)was;
203     TRACE("=> (%d)\n", ret);
204     ret = MMSYSERR_NOERROR;
205     if (!(fdwOpen & ACM_STREAMOPENF_QUERY))
206         return ret;
207 errCleanUp:             
208     HeapFree(MSACM_hHeap, 0, was);
209     TRACE("=> (%d)\n", ret);
210     return ret;
211 }
212
213
214 /***********************************************************************
215  *           acmStreamPrepareHeader (MSACM32.41)
216  */
217 MMRESULT WINAPI acmStreamPrepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash, 
218                                        DWORD fdwPrepare)
219 {
220     PWINE_ACMSTREAM     was;
221     MMRESULT            ret = MMSYSERR_NOERROR;
222     PACMDRVSTREAMHEADER padsh;
223
224     TRACE("(0x%08x, %p, %ld)\n", has, pash, fdwPrepare);
225     
226     if ((was = ACM_GetStream(has)) == NULL)
227         return MMSYSERR_INVALHANDLE;
228     if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER))
229         return MMSYSERR_INVALPARAM;
230     if (fdwPrepare)
231         ret = MMSYSERR_INVALFLAG;
232
233     if (pash->fdwStatus & ACMSTREAMHEADER_STATUSF_DONE)
234         return MMSYSERR_NOERROR;
235
236     /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
237      * size. some fields are private to msacm internals, and are exposed
238      * in ACMSTREAMHEADER in the dwReservedDriver array
239      */
240     padsh = (PACMDRVSTREAMHEADER)pash;
241
242     padsh->fdwConvert = fdwPrepare;
243     padsh->padshNext = NULL;
244     padsh->fdwDriver = padsh->dwDriver = 0L;
245
246     padsh->fdwPrepared = 0;
247     padsh->dwPrepared = 0;
248     padsh->pbPreparedSrc = 0;
249     padsh->cbPreparedSrcLength = 0;
250     padsh->pbPreparedDst = 0;
251     padsh->cbPreparedDstLength = 0;
252
253     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_PREPARE, (DWORD)&was->drvInst, (DWORD)padsh);
254     if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
255         ret = MMSYSERR_NOERROR;
256         padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE);
257         padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_PREPARED;
258         padsh->fdwPrepared = padsh->fdwStatus;
259         padsh->dwPrepared = 0;
260         padsh->pbPreparedSrc = padsh->pbSrc;
261         padsh->cbPreparedSrcLength = padsh->cbSrcLength;
262         padsh->pbPreparedDst = padsh->pbDst;
263         padsh->cbPreparedDstLength = padsh->cbDstLength;
264     } else {
265         padsh->fdwPrepared = 0;
266         padsh->dwPrepared = 0;
267         padsh->pbPreparedSrc = 0;
268         padsh->cbPreparedSrcLength = 0;
269         padsh->pbPreparedDst = 0;
270         padsh->cbPreparedDstLength = 0;
271     }
272     TRACE("=> (%d)\n", ret);
273     return ret;
274 }
275
276 /***********************************************************************
277  *           acmStreamReset (MSACM32.42)
278  */
279 MMRESULT WINAPI acmStreamReset(HACMSTREAM has, DWORD fdwReset)
280 {
281     PWINE_ACMSTREAM     was;
282     MMRESULT            ret = MMSYSERR_NOERROR;
283
284     TRACE("(0x%08x, %ld)\n", has, fdwReset);
285
286     if (fdwReset) {
287         ret = MMSYSERR_INVALFLAG;
288     } else if ((was = ACM_GetStream(has)) == NULL) {
289         return MMSYSERR_INVALHANDLE;
290     } else if (was->drvInst.fdwOpen & ACM_STREAMOPENF_ASYNC) {
291         ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_RESET, (DWORD)&was->drvInst, 0);
292     }
293     TRACE("=> (%d)\n", ret);
294     return ret;
295 }
296
297 /***********************************************************************
298  *           acmStreamSize (MSACM32.43)
299  */
300 MMRESULT WINAPI acmStreamSize(HACMSTREAM has, DWORD cbInput, 
301                               LPDWORD pdwOutputBytes, DWORD fdwSize)
302 {
303     PWINE_ACMSTREAM     was;
304     ACMDRVSTREAMSIZE    adss;
305     MMRESULT            ret;
306     
307     TRACE("(0x%08x, %ld, %p, %ld)\n", has, cbInput, pdwOutputBytes, fdwSize);
308     
309     if ((was = ACM_GetStream(has)) == NULL) {
310         return MMSYSERR_INVALHANDLE;
311     }
312     if ((fdwSize & ~ACM_STREAMSIZEF_QUERYMASK) != 0) {
313         return MMSYSERR_INVALFLAG;
314     }
315
316     *pdwOutputBytes = 0L;
317     
318     switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
319     case ACM_STREAMSIZEF_DESTINATION:
320         adss.cbDstLength = cbInput;
321         adss.cbSrcLength = 0;
322         break;
323     case ACM_STREAMSIZEF_SOURCE:
324         adss.cbSrcLength = cbInput;
325         adss.cbDstLength = 0;
326         break;
327     default:    
328         return MMSYSERR_INVALFLAG;
329     }
330     
331     adss.cbStruct = sizeof(adss);
332     adss.fdwSize = fdwSize;
333     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_SIZE, 
334                             (DWORD)&was->drvInst, (DWORD)&adss);
335     if (ret == MMSYSERR_NOERROR) {
336         switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
337         case ACM_STREAMSIZEF_DESTINATION:
338             *pdwOutputBytes = adss.cbSrcLength;
339             break;
340         case ACM_STREAMSIZEF_SOURCE:
341             *pdwOutputBytes = adss.cbDstLength;
342             break;
343         }
344     }
345     TRACE("=> (%d) [%lu]\n", ret, *pdwOutputBytes);
346     return ret;
347 }
348
349 /***********************************************************************
350  *           acmStreamUnprepareHeader (MSACM32.44)
351  */
352 MMRESULT WINAPI acmStreamUnprepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash, 
353                                          DWORD fdwUnprepare)
354 {
355     PWINE_ACMSTREAM     was;
356     MMRESULT            ret = MMSYSERR_NOERROR;
357     PACMDRVSTREAMHEADER padsh;
358
359     TRACE("(0x%08x, %p, %ld)\n", has, pash, fdwUnprepare);
360     
361     if ((was = ACM_GetStream(has)) == NULL)
362         return MMSYSERR_INVALHANDLE;
363     if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER))
364         return MMSYSERR_INVALPARAM;
365
366     if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED))
367         return ACMERR_UNPREPARED;
368
369     /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
370      * size. some fields are private to msacm internals, and are exposed
371      * in ACMSTREAMHEADER in the dwReservedDriver array
372      */
373     padsh = (PACMDRVSTREAMHEADER)pash;
374
375     /* check that pointers have not been modified */
376     if (padsh->pbPreparedSrc != padsh->pbSrc ||
377         padsh->cbPreparedSrcLength < padsh->cbSrcLength ||
378         padsh->pbPreparedDst != padsh->pbDst ||
379         padsh->cbPreparedDstLength < padsh->cbDstLength) {
380         return MMSYSERR_INVALPARAM;
381     }   
382
383     padsh->fdwConvert = fdwUnprepare;
384
385     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_UNPREPARE, (DWORD)&was->drvInst, (DWORD)padsh);
386     if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
387         ret = MMSYSERR_NOERROR;
388         padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE|ACMSTREAMHEADER_STATUSF_PREPARED);
389     }
390     TRACE("=> (%d)\n", ret);
391     return ret;
392 }