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