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