| /* -*- tab-width: 8; c-basic-offset: 4 -*- */ | |
| /* | |
| * MSACM32 library | |
| * | |
| * Copyright 1998 Patrik Stridvall | |
| * 1999 Eric Pouech | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| /* TODO | |
| * + asynchronous conversion is not implemented | |
| * + callback/notification | |
| * * acmStreamMessage | |
| * + properly close ACM streams | |
| */ | |
| #include <stdarg.h> | |
| #include <string.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winerror.h" | |
| #include "wine/debug.h" | |
| #include "mmsystem.h" | |
| #define NOBITMAP | |
| #include "mmreg.h" | |
| #include "msacm.h" | |
| #include "msacmdrv.h" | |
| #include "wineacm.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(msacm); | |
| static PWINE_ACMSTREAM ACM_GetStream(HACMSTREAM has) | |
| { | |
| TRACE("(%p)\n", has); | |
| return (PWINE_ACMSTREAM)has; | |
| } | |
| /*********************************************************************** | |
| * acmStreamClose (MSACM32.@) | |
| */ | |
| MMRESULT WINAPI acmStreamClose(HACMSTREAM has, DWORD fdwClose) | |
| { | |
| PWINE_ACMSTREAM was; | |
| MMRESULT ret; | |
| TRACE("(%p, %ld)\n", has, fdwClose); | |
| if ((was = ACM_GetStream(has)) == NULL) { | |
| WARN("invalid handle\n"); | |
| return MMSYSERR_INVALHANDLE; | |
| } | |
| ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CLOSE, (DWORD)&was->drvInst, 0); | |
| if (ret == MMSYSERR_NOERROR) { | |
| if (was->hAcmDriver) | |
| acmDriverClose(was->hAcmDriver, 0L); | |
| HeapFree(MSACM_hHeap, 0, was); | |
| } | |
| TRACE("=> (%d)\n", ret); | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * acmStreamConvert (MSACM32.@) | |
| */ | |
| MMRESULT WINAPI acmStreamConvert(HACMSTREAM has, PACMSTREAMHEADER pash, | |
| DWORD fdwConvert) | |
| { | |
| PWINE_ACMSTREAM was; | |
| MMRESULT ret = MMSYSERR_NOERROR; | |
| PACMDRVSTREAMHEADER padsh; | |
| TRACE("(%p, %p, %ld)\n", has, pash, fdwConvert); | |
| if ((was = ACM_GetStream(has)) == NULL) { | |
| WARN("invalid handle\n"); | |
| return MMSYSERR_INVALHANDLE; | |
| } | |
| if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) { | |
| WARN("invalid parameter\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)) { | |
| WARN("unprepared header\n"); | |
| return ACMERR_UNPREPARED; | |
| } | |
| /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same | |
| * size. some fields are private to msacm internals, and are exposed | |
| * in ACMSTREAMHEADER in the dwReservedDriver array | |
| */ | |
| padsh = (PACMDRVSTREAMHEADER)pash; | |
| /* check that pointers have not been modified */ | |
| if (padsh->pbPreparedSrc != padsh->pbSrc || | |
| padsh->cbPreparedSrcLength < padsh->cbSrcLength || | |
| padsh->pbPreparedDst != padsh->pbDst || | |
| padsh->cbPreparedDstLength < padsh->cbDstLength) { | |
| WARN("invalid parameter\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| padsh->fdwConvert = fdwConvert; | |
| ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CONVERT, (DWORD)&was->drvInst, (DWORD)padsh); | |
| if (ret == MMSYSERR_NOERROR) { | |
| padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_DONE; | |
| } | |
| TRACE("=> (%d)\n", ret); | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * acmStreamMessage (MSACM32.@) | |
| */ | |
| MMRESULT WINAPI acmStreamMessage(HACMSTREAM has, UINT uMsg, LPARAM lParam1, | |
| LPARAM lParam2) | |
| { | |
| FIXME("(%p, %u, %ld, %ld): stub\n", has, uMsg, lParam1, lParam2); | |
| SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | |
| return MMSYSERR_ERROR; | |
| } | |
| /*********************************************************************** | |
| * acmStreamOpen (MSACM32.@) | |
| */ | |
| MMRESULT WINAPI acmStreamOpen(PHACMSTREAM phas, HACMDRIVER had, PWAVEFORMATEX pwfxSrc, | |
| PWAVEFORMATEX pwfxDst, PWAVEFILTER pwfltr, DWORD dwCallback, | |
| DWORD dwInstance, DWORD fdwOpen) | |
| { | |
| PWINE_ACMSTREAM was; | |
| PWINE_ACMDRIVER wad; | |
| MMRESULT ret; | |
| int wfxSrcSize; | |
| int wfxDstSize; | |
| WAVEFORMATEX wfxSrc, wfxDst; | |
| TRACE("(%p, %p, %p, %p, %p, %ld, %ld, %ld)\n", | |
| phas, had, pwfxSrc, pwfxDst, pwfltr, dwCallback, dwInstance, fdwOpen); | |
| /* NOTE: pwfxSrc and/or pwfxDst can point to a structure smaller than | |
| * WAVEFORMATEX so don't use them directly when not sure */ | |
| if (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) { | |
| memcpy(&wfxSrc, pwfxSrc, sizeof(PCMWAVEFORMAT)); | |
| wfxSrc.wBitsPerSample = pwfxSrc->wBitsPerSample; | |
| wfxSrc.cbSize = 0; | |
| pwfxSrc = &wfxSrc; | |
| } | |
| if (pwfxDst->wFormatTag == WAVE_FORMAT_PCM) { | |
| memcpy(&wfxDst, pwfxDst, sizeof(PCMWAVEFORMAT)); | |
| wfxDst.wBitsPerSample = pwfxDst->wBitsPerSample; | |
| wfxDst.cbSize = 0; | |
| pwfxDst = &wfxDst; | |
| } | |
| TRACE("src [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n", | |
| pwfxSrc->wFormatTag, pwfxSrc->nChannels, pwfxSrc->nSamplesPerSec, pwfxSrc->nAvgBytesPerSec, | |
| pwfxSrc->nBlockAlign, pwfxSrc->wBitsPerSample, pwfxSrc->cbSize); | |
| TRACE("dst [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n", | |
| pwfxDst->wFormatTag, pwfxDst->nChannels, pwfxDst->nSamplesPerSec, pwfxDst->nAvgBytesPerSec, | |
| pwfxDst->nBlockAlign, pwfxDst->wBitsPerSample, pwfxDst->cbSize); | |
| /* (WS) In query mode, phas should be NULL. If it is not, then instead | |
| * of returning an error we are making sure it is NULL, preventing some | |
| * applications that pass garbage for phas from crashing. | |
| */ | |
| if (fdwOpen & ACM_STREAMOPENF_QUERY) phas = NULL; | |
| if (pwfltr && (pwfxSrc->wFormatTag != pwfxDst->wFormatTag)) { | |
| WARN("invalid parameter\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| wfxSrcSize = wfxDstSize = sizeof(WAVEFORMATEX); | |
| if (pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) wfxSrcSize += pwfxSrc->cbSize; | |
| if (pwfxDst->wFormatTag != WAVE_FORMAT_PCM) wfxDstSize += pwfxDst->cbSize; | |
| was = HeapAlloc(MSACM_hHeap, 0, sizeof(*was) + wfxSrcSize + wfxDstSize + | |
| ((pwfltr) ? sizeof(WAVEFILTER) : 0)); | |
| if (was == NULL) { | |
| WARN("no memory\n"); | |
| return MMSYSERR_NOMEM; | |
| } | |
| was->drvInst.cbStruct = sizeof(was->drvInst); | |
| was->drvInst.pwfxSrc = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was)); | |
| memcpy(was->drvInst.pwfxSrc, pwfxSrc, wfxSrcSize); | |
| was->drvInst.pwfxDst = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was) + wfxSrcSize); | |
| memcpy(was->drvInst.pwfxDst, pwfxDst, wfxDstSize); | |
| if (pwfltr) { | |
| was->drvInst.pwfltr = (PWAVEFILTER)((LPSTR)was + sizeof(*was) + wfxSrcSize + wfxDstSize); | |
| memcpy(was->drvInst.pwfltr, pwfltr, sizeof(WAVEFILTER)); | |
| } else { | |
| was->drvInst.pwfltr = NULL; | |
| } | |
| was->drvInst.dwCallback = dwCallback; | |
| was->drvInst.dwInstance = dwInstance; | |
| was->drvInst.fdwOpen = fdwOpen; | |
| was->drvInst.fdwDriver = 0L; | |
| was->drvInst.dwDriver = 0L; | |
| /* real value will be stored once ACMDM_STREAM_OPEN succeeds */ | |
| was->drvInst.has = 0L; | |
| if (had) { | |
| if (!(wad = MSACM_GetDriver(had))) { | |
| ret = MMSYSERR_INVALPARAM; | |
| goto errCleanUp; | |
| } | |
| was->obj.dwType = WINE_ACMOBJ_STREAM; | |
| was->obj.pACMDriverID = wad->obj.pACMDriverID; | |
| was->pDrv = wad; | |
| was->hAcmDriver = 0; /* not to close it in acmStreamClose */ | |
| ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L); | |
| if (ret != MMSYSERR_NOERROR) | |
| goto errCleanUp; | |
| } else { | |
| PWINE_ACMDRIVERID wadi; | |
| ret = ACMERR_NOTPOSSIBLE; | |
| for (wadi = MSACM_pFirstACMDriverID; wadi; wadi = wadi->pNextACMDriverID) { | |
| if ((wadi->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) || | |
| !MSACM_FindFormatTagInCache(wadi, pwfxSrc->wFormatTag, NULL) || | |
| !MSACM_FindFormatTagInCache(wadi, pwfxDst->wFormatTag, NULL)) | |
| continue; | |
| ret = acmDriverOpen(&had, (HACMDRIVERID)wadi, 0L); | |
| if (ret != MMSYSERR_NOERROR) | |
| continue; | |
| if ((wad = MSACM_GetDriver(had)) != 0) { | |
| was->obj.dwType = WINE_ACMOBJ_STREAM; | |
| was->obj.pACMDriverID = wad->obj.pACMDriverID; | |
| was->pDrv = wad; | |
| was->hAcmDriver = had; | |
| ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L); | |
| TRACE("%s => %08x\n", debugstr_w(wadi->pszDriverAlias), ret); | |
| if (ret == MMSYSERR_NOERROR) { | |
| if (fdwOpen & ACM_STREAMOPENF_QUERY) { | |
| acmDriverClose(had, 0L); | |
| } | |
| break; | |
| } | |
| } | |
| /* no match, close this acm driver and try next one */ | |
| acmDriverClose(had, 0L); | |
| } | |
| if (ret != MMSYSERR_NOERROR) { | |
| ret = ACMERR_NOTPOSSIBLE; | |
| goto errCleanUp; | |
| } | |
| } | |
| ret = MMSYSERR_NOERROR; | |
| was->drvInst.has = (HACMSTREAM)was; | |
| if (!(fdwOpen & ACM_STREAMOPENF_QUERY)) { | |
| if (phas) | |
| *phas = (HACMSTREAM)was; | |
| TRACE("=> (%d)\n", ret); | |
| return ret; | |
| } | |
| errCleanUp: | |
| if (phas) | |
| *phas = NULL; | |
| HeapFree(MSACM_hHeap, 0, was); | |
| TRACE("=> (%d)\n", ret); | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * acmStreamPrepareHeader (MSACM32.@) | |
| */ | |
| MMRESULT WINAPI acmStreamPrepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash, | |
| DWORD fdwPrepare) | |
| { | |
| PWINE_ACMSTREAM was; | |
| MMRESULT ret = MMSYSERR_NOERROR; | |
| PACMDRVSTREAMHEADER padsh; | |
| TRACE("(%p, %p, %ld)\n", has, pash, fdwPrepare); | |
| if ((was = ACM_GetStream(has)) == NULL) { | |
| WARN("invalid handle\n"); | |
| return MMSYSERR_INVALHANDLE; | |
| } | |
| if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) { | |
| WARN("invalid parameter\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| if (fdwPrepare) | |
| ret = MMSYSERR_INVALFLAG; | |
| if (pash->fdwStatus & ACMSTREAMHEADER_STATUSF_DONE) | |
| return MMSYSERR_NOERROR; | |
| /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same | |
| * size. some fields are private to msacm internals, and are exposed | |
| * in ACMSTREAMHEADER in the dwReservedDriver array | |
| */ | |
| padsh = (PACMDRVSTREAMHEADER)pash; | |
| padsh->fdwConvert = fdwPrepare; | |
| padsh->padshNext = NULL; | |
| padsh->fdwDriver = padsh->dwDriver = 0L; | |
| padsh->fdwPrepared = 0; | |
| padsh->dwPrepared = 0; | |
| padsh->pbPreparedSrc = 0; | |
| padsh->cbPreparedSrcLength = 0; | |
| padsh->pbPreparedDst = 0; | |
| padsh->cbPreparedDstLength = 0; | |
| ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_PREPARE, (DWORD)&was->drvInst, (DWORD)padsh); | |
| if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) { | |
| ret = MMSYSERR_NOERROR; | |
| padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE); | |
| padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_PREPARED; | |
| padsh->fdwPrepared = padsh->fdwStatus; | |
| padsh->dwPrepared = 0; | |
| padsh->pbPreparedSrc = padsh->pbSrc; | |
| padsh->cbPreparedSrcLength = padsh->cbSrcLength; | |
| padsh->pbPreparedDst = padsh->pbDst; | |
| padsh->cbPreparedDstLength = padsh->cbDstLength; | |
| } else { | |
| padsh->fdwPrepared = 0; | |
| padsh->dwPrepared = 0; | |
| padsh->pbPreparedSrc = 0; | |
| padsh->cbPreparedSrcLength = 0; | |
| padsh->pbPreparedDst = 0; | |
| padsh->cbPreparedDstLength = 0; | |
| } | |
| TRACE("=> (%d)\n", ret); | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * acmStreamReset (MSACM32.@) | |
| */ | |
| MMRESULT WINAPI acmStreamReset(HACMSTREAM has, DWORD fdwReset) | |
| { | |
| PWINE_ACMSTREAM was; | |
| MMRESULT ret = MMSYSERR_NOERROR; | |
| TRACE("(%p, %ld)\n", has, fdwReset); | |
| if (fdwReset) { | |
| WARN("invalid flag\n"); | |
| ret = MMSYSERR_INVALFLAG; | |
| } else if ((was = ACM_GetStream(has)) == NULL) { | |
| WARN("invalid handle\n"); | |
| return MMSYSERR_INVALHANDLE; | |
| } else if (was->drvInst.fdwOpen & ACM_STREAMOPENF_ASYNC) { | |
| ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_RESET, (DWORD)&was->drvInst, 0); | |
| } | |
| TRACE("=> (%d)\n", ret); | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * acmStreamSize (MSACM32.@) | |
| */ | |
| MMRESULT WINAPI acmStreamSize(HACMSTREAM has, DWORD cbInput, | |
| LPDWORD pdwOutputBytes, DWORD fdwSize) | |
| { | |
| PWINE_ACMSTREAM was; | |
| ACMDRVSTREAMSIZE adss; | |
| MMRESULT ret; | |
| TRACE("(%p, %ld, %p, %ld)\n", has, cbInput, pdwOutputBytes, fdwSize); | |
| if ((was = ACM_GetStream(has)) == NULL) { | |
| WARN("invalid handle\n"); | |
| return MMSYSERR_INVALHANDLE; | |
| } | |
| if ((fdwSize & ~ACM_STREAMSIZEF_QUERYMASK) != 0) { | |
| WARN("invalid flag\n"); | |
| return MMSYSERR_INVALFLAG; | |
| } | |
| *pdwOutputBytes = 0L; | |
| switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) { | |
| case ACM_STREAMSIZEF_DESTINATION: | |
| adss.cbDstLength = cbInput; | |
| adss.cbSrcLength = 0; | |
| break; | |
| case ACM_STREAMSIZEF_SOURCE: | |
| adss.cbSrcLength = cbInput; | |
| adss.cbDstLength = 0; | |
| break; | |
| default: | |
| WARN("invalid flag\n"); | |
| return MMSYSERR_INVALFLAG; | |
| } | |
| adss.cbStruct = sizeof(adss); | |
| adss.fdwSize = fdwSize; | |
| ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_SIZE, | |
| (DWORD)&was->drvInst, (DWORD)&adss); | |
| if (ret == MMSYSERR_NOERROR) { | |
| switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) { | |
| case ACM_STREAMSIZEF_DESTINATION: | |
| *pdwOutputBytes = adss.cbSrcLength; | |
| break; | |
| case ACM_STREAMSIZEF_SOURCE: | |
| *pdwOutputBytes = adss.cbDstLength; | |
| break; | |
| } | |
| } | |
| TRACE("=> (%d) [%lu]\n", ret, *pdwOutputBytes); | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * acmStreamUnprepareHeader (MSACM32.@) | |
| */ | |
| MMRESULT WINAPI acmStreamUnprepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash, | |
| DWORD fdwUnprepare) | |
| { | |
| PWINE_ACMSTREAM was; | |
| MMRESULT ret = MMSYSERR_NOERROR; | |
| PACMDRVSTREAMHEADER padsh; | |
| TRACE("(%p, %p, %ld)\n", has, pash, fdwUnprepare); | |
| if ((was = ACM_GetStream(has)) == NULL) { | |
| WARN("invalid handle\n"); | |
| return MMSYSERR_INVALHANDLE; | |
| } | |
| if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) { | |
| WARN("invalid parameter\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)) { | |
| WARN("unprepared header\n"); | |
| return ACMERR_UNPREPARED; | |
| } | |
| /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same | |
| * size. some fields are private to msacm internals, and are exposed | |
| * in ACMSTREAMHEADER in the dwReservedDriver array | |
| */ | |
| padsh = (PACMDRVSTREAMHEADER)pash; | |
| /* check that pointers have not been modified */ | |
| if (padsh->pbPreparedSrc != padsh->pbSrc || | |
| padsh->cbPreparedSrcLength < padsh->cbSrcLength || | |
| padsh->pbPreparedDst != padsh->pbDst || | |
| padsh->cbPreparedDstLength < padsh->cbDstLength) { | |
| WARN("invalid parameter\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| padsh->fdwConvert = fdwUnprepare; | |
| ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_UNPREPARE, (DWORD)&was->drvInst, (DWORD)padsh); | |
| if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) { | |
| ret = MMSYSERR_NOERROR; | |
| padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE|ACMSTREAMHEADER_STATUSF_PREPARED); | |
| } | |
| TRACE("=> (%d)\n", ret); | |
| return ret; | |
| } |