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