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