Use a struct as specified in the VBE 3.0 standard for the
[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  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 /* TODO
25  *      + asynchronous conversion is not implemented
26  *      + callback/notification
27  *      * acmStreamMessage
28  *      + properly close ACM streams
29  */
30
31 #include <stdarg.h>
32 #include <string.h>
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "wine/debug.h"
37 #include "mmsystem.h"
38 #include "mmreg.h"
39 #include "msacm.h"
40 #include "msacmdrv.h"
41 #include "wineacm.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(msacm);
44
45 static PWINE_ACMSTREAM  ACM_GetStream(HACMSTREAM has)
46 {
47     return (PWINE_ACMSTREAM)has;
48 }
49
50 /***********************************************************************
51  *           acmStreamClose (MSACM32.@)
52  */
53 MMRESULT WINAPI acmStreamClose(HACMSTREAM has, DWORD fdwClose)
54 {
55     PWINE_ACMSTREAM     was;
56     MMRESULT            ret;
57
58     TRACE("(%p, %ld)\n", has, fdwClose);
59
60     if ((was = ACM_GetStream(has)) == NULL) {
61         return MMSYSERR_INVALHANDLE;
62     }
63     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CLOSE, (DWORD)&was->drvInst, 0);
64     if (ret == MMSYSERR_NOERROR) {
65         if (was->hAcmDriver)
66             acmDriverClose(was->hAcmDriver, 0L);
67         HeapFree(MSACM_hHeap, 0, was);
68     }
69     TRACE("=> (%d)\n", ret);
70     return ret;
71 }
72
73 /***********************************************************************
74  *           acmStreamConvert (MSACM32.@)
75  */
76 MMRESULT WINAPI acmStreamConvert(HACMSTREAM has, PACMSTREAMHEADER pash,
77                                  DWORD fdwConvert)
78 {
79     PWINE_ACMSTREAM     was;
80     MMRESULT            ret = MMSYSERR_NOERROR;
81     PACMDRVSTREAMHEADER padsh;
82
83     TRACE("(%p, %p, %ld)\n", has, pash, fdwConvert);
84
85     if ((was = ACM_GetStream(has)) == NULL)
86         return MMSYSERR_INVALHANDLE;
87     if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER))
88         return MMSYSERR_INVALPARAM;
89
90     if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED))
91         return ACMERR_UNPREPARED;
92
93     /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
94      * size. some fields are private to msacm internals, and are exposed
95      * in ACMSTREAMHEADER in the dwReservedDriver array
96      */
97     padsh = (PACMDRVSTREAMHEADER)pash;
98
99     /* check that pointers have not been modified */
100     if (padsh->pbPreparedSrc != padsh->pbSrc ||
101         padsh->cbPreparedSrcLength < padsh->cbSrcLength ||
102         padsh->pbPreparedDst != padsh->pbDst ||
103         padsh->cbPreparedDstLength < padsh->cbDstLength) {
104         return MMSYSERR_INVALPARAM;
105     }
106
107     padsh->fdwConvert = fdwConvert;
108
109     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CONVERT, (DWORD)&was->drvInst, (DWORD)padsh);
110     if (ret == MMSYSERR_NOERROR) {
111         padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_DONE;
112     }
113     TRACE("=> (%d)\n", ret);
114     return ret;
115 }
116
117 /***********************************************************************
118  *           acmStreamMessage (MSACM32.@)
119  */
120 MMRESULT WINAPI acmStreamMessage(HACMSTREAM has, UINT uMsg, LPARAM lParam1,
121                                  LPARAM lParam2)
122 {
123     FIXME("(%p, %u, %ld, %ld): stub\n", has, uMsg, lParam1, lParam2);
124     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
125     return MMSYSERR_ERROR;
126 }
127
128 /***********************************************************************
129  *           acmStreamOpen (MSACM32.@)
130  */
131 MMRESULT WINAPI acmStreamOpen(PHACMSTREAM phas, HACMDRIVER had, PWAVEFORMATEX pwfxSrc,
132                               PWAVEFORMATEX pwfxDst, PWAVEFILTER pwfltr, DWORD dwCallback,
133                               DWORD dwInstance, DWORD fdwOpen)
134 {
135     PWINE_ACMSTREAM     was;
136     PWINE_ACMDRIVER     wad;
137     MMRESULT            ret;
138     int                 wfxSrcSize;
139     int                 wfxDstSize;
140
141     TRACE("(%p, %p, %p, %p, %p, %ld, %ld, %ld)\n",
142           phas, had, pwfxSrc, pwfxDst, pwfltr, dwCallback, dwInstance, fdwOpen);
143
144     TRACE("src [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
145           pwfxSrc->wFormatTag, pwfxSrc->nChannels, pwfxSrc->nSamplesPerSec, pwfxSrc->nAvgBytesPerSec,
146           pwfxSrc->nBlockAlign, pwfxSrc->wBitsPerSample, pwfxSrc->cbSize);
147
148     TRACE("dst [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
149           pwfxDst->wFormatTag, pwfxDst->nChannels, pwfxDst->nSamplesPerSec, pwfxDst->nAvgBytesPerSec,
150           pwfxDst->nBlockAlign, pwfxDst->wBitsPerSample, pwfxDst->cbSize);
151
152     /* (WS) In query mode, phas should be NULL. If it is not, then instead
153      * of returning an error we are making sure it is NULL, preventing some
154      * applications that pass garbage for phas from crashing.
155      */
156     if (fdwOpen & ACM_STREAMOPENF_QUERY) phas = NULL;
157
158     if (pwfltr && (pwfxSrc->wFormatTag != pwfxDst->wFormatTag)) return MMSYSERR_INVALPARAM;
159
160     wfxSrcSize = wfxDstSize = sizeof(WAVEFORMATEX);
161     if (pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) wfxSrcSize += pwfxSrc->cbSize;
162     if (pwfxDst->wFormatTag != WAVE_FORMAT_PCM) wfxDstSize += pwfxDst->cbSize;
163
164     was = HeapAlloc(MSACM_hHeap, 0, sizeof(*was) + wfxSrcSize + wfxDstSize +
165                     ((pwfltr) ? sizeof(WAVEFILTER) : 0));
166     if (was == NULL)
167         return MMSYSERR_NOMEM;
168
169     was->drvInst.cbStruct = sizeof(was->drvInst);
170     was->drvInst.pwfxSrc = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was));
171     memcpy(was->drvInst.pwfxSrc, pwfxSrc, wfxSrcSize);
172     was->drvInst.pwfxDst = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was) + wfxSrcSize);
173     memcpy(was->drvInst.pwfxDst, pwfxDst, wfxDstSize);
174     if (pwfltr) {
175         was->drvInst.pwfltr = (PWAVEFILTER)((LPSTR)was + sizeof(*was) + wfxSrcSize + wfxDstSize);
176         memcpy(was->drvInst.pwfltr, pwfltr, sizeof(WAVEFILTER));
177     } else {
178         was->drvInst.pwfltr = NULL;
179     }
180     was->drvInst.dwCallback = dwCallback;
181     was->drvInst.dwInstance = dwInstance;
182     was->drvInst.fdwOpen = fdwOpen;
183     was->drvInst.fdwDriver = 0L;
184     was->drvInst.dwDriver = 0L;
185     /* real value will be stored once ACMDM_STREAM_OPEN succeeds */
186     was->drvInst.has = 0L;
187
188     if (had) {
189         if (!(wad = MSACM_GetDriver(had))) {
190             ret = MMSYSERR_INVALPARAM;
191             goto errCleanUp;
192         }
193
194         was->obj.dwType = WINE_ACMOBJ_STREAM;
195         was->obj.pACMDriverID = wad->obj.pACMDriverID;
196         was->pDrv = wad;
197         was->hAcmDriver = 0; /* not to close it in acmStreamClose */
198
199         ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L);
200         if (ret != MMSYSERR_NOERROR)
201             goto errCleanUp;
202     } else {
203         PWINE_ACMDRIVERID wadi;
204
205         ret = ACMERR_NOTPOSSIBLE;
206         for (wadi = MSACM_pFirstACMDriverID; wadi; wadi = wadi->pNextACMDriverID) {
207             if ((wadi->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
208                 !MSACM_FindFormatTagInCache(wadi, pwfxSrc->wFormatTag, NULL) ||
209                 !MSACM_FindFormatTagInCache(wadi, pwfxDst->wFormatTag, NULL))
210                 continue;
211             ret = acmDriverOpen(&had, (HACMDRIVERID)wadi, 0L);
212             if (ret != MMSYSERR_NOERROR)
213                 continue;
214             if ((wad = MSACM_GetDriver(had)) != 0) {
215                 was->obj.dwType = WINE_ACMOBJ_STREAM;
216                 was->obj.pACMDriverID = wad->obj.pACMDriverID;
217                 was->pDrv = wad;
218                 was->hAcmDriver = had;
219
220                 ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L);
221                 TRACE("%s => %08x\n", debugstr_w(wadi->pszDriverAlias), ret);
222                 if (ret == MMSYSERR_NOERROR) {
223                     if (fdwOpen & ACM_STREAMOPENF_QUERY) {
224                         acmDriverClose(had, 0L);
225                     }
226                     break;
227                 }
228             }
229             /* no match, close this acm driver and try next one */
230             acmDriverClose(had, 0L);
231         }
232         if (ret != MMSYSERR_NOERROR) {
233             ret = ACMERR_NOTPOSSIBLE;
234             goto errCleanUp;
235         }
236     }
237     ret = MMSYSERR_NOERROR;
238     was->drvInst.has = (HACMSTREAM)was;
239     if (!(fdwOpen & ACM_STREAMOPENF_QUERY)) {
240         if (phas)
241             *phas = (HACMSTREAM)was;
242         TRACE("=> (%d)\n", ret);
243         return ret;
244     }
245 errCleanUp:
246     if (phas)
247         *phas = NULL;
248     HeapFree(MSACM_hHeap, 0, was);
249     TRACE("=> (%d)\n", ret);
250     return ret;
251 }
252
253
254 /***********************************************************************
255  *           acmStreamPrepareHeader (MSACM32.@)
256  */
257 MMRESULT WINAPI acmStreamPrepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash,
258                                        DWORD fdwPrepare)
259 {
260     PWINE_ACMSTREAM     was;
261     MMRESULT            ret = MMSYSERR_NOERROR;
262     PACMDRVSTREAMHEADER padsh;
263
264     TRACE("(%p, %p, %ld)\n", has, pash, fdwPrepare);
265
266     if ((was = ACM_GetStream(has)) == NULL)
267         return MMSYSERR_INVALHANDLE;
268     if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER))
269         return MMSYSERR_INVALPARAM;
270     if (fdwPrepare)
271         ret = MMSYSERR_INVALFLAG;
272
273     if (pash->fdwStatus & ACMSTREAMHEADER_STATUSF_DONE)
274         return MMSYSERR_NOERROR;
275
276     /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
277      * size. some fields are private to msacm internals, and are exposed
278      * in ACMSTREAMHEADER in the dwReservedDriver array
279      */
280     padsh = (PACMDRVSTREAMHEADER)pash;
281
282     padsh->fdwConvert = fdwPrepare;
283     padsh->padshNext = NULL;
284     padsh->fdwDriver = padsh->dwDriver = 0L;
285
286     padsh->fdwPrepared = 0;
287     padsh->dwPrepared = 0;
288     padsh->pbPreparedSrc = 0;
289     padsh->cbPreparedSrcLength = 0;
290     padsh->pbPreparedDst = 0;
291     padsh->cbPreparedDstLength = 0;
292
293     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_PREPARE, (DWORD)&was->drvInst, (DWORD)padsh);
294     if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
295         ret = MMSYSERR_NOERROR;
296         padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE);
297         padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_PREPARED;
298         padsh->fdwPrepared = padsh->fdwStatus;
299         padsh->dwPrepared = 0;
300         padsh->pbPreparedSrc = padsh->pbSrc;
301         padsh->cbPreparedSrcLength = padsh->cbSrcLength;
302         padsh->pbPreparedDst = padsh->pbDst;
303         padsh->cbPreparedDstLength = padsh->cbDstLength;
304     } else {
305         padsh->fdwPrepared = 0;
306         padsh->dwPrepared = 0;
307         padsh->pbPreparedSrc = 0;
308         padsh->cbPreparedSrcLength = 0;
309         padsh->pbPreparedDst = 0;
310         padsh->cbPreparedDstLength = 0;
311     }
312     TRACE("=> (%d)\n", ret);
313     return ret;
314 }
315
316 /***********************************************************************
317  *           acmStreamReset (MSACM32.@)
318  */
319 MMRESULT WINAPI acmStreamReset(HACMSTREAM has, DWORD fdwReset)
320 {
321     PWINE_ACMSTREAM     was;
322     MMRESULT            ret = MMSYSERR_NOERROR;
323
324     TRACE("(%p, %ld)\n", has, fdwReset);
325
326     if (fdwReset) {
327         ret = MMSYSERR_INVALFLAG;
328     } else if ((was = ACM_GetStream(has)) == NULL) {
329         return MMSYSERR_INVALHANDLE;
330     } else if (was->drvInst.fdwOpen & ACM_STREAMOPENF_ASYNC) {
331         ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_RESET, (DWORD)&was->drvInst, 0);
332     }
333     TRACE("=> (%d)\n", ret);
334     return ret;
335 }
336
337 /***********************************************************************
338  *           acmStreamSize (MSACM32.@)
339  */
340 MMRESULT WINAPI acmStreamSize(HACMSTREAM has, DWORD cbInput,
341                               LPDWORD pdwOutputBytes, DWORD fdwSize)
342 {
343     PWINE_ACMSTREAM     was;
344     ACMDRVSTREAMSIZE    adss;
345     MMRESULT            ret;
346
347     TRACE("(%p, %ld, %p, %ld)\n", has, cbInput, pdwOutputBytes, fdwSize);
348
349     if ((was = ACM_GetStream(has)) == NULL) {
350         return MMSYSERR_INVALHANDLE;
351     }
352     if ((fdwSize & ~ACM_STREAMSIZEF_QUERYMASK) != 0) {
353         return MMSYSERR_INVALFLAG;
354     }
355
356     *pdwOutputBytes = 0L;
357
358     switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
359     case ACM_STREAMSIZEF_DESTINATION:
360         adss.cbDstLength = cbInput;
361         adss.cbSrcLength = 0;
362         break;
363     case ACM_STREAMSIZEF_SOURCE:
364         adss.cbSrcLength = cbInput;
365         adss.cbDstLength = 0;
366         break;
367     default:
368         return MMSYSERR_INVALFLAG;
369     }
370
371     adss.cbStruct = sizeof(adss);
372     adss.fdwSize = fdwSize;
373     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_SIZE,
374                             (DWORD)&was->drvInst, (DWORD)&adss);
375     if (ret == MMSYSERR_NOERROR) {
376         switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
377         case ACM_STREAMSIZEF_DESTINATION:
378             *pdwOutputBytes = adss.cbSrcLength;
379             break;
380         case ACM_STREAMSIZEF_SOURCE:
381             *pdwOutputBytes = adss.cbDstLength;
382             break;
383         }
384     }
385     TRACE("=> (%d) [%lu]\n", ret, *pdwOutputBytes);
386     return ret;
387 }
388
389 /***********************************************************************
390  *           acmStreamUnprepareHeader (MSACM32.@)
391  */
392 MMRESULT WINAPI acmStreamUnprepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash,
393                                          DWORD fdwUnprepare)
394 {
395     PWINE_ACMSTREAM     was;
396     MMRESULT            ret = MMSYSERR_NOERROR;
397     PACMDRVSTREAMHEADER padsh;
398
399     TRACE("(%p, %p, %ld)\n", has, pash, fdwUnprepare);
400
401     if ((was = ACM_GetStream(has)) == NULL)
402         return MMSYSERR_INVALHANDLE;
403     if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER))
404         return MMSYSERR_INVALPARAM;
405
406     if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED))
407         return ACMERR_UNPREPARED;
408
409     /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
410      * size. some fields are private to msacm internals, and are exposed
411      * in ACMSTREAMHEADER in the dwReservedDriver array
412      */
413     padsh = (PACMDRVSTREAMHEADER)pash;
414
415     /* check that pointers have not been modified */
416     if (padsh->pbPreparedSrc != padsh->pbSrc ||
417         padsh->cbPreparedSrcLength < padsh->cbSrcLength ||
418         padsh->pbPreparedDst != padsh->pbDst ||
419         padsh->cbPreparedDstLength < padsh->cbDstLength) {
420         return MMSYSERR_INVALPARAM;
421     }
422
423     padsh->fdwConvert = fdwUnprepare;
424
425     ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_UNPREPARE, (DWORD)&was->drvInst, (DWORD)padsh);
426     if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
427         ret = MMSYSERR_NOERROR;
428         padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE|ACMSTREAMHEADER_STATUSF_PREPARED);
429     }
430     TRACE("=> (%d)\n", ret);
431     return ret;
432 }