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