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