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