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