1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
6 * Copyright 1998 Patrik Stridvall
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.
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.
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
25 * + asynchronous conversion is not implemented
26 * + callback/notification
28 * + properly close ACM streams
36 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(msacm
);
46 static PWINE_ACMSTREAM
ACM_GetStream(HACMSTREAM has
)
50 return (PWINE_ACMSTREAM
)has
;
53 static BOOL
ACM_ValidatePointers(PACMDRVSTREAMHEADER padsh
)
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
);
62 /***********************************************************************
63 * acmStreamClose (MSACM32.@)
65 MMRESULT WINAPI
acmStreamClose(HACMSTREAM has
, DWORD fdwClose
)
70 TRACE("(%p, %ld)\n", has
, fdwClose
);
72 if ((was
= ACM_GetStream(has
)) == NULL
) {
73 WARN("invalid handle\n");
74 return MMSYSERR_INVALHANDLE
;
76 ret
= MSACM_Message((HACMDRIVER
)was
->pDrv
, ACMDM_STREAM_CLOSE
, (LPARAM
)&was
->drvInst
, 0);
77 if (ret
== MMSYSERR_NOERROR
) {
79 acmDriverClose(was
->hAcmDriver
, 0L);
80 HeapFree(MSACM_hHeap
, 0, was
);
82 TRACE("=> (%d)\n", ret
);
86 /***********************************************************************
87 * acmStreamConvert (MSACM32.@)
89 MMRESULT WINAPI
acmStreamConvert(HACMSTREAM has
, PACMSTREAMHEADER pash
,
93 MMRESULT ret
= MMSYSERR_NOERROR
;
94 PACMDRVSTREAMHEADER padsh
;
96 TRACE("(%p, %p, %ld)\n", has
, pash
, fdwConvert
);
98 if ((was
= ACM_GetStream(has
)) == NULL
) {
99 WARN("invalid handle\n");
100 return MMSYSERR_INVALHANDLE
;
102 if (!pash
|| pash
->cbStruct
< sizeof(ACMSTREAMHEADER
)) {
103 WARN("invalid parameter\n");
104 return MMSYSERR_INVALPARAM
;
106 if (!(pash
->fdwStatus
& ACMSTREAMHEADER_STATUSF_PREPARED
)) {
107 WARN("unprepared header\n");
108 return ACMERR_UNPREPARED
;
111 pash
->cbSrcLengthUsed
= 0;
112 pash
->cbDstLengthUsed
= 0;
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
118 padsh
= (PACMDRVSTREAMHEADER
)pash
;
120 if (!ACM_ValidatePointers(padsh
)) {
121 WARN("invalid parameter\n");
122 return MMSYSERR_INVALPARAM
;
125 padsh
->fdwConvert
= fdwConvert
;
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
;
131 TRACE("=> (%d)\n", ret
);
135 /***********************************************************************
136 * acmStreamMessage (MSACM32.@)
138 MMRESULT WINAPI
acmStreamMessage(HACMSTREAM has
, UINT uMsg
, LPARAM lParam1
,
141 FIXME("(%p, %u, %Id, %Id): stub\n", has
, uMsg
, lParam1
, lParam2
);
142 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
143 return MMSYSERR_ERROR
;
146 /***********************************************************************
147 * acmStreamOpen (MSACM32.@)
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
)
159 WAVEFORMATEX wfxSrc
, wfxDst
;
161 TRACE("(%p, %p, %p, %p, %p, %Id, %Id, %ld)\n",
162 phas
, had
, pwfxSrc
, pwfxDst
, pwfltr
, dwCallback
, dwInstance
, fdwOpen
);
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
;
173 if (pwfxDst
->wFormatTag
== WAVE_FORMAT_PCM
) {
174 memcpy(&wfxDst
, pwfxDst
, sizeof(PCMWAVEFORMAT
));
175 wfxDst
.wBitsPerSample
= pwfxDst
->wBitsPerSample
;
180 TRACE("src [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
181 pwfxSrc
->wFormatTag
, pwfxSrc
->nChannels
, pwfxSrc
->nSamplesPerSec
, pwfxSrc
->nAvgBytesPerSec
,
182 pwfxSrc
->nBlockAlign
, pwfxSrc
->wBitsPerSample
, pwfxSrc
->cbSize
);
184 TRACE("dst [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
185 pwfxDst
->wFormatTag
, pwfxDst
->nChannels
, pwfxDst
->nSamplesPerSec
, pwfxDst
->nAvgBytesPerSec
,
186 pwfxDst
->nBlockAlign
, pwfxDst
->wBitsPerSample
, pwfxDst
->cbSize
);
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.
192 if (fdwOpen
& ACM_STREAMOPENF_QUERY
) phas
= NULL
;
194 if (pwfltr
&& (pwfxSrc
->wFormatTag
!= pwfxDst
->wFormatTag
)) {
195 WARN("invalid parameter\n");
196 return MMSYSERR_INVALPARAM
;
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
;
203 was
= HeapAlloc(MSACM_hHeap
, 0, sizeof(*was
) + wfxSrcSize
+ wfxDstSize
+
204 ((pwfltr
) ? sizeof(WAVEFILTER
) : 0));
207 return MMSYSERR_NOMEM
;
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
);
216 was
->drvInst
.pwfltr
= (PWAVEFILTER
)((LPSTR
)was
+ sizeof(*was
) + wfxSrcSize
+ wfxDstSize
);
217 memcpy(was
->drvInst
.pwfltr
, pwfltr
, sizeof(WAVEFILTER
));
219 was
->drvInst
.pwfltr
= NULL
;
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;
230 if (!(wad
= MSACM_GetDriver(had
))) {
231 ret
= MMSYSERR_INVALPARAM
;
235 was
->obj
.dwType
= WINE_ACMOBJ_STREAM
;
236 was
->obj
.pACMDriverID
= wad
->obj
.pACMDriverID
;
238 was
->hAcmDriver
= 0; /* not to close it in acmStreamClose */
240 ret
= MSACM_Message((HACMDRIVER
)wad
, ACMDM_STREAM_OPEN
, (LPARAM
)&was
->drvInst
, 0L);
241 if (ret
!= MMSYSERR_NOERROR
)
244 PWINE_ACMDRIVERID wadi
;
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
))
252 ret
= acmDriverOpen(&had
, (HACMDRIVERID
)wadi
, 0L);
253 if (ret
!= MMSYSERR_NOERROR
)
255 if ((wad
= MSACM_GetDriver(had
)) != 0) {
256 was
->obj
.dwType
= WINE_ACMOBJ_STREAM
;
257 was
->obj
.pACMDriverID
= wad
->obj
.pACMDriverID
;
259 was
->hAcmDriver
= had
;
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);
271 /* no match, close this acm driver and try next one */
272 acmDriverClose(had
, 0L);
274 if (ret
!= MMSYSERR_NOERROR
) {
275 ret
= ACMERR_NOTPOSSIBLE
;
279 ret
= MMSYSERR_NOERROR
;
280 was
->drvInst
.has
= (HACMSTREAM
)was
;
281 if (!(fdwOpen
& ACM_STREAMOPENF_QUERY
)) {
283 *phas
= (HACMSTREAM
)was
;
284 TRACE("=> (%d)\n", ret
);
290 HeapFree(MSACM_hHeap
, 0, was
);
291 TRACE("=> (%d)\n", ret
);
296 /***********************************************************************
297 * acmStreamPrepareHeader (MSACM32.@)
299 MMRESULT WINAPI
acmStreamPrepareHeader(HACMSTREAM has
, PACMSTREAMHEADER pash
,
303 MMRESULT ret
= MMSYSERR_NOERROR
;
304 PACMDRVSTREAMHEADER padsh
;
306 TRACE("(%p, %p, %ld)\n", has
, pash
, fdwPrepare
);
308 if ((was
= ACM_GetStream(has
)) == NULL
) {
309 WARN("invalid handle\n");
310 return MMSYSERR_INVALHANDLE
;
312 if (!pash
|| pash
->cbStruct
< sizeof(ACMSTREAMHEADER
)) {
313 WARN("invalid parameter\n");
314 return MMSYSERR_INVALPARAM
;
317 WARN("invalid use of reserved parameter\n");
318 return MMSYSERR_INVALFLAG
;
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 (%ld < %d)\n",
324 pash
->cbSrcLength
, was
->drvInst
.pwfxSrc
->nBlockAlign
);
325 return pash
->cbSrcLength
? ACMERR_NOTPOSSIBLE
: MMSYSERR_INVALPARAM
;
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
332 padsh
= (PACMDRVSTREAMHEADER
)pash
;
334 padsh
->fdwConvert
= fdwPrepare
;
335 padsh
->padshNext
= NULL
;
336 padsh
->fdwDriver
= padsh
->dwDriver
= 0L;
338 padsh
->fdwPrepared
= 0;
339 padsh
->dwPrepared
= 0;
340 padsh
->pbPreparedSrc
= 0;
341 padsh
->cbPreparedSrcLength
= 0;
342 padsh
->pbPreparedDst
= 0;
343 padsh
->cbPreparedDstLength
= 0;
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
;
357 padsh
->fdwPrepared
= 0;
358 padsh
->dwPrepared
= 0;
359 padsh
->pbPreparedSrc
= 0;
360 padsh
->cbPreparedSrcLength
= 0;
361 padsh
->pbPreparedDst
= 0;
362 padsh
->cbPreparedDstLength
= 0;
364 TRACE("=> (%d)\n", ret
);
368 /***********************************************************************
369 * acmStreamReset (MSACM32.@)
371 MMRESULT WINAPI
acmStreamReset(HACMSTREAM has
, DWORD fdwReset
)
374 MMRESULT ret
= MMSYSERR_NOERROR
;
376 TRACE("(%p, %ld)\n", has
, 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);
387 TRACE("=> (%d)\n", ret
);
391 /***********************************************************************
392 * acmStreamSize (MSACM32.@)
394 MMRESULT WINAPI
acmStreamSize(HACMSTREAM has
, DWORD cbInput
,
395 LPDWORD pdwOutputBytes
, DWORD fdwSize
)
398 ACMDRVSTREAMSIZE adss
;
401 TRACE("(%p, %ld, %p, %ld)\n", has
, cbInput
, pdwOutputBytes
, fdwSize
);
403 if ((was
= ACM_GetStream(has
)) == NULL
) {
404 WARN("invalid handle\n");
405 return MMSYSERR_INVALHANDLE
;
407 if ((fdwSize
& ~ACM_STREAMSIZEF_QUERYMASK
) != 0) {
408 WARN("invalid flag\n");
409 return MMSYSERR_INVALFLAG
;
412 *pdwOutputBytes
= 0L;
414 switch (fdwSize
& ACM_STREAMSIZEF_QUERYMASK
) {
415 case ACM_STREAMSIZEF_DESTINATION
:
416 adss
.cbDstLength
= cbInput
;
417 adss
.cbSrcLength
= 0;
419 case ACM_STREAMSIZEF_SOURCE
:
420 adss
.cbSrcLength
= cbInput
;
421 adss
.cbDstLength
= 0;
424 WARN("invalid flag\n");
425 return MMSYSERR_INVALFLAG
;
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
;
437 case ACM_STREAMSIZEF_SOURCE
:
438 *pdwOutputBytes
= adss
.cbDstLength
;
442 TRACE("=> (%d) [%lu]\n", ret
, *pdwOutputBytes
);
446 /***********************************************************************
447 * acmStreamUnprepareHeader (MSACM32.@)
449 MMRESULT WINAPI
acmStreamUnprepareHeader(HACMSTREAM has
, PACMSTREAMHEADER pash
,
453 MMRESULT ret
= MMSYSERR_NOERROR
;
454 PACMDRVSTREAMHEADER padsh
;
456 TRACE("(%p, %p, %ld)\n", has
, pash
, fdwUnprepare
);
458 if ((was
= ACM_GetStream(has
)) == NULL
) {
459 WARN("invalid handle\n");
460 return MMSYSERR_INVALHANDLE
;
462 if (!pash
|| pash
->cbStruct
< sizeof(ACMSTREAMHEADER
)) {
463 WARN("invalid parameter\n");
464 return MMSYSERR_INVALPARAM
;
466 if (!(pash
->fdwStatus
& ACMSTREAMHEADER_STATUSF_PREPARED
)) {
467 WARN("unprepared header\n");
468 return ACMERR_UNPREPARED
;
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
475 padsh
= (PACMDRVSTREAMHEADER
)pash
;
477 if (!ACM_ValidatePointers(padsh
)) {
478 WARN("invalid parameter\n");
479 return MMSYSERR_INVALPARAM
;
482 padsh
->fdwConvert
= fdwUnprepare
;
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
);
489 TRACE("=> (%d)\n", ret
);