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