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