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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 /***********************************************************************
54 * acmStreamClose (MSACM32.@)
56 MMRESULT WINAPI acmStreamClose(HACMSTREAM has, DWORD fdwClose)
61 TRACE("(%p, %ld)\n", has, fdwClose);
63 if ((was = ACM_GetStream(has)) == NULL) {
64 WARN("invalid handle\n");
65 return MMSYSERR_INVALHANDLE;
67 ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CLOSE, (DWORD)&was->drvInst, 0);
68 if (ret == MMSYSERR_NOERROR) {
70 acmDriverClose(was->hAcmDriver, 0L);
71 HeapFree(MSACM_hHeap, 0, was);
73 TRACE("=> (%d)\n", ret);
77 /***********************************************************************
78 * acmStreamConvert (MSACM32.@)
80 MMRESULT WINAPI acmStreamConvert(HACMSTREAM has, PACMSTREAMHEADER pash,
84 MMRESULT ret = MMSYSERR_NOERROR;
85 PACMDRVSTREAMHEADER padsh;
87 TRACE("(%p, %p, %ld)\n", has, pash, fdwConvert);
89 if ((was = ACM_GetStream(has)) == NULL) {
90 WARN("invalid handle\n");
91 return MMSYSERR_INVALHANDLE;
93 if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) {
94 WARN("invalid parameter\n");
95 return MMSYSERR_INVALPARAM;
97 if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)) {
98 WARN("unprepared header\n");
99 return ACMERR_UNPREPARED;
102 /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
103 * size. some fields are private to msacm internals, and are exposed
104 * in ACMSTREAMHEADER in the dwReservedDriver array
106 padsh = (PACMDRVSTREAMHEADER)pash;
108 /* check that pointers have not been modified */
109 if (padsh->pbPreparedSrc != padsh->pbSrc ||
110 padsh->cbPreparedSrcLength < padsh->cbSrcLength ||
111 padsh->pbPreparedDst != padsh->pbDst ||
112 padsh->cbPreparedDstLength < padsh->cbDstLength) {
113 WARN("invalid parameter\n");
114 return MMSYSERR_INVALPARAM;
117 padsh->fdwConvert = fdwConvert;
119 ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CONVERT, (DWORD)&was->drvInst, (DWORD)padsh);
120 if (ret == MMSYSERR_NOERROR) {
121 padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_DONE;
123 TRACE("=> (%d)\n", ret);
127 /***********************************************************************
128 * acmStreamMessage (MSACM32.@)
130 MMRESULT WINAPI acmStreamMessage(HACMSTREAM has, UINT uMsg, LPARAM lParam1,
133 FIXME("(%p, %u, %ld, %ld): stub\n", has, uMsg, lParam1, lParam2);
134 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
135 return MMSYSERR_ERROR;
138 /***********************************************************************
139 * acmStreamOpen (MSACM32.@)
141 MMRESULT WINAPI acmStreamOpen(PHACMSTREAM phas, HACMDRIVER had, PWAVEFORMATEX pwfxSrc,
142 PWAVEFORMATEX pwfxDst, PWAVEFILTER pwfltr, DWORD dwCallback,
143 DWORD dwInstance, DWORD fdwOpen)
150 WAVEFORMATEX wfxSrc, wfxDst;
152 TRACE("(%p, %p, %p, %p, %p, %ld, %ld, %ld)\n",
153 phas, had, pwfxSrc, pwfxDst, pwfltr, dwCallback, dwInstance, fdwOpen);
155 /* NOTE: pwfxSrc and/or pwfxDst can point to a structure smaller than
156 * WAVEFORMATEX so don't use them directly when not sure */
157 if (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) {
158 memcpy(&wfxSrc, pwfxSrc, sizeof(PCMWAVEFORMAT));
159 wfxSrc.wBitsPerSample = pwfxSrc->wBitsPerSample;
164 if (pwfxDst->wFormatTag == WAVE_FORMAT_PCM) {
165 memcpy(&wfxDst, pwfxDst, sizeof(PCMWAVEFORMAT));
166 wfxDst.wBitsPerSample = pwfxDst->wBitsPerSample;
171 TRACE("src [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
172 pwfxSrc->wFormatTag, pwfxSrc->nChannels, pwfxSrc->nSamplesPerSec, pwfxSrc->nAvgBytesPerSec,
173 pwfxSrc->nBlockAlign, pwfxSrc->wBitsPerSample, pwfxSrc->cbSize);
175 TRACE("dst [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
176 pwfxDst->wFormatTag, pwfxDst->nChannels, pwfxDst->nSamplesPerSec, pwfxDst->nAvgBytesPerSec,
177 pwfxDst->nBlockAlign, pwfxDst->wBitsPerSample, pwfxDst->cbSize);
179 /* (WS) In query mode, phas should be NULL. If it is not, then instead
180 * of returning an error we are making sure it is NULL, preventing some
181 * applications that pass garbage for phas from crashing.
183 if (fdwOpen & ACM_STREAMOPENF_QUERY) phas = NULL;
185 if (pwfltr && (pwfxSrc->wFormatTag != pwfxDst->wFormatTag)) {
186 WARN("invalid parameter\n");
187 return MMSYSERR_INVALPARAM;
190 wfxSrcSize = wfxDstSize = sizeof(WAVEFORMATEX);
191 if (pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) wfxSrcSize += pwfxSrc->cbSize;
192 if (pwfxDst->wFormatTag != WAVE_FORMAT_PCM) wfxDstSize += pwfxDst->cbSize;
194 was = HeapAlloc(MSACM_hHeap, 0, sizeof(*was) + wfxSrcSize + wfxDstSize +
195 ((pwfltr) ? sizeof(WAVEFILTER) : 0));
198 return MMSYSERR_NOMEM;
201 was->drvInst.cbStruct = sizeof(was->drvInst);
202 was->drvInst.pwfxSrc = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was));
203 memcpy(was->drvInst.pwfxSrc, pwfxSrc, wfxSrcSize);
204 was->drvInst.pwfxDst = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was) + wfxSrcSize);
205 memcpy(was->drvInst.pwfxDst, pwfxDst, wfxDstSize);
207 was->drvInst.pwfltr = (PWAVEFILTER)((LPSTR)was + sizeof(*was) + wfxSrcSize + wfxDstSize);
208 memcpy(was->drvInst.pwfltr, pwfltr, sizeof(WAVEFILTER));
210 was->drvInst.pwfltr = NULL;
212 was->drvInst.dwCallback = dwCallback;
213 was->drvInst.dwInstance = dwInstance;
214 was->drvInst.fdwOpen = fdwOpen;
215 was->drvInst.fdwDriver = 0L;
216 was->drvInst.dwDriver = 0L;
217 /* real value will be stored once ACMDM_STREAM_OPEN succeeds */
218 was->drvInst.has = 0L;
221 if (!(wad = MSACM_GetDriver(had))) {
222 ret = MMSYSERR_INVALPARAM;
226 was->obj.dwType = WINE_ACMOBJ_STREAM;
227 was->obj.pACMDriverID = wad->obj.pACMDriverID;
229 was->hAcmDriver = 0; /* not to close it in acmStreamClose */
231 ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L);
232 if (ret != MMSYSERR_NOERROR)
235 PWINE_ACMDRIVERID wadi;
237 ret = ACMERR_NOTPOSSIBLE;
238 for (wadi = MSACM_pFirstACMDriverID; wadi; wadi = wadi->pNextACMDriverID) {
239 if ((wadi->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
240 !MSACM_FindFormatTagInCache(wadi, pwfxSrc->wFormatTag, NULL) ||
241 !MSACM_FindFormatTagInCache(wadi, pwfxDst->wFormatTag, NULL))
243 ret = acmDriverOpen(&had, (HACMDRIVERID)wadi, 0L);
244 if (ret != MMSYSERR_NOERROR)
246 if ((wad = MSACM_GetDriver(had)) != 0) {
247 was->obj.dwType = WINE_ACMOBJ_STREAM;
248 was->obj.pACMDriverID = wad->obj.pACMDriverID;
250 was->hAcmDriver = had;
252 ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L);
253 TRACE("%s => %08x\n", debugstr_w(wadi->pszDriverAlias), ret);
254 if (ret == MMSYSERR_NOERROR) {
255 if (fdwOpen & ACM_STREAMOPENF_QUERY) {
256 acmDriverClose(had, 0L);
261 /* no match, close this acm driver and try next one */
262 acmDriverClose(had, 0L);
264 if (ret != MMSYSERR_NOERROR) {
265 ret = ACMERR_NOTPOSSIBLE;
269 ret = MMSYSERR_NOERROR;
270 was->drvInst.has = (HACMSTREAM)was;
271 if (!(fdwOpen & ACM_STREAMOPENF_QUERY)) {
273 *phas = (HACMSTREAM)was;
274 TRACE("=> (%d)\n", ret);
280 HeapFree(MSACM_hHeap, 0, was);
281 TRACE("=> (%d)\n", ret);
286 /***********************************************************************
287 * acmStreamPrepareHeader (MSACM32.@)
289 MMRESULT WINAPI acmStreamPrepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash,
293 MMRESULT ret = MMSYSERR_NOERROR;
294 PACMDRVSTREAMHEADER padsh;
296 TRACE("(%p, %p, %ld)\n", has, pash, fdwPrepare);
298 if ((was = ACM_GetStream(has)) == NULL) {
299 WARN("invalid handle\n");
300 return MMSYSERR_INVALHANDLE;
302 if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) {
303 WARN("invalid parameter\n");
304 return MMSYSERR_INVALPARAM;
307 ret = MMSYSERR_INVALFLAG;
309 if (pash->fdwStatus & ACMSTREAMHEADER_STATUSF_DONE)
310 return MMSYSERR_NOERROR;
312 /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
313 * size. some fields are private to msacm internals, and are exposed
314 * in ACMSTREAMHEADER in the dwReservedDriver array
316 padsh = (PACMDRVSTREAMHEADER)pash;
318 padsh->fdwConvert = fdwPrepare;
319 padsh->padshNext = NULL;
320 padsh->fdwDriver = padsh->dwDriver = 0L;
322 padsh->fdwPrepared = 0;
323 padsh->dwPrepared = 0;
324 padsh->pbPreparedSrc = 0;
325 padsh->cbPreparedSrcLength = 0;
326 padsh->pbPreparedDst = 0;
327 padsh->cbPreparedDstLength = 0;
329 ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_PREPARE, (DWORD)&was->drvInst, (DWORD)padsh);
330 if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
331 ret = MMSYSERR_NOERROR;
332 padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE);
333 padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_PREPARED;
334 padsh->fdwPrepared = padsh->fdwStatus;
335 padsh->dwPrepared = 0;
336 padsh->pbPreparedSrc = padsh->pbSrc;
337 padsh->cbPreparedSrcLength = padsh->cbSrcLength;
338 padsh->pbPreparedDst = padsh->pbDst;
339 padsh->cbPreparedDstLength = padsh->cbDstLength;
341 padsh->fdwPrepared = 0;
342 padsh->dwPrepared = 0;
343 padsh->pbPreparedSrc = 0;
344 padsh->cbPreparedSrcLength = 0;
345 padsh->pbPreparedDst = 0;
346 padsh->cbPreparedDstLength = 0;
348 TRACE("=> (%d)\n", ret);
352 /***********************************************************************
353 * acmStreamReset (MSACM32.@)
355 MMRESULT WINAPI acmStreamReset(HACMSTREAM has, DWORD fdwReset)
358 MMRESULT ret = MMSYSERR_NOERROR;
360 TRACE("(%p, %ld)\n", has, fdwReset);
363 WARN("invalid flag\n");
364 ret = MMSYSERR_INVALFLAG;
365 } else if ((was = ACM_GetStream(has)) == NULL) {
366 WARN("invalid handle\n");
367 return MMSYSERR_INVALHANDLE;
368 } else if (was->drvInst.fdwOpen & ACM_STREAMOPENF_ASYNC) {
369 ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_RESET, (DWORD)&was->drvInst, 0);
371 TRACE("=> (%d)\n", ret);
375 /***********************************************************************
376 * acmStreamSize (MSACM32.@)
378 MMRESULT WINAPI acmStreamSize(HACMSTREAM has, DWORD cbInput,
379 LPDWORD pdwOutputBytes, DWORD fdwSize)
382 ACMDRVSTREAMSIZE adss;
385 TRACE("(%p, %ld, %p, %ld)\n", has, cbInput, pdwOutputBytes, fdwSize);
387 if ((was = ACM_GetStream(has)) == NULL) {
388 WARN("invalid handle\n");
389 return MMSYSERR_INVALHANDLE;
391 if ((fdwSize & ~ACM_STREAMSIZEF_QUERYMASK) != 0) {
392 WARN("invalid flag\n");
393 return MMSYSERR_INVALFLAG;
396 *pdwOutputBytes = 0L;
398 switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
399 case ACM_STREAMSIZEF_DESTINATION:
400 adss.cbDstLength = cbInput;
401 adss.cbSrcLength = 0;
403 case ACM_STREAMSIZEF_SOURCE:
404 adss.cbSrcLength = cbInput;
405 adss.cbDstLength = 0;
408 WARN("invalid flag\n");
409 return MMSYSERR_INVALFLAG;
412 adss.cbStruct = sizeof(adss);
413 adss.fdwSize = fdwSize;
414 ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_SIZE,
415 (DWORD)&was->drvInst, (DWORD)&adss);
416 if (ret == MMSYSERR_NOERROR) {
417 switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
418 case ACM_STREAMSIZEF_DESTINATION:
419 *pdwOutputBytes = adss.cbSrcLength;
421 case ACM_STREAMSIZEF_SOURCE:
422 *pdwOutputBytes = adss.cbDstLength;
426 TRACE("=> (%d) [%lu]\n", ret, *pdwOutputBytes);
430 /***********************************************************************
431 * acmStreamUnprepareHeader (MSACM32.@)
433 MMRESULT WINAPI acmStreamUnprepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash,
437 MMRESULT ret = MMSYSERR_NOERROR;
438 PACMDRVSTREAMHEADER padsh;
440 TRACE("(%p, %p, %ld)\n", has, pash, fdwUnprepare);
442 if ((was = ACM_GetStream(has)) == NULL) {
443 WARN("invalid handle\n");
444 return MMSYSERR_INVALHANDLE;
446 if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) {
447 WARN("invalid parameter\n");
448 return MMSYSERR_INVALPARAM;
450 if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)) {
451 WARN("unprepared header\n");
452 return ACMERR_UNPREPARED;
455 /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
456 * size. some fields are private to msacm internals, and are exposed
457 * in ACMSTREAMHEADER in the dwReservedDriver array
459 padsh = (PACMDRVSTREAMHEADER)pash;
461 /* check that pointers have not been modified */
462 if (padsh->pbPreparedSrc != padsh->pbSrc ||
463 padsh->cbPreparedSrcLength < padsh->cbSrcLength ||
464 padsh->pbPreparedDst != padsh->pbDst ||
465 padsh->cbPreparedDstLength < padsh->cbDstLength) {
466 WARN("invalid parameter\n");
467 return MMSYSERR_INVALPARAM;
470 padsh->fdwConvert = fdwUnprepare;
472 ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_UNPREPARE, (DWORD)&was->drvInst, (DWORD)padsh);
473 if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
474 ret = MMSYSERR_NOERROR;
475 padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE|ACMSTREAMHEADER_STATUSF_PREPARED);
477 TRACE("=> (%d)\n", ret);