Sync with trunk r58033.
[reactos.git] / dll / win32 / 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 acmDriverClose(had, 0L);
261 }
262 break;
263 }
264 }
265 /* no match, close this acm driver and try next one */
266 acmDriverClose(had, 0L);
267 }
268 if (ret != MMSYSERR_NOERROR) {
269 ret = ACMERR_NOTPOSSIBLE;
270 goto errCleanUp;
271 }
272 }
273 ret = MMSYSERR_NOERROR;
274 was->drvInst.has = (HACMSTREAM)was;
275 if (!(fdwOpen & ACM_STREAMOPENF_QUERY)) {
276 if (phas)
277 *phas = (HACMSTREAM)was;
278 TRACE("=> (%d)\n", ret);
279 return ret;
280 }
281 errCleanUp:
282 if (phas)
283 *phas = NULL;
284 HeapFree(MSACM_hHeap, 0, was);
285 TRACE("=> (%d)\n", ret);
286 return ret;
287 }
288
289
290 /***********************************************************************
291 * acmStreamPrepareHeader (MSACM32.@)
292 */
293 MMRESULT WINAPI acmStreamPrepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash,
294 DWORD fdwPrepare)
295 {
296 PWINE_ACMSTREAM was;
297 MMRESULT ret = MMSYSERR_NOERROR;
298 PACMDRVSTREAMHEADER padsh;
299
300 TRACE("(%p, %p, %d)\n", has, pash, fdwPrepare);
301
302 if ((was = ACM_GetStream(has)) == NULL) {
303 WARN("invalid handle\n");
304 return MMSYSERR_INVALHANDLE;
305 }
306 if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) {
307 WARN("invalid parameter\n");
308 return MMSYSERR_INVALPARAM;
309 }
310 if (fdwPrepare)
311 ret = MMSYSERR_INVALFLAG;
312
313 if (pash->fdwStatus & ACMSTREAMHEADER_STATUSF_DONE)
314 return MMSYSERR_NOERROR;
315
316 /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
317 * size. some fields are private to msacm internals, and are exposed
318 * in ACMSTREAMHEADER in the dwReservedDriver array
319 */
320 padsh = (PACMDRVSTREAMHEADER)pash;
321
322 padsh->fdwConvert = fdwPrepare;
323 padsh->padshNext = NULL;
324 padsh->fdwDriver = padsh->dwDriver = 0L;
325
326 padsh->fdwPrepared = 0;
327 padsh->dwPrepared = 0;
328 padsh->pbPreparedSrc = 0;
329 padsh->cbPreparedSrcLength = 0;
330 padsh->pbPreparedDst = 0;
331 padsh->cbPreparedDstLength = 0;
332
333 ret = MSACM_Message((HACMDRIVER)was->pDrv, ACMDM_STREAM_PREPARE, (LPARAM)&was->drvInst, (LPARAM)padsh);
334 if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
335 ret = MMSYSERR_NOERROR;
336 padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE);
337 padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_PREPARED;
338 padsh->fdwPrepared = padsh->fdwStatus;
339 padsh->dwPrepared = 0;
340 padsh->pbPreparedSrc = padsh->pbSrc;
341 padsh->cbPreparedSrcLength = padsh->cbSrcLength;
342 padsh->pbPreparedDst = padsh->pbDst;
343 padsh->cbPreparedDstLength = padsh->cbDstLength;
344 } else {
345 padsh->fdwPrepared = 0;
346 padsh->dwPrepared = 0;
347 padsh->pbPreparedSrc = 0;
348 padsh->cbPreparedSrcLength = 0;
349 padsh->pbPreparedDst = 0;
350 padsh->cbPreparedDstLength = 0;
351 }
352 TRACE("=> (%d)\n", ret);
353 return ret;
354 }
355
356 /***********************************************************************
357 * acmStreamReset (MSACM32.@)
358 */
359 MMRESULT WINAPI acmStreamReset(HACMSTREAM has, DWORD fdwReset)
360 {
361 PWINE_ACMSTREAM was;
362 MMRESULT ret = MMSYSERR_NOERROR;
363
364 TRACE("(%p, %d)\n", has, fdwReset);
365
366 if (fdwReset) {
367 WARN("invalid flag\n");
368 ret = MMSYSERR_INVALFLAG;
369 } else if ((was = ACM_GetStream(has)) == NULL) {
370 WARN("invalid handle\n");
371 return MMSYSERR_INVALHANDLE;
372 } else if (was->drvInst.fdwOpen & ACM_STREAMOPENF_ASYNC) {
373 ret = MSACM_Message((HACMDRIVER)was->pDrv, ACMDM_STREAM_RESET, (LPARAM)&was->drvInst, 0);
374 }
375 TRACE("=> (%d)\n", ret);
376 return ret;
377 }
378
379 /***********************************************************************
380 * acmStreamSize (MSACM32.@)
381 */
382 MMRESULT WINAPI acmStreamSize(HACMSTREAM has, DWORD cbInput,
383 LPDWORD pdwOutputBytes, DWORD fdwSize)
384 {
385 PWINE_ACMSTREAM was;
386 ACMDRVSTREAMSIZE adss;
387 MMRESULT ret;
388
389 TRACE("(%p, %d, %p, %d)\n", has, cbInput, pdwOutputBytes, fdwSize);
390
391 if ((was = ACM_GetStream(has)) == NULL) {
392 WARN("invalid handle\n");
393 return MMSYSERR_INVALHANDLE;
394 }
395 if ((fdwSize & ~ACM_STREAMSIZEF_QUERYMASK) != 0) {
396 WARN("invalid flag\n");
397 return MMSYSERR_INVALFLAG;
398 }
399
400 *pdwOutputBytes = 0L;
401
402 switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
403 case ACM_STREAMSIZEF_DESTINATION:
404 adss.cbDstLength = cbInput;
405 adss.cbSrcLength = 0;
406 break;
407 case ACM_STREAMSIZEF_SOURCE:
408 adss.cbSrcLength = cbInput;
409 adss.cbDstLength = 0;
410 break;
411 default:
412 WARN("invalid flag\n");
413 return MMSYSERR_INVALFLAG;
414 }
415
416 adss.cbStruct = sizeof(adss);
417 adss.fdwSize = fdwSize;
418 ret = MSACM_Message((HACMDRIVER)was->pDrv, ACMDM_STREAM_SIZE,
419 (LPARAM)&was->drvInst, (LPARAM)&adss);
420 if (ret == MMSYSERR_NOERROR) {
421 switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
422 case ACM_STREAMSIZEF_DESTINATION:
423 *pdwOutputBytes = adss.cbSrcLength;
424 break;
425 case ACM_STREAMSIZEF_SOURCE:
426 *pdwOutputBytes = adss.cbDstLength;
427 break;
428 }
429 }
430 TRACE("=> (%d) [%u]\n", ret, *pdwOutputBytes);
431 return ret;
432 }
433
434 /***********************************************************************
435 * acmStreamUnprepareHeader (MSACM32.@)
436 */
437 MMRESULT WINAPI acmStreamUnprepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash,
438 DWORD fdwUnprepare)
439 {
440 PWINE_ACMSTREAM was;
441 MMRESULT ret = MMSYSERR_NOERROR;
442 PACMDRVSTREAMHEADER padsh;
443
444 TRACE("(%p, %p, %d)\n", has, pash, fdwUnprepare);
445
446 if ((was = ACM_GetStream(has)) == NULL) {
447 WARN("invalid handle\n");
448 return MMSYSERR_INVALHANDLE;
449 }
450 if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) {
451 WARN("invalid parameter\n");
452 return MMSYSERR_INVALPARAM;
453 }
454 if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)) {
455 WARN("unprepared header\n");
456 return ACMERR_UNPREPARED;
457 }
458
459 /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
460 * size. some fields are private to msacm internals, and are exposed
461 * in ACMSTREAMHEADER in the dwReservedDriver array
462 */
463 padsh = (PACMDRVSTREAMHEADER)pash;
464
465 /* check that pointers have not been modified */
466 if (padsh->pbPreparedSrc != padsh->pbSrc ||
467 padsh->cbPreparedSrcLength < padsh->cbSrcLength ||
468 padsh->pbPreparedDst != padsh->pbDst ||
469 padsh->cbPreparedDstLength < padsh->cbDstLength) {
470 WARN("invalid parameter\n");
471 return MMSYSERR_INVALPARAM;
472 }
473
474 padsh->fdwConvert = fdwUnprepare;
475
476 ret = MSACM_Message((HACMDRIVER)was->pDrv, ACMDM_STREAM_UNPREPARE, (LPARAM)&was->drvInst, (LPARAM)padsh);
477 if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
478 ret = MMSYSERR_NOERROR;
479 padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE|ACMSTREAMHEADER_STATUSF_PREPARED);
480 }
481 TRACE("=> (%d)\n", ret);
482 return ret;
483 }