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