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