1 /* 2 * Wine Driver for MCI wave forms 3 * 4 * Copyright 1994 Martin Ayotte 5 * 1999,2000,2005 Eric Pouech 6 * 2000 Francois Jacques 7 * 2009 Jörg Höhle 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 #include <assert.h> 25 #include <stdarg.h> 26 27 #include "windef.h" 28 #include "winbase.h" 29 #include "wingdi.h" 30 #include "winuser.h" 31 #include "mmddk.h" 32 #include "wownt32.h" 33 #include "digitalv.h" 34 #include "wine/debug.h" 35 #include "wine/unicode.h" 36 37 WINE_DEFAULT_DEBUG_CHANNEL(mciwave); 38 39 typedef struct { 40 UINT wDevID; 41 HANDLE hWave; 42 int nUseCount; /* Incremented for each shared open */ 43 HMMIO hFile; /* mmio file handle open as Element */ 44 MCIDEVICEID wNotifyDeviceID; /* MCI device ID with a pending notification */ 45 HANDLE hCallback; /* Callback handle for pending notification */ 46 LPWSTR lpFileName; /* Name of file (if any) */ 47 WAVEFORMATEX wfxRef; 48 LPWAVEFORMATEX lpWaveFormat; /* Points to wfxRef until set by OPEN or RECORD */ 49 BOOL fInput; /* FALSE = Output, TRUE = Input */ 50 WORD wInput; /* wave input device */ 51 WORD wOutput; /* wave output device */ 52 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */ 53 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */ 54 DWORD dwPosition; /* position in bytes in chunk */ 55 HANDLE hEvent; /* for synchronization */ 56 LONG dwEventCount; /* for synchronization */ 57 MMCKINFO ckMainRIFF; /* main RIFF chunk */ 58 MMCKINFO ckWaveData; /* data chunk */ 59 } WINE_MCIWAVE; 60 61 /* =================================================================== 62 * =================================================================== 63 * FIXME: should be using the new mmThreadXXXX functions from WINMM 64 * instead of those 65 * it would require to add a wine internal flag to mmThreadCreate 66 * in order to pass a 32 bit function instead of a 16 bit one 67 * =================================================================== 68 * =================================================================== */ 69 70 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt); 71 72 struct SCA { 73 async_cmd cmd; 74 HANDLE evt; 75 UINT wDevID; 76 DWORD_PTR dwParam1; 77 DWORD_PTR dwParam2; 78 }; 79 80 /************************************************************************** 81 * MCI_SCAStarter [internal] 82 */ 83 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg) 84 { 85 struct SCA* sca = (struct SCA*)arg; 86 DWORD ret; 87 88 TRACE("In thread before async command (%08x,%08lx,%08lx)\n", 89 sca->wDevID, sca->dwParam1, sca->dwParam2); 90 ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt); 91 TRACE("In thread after async command (%08x,%08lx,%08lx)\n", 92 sca->wDevID, sca->dwParam1, sca->dwParam2); 93 HeapFree(GetProcessHeap(), 0, sca); 94 return ret; 95 } 96 97 /************************************************************************** 98 * MCI_SendCommandAsync [internal] 99 */ 100 static DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1, 101 DWORD_PTR dwParam2, UINT size) 102 { 103 HANDLE handles[2]; 104 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size); 105 106 if (sca == 0) 107 return MCIERR_OUT_OF_MEMORY; 108 109 sca->wDevID = wDevID; 110 sca->cmd = cmd; 111 sca->dwParam1 = dwParam1; 112 113 if (size && dwParam2) { 114 sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA); 115 /* copy structure passed by program in dwParam2 to be sure 116 * we can still use it whatever the program does 117 */ 118 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size); 119 } else { 120 sca->dwParam2 = dwParam2; 121 } 122 123 if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL || 124 (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) { 125 WARN("Couldn't allocate thread for async command handling, sending synchronously\n"); 126 if (handles[1]) CloseHandle(handles[1]); 127 sca->evt = NULL; 128 return MCI_SCAStarter(&sca); 129 } 130 131 SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL); 132 /* wait until either: 133 * - the thread has finished (handles[0], likely an error) 134 * - init phase of async command is done (handles[1]) 135 */ 136 WaitForMultipleObjects(2, handles, FALSE, INFINITE); 137 CloseHandle(handles[0]); 138 CloseHandle(handles[1]); 139 return 0; 140 } 141 142 /*======================================================================* 143 * MCI WAVE implementation * 144 *======================================================================*/ 145 146 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms); 147 148 /************************************************************************** 149 * MCIWAVE_drvOpen [internal] 150 */ 151 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp) 152 { 153 WINE_MCIWAVE* wmw; 154 155 if (modp == NULL) return 0xFFFFFFFF; 156 157 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE)); 158 159 if (!wmw) 160 return 0; 161 162 wmw->wDevID = modp->wDeviceID; 163 mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw); 164 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE; 165 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO; 166 167 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM; 168 wmw->wfxRef.nChannels = 1; /* MONO */ 169 wmw->wfxRef.nSamplesPerSec = 11025; 170 wmw->wfxRef.nAvgBytesPerSec = 11025; 171 wmw->wfxRef.nBlockAlign = 1; 172 wmw->wfxRef.wBitsPerSample = 8; 173 wmw->wfxRef.cbSize = 0; /* don't care */ 174 175 return modp->wDeviceID; 176 } 177 178 /************************************************************************** 179 * MCIWAVE_drvClose [internal] 180 */ 181 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID) 182 { 183 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID); 184 185 if (wmw) { 186 HeapFree(GetProcessHeap(), 0, wmw); 187 mciSetDriverData(dwDevID, 0); 188 return 1; 189 } 190 return (dwDevID == 0xFFFFFFFF) ? 1 : 0; 191 } 192 193 /************************************************************************** 194 * WAVE_mciGetOpenDev [internal] 195 */ 196 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID) 197 { 198 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID); 199 200 if (wmw == NULL || wmw->nUseCount == 0) { 201 WARN("Invalid wDevID=%u\n", wDevID); 202 return 0; 203 } 204 return wmw; 205 } 206 207 /************************************************************************** 208 * WAVE_mciNotify [internal] 209 * 210 * Notifications in MCI work like a 1-element queue. 211 * Each new notification request supersedes the previous one. 212 * This affects Play and Record; other commands are immediate. 213 */ 214 static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus) 215 { 216 /* We simply save one parameter by not passing the wDevID local 217 * to the command. They are the same (via mciGetDriverData). 218 */ 219 MCIDEVICEID wDevID = wmw->wNotifyDeviceID; 220 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL); 221 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED); 222 mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus); 223 } 224 225 /************************************************************************** 226 * WAVE_ConvertByteToTimeFormat [internal] 227 */ 228 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val) 229 { 230 DWORD ret = 0; 231 232 switch (wmw->dwMciTimeFormat) { 233 case MCI_FORMAT_MILLISECONDS: 234 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec); 235 break; 236 case MCI_FORMAT_BYTES: 237 ret = val; 238 break; 239 case MCI_FORMAT_SAMPLES: 240 ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec); 241 break; 242 default: 243 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat); 244 } 245 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret); 246 return ret; 247 } 248 249 /************************************************************************** 250 * WAVE_ConvertTimeFormatToByte [internal] 251 */ 252 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val) 253 { 254 DWORD ret = 0; 255 256 switch (wmw->dwMciTimeFormat) { 257 case MCI_FORMAT_MILLISECONDS: 258 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000); 259 if (ret > wmw->ckWaveData.cksize && 260 val == WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize)) 261 ret = wmw->ckWaveData.cksize; 262 break; 263 case MCI_FORMAT_BYTES: 264 ret = val; 265 break; 266 case MCI_FORMAT_SAMPLES: 267 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec); 268 break; 269 default: 270 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat); 271 } 272 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret); 273 return ret; 274 } 275 276 /************************************************************************** 277 * WAVE_mciReadFmt [internal] 278 */ 279 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF) 280 { 281 MMCKINFO mmckInfo; 282 LONG r; 283 LPWAVEFORMATEX pwfx; 284 285 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' '); 286 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0) 287 return MCIERR_INVALID_FILE; 288 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n", 289 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize); 290 291 pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize); 292 if (!pwfx) return MCIERR_OUT_OF_MEMORY; 293 294 r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize); 295 if (r < sizeof(PCMWAVEFORMAT)) { 296 HeapFree(GetProcessHeap(), 0, pwfx); 297 return MCIERR_INVALID_FILE; 298 } 299 TRACE("wFormatTag=%04X !\n", pwfx->wFormatTag); 300 TRACE("nChannels=%d\n", pwfx->nChannels); 301 TRACE("nSamplesPerSec=%d\n", pwfx->nSamplesPerSec); 302 TRACE("nAvgBytesPerSec=%d\n", pwfx->nAvgBytesPerSec); 303 TRACE("nBlockAlign=%d\n", pwfx->nBlockAlign); 304 TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample); 305 if (r >= sizeof(WAVEFORMATEX)) 306 TRACE("cbSize=%u !\n", pwfx->cbSize); 307 if ((pwfx->wFormatTag != WAVE_FORMAT_PCM) 308 && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) { 309 HeapFree(GetProcessHeap(), 0, pwfx); 310 return MCIERR_INVALID_FILE; 311 } 312 wmw->lpWaveFormat = pwfx; 313 314 mmioAscend(wmw->hFile, &mmckInfo, 0); 315 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a'); 316 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) { 317 TRACE("can't find data chunk\n"); 318 return MCIERR_INVALID_FILE; 319 } 320 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n", 321 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize); 322 return 0; 323 } 324 325 /************************************************************************** 326 * WAVE_mciDefaultFmt [internal] 327 * 328 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef 329 * until either Open File or Record. It becomes immutable afterwards, 330 * i.e. Set wave format or channels etc. is subsequently refused. 331 */ 332 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw) 333 { 334 wmw->lpWaveFormat = &wmw->wfxRef; 335 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM; 336 wmw->lpWaveFormat->nChannels = 1; 337 wmw->lpWaveFormat->nSamplesPerSec = 11025; 338 wmw->lpWaveFormat->nAvgBytesPerSec = 11025; 339 wmw->lpWaveFormat->nBlockAlign = 1; 340 wmw->lpWaveFormat->wBitsPerSample = 8; 341 wmw->lpWaveFormat->cbSize = 0; 342 } 343 344 /************************************************************************** 345 * WAVE_mciCreateRIFFSkeleton [internal] 346 */ 347 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw) 348 { 349 MMCKINFO ckWaveFormat; 350 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF); 351 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData); 352 353 lpckRIFF->ckid = FOURCC_RIFF; 354 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E'); 355 lpckRIFF->cksize = 0; 356 357 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF)) 358 goto err; 359 360 ckWaveFormat.fccType = 0; 361 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' '); 362 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT); 363 364 /* Set wave format accepts PCM only, however open an 365 * existing ADPCM file, record into it and the MCI will 366 * happily save back in that format. */ 367 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) { 368 if (wmw->lpWaveFormat->nBlockAlign != 369 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) { 370 WORD size = wmw->lpWaveFormat->nChannels * 371 wmw->lpWaveFormat->wBitsPerSample/8; 372 WARN("Incorrect nBlockAlign (%d), setting it to %d\n", 373 wmw->lpWaveFormat->nBlockAlign, size); 374 wmw->lpWaveFormat->nBlockAlign = size; 375 } 376 if (wmw->lpWaveFormat->nAvgBytesPerSec != 377 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) { 378 DWORD speed = wmw->lpWaveFormat->nSamplesPerSec * 379 wmw->lpWaveFormat->nBlockAlign; 380 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n", 381 wmw->lpWaveFormat->nAvgBytesPerSec, speed); 382 wmw->lpWaveFormat->nAvgBytesPerSec = speed; 383 } 384 } 385 if (wmw->lpWaveFormat == &wmw->wfxRef) { 386 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX)); 387 if (!pwfx) return MCIERR_OUT_OF_MEMORY; 388 /* Set wave format accepts PCM only so the size is known. */ 389 assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM); 390 *pwfx = wmw->wfxRef; 391 wmw->lpWaveFormat = pwfx; 392 } 393 394 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0)) 395 goto err; 396 397 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag) 398 ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize)) 399 goto err; 400 401 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0)) 402 goto err; 403 404 lpckWaveData->cksize = 0; 405 lpckWaveData->fccType = 0; 406 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a'); 407 408 /* create data chunk */ 409 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0)) 410 goto err; 411 412 return 0; 413 414 err: 415 /* mciClose takes care of wmw->lpWaveFormat. */ 416 return MCIERR_INVALID_FILE; 417 } 418 419 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName) 420 { 421 WCHAR szTmpPath[MAX_PATH]; 422 WCHAR szPrefix[4]; 423 DWORD dwRet = MMSYSERR_NOERROR; 424 425 szPrefix[0] = 'M'; 426 szPrefix[1] = 'C'; 427 szPrefix[2] = 'I'; 428 szPrefix[3] = '\0'; 429 430 if (!GetTempPathW(ARRAY_SIZE(szTmpPath), szTmpPath)) { 431 WARN("can't retrieve temp path!\n"); 432 *pszTmpFileName = NULL; 433 return MCIERR_FILE_NOT_FOUND; 434 } 435 436 *pszTmpFileName = HeapAlloc(GetProcessHeap(), 437 HEAP_ZERO_MEMORY, 438 MAX_PATH * sizeof(WCHAR)); 439 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) { 440 WARN("can't retrieve temp file name!\n"); 441 HeapFree(GetProcessHeap(), 0, *pszTmpFileName); 442 return MCIERR_FILE_NOT_FOUND; 443 } 444 445 TRACE("%s!\n", debugstr_w(*pszTmpFileName)); 446 447 if (*pszTmpFileName && (*pszTmpFileName)[0]) { 448 449 *hFile = mmioOpenW(*pszTmpFileName, NULL, 450 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE); 451 452 if (*hFile == 0) { 453 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName)); 454 /* temporary file could not be created. clean filename. */ 455 HeapFree(GetProcessHeap(), 0, *pszTmpFileName); 456 dwRet = MCIERR_FILE_NOT_FOUND; 457 } 458 } 459 return dwRet; 460 } 461 462 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename) 463 { 464 LRESULT dwRet = MMSYSERR_NOERROR; 465 LPWSTR fn; 466 467 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR)); 468 if (!fn) return MCIERR_OUT_OF_MEMORY; 469 strcpyW(fn, filename); 470 HeapFree(GetProcessHeap(), 0, wmw->lpFileName); 471 wmw->lpFileName = fn; 472 473 if (filename[0]) { 474 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */ 475 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename)); 476 477 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL, 478 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ); 479 480 if (wmw->hFile == 0) { 481 WARN("can't find file=%s!\n", debugstr_w(filename)); 482 dwRet = MCIERR_FILE_NOT_FOUND; 483 } 484 else 485 { 486 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF; 487 488 /* make sure we're at the beginning of the file */ 489 mmioSeek(wmw->hFile, 0, SEEK_SET); 490 491 /* first reading of this file. read the waveformat chunk */ 492 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) { 493 dwRet = MCIERR_INVALID_FILE; 494 } else { 495 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n", 496 (LPSTR)&(lpckMainRIFF->ckid), 497 (LPSTR) &(lpckMainRIFF->fccType), 498 (lpckMainRIFF->cksize)); 499 500 if ((lpckMainRIFF->ckid != FOURCC_RIFF) || 501 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) { 502 dwRet = MCIERR_INVALID_FILE; 503 } else { 504 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF); 505 } 506 } 507 } 508 } 509 return dwRet; 510 } 511 512 /************************************************************************** 513 * WAVE_mciOpen [internal] 514 */ 515 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms) 516 { 517 DWORD dwRet = 0; 518 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID); 519 520 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms); 521 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 522 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 523 524 if (dwFlags & MCI_OPEN_SHAREABLE) 525 return MCIERR_UNSUPPORTED_FUNCTION; 526 527 if (wmw->nUseCount > 0) { 528 /* The driver is already opened on this channel 529 * Wave driver cannot be shared 530 */ 531 return MCIERR_DEVICE_OPEN; 532 } 533 534 wmw->nUseCount++; 535 536 wmw->wInput = wmw->wOutput = WAVE_MAPPER; 537 wmw->fInput = FALSE; 538 wmw->hWave = 0; 539 wmw->dwStatus = MCI_MODE_NOT_READY; 540 wmw->hFile = 0; 541 wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */ 542 wmw->hCallback = NULL; 543 WAVE_mciDefaultFmt(wmw); 544 545 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID); 546 /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */ 547 wmw->wNotifyDeviceID = wDevID; 548 549 if (dwFlags & MCI_OPEN_ELEMENT) { 550 if (dwFlags & MCI_OPEN_ELEMENT_ID) { 551 /* could it be that (DWORD)lpOpenParms->lpstrElementName 552 * contains the hFile value ? 553 */ 554 dwRet = MCIERR_UNRECOGNIZED_COMMAND; 555 } else { 556 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName); 557 } 558 } 559 TRACE("hFile=%p\n", wmw->hFile); 560 561 if (dwRet == 0) { 562 wmw->dwPosition = 0; 563 564 wmw->dwStatus = MCI_MODE_STOP; 565 566 if (dwFlags & MCI_NOTIFY) 567 WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 568 } else { 569 wmw->nUseCount--; 570 if (wmw->hFile != 0) 571 mmioClose(wmw->hFile, 0); 572 wmw->hFile = 0; 573 HeapFree(GetProcessHeap(), 0, wmw->lpFileName); 574 wmw->lpFileName = NULL; 575 } 576 return dwRet; 577 } 578 579 /************************************************************************** 580 * WAVE_mciCue [internal] 581 */ 582 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 583 { 584 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 585 586 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 587 588 /* Tests on systems without sound drivers show that Cue, like 589 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE. 590 * The first Cue Notify does not immediately return the 591 * notification, as if a player or recorder thread is started. 592 * PAUSE mode is reported when successful, but this mode is 593 * different from the normal Pause, because a) Pause then returns 594 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is 595 * still accepted, returning the original notification as ABORTED. 596 * I.e. Cue allows subsequent format changes, unlike Record or 597 * Open file, closes winmm if the format changes and stops this 598 * thread. 599 * Wine creates one player or recorder thread per async. Play or 600 * Record command. Notification behaviour suggests that MS-W* 601 * reuses a single thread to improve response times. Having Cue 602 * start this thread early helps to improve Play/Record's initial 603 * response time. In effect, Cue is a performance hint, which 604 * justifies our almost no-op implementation. 605 */ 606 607 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 608 if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION; 609 610 if ((dwFlags & MCI_NOTIFY) && lpParms) 611 WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL); 612 613 return MMSYSERR_NOERROR; 614 } 615 616 /************************************************************************** 617 * WAVE_mciStop [internal] 618 */ 619 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 620 { 621 DWORD dwRet = 0; 622 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 623 624 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 625 626 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 627 628 if (wmw->dwStatus != MCI_MODE_STOP) { 629 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL); 630 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED); 631 } 632 633 /* wait for playback thread (if any) to exit before processing further */ 634 switch (wmw->dwStatus) { 635 case MCI_MODE_PAUSE: 636 case MCI_MODE_PLAY: 637 case MCI_MODE_RECORD: 638 { 639 int oldStat = wmw->dwStatus; 640 wmw->dwStatus = MCI_MODE_NOT_READY; 641 if (oldStat == MCI_MODE_PAUSE) 642 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave); 643 } 644 while (wmw->dwStatus != MCI_MODE_STOP) 645 Sleep(10); 646 break; 647 } 648 649 /* sanity resets */ 650 wmw->dwStatus = MCI_MODE_STOP; 651 652 if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet) 653 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 654 655 return dwRet; 656 } 657 658 /************************************************************************** 659 * WAVE_mciClose [internal] 660 */ 661 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 662 { 663 DWORD dwRet = 0; 664 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 665 666 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 667 668 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 669 670 if (wmw->dwStatus != MCI_MODE_STOP) { 671 /* mciStop handles MCI_NOTIFY_ABORTED */ 672 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms); 673 } 674 675 wmw->nUseCount--; 676 677 if (wmw->nUseCount == 0) { 678 if (wmw->hFile != 0) { 679 mmioClose(wmw->hFile, 0); 680 wmw->hFile = 0; 681 } 682 } 683 684 if (wmw->lpWaveFormat != &wmw->wfxRef) 685 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat); 686 wmw->lpWaveFormat = &wmw->wfxRef; 687 HeapFree(GetProcessHeap(), 0, wmw->lpFileName); 688 wmw->lpFileName = NULL; 689 690 if ((dwFlags & MCI_NOTIFY) && lpParms) { 691 WAVE_mciNotify(lpParms->dwCallback, wmw, 692 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE); 693 } 694 695 return 0; 696 } 697 698 /************************************************************************** 699 * WAVE_mciPlayCallback [internal] 700 */ 701 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg, 702 DWORD_PTR dwInstance, 703 LPARAM dwParam1, LPARAM dwParam2) 704 { 705 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance; 706 707 switch (uMsg) { 708 case WOM_OPEN: 709 case WOM_CLOSE: 710 break; 711 case WOM_DONE: 712 InterlockedIncrement(&wmw->dwEventCount); 713 TRACE("Returning waveHdr=%lx\n", dwParam1); 714 SetEvent(wmw->hEvent); 715 break; 716 default: 717 ERR("Unknown uMsg=%d\n", uMsg); 718 } 719 } 720 721 /****************************************************************** 722 * WAVE_mciPlayWaitDone [internal] 723 */ 724 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw) 725 { 726 for (;;) { 727 ResetEvent(wmw->hEvent); 728 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) { 729 break; 730 } 731 InterlockedIncrement(&wmw->dwEventCount); 732 733 WaitForSingleObject(wmw->hEvent, INFINITE); 734 } 735 } 736 737 /************************************************************************** 738 * WAVE_mciPlay [internal] 739 */ 740 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent) 741 { 742 LPMCI_PLAY_PARMS lpParms = (void*)pmt; 743 DWORD end; 744 LONG bufsize, count, left; 745 DWORD dwRet; 746 LPWAVEHDR waveHdr = NULL; 747 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 748 HANDLE oldcb; 749 int whidx; 750 751 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms); 752 753 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 754 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 755 756 if (wmw->hFile == 0) { 757 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName)); 758 return MCIERR_FILE_NOT_FOUND; 759 } 760 761 if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput && !(dwFlags & (MCI_FROM | MCI_TO))) { 762 /* FIXME: notification is different with Resume than Play */ 763 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms); 764 } 765 766 /** This function will be called again by a thread when async is used. 767 * We have to set MCI_MODE_PLAY before we do this so that the app can spin 768 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread. 769 */ 770 if ( !(wmw->dwStatus == MCI_MODE_STOP) && 771 !((wmw->dwStatus == MCI_MODE_PLAY) && (dwFlags & MCI_WAIT) && !wmw->hWave)) { 772 /* FIXME: Check FROM/TO parameters first. */ 773 /* FIXME: Play; Play [notify|wait] must hook into the running player. */ 774 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, NULL); 775 if (dwRet) return dwRet; 776 } 777 778 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) { 779 if (wmw->lpWaveFormat->nBlockAlign != 780 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) { 781 WARN("Incorrect nBlockAlign (%d), setting it to %d\n", 782 wmw->lpWaveFormat->nBlockAlign, 783 wmw->lpWaveFormat->nChannels * 784 wmw->lpWaveFormat->wBitsPerSample/8); 785 wmw->lpWaveFormat->nBlockAlign = 786 wmw->lpWaveFormat->nChannels * 787 wmw->lpWaveFormat->wBitsPerSample/8; 788 } 789 if (wmw->lpWaveFormat->nAvgBytesPerSec != 790 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) { 791 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n", 792 wmw->lpWaveFormat->nAvgBytesPerSec, 793 wmw->lpWaveFormat->nSamplesPerSec * 794 wmw->lpWaveFormat->nBlockAlign); 795 wmw->lpWaveFormat->nAvgBytesPerSec = 796 wmw->lpWaveFormat->nSamplesPerSec * 797 wmw->lpWaveFormat->nBlockAlign; 798 } 799 } 800 801 end = wmw->ckWaveData.cksize; 802 if (dwFlags & MCI_TO) { 803 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); 804 if (position > end) return MCIERR_OUTOFRANGE; 805 end = position; 806 } 807 if (dwFlags & MCI_FROM) { 808 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); 809 if (position > end) return MCIERR_OUTOFRANGE; 810 /* Seek rounds down, so do we. */ 811 position /= wmw->lpWaveFormat->nBlockAlign; 812 position *= wmw->lpWaveFormat->nBlockAlign; 813 wmw->dwPosition = position; 814 } 815 if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE; 816 left = end - wmw->dwPosition; 817 if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */ 818 819 wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */ 820 wmw->dwStatus = MCI_MODE_PLAY; 821 822 if (!(dwFlags & MCI_WAIT)) { 823 return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags, 824 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS)); 825 } 826 827 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end); 828 829 oldcb = InterlockedExchangePointer(&wmw->hCallback, 830 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL); 831 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED); 832 oldcb = NULL; 833 834 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \ 835 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign) 836 837 /* go back to beginning of chunk plus the requested position */ 838 /* FIXME: I'm not sure this is correct, notably because some data linked to 839 * the decompression state machine will not be correctly initialized. 840 * try it this way (other way would be to decompress from 0 up to dwPosition 841 * and to start sending to hWave when dwPosition is reached) 842 */ 843 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */ 844 845 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, wmw->wOutput, wmw->lpWaveFormat, 846 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION); 847 848 if (dwRet != 0) { 849 TRACE("Can't open low level audio device %d\n", dwRet); 850 dwRet = MCIERR_DEVICE_OPEN; 851 wmw->hWave = 0; 852 goto cleanUp; 853 } 854 855 /* make it so that 3 buffers per second are needed */ 856 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3); 857 858 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize); 859 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR); 860 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize; 861 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L; 862 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L; 863 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L; 864 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize; 865 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || 866 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) { 867 dwRet = MCIERR_INTERNAL; 868 goto cleanUp; 869 } 870 871 whidx = 0; 872 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 873 if (!wmw->hEvent) { 874 dwRet = MCIERR_OUT_OF_MEMORY; 875 goto cleanUp; 876 } 877 wmw->dwEventCount = 1L; /* for first buffer */ 878 879 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left); 880 if (hEvent) SetEvent(hEvent); 881 882 /* FIXME: this doesn't work if wmw->dwPosition != 0 */ 883 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) { 884 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left)); 885 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count); 886 if (count < 1) 887 break; 888 /* count is always <= bufsize, so this is correct regarding the 889 * waveOutPrepareHeader function 890 */ 891 waveHdr[whidx].dwBufferLength = count; 892 waveHdr[whidx].dwFlags &= ~WHDR_DONE; 893 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n", 894 &waveHdr[whidx], waveHdr[whidx].dwBufferLength); 895 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR)); 896 if (dwRet) { 897 ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet); 898 dwRet = MCIERR_HARDWARE; 899 break; 900 } 901 left -= count; 902 wmw->dwPosition += count; 903 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition); 904 /* InterlockedDecrement if and only if waveOutWrite is successful */ 905 WAVE_mciPlayWaitDone(wmw); 906 whidx ^= 1; 907 } 908 909 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */ 910 911 /* just to get rid of some race conditions between play, stop and pause */ 912 waveOutReset(wmw->hWave); 913 914 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)); 915 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR)); 916 917 cleanUp: 918 if (dwFlags & MCI_NOTIFY) 919 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL); 920 921 HeapFree(GetProcessHeap(), 0, waveHdr); 922 923 if (wmw->hWave) { 924 waveOutClose(wmw->hWave); 925 wmw->hWave = 0; 926 } 927 CloseHandle(wmw->hEvent); 928 wmw->hEvent = NULL; 929 930 wmw->dwStatus = MCI_MODE_STOP; 931 932 /* Let the potentially asynchronous commands support FAILURE notification. */ 933 if (oldcb) mciDriverNotify(oldcb, wDevID, 934 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); 935 936 return dwRet; 937 } 938 939 /************************************************************************** 940 * WAVE_mciRecordCallback [internal] 941 */ 942 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg, 943 DWORD_PTR dwInstance, 944 LPARAM dwParam1, LPARAM dwParam2) 945 { 946 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance; 947 LPWAVEHDR lpWaveHdr; 948 LONG count = 0; 949 950 switch (uMsg) { 951 case WIM_OPEN: 952 case WIM_CLOSE: 953 break; 954 case WIM_DATA: 955 lpWaveHdr = (LPWAVEHDR) dwParam1; 956 957 InterlockedIncrement(&wmw->dwEventCount); 958 959 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded); 960 961 lpWaveHdr->dwFlags &= ~WHDR_DONE; 962 if (count > 0) 963 wmw->dwPosition += count; 964 /* else error reporting ?? */ 965 if (wmw->dwStatus == MCI_MODE_RECORD) 966 { 967 /* Only queue up another buffer if we are recording. We could receive this 968 message also when waveInReset() is called, since it notifies on all wave 969 buffers that are outstanding. Queueing up more sometimes causes waveInClose 970 to fail. */ 971 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr)); 972 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition); 973 } 974 975 SetEvent(wmw->hEvent); 976 break; 977 default: 978 ERR("Unknown uMsg=%d\n", uMsg); 979 } 980 } 981 982 /****************************************************************** 983 * WAVE_mciRecordWaitDone [internal] 984 */ 985 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw) 986 { 987 for (;;) { 988 ResetEvent(wmw->hEvent); 989 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) { 990 break; 991 } 992 InterlockedIncrement(&wmw->dwEventCount); 993 994 WaitForSingleObject(wmw->hEvent, INFINITE); 995 } 996 } 997 998 /************************************************************************** 999 * WAVE_mciRecord [internal] 1000 */ 1001 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent) 1002 { 1003 LPMCI_RECORD_PARMS lpParms = (void*)pmt; 1004 DWORD end; 1005 DWORD dwRet = MMSYSERR_NOERROR; 1006 LONG bufsize; 1007 LPWAVEHDR waveHdr = NULL; 1008 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 1009 HANDLE oldcb; 1010 1011 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms); 1012 1013 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 1014 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1015 1016 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) { 1017 /* FIXME: parameters (start/end) in lpParams may not be used */ 1018 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms); 1019 } 1020 1021 /** This function will be called again by a thread when async is used. 1022 * We have to set MCI_MODE_RECORD before we do this so that the app can spin 1023 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread. 1024 */ 1025 if ( !(wmw->dwStatus == MCI_MODE_STOP) && 1026 !((wmw->dwStatus == MCI_MODE_RECORD) && (dwFlags & MCI_WAIT) && !wmw->hWave)) { 1027 return MCIERR_INTERNAL; 1028 } 1029 1030 wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */ 1031 wmw->dwStatus = MCI_MODE_RECORD; 1032 1033 if (!(dwFlags & MCI_WAIT)) { 1034 return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags, 1035 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS)); 1036 } 1037 1038 /* FIXME: we only re-create the RIFF structure from an existing file (if any) 1039 * we don't modify the wave part of an existing file (ie. we always erase an 1040 * existing content, we don't overwrite) 1041 */ 1042 HeapFree(GetProcessHeap(), 0, wmw->lpFileName); 1043 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName); 1044 if (dwRet != 0) return dwRet; 1045 1046 /* new RIFF file, lpWaveFormat now valid */ 1047 dwRet = WAVE_mciCreateRIFFSkeleton(wmw); 1048 if (dwRet != 0) return dwRet; 1049 1050 if (dwFlags & MCI_TO) { 1051 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); 1052 } else end = 0xFFFFFFFF; 1053 if (dwFlags & MCI_FROM) { 1054 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); 1055 if (wmw->ckWaveData.cksize < position) return MCIERR_OUTOFRANGE; 1056 /* Seek rounds down, so do we. */ 1057 position /= wmw->lpWaveFormat->nBlockAlign; 1058 position *= wmw->lpWaveFormat->nBlockAlign; 1059 wmw->dwPosition = position; 1060 } 1061 if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */ 1062 1063 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end); 1064 1065 oldcb = InterlockedExchangePointer(&wmw->hCallback, 1066 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL); 1067 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED); 1068 oldcb = NULL; 1069 1070 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \ 1071 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign) 1072 1073 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize); 1074 1075 /* Go back to the beginning of the chunk plus the requested position */ 1076 /* FIXME: I'm not sure this is correct, notably because some data linked to 1077 * the decompression state machine will not be correctly initialized. 1078 * Try it this way (other way would be to decompress from 0 up to dwPosition 1079 * and to start sending to hWave when dwPosition is reached). 1080 */ 1081 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */ 1082 1083 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, wmw->wInput, wmw->lpWaveFormat, 1084 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION); 1085 1086 if (dwRet != MMSYSERR_NOERROR) { 1087 TRACE("Can't open low level audio device %d\n", dwRet); 1088 dwRet = MCIERR_DEVICE_OPEN; 1089 wmw->hWave = 0; 1090 goto cleanUp; 1091 } 1092 1093 /* make it so that 3 buffers per second are needed */ 1094 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3); 1095 1096 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize); 1097 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR); 1098 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize; 1099 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L; 1100 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L; 1101 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L; 1102 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize; 1103 1104 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || 1105 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) { 1106 dwRet = MCIERR_INTERNAL; 1107 goto cleanUp; 1108 } 1109 1110 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || 1111 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) { 1112 dwRet = MCIERR_INTERNAL; 1113 goto cleanUp; 1114 } 1115 1116 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 1117 wmw->dwEventCount = 1L; /* for first buffer */ 1118 1119 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition); 1120 1121 waveInStart(wmw->hWave); 1122 1123 if (hEvent) SetEvent(hEvent); 1124 1125 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) { 1126 WAVE_mciRecordWaitDone(wmw); 1127 } 1128 /* Grab callback before another thread kicks in after we change dwStatus. */ 1129 if (dwFlags & MCI_NOTIFY) { 1130 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL); 1131 dwFlags &= ~MCI_NOTIFY; 1132 } 1133 /* needed so that the callback above won't add again the buffers returned by the reset */ 1134 wmw->dwStatus = MCI_MODE_STOP; 1135 1136 waveInReset(wmw->hWave); 1137 1138 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)); 1139 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR)); 1140 1141 dwRet = 0; 1142 1143 cleanUp: 1144 if (dwFlags & MCI_NOTIFY) 1145 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL); 1146 1147 HeapFree(GetProcessHeap(), 0, waveHdr); 1148 1149 if (wmw->hWave) { 1150 waveInClose(wmw->hWave); 1151 wmw->hWave = 0; 1152 } 1153 CloseHandle(wmw->hEvent); 1154 1155 wmw->dwStatus = MCI_MODE_STOP; 1156 1157 if (oldcb) mciDriverNotify(oldcb, wDevID, 1158 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); 1159 1160 return dwRet; 1161 1162 } 1163 1164 /************************************************************************** 1165 * WAVE_mciPause [internal] 1166 */ 1167 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 1168 { 1169 DWORD dwRet; 1170 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 1171 1172 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 1173 1174 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 1175 1176 switch (wmw->dwStatus) { 1177 case MCI_MODE_PLAY: 1178 dwRet = waveOutPause(wmw->hWave); 1179 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE; 1180 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */ 1181 ERR("waveOutPause error %d\n",dwRet); 1182 dwRet = MCIERR_INTERNAL; 1183 } 1184 break; 1185 case MCI_MODE_RECORD: 1186 dwRet = waveInStop(wmw->hWave); 1187 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE; 1188 else { 1189 ERR("waveInStop error %d\n",dwRet); 1190 dwRet = MCIERR_INTERNAL; 1191 } 1192 break; 1193 case MCI_MODE_PAUSE: 1194 dwRet = MMSYSERR_NOERROR; 1195 break; 1196 default: 1197 dwRet = MCIERR_NONAPPLICABLE_FUNCTION; 1198 } 1199 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms) 1200 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 1201 return dwRet; 1202 } 1203 1204 /************************************************************************** 1205 * WAVE_mciResume [internal] 1206 */ 1207 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 1208 { 1209 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 1210 DWORD dwRet; 1211 1212 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 1213 1214 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 1215 1216 switch (wmw->dwStatus) { 1217 case MCI_MODE_PAUSE: 1218 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */ 1219 if (wmw->fInput) { 1220 dwRet = waveInStart(wmw->hWave); 1221 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD; 1222 else { 1223 ERR("waveInStart error %d\n",dwRet); 1224 dwRet = MCIERR_INTERNAL; 1225 } 1226 } else { 1227 dwRet = waveOutRestart(wmw->hWave); 1228 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY; 1229 else { 1230 ERR("waveOutRestart error %d\n",dwRet); 1231 dwRet = MCIERR_INTERNAL; 1232 } 1233 } 1234 break; 1235 case MCI_MODE_PLAY: 1236 case MCI_MODE_RECORD: 1237 dwRet = MMSYSERR_NOERROR; 1238 break; 1239 default: 1240 dwRet = MCIERR_NONAPPLICABLE_FUNCTION; 1241 } 1242 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms) 1243 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 1244 return dwRet; 1245 } 1246 1247 /************************************************************************** 1248 * WAVE_mciSeek [internal] 1249 */ 1250 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms) 1251 { 1252 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 1253 DWORD position, dwRet; 1254 1255 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 1256 1257 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1258 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 1259 1260 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO); 1261 if (!position) return MCIERR_MISSING_PARAMETER; 1262 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE; 1263 1264 /* Stop sends MCI_NOTIFY_ABORTED when needed */ 1265 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0); 1266 if (dwRet != MMSYSERR_NOERROR) return dwRet; 1267 1268 if (dwFlags & MCI_TO) { 1269 position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); 1270 if (position > wmw->ckWaveData.cksize) 1271 return MCIERR_OUTOFRANGE; 1272 } else if (dwFlags & MCI_SEEK_TO_START) { 1273 position = 0; 1274 } else { 1275 position = wmw->ckWaveData.cksize; 1276 } 1277 /* Seek rounds down, unless at end */ 1278 if (position != wmw->ckWaveData.cksize) { 1279 position /= wmw->lpWaveFormat->nBlockAlign; 1280 position *= wmw->lpWaveFormat->nBlockAlign; 1281 } 1282 wmw->dwPosition = position; 1283 TRACE("Seeking to position=%u bytes\n", position); 1284 1285 if (dwFlags & MCI_NOTIFY) 1286 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 1287 1288 return MMSYSERR_NOERROR; 1289 } 1290 1291 /************************************************************************** 1292 * WAVE_mciSet [internal] 1293 */ 1294 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_SET_PARMS lpParms) 1295 { 1296 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 1297 1298 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 1299 1300 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1301 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 1302 1303 if (dwFlags & MCI_SET_TIME_FORMAT) { 1304 switch (lpParms->dwTimeFormat) { 1305 case MCI_FORMAT_MILLISECONDS: 1306 TRACE("MCI_FORMAT_MILLISECONDS !\n"); 1307 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS; 1308 break; 1309 case MCI_FORMAT_BYTES: 1310 TRACE("MCI_FORMAT_BYTES !\n"); 1311 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES; 1312 break; 1313 case MCI_FORMAT_SAMPLES: 1314 TRACE("MCI_FORMAT_SAMPLES !\n"); 1315 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES; 1316 break; 1317 default: 1318 WARN("Bad time format %u!\n", lpParms->dwTimeFormat); 1319 return MCIERR_BAD_TIME_FORMAT; 1320 } 1321 } 1322 if (dwFlags & MCI_SET_VIDEO) { 1323 TRACE("No support for video !\n"); 1324 return MCIERR_UNSUPPORTED_FUNCTION; 1325 } 1326 if (dwFlags & MCI_SET_DOOR_OPEN) { 1327 TRACE("No support for door open !\n"); 1328 return MCIERR_UNSUPPORTED_FUNCTION; 1329 } 1330 if (dwFlags & MCI_SET_DOOR_CLOSED) { 1331 TRACE("No support for door close !\n"); 1332 return MCIERR_UNSUPPORTED_FUNCTION; 1333 } 1334 if (dwFlags & MCI_SET_AUDIO) { 1335 if (dwFlags & MCI_SET_ON) { 1336 TRACE("MCI_SET_ON audio !\n"); 1337 } else if (dwFlags & MCI_SET_OFF) { 1338 TRACE("MCI_SET_OFF audio !\n"); 1339 } else { 1340 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n"); 1341 return MCIERR_BAD_INTEGER; 1342 } 1343 1344 switch (lpParms->dwAudio) 1345 { 1346 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break; 1347 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break; 1348 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break; 1349 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break; 1350 } 1351 } 1352 if (dwFlags & MCI_WAVE_INPUT) { 1353 TRACE("MCI_WAVE_INPUT = %d\n", lpParms->wInput); 1354 if (lpParms->wInput >= waveInGetNumDevs()) 1355 return MCIERR_OUTOFRANGE; 1356 if (wmw->wInput != (WORD)lpParms->wInput) 1357 WAVE_mciStop(wDevID, MCI_WAIT, NULL); 1358 wmw->wInput = lpParms->wInput; 1359 } 1360 if (dwFlags & MCI_WAVE_OUTPUT) { 1361 TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms->wOutput); 1362 if (lpParms->wOutput >= waveOutGetNumDevs()) 1363 return MCIERR_OUTOFRANGE; 1364 if (wmw->wOutput != (WORD)lpParms->wOutput) 1365 WAVE_mciStop(wDevID, MCI_WAIT, NULL); 1366 wmw->wOutput = lpParms->wOutput; 1367 } 1368 if (dwFlags & MCI_WAVE_SET_ANYINPUT) { 1369 TRACE("MCI_WAVE_SET_ANYINPUT\n"); 1370 if (wmw->wInput != (WORD)lpParms->wInput) 1371 WAVE_mciStop(wDevID, MCI_WAIT, NULL); 1372 wmw->wInput = WAVE_MAPPER; 1373 } 1374 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) { 1375 TRACE("MCI_WAVE_SET_ANYOUTPUT\n"); 1376 if (wmw->wOutput != (WORD)lpParms->wOutput) 1377 WAVE_mciStop(wDevID, MCI_WAIT, NULL); 1378 wmw->wOutput = WAVE_MAPPER; 1379 } 1380 /* Set wave format parameters is refused after Open or Record.*/ 1381 if (dwFlags & MCI_WAVE_SET_FORMATTAG) { 1382 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms->wFormatTag); 1383 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 1384 if (lpParms->wFormatTag != WAVE_FORMAT_PCM) 1385 return MCIERR_OUTOFRANGE; 1386 } 1387 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) { 1388 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 1389 wmw->wfxRef.nAvgBytesPerSec = lpParms->nAvgBytesPerSec; 1390 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec); 1391 } 1392 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) { 1393 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 1394 wmw->wfxRef.wBitsPerSample = lpParms->wBitsPerSample; 1395 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample); 1396 } 1397 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) { 1398 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 1399 wmw->wfxRef.nBlockAlign = lpParms->nBlockAlign; 1400 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign); 1401 } 1402 if (dwFlags & MCI_WAVE_SET_CHANNELS) { 1403 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 1404 wmw->wfxRef.nChannels = lpParms->nChannels; 1405 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels); 1406 } 1407 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) { 1408 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 1409 wmw->wfxRef.nSamplesPerSec = lpParms->nSamplesPerSec; 1410 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec); 1411 } 1412 if (dwFlags & MCI_NOTIFY) 1413 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 1414 return 0; 1415 } 1416 1417 /************************************************************************** 1418 * WAVE_mciSave [internal] 1419 */ 1420 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms) 1421 { 1422 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 1423 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet; 1424 1425 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms); 1426 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1427 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 1428 1429 if (dwFlags & MCI_WAIT) 1430 { 1431 FIXME("MCI_WAIT not implemented\n"); 1432 } 1433 WAVE_mciStop(wDevID, 0, NULL); 1434 1435 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0); 1436 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0); 1437 1438 ret = mmioClose(wmw->hFile, 0); 1439 wmw->hFile = 0; 1440 1441 /* 1442 If the destination file already exists, it has to be overwritten. (Behaviour 1443 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of 1444 my applications. We are making use of mmioRename, which WILL NOT overwrite 1445 the destination file (which is what Windows does, also verified in Win2K) 1446 So, lets delete the destination file before calling mmioRename. If the 1447 destination file DOESN'T exist, the delete will fail silently. Let's also be 1448 careful not to lose our previous error code. 1449 */ 1450 tmpRet = GetLastError(); 1451 DeleteFileW (lpParms->lpfilename); 1452 SetLastError(tmpRet); 1453 1454 /* FIXME: Open file.wav; Save; must not rename the original file. 1455 * Nor must Save a.wav; Save b.wav rename a. */ 1456 if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) { 1457 ret = MMSYSERR_NOERROR; 1458 } 1459 1460 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY)) 1461 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 1462 1463 if (ret == MMSYSERR_NOERROR) 1464 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename); 1465 1466 return ret; 1467 } 1468 1469 /************************************************************************** 1470 * WAVE_mciStatus [internal] 1471 */ 1472 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms) 1473 { 1474 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 1475 DWORD ret = 0; 1476 1477 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 1478 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1479 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 1480 if (!(dwFlags & MCI_STATUS_ITEM)) return MCIERR_MISSING_PARAMETER; 1481 1482 if (dwFlags & MCI_STATUS_ITEM) { 1483 switch (lpParms->dwItem) { 1484 case MCI_STATUS_CURRENT_TRACK: 1485 lpParms->dwReturn = 1; 1486 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn); 1487 break; 1488 case MCI_STATUS_LENGTH: 1489 if (!wmw->hFile) { 1490 lpParms->dwReturn = 0; 1491 return MCIERR_UNSUPPORTED_FUNCTION; 1492 } 1493 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */ 1494 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize); 1495 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn); 1496 break; 1497 case MCI_STATUS_MODE: 1498 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus); 1499 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus); 1500 ret = MCI_RESOURCE_RETURNED; 1501 break; 1502 case MCI_STATUS_MEDIA_PRESENT: 1503 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n"); 1504 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1505 ret = MCI_RESOURCE_RETURNED; 1506 break; 1507 case MCI_STATUS_NUMBER_OF_TRACKS: 1508 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */ 1509 lpParms->dwReturn = 1; 1510 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn); 1511 break; 1512 case MCI_STATUS_POSITION: 1513 if (!wmw->hFile) { 1514 lpParms->dwReturn = 0; 1515 return MCIERR_UNSUPPORTED_FUNCTION; 1516 } 1517 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */ 1518 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, 1519 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition); 1520 TRACE("MCI_STATUS_POSITION %s => %lu\n", 1521 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn); 1522 break; 1523 case MCI_STATUS_READY: 1524 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ? 1525 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1526 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn)); 1527 ret = MCI_RESOURCE_RETURNED; 1528 break; 1529 case MCI_STATUS_TIME_FORMAT: 1530 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat); 1531 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn); 1532 ret = MCI_RESOURCE_RETURNED; 1533 break; 1534 case MCI_WAVE_INPUT: 1535 if (wmw->wInput != (WORD)WAVE_MAPPER) 1536 lpParms->dwReturn = wmw->wInput; 1537 else { 1538 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S); 1539 ret = MCI_RESOURCE_RETURNED; 1540 } 1541 TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw->wInput); 1542 break; 1543 case MCI_WAVE_OUTPUT: 1544 if (wmw->wOutput != (WORD)WAVE_MAPPER) 1545 lpParms->dwReturn = wmw->wOutput; 1546 else { 1547 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S); 1548 ret = MCI_RESOURCE_RETURNED; 1549 } 1550 TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw->wOutput); 1551 break; 1552 /* It is always ok to query wave format parameters, 1553 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */ 1554 case MCI_WAVE_STATUS_FORMATTAG: 1555 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM) 1556 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag; 1557 else { 1558 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_FORMAT_PCM, WAVE_FORMAT_PCM_S); 1559 ret = MCI_RESOURCE_RETURNED; 1560 } 1561 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn); 1562 break; 1563 case MCI_WAVE_STATUS_AVGBYTESPERSEC: 1564 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec; 1565 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn); 1566 break; 1567 case MCI_WAVE_STATUS_BITSPERSAMPLE: 1568 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample; 1569 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn); 1570 break; 1571 case MCI_WAVE_STATUS_BLOCKALIGN: 1572 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign; 1573 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn); 1574 break; 1575 case MCI_WAVE_STATUS_CHANNELS: 1576 lpParms->dwReturn = wmw->lpWaveFormat->nChannels; 1577 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn); 1578 break; 1579 case MCI_WAVE_STATUS_SAMPLESPERSEC: 1580 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec; 1581 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn); 1582 break; 1583 case MCI_WAVE_STATUS_LEVEL: 1584 TRACE("MCI_WAVE_STATUS_LEVEL !\n"); 1585 lpParms->dwReturn = 0xAAAA5555; 1586 break; 1587 default: 1588 WARN("unknown command %08X !\n", lpParms->dwItem); 1589 return MCIERR_UNSUPPORTED_FUNCTION; 1590 } 1591 } 1592 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0) 1593 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 1594 return ret; 1595 } 1596 1597 /************************************************************************** 1598 * WAVE_mciGetDevCaps [internal] 1599 */ 1600 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags, 1601 LPMCI_GETDEVCAPS_PARMS lpParms) 1602 { 1603 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 1604 DWORD ret = 0; 1605 1606 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 1607 1608 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1609 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 1610 1611 if (dwFlags & MCI_GETDEVCAPS_ITEM) { 1612 switch(lpParms->dwItem) { 1613 case MCI_GETDEVCAPS_DEVICE_TYPE: 1614 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO); 1615 ret = MCI_RESOURCE_RETURNED; 1616 break; 1617 case MCI_GETDEVCAPS_HAS_AUDIO: 1618 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1619 ret = MCI_RESOURCE_RETURNED; 1620 break; 1621 case MCI_GETDEVCAPS_HAS_VIDEO: 1622 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 1623 ret = MCI_RESOURCE_RETURNED; 1624 break; 1625 case MCI_GETDEVCAPS_USES_FILES: 1626 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1627 ret = MCI_RESOURCE_RETURNED; 1628 break; 1629 case MCI_GETDEVCAPS_COMPOUND_DEVICE: 1630 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1631 ret = MCI_RESOURCE_RETURNED; 1632 break; 1633 case MCI_GETDEVCAPS_CAN_RECORD: 1634 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1635 ret = MCI_RESOURCE_RETURNED; 1636 break; 1637 case MCI_GETDEVCAPS_CAN_EJECT: 1638 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 1639 ret = MCI_RESOURCE_RETURNED; 1640 break; 1641 case MCI_GETDEVCAPS_CAN_PLAY: 1642 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1643 ret = MCI_RESOURCE_RETURNED; 1644 break; 1645 case MCI_GETDEVCAPS_CAN_SAVE: 1646 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1647 ret = MCI_RESOURCE_RETURNED; 1648 break; 1649 case MCI_WAVE_GETDEVCAPS_INPUTS: 1650 lpParms->dwReturn = waveInGetNumDevs(); 1651 break; 1652 case MCI_WAVE_GETDEVCAPS_OUTPUTS: 1653 lpParms->dwReturn = waveOutGetNumDevs(); 1654 break; 1655 default: 1656 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem); 1657 return MCIERR_UNRECOGNIZED_COMMAND; 1658 } 1659 } else { 1660 WARN("No GetDevCaps-Item !\n"); 1661 return MCIERR_UNRECOGNIZED_COMMAND; 1662 } 1663 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0) 1664 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 1665 return ret; 1666 } 1667 1668 /************************************************************************** 1669 * WAVE_mciInfo [internal] 1670 */ 1671 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms) 1672 { 1673 DWORD ret = 0; 1674 LPCWSTR str = 0; 1675 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 1676 1677 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 1678 1679 if (!lpParms || !lpParms->lpstrReturn) 1680 return MCIERR_NULL_PARAMETER_BLOCK; 1681 1682 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize); 1683 1684 if (wmw == NULL) { 1685 ret = MCIERR_INVALID_DEVICE_ID; 1686 } else { 1687 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0}; 1688 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0}; 1689 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0}; 1690 1691 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) { 1692 case MCI_INFO_PRODUCT: str = wszAudio; break; 1693 case MCI_INFO_FILE: str = wmw->lpFileName; break; 1694 case MCI_WAVE_INPUT: str = wszWaveIn; break; 1695 case MCI_WAVE_OUTPUT: str = wszWaveOut; break; 1696 default: 1697 WARN("Don't know this info command (%u)\n", dwFlags); 1698 ret = MCIERR_UNRECOGNIZED_KEYWORD; 1699 } 1700 } 1701 if (!ret) { 1702 if (lpParms->dwRetSize) { 1703 WCHAR zero = 0; 1704 /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize 1705 * to the number of characters written, excluding \0. */ 1706 lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize); 1707 } else ret = MCIERR_PARAM_OVERFLOW; 1708 } 1709 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY)) 1710 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 1711 return ret; 1712 } 1713 1714 /************************************************************************** 1715 * DriverProc (MCIWAVE.@) 1716 */ 1717 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, 1718 LPARAM dwParam1, LPARAM dwParam2) 1719 { 1720 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n", 1721 dwDevID, hDriv, wMsg, dwParam1, dwParam2); 1722 1723 switch (wMsg) { 1724 case DRV_LOAD: return 1; 1725 case DRV_FREE: return 1; 1726 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2); 1727 case DRV_CLOSE: return WAVE_drvClose(dwDevID); 1728 case DRV_ENABLE: return 1; 1729 case DRV_DISABLE: return 1; 1730 case DRV_QUERYCONFIGURE: return 1; 1731 case DRV_CONFIGURE: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1; 1732 case DRV_INSTALL: return DRVCNF_RESTART; 1733 case DRV_REMOVE: return DRVCNF_RESTART; 1734 } 1735 1736 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION; 1737 1738 switch (wMsg) { 1739 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2); 1740 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1741 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1742 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL); 1743 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL); 1744 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1745 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_WAVE_SET_PARMS) dwParam2); 1746 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1747 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1748 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2); 1749 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2); 1750 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2); 1751 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2); 1752 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2); 1753 /* commands that should be supported */ 1754 case MCI_LOAD: 1755 case MCI_FREEZE: 1756 case MCI_PUT: 1757 case MCI_REALIZE: 1758 case MCI_UNFREEZE: 1759 case MCI_UPDATE: 1760 case MCI_WHERE: 1761 case MCI_STEP: 1762 case MCI_SPIN: 1763 case MCI_ESCAPE: 1764 case MCI_COPY: 1765 case MCI_CUT: 1766 case MCI_DELETE: 1767 case MCI_PASTE: 1768 FIXME("Unsupported command [%u]\n", wMsg); 1769 break; 1770 case MCI_WINDOW: 1771 TRACE("Unsupported command [%u]\n", wMsg); 1772 break; 1773 /* option which can be silenced */ 1774 case MCI_CONFIGURE: 1775 return 0; 1776 case MCI_OPEN: 1777 case MCI_CLOSE: 1778 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n"); 1779 break; 1780 default: 1781 FIXME("is probably wrong msg [%u]\n", wMsg); 1782 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); 1783 } 1784 return MCIERR_UNRECOGNIZED_COMMAND; 1785 } 1786