Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenmciwave.c
Go to the documentation of this file.
00001 /* 00002 * Wine Driver for MCI wave forms 00003 * 00004 * Copyright 1994 Martin Ayotte 00005 * 1999,2000,2005 Eric Pouech 00006 * 2000 Francois Jacques 00007 * 2009 Jörg Höhle 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 2.1 of the License, or (at your option) any later version. 00013 * 00014 * This library is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Lesser General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Lesser General Public 00020 * License along with this library; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00022 */ 00023 00024 #include <assert.h> 00025 #include <stdarg.h> 00026 00027 #include "windef.h" 00028 #include "winbase.h" 00029 #include "wingdi.h" 00030 #include "winuser.h" 00031 #include "mmddk.h" 00032 #include "wownt32.h" 00033 #include "digitalv.h" 00034 #include "wine/debug.h" 00035 #include "wine/unicode.h" 00036 00037 WINE_DEFAULT_DEBUG_CHANNEL(mciwave); 00038 00039 typedef struct { 00040 UINT wDevID; 00041 HANDLE hWave; 00042 int nUseCount; /* Incremented for each shared open */ 00043 HMMIO hFile; /* mmio file handle open as Element */ 00044 MCIDEVICEID wNotifyDeviceID; /* MCI device ID with a pending notification */ 00045 HANDLE hCallback; /* Callback handle for pending notification */ 00046 LPWSTR lpFileName; /* Name of file (if any) */ 00047 WAVEFORMATEX wfxRef; 00048 LPWAVEFORMATEX lpWaveFormat; /* Points to wfxRef until set by OPEN or RECORD */ 00049 BOOL fInput; /* FALSE = Output, TRUE = Input */ 00050 WORD wInput; /* wave input device */ 00051 WORD wOutput; /* wave output device */ 00052 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */ 00053 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */ 00054 DWORD dwPosition; /* position in bytes in chunk */ 00055 HANDLE hEvent; /* for synchronization */ 00056 LONG dwEventCount; /* for synchronization */ 00057 MMCKINFO ckMainRIFF; /* main RIFF chunk */ 00058 MMCKINFO ckWaveData; /* data chunk */ 00059 } WINE_MCIWAVE; 00060 00061 /* =================================================================== 00062 * =================================================================== 00063 * FIXME: should be using the new mmThreadXXXX functions from WINMM 00064 * instead of those 00065 * it would require to add a wine internal flag to mmThreadCreate 00066 * in order to pass a 32 bit function instead of a 16 bit one 00067 * =================================================================== 00068 * =================================================================== */ 00069 00070 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt); 00071 00072 struct SCA { 00073 async_cmd cmd; 00074 HANDLE evt; 00075 UINT wDevID; 00076 DWORD_PTR dwParam1; 00077 DWORD_PTR dwParam2; 00078 }; 00079 00080 /************************************************************************** 00081 * MCI_SCAStarter [internal] 00082 */ 00083 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg) 00084 { 00085 struct SCA* sca = (struct SCA*)arg; 00086 DWORD ret; 00087 00088 TRACE("In thread before async command (%08x,%08lx,%08lx)\n", 00089 sca->wDevID, sca->dwParam1, sca->dwParam2); 00090 ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt); 00091 TRACE("In thread after async command (%08x,%08lx,%08lx)\n", 00092 sca->wDevID, sca->dwParam1, sca->dwParam2); 00093 HeapFree(GetProcessHeap(), 0, sca); 00094 return ret; 00095 } 00096 00097 /************************************************************************** 00098 * MCI_SendCommandAsync [internal] 00099 */ 00100 static DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1, 00101 DWORD_PTR dwParam2, UINT size) 00102 { 00103 HANDLE handles[2]; 00104 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size); 00105 00106 if (sca == 0) 00107 return MCIERR_OUT_OF_MEMORY; 00108 00109 sca->wDevID = wDevID; 00110 sca->cmd = cmd; 00111 sca->dwParam1 = dwParam1; 00112 00113 if (size && dwParam2) { 00114 sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA); 00115 /* copy structure passed by program in dwParam2 to be sure 00116 * we can still use it whatever the program does 00117 */ 00118 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size); 00119 } else { 00120 sca->dwParam2 = dwParam2; 00121 } 00122 00123 if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL || 00124 (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) { 00125 WARN("Couldn't allocate thread for async command handling, sending synchronously\n"); 00126 if (handles[1]) CloseHandle(handles[1]); 00127 sca->evt = NULL; 00128 return MCI_SCAStarter(&sca); 00129 } 00130 00131 SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL); 00132 /* wait until either: 00133 * - the thread has finished (handles[0], likely an error) 00134 * - init phase of async command is done (handles[1]) 00135 */ 00136 WaitForMultipleObjects(2, handles, FALSE, INFINITE); 00137 CloseHandle(handles[0]); 00138 CloseHandle(handles[1]); 00139 return 0; 00140 } 00141 00142 /*======================================================================* 00143 * MCI WAVE implementation * 00144 *======================================================================*/ 00145 00146 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms); 00147 00148 /************************************************************************** 00149 * MCIWAVE_drvOpen [internal] 00150 */ 00151 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp) 00152 { 00153 WINE_MCIWAVE* wmw; 00154 00155 if (modp == NULL) return 0xFFFFFFFF; 00156 00157 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE)); 00158 00159 if (!wmw) 00160 return 0; 00161 00162 wmw->wDevID = modp->wDeviceID; 00163 mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw); 00164 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE; 00165 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO; 00166 00167 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM; 00168 wmw->wfxRef.nChannels = 1; /* MONO */ 00169 wmw->wfxRef.nSamplesPerSec = 11025; 00170 wmw->wfxRef.nAvgBytesPerSec = 11025; 00171 wmw->wfxRef.nBlockAlign = 1; 00172 wmw->wfxRef.wBitsPerSample = 8; 00173 wmw->wfxRef.cbSize = 0; /* don't care */ 00174 00175 return modp->wDeviceID; 00176 } 00177 00178 /************************************************************************** 00179 * MCIWAVE_drvClose [internal] 00180 */ 00181 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID) 00182 { 00183 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID); 00184 00185 if (wmw) { 00186 HeapFree(GetProcessHeap(), 0, wmw); 00187 mciSetDriverData(dwDevID, 0); 00188 return 1; 00189 } 00190 return (dwDevID == 0xFFFFFFFF) ? 1 : 0; 00191 } 00192 00193 /************************************************************************** 00194 * WAVE_mciGetOpenDev [internal] 00195 */ 00196 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID) 00197 { 00198 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID); 00199 00200 if (wmw == NULL || wmw->nUseCount == 0) { 00201 WARN("Invalid wDevID=%u\n", wDevID); 00202 return 0; 00203 } 00204 return wmw; 00205 } 00206 00207 /************************************************************************** 00208 * WAVE_mciNotify [internal] 00209 * 00210 * Notifications in MCI work like a 1-element queue. 00211 * Each new notification request supersedes the previous one. 00212 * This affects Play and Record; other commands are immediate. 00213 */ 00214 static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus) 00215 { 00216 /* We simply save one parameter by not passing the wDevID local 00217 * to the command. They are the same (via mciGetDriverData). 00218 */ 00219 MCIDEVICEID wDevID = wmw->wNotifyDeviceID; 00220 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL); 00221 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED); 00222 mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus); 00223 } 00224 00225 /************************************************************************** 00226 * WAVE_ConvertByteToTimeFormat [internal] 00227 */ 00228 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val) 00229 { 00230 DWORD ret = 0; 00231 00232 switch (wmw->dwMciTimeFormat) { 00233 case MCI_FORMAT_MILLISECONDS: 00234 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec); 00235 break; 00236 case MCI_FORMAT_BYTES: 00237 ret = val; 00238 break; 00239 case MCI_FORMAT_SAMPLES: 00240 ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec); 00241 break; 00242 default: 00243 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat); 00244 } 00245 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret); 00246 return ret; 00247 } 00248 00249 /************************************************************************** 00250 * WAVE_ConvertTimeFormatToByte [internal] 00251 */ 00252 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val) 00253 { 00254 DWORD ret = 0; 00255 00256 switch (wmw->dwMciTimeFormat) { 00257 case MCI_FORMAT_MILLISECONDS: 00258 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000); 00259 if (ret > wmw->ckWaveData.cksize && 00260 val == WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize)) 00261 ret = wmw->ckWaveData.cksize; 00262 break; 00263 case MCI_FORMAT_BYTES: 00264 ret = val; 00265 break; 00266 case MCI_FORMAT_SAMPLES: 00267 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec); 00268 break; 00269 default: 00270 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat); 00271 } 00272 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret); 00273 return ret; 00274 } 00275 00276 /************************************************************************** 00277 * WAVE_mciReadFmt [internal] 00278 */ 00279 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF) 00280 { 00281 MMCKINFO mmckInfo; 00282 LONG r; 00283 LPWAVEFORMATEX pwfx; 00284 00285 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' '); 00286 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0) 00287 return MCIERR_INVALID_FILE; 00288 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n", 00289 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize); 00290 00291 pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize); 00292 if (!pwfx) return MCIERR_OUT_OF_MEMORY; 00293 00294 r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize); 00295 if (r < sizeof(PCMWAVEFORMAT)) { 00296 HeapFree(GetProcessHeap(), 0, pwfx); 00297 return MCIERR_INVALID_FILE; 00298 } 00299 TRACE("wFormatTag=%04X !\n", pwfx->wFormatTag); 00300 TRACE("nChannels=%d\n", pwfx->nChannels); 00301 TRACE("nSamplesPerSec=%d\n", pwfx->nSamplesPerSec); 00302 TRACE("nAvgBytesPerSec=%d\n", pwfx->nAvgBytesPerSec); 00303 TRACE("nBlockAlign=%d\n", pwfx->nBlockAlign); 00304 TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample); 00305 if (r >= sizeof(WAVEFORMATEX)) 00306 TRACE("cbSize=%u !\n", pwfx->cbSize); 00307 if ((pwfx->wFormatTag != WAVE_FORMAT_PCM) 00308 && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) { 00309 HeapFree(GetProcessHeap(), 0, pwfx); 00310 return MCIERR_INVALID_FILE; 00311 } 00312 wmw->lpWaveFormat = pwfx; 00313 00314 mmioAscend(wmw->hFile, &mmckInfo, 0); 00315 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a'); 00316 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) { 00317 TRACE("can't find data chunk\n"); 00318 return MCIERR_INVALID_FILE; 00319 } 00320 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n", 00321 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize); 00322 return 0; 00323 } 00324 00325 /************************************************************************** 00326 * WAVE_mciDefaultFmt [internal] 00327 * 00328 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef 00329 * until either Open File or Record. It becomes immutable afterwards, 00330 * i.e. Set wave format or channels etc. is subsequently refused. 00331 */ 00332 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw) 00333 { 00334 wmw->lpWaveFormat = &wmw->wfxRef; 00335 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM; 00336 wmw->lpWaveFormat->nChannels = 1; 00337 wmw->lpWaveFormat->nSamplesPerSec = 11025; 00338 wmw->lpWaveFormat->nAvgBytesPerSec = 11025; 00339 wmw->lpWaveFormat->nBlockAlign = 1; 00340 wmw->lpWaveFormat->wBitsPerSample = 8; 00341 wmw->lpWaveFormat->cbSize = 0; 00342 } 00343 00344 /************************************************************************** 00345 * WAVE_mciCreateRIFFSkeleton [internal] 00346 */ 00347 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw) 00348 { 00349 MMCKINFO ckWaveFormat; 00350 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF); 00351 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData); 00352 00353 lpckRIFF->ckid = FOURCC_RIFF; 00354 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E'); 00355 lpckRIFF->cksize = 0; 00356 00357 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF)) 00358 goto err; 00359 00360 ckWaveFormat.fccType = 0; 00361 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' '); 00362 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT); 00363 00364 /* Set wave format accepts PCM only, however open an 00365 * existing ADPCM file, record into it and the MCI will 00366 * happily save back in that format. */ 00367 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) { 00368 if (wmw->lpWaveFormat->nBlockAlign != 00369 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) { 00370 WORD size = wmw->lpWaveFormat->nChannels * 00371 wmw->lpWaveFormat->wBitsPerSample/8; 00372 WARN("Incorrect nBlockAlign (%d), setting it to %d\n", 00373 wmw->lpWaveFormat->nBlockAlign, size); 00374 wmw->lpWaveFormat->nBlockAlign = size; 00375 } 00376 if (wmw->lpWaveFormat->nAvgBytesPerSec != 00377 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) { 00378 DWORD speed = wmw->lpWaveFormat->nSamplesPerSec * 00379 wmw->lpWaveFormat->nBlockAlign; 00380 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n", 00381 wmw->lpWaveFormat->nAvgBytesPerSec, speed); 00382 wmw->lpWaveFormat->nAvgBytesPerSec = speed; 00383 } 00384 } 00385 if (wmw->lpWaveFormat == &wmw->wfxRef) { 00386 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX)); 00387 if (!pwfx) return MCIERR_OUT_OF_MEMORY; 00388 /* Set wave format accepts PCM only so the size is known. */ 00389 assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM); 00390 *pwfx = wmw->wfxRef; 00391 wmw->lpWaveFormat = pwfx; 00392 } 00393 00394 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0)) 00395 goto err; 00396 00397 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag) 00398 ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize)) 00399 goto err; 00400 00401 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0)) 00402 goto err; 00403 00404 lpckWaveData->cksize = 0; 00405 lpckWaveData->fccType = 0; 00406 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a'); 00407 00408 /* create data chunk */ 00409 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0)) 00410 goto err; 00411 00412 return 0; 00413 00414 err: 00415 /* mciClose takes care of wmw->lpWaveFormat. */ 00416 return MCIERR_INVALID_FILE; 00417 } 00418 00419 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName) 00420 { 00421 WCHAR szTmpPath[MAX_PATH]; 00422 WCHAR szPrefix[4]; 00423 DWORD dwRet = MMSYSERR_NOERROR; 00424 00425 szPrefix[0] = 'M'; 00426 szPrefix[1] = 'C'; 00427 szPrefix[2] = 'I'; 00428 szPrefix[3] = '\0'; 00429 00430 if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) { 00431 WARN("can't retrieve temp path!\n"); 00432 *pszTmpFileName = NULL; 00433 return MCIERR_FILE_NOT_FOUND; 00434 } 00435 00436 *pszTmpFileName = HeapAlloc(GetProcessHeap(), 00437 HEAP_ZERO_MEMORY, 00438 MAX_PATH * sizeof(WCHAR)); 00439 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) { 00440 WARN("can't retrieve temp file name!\n"); 00441 HeapFree(GetProcessHeap(), 0, *pszTmpFileName); 00442 return MCIERR_FILE_NOT_FOUND; 00443 } 00444 00445 TRACE("%s!\n", debugstr_w(*pszTmpFileName)); 00446 00447 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) { 00448 00449 *hFile = mmioOpenW(*pszTmpFileName, NULL, 00450 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE); 00451 00452 if (*hFile == 0) { 00453 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName)); 00454 /* temporary file could not be created. clean filename. */ 00455 HeapFree(GetProcessHeap(), 0, *pszTmpFileName); 00456 dwRet = MCIERR_FILE_NOT_FOUND; 00457 } 00458 } 00459 return dwRet; 00460 } 00461 00462 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename) 00463 { 00464 LRESULT dwRet = MMSYSERR_NOERROR; 00465 LPWSTR fn; 00466 00467 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR)); 00468 if (!fn) return MCIERR_OUT_OF_MEMORY; 00469 strcpyW(fn, filename); 00470 HeapFree(GetProcessHeap(), 0, wmw->lpFileName); 00471 wmw->lpFileName = fn; 00472 00473 if (strlenW(filename) > 0) { 00474 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */ 00475 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename)); 00476 00477 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL, 00478 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ); 00479 00480 if (wmw->hFile == 0) { 00481 WARN("can't find file=%s!\n", debugstr_w(filename)); 00482 dwRet = MCIERR_FILE_NOT_FOUND; 00483 } 00484 else 00485 { 00486 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF; 00487 00488 /* make sure we're at the beginning of the file */ 00489 mmioSeek(wmw->hFile, 0, SEEK_SET); 00490 00491 /* first reading of this file. read the waveformat chunk */ 00492 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) { 00493 dwRet = MCIERR_INVALID_FILE; 00494 } else { 00495 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n", 00496 (LPSTR)&(lpckMainRIFF->ckid), 00497 (LPSTR) &(lpckMainRIFF->fccType), 00498 (lpckMainRIFF->cksize)); 00499 00500 if ((lpckMainRIFF->ckid != FOURCC_RIFF) || 00501 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) { 00502 dwRet = MCIERR_INVALID_FILE; 00503 } else { 00504 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF); 00505 } 00506 } 00507 } 00508 } 00509 return dwRet; 00510 } 00511 00512 /************************************************************************** 00513 * WAVE_mciOpen [internal] 00514 */ 00515 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms) 00516 { 00517 DWORD dwRet = 0; 00518 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID); 00519 00520 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms); 00521 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 00522 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 00523 00524 if (dwFlags & MCI_OPEN_SHAREABLE) 00525 return MCIERR_UNSUPPORTED_FUNCTION; 00526 00527 if (wmw->nUseCount > 0) { 00528 /* The driver is already opened on this channel 00529 * Wave driver cannot be shared 00530 */ 00531 return MCIERR_DEVICE_OPEN; 00532 } 00533 00534 wmw->nUseCount++; 00535 00536 wmw->wInput = wmw->wOutput = WAVE_MAPPER; 00537 wmw->fInput = FALSE; 00538 wmw->hWave = 0; 00539 wmw->dwStatus = MCI_MODE_NOT_READY; 00540 wmw->hFile = 0; 00541 wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */ 00542 wmw->hCallback = NULL; 00543 WAVE_mciDefaultFmt(wmw); 00544 00545 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID); 00546 /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */ 00547 wmw->wNotifyDeviceID = wDevID; 00548 00549 if (dwFlags & MCI_OPEN_ELEMENT) { 00550 if (dwFlags & MCI_OPEN_ELEMENT_ID) { 00551 /* could it be that (DWORD)lpOpenParms->lpstrElementName 00552 * contains the hFile value ? 00553 */ 00554 dwRet = MCIERR_UNRECOGNIZED_COMMAND; 00555 } else { 00556 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName); 00557 } 00558 } 00559 TRACE("hFile=%p\n", wmw->hFile); 00560 00561 if (dwRet == 0) { 00562 wmw->dwPosition = 0; 00563 00564 wmw->dwStatus = MCI_MODE_STOP; 00565 00566 if (dwFlags & MCI_NOTIFY) 00567 WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 00568 } else { 00569 wmw->nUseCount--; 00570 if (wmw->hFile != 0) 00571 mmioClose(wmw->hFile, 0); 00572 wmw->hFile = 0; 00573 HeapFree(GetProcessHeap(), 0, wmw->lpFileName); 00574 wmw->lpFileName = NULL; 00575 } 00576 return dwRet; 00577 } 00578 00579 /************************************************************************** 00580 * WAVE_mciCue [internal] 00581 */ 00582 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 00583 { 00584 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 00585 00586 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 00587 00588 /* Tests on systems without sound drivers show that Cue, like 00589 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE. 00590 * The first Cue Notify does not immediately return the 00591 * notification, as if a player or recorder thread is started. 00592 * PAUSE mode is reported when successful, but this mode is 00593 * different from the normal Pause, because a) Pause then returns 00594 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is 00595 * still accepted, returning the original notification as ABORTED. 00596 * I.e. Cue allows subsequent format changes, unlike Record or 00597 * Open file, closes winmm if the format changes and stops this 00598 * thread. 00599 * Wine creates one player or recorder thread per async. Play or 00600 * Record command. Notification behaviour suggests that MS-W* 00601 * reuses a single thread to improve response times. Having Cue 00602 * start this thread early helps to improve Play/Record's initial 00603 * response time. In effect, Cue is a performance hint, which 00604 * justifies our almost no-op implementation. 00605 */ 00606 00607 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 00608 if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION; 00609 00610 if ((dwFlags & MCI_NOTIFY) && lpParms) 00611 WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL); 00612 00613 return MMSYSERR_NOERROR; 00614 } 00615 00616 /************************************************************************** 00617 * WAVE_mciStop [internal] 00618 */ 00619 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 00620 { 00621 DWORD dwRet = 0; 00622 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 00623 00624 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 00625 00626 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 00627 00628 if (wmw->dwStatus != MCI_MODE_STOP) { 00629 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL); 00630 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED); 00631 } 00632 00633 /* wait for playback thread (if any) to exit before processing further */ 00634 switch (wmw->dwStatus) { 00635 case MCI_MODE_PAUSE: 00636 case MCI_MODE_PLAY: 00637 case MCI_MODE_RECORD: 00638 { 00639 int oldStat = wmw->dwStatus; 00640 wmw->dwStatus = MCI_MODE_NOT_READY; 00641 if (oldStat == MCI_MODE_PAUSE) 00642 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave); 00643 } 00644 while (wmw->dwStatus != MCI_MODE_STOP) 00645 Sleep(10); 00646 break; 00647 } 00648 00649 /* sanity resets */ 00650 wmw->dwStatus = MCI_MODE_STOP; 00651 00652 if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet) 00653 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 00654 00655 return dwRet; 00656 } 00657 00658 /************************************************************************** 00659 * WAVE_mciClose [internal] 00660 */ 00661 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 00662 { 00663 DWORD dwRet = 0; 00664 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 00665 00666 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 00667 00668 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 00669 00670 if (wmw->dwStatus != MCI_MODE_STOP) { 00671 /* mciStop handles MCI_NOTIFY_ABORTED */ 00672 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms); 00673 } 00674 00675 wmw->nUseCount--; 00676 00677 if (wmw->nUseCount == 0) { 00678 if (wmw->hFile != 0) { 00679 mmioClose(wmw->hFile, 0); 00680 wmw->hFile = 0; 00681 } 00682 } 00683 00684 if (wmw->lpWaveFormat != &wmw->wfxRef) 00685 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat); 00686 wmw->lpWaveFormat = &wmw->wfxRef; 00687 HeapFree(GetProcessHeap(), 0, wmw->lpFileName); 00688 wmw->lpFileName = NULL; 00689 00690 if ((dwFlags & MCI_NOTIFY) && lpParms) { 00691 WAVE_mciNotify(lpParms->dwCallback, wmw, 00692 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE); 00693 } 00694 00695 return 0; 00696 } 00697 00698 /************************************************************************** 00699 * WAVE_mciPlayCallback [internal] 00700 */ 00701 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg, 00702 DWORD_PTR dwInstance, 00703 LPARAM dwParam1, LPARAM dwParam2) 00704 { 00705 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance; 00706 00707 switch (uMsg) { 00708 case WOM_OPEN: 00709 case WOM_CLOSE: 00710 break; 00711 case WOM_DONE: 00712 InterlockedIncrement(&wmw->dwEventCount); 00713 TRACE("Returning waveHdr=%lx\n", dwParam1); 00714 SetEvent(wmw->hEvent); 00715 break; 00716 default: 00717 ERR("Unknown uMsg=%d\n", uMsg); 00718 } 00719 } 00720 00721 /****************************************************************** 00722 * WAVE_mciPlayWaitDone [internal] 00723 */ 00724 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw) 00725 { 00726 for (;;) { 00727 ResetEvent(wmw->hEvent); 00728 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) { 00729 break; 00730 } 00731 InterlockedIncrement(&wmw->dwEventCount); 00732 00733 WaitForSingleObject(wmw->hEvent, INFINITE); 00734 } 00735 } 00736 00737 /************************************************************************** 00738 * WAVE_mciPlay [internal] 00739 */ 00740 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent) 00741 { 00742 LPMCI_PLAY_PARMS lpParms = (void*)pmt; 00743 DWORD end; 00744 LONG bufsize, count, left; 00745 DWORD dwRet; 00746 LPWAVEHDR waveHdr = NULL; 00747 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 00748 HANDLE oldcb; 00749 int whidx; 00750 00751 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms); 00752 00753 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 00754 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 00755 00756 if (wmw->hFile == 0) { 00757 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName)); 00758 return MCIERR_FILE_NOT_FOUND; 00759 } 00760 00761 if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput && !(dwFlags & (MCI_FROM | MCI_TO))) { 00762 /* FIXME: notification is different with Resume than Play */ 00763 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms); 00764 } 00765 00770 if ( !(wmw->dwStatus == MCI_MODE_STOP) && 00771 !((wmw->dwStatus == MCI_MODE_PLAY) && (dwFlags & MCI_WAIT) && !wmw->hWave)) { 00772 /* FIXME: Check FROM/TO parameters first. */ 00773 /* FIXME: Play; Play [notify|wait] must hook into the running player. */ 00774 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, NULL); 00775 if (dwRet) return dwRet; 00776 } 00777 00778 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) { 00779 if (wmw->lpWaveFormat->nBlockAlign != 00780 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) { 00781 WARN("Incorrect nBlockAlign (%d), setting it to %d\n", 00782 wmw->lpWaveFormat->nBlockAlign, 00783 wmw->lpWaveFormat->nChannels * 00784 wmw->lpWaveFormat->wBitsPerSample/8); 00785 wmw->lpWaveFormat->nBlockAlign = 00786 wmw->lpWaveFormat->nChannels * 00787 wmw->lpWaveFormat->wBitsPerSample/8; 00788 } 00789 if (wmw->lpWaveFormat->nAvgBytesPerSec != 00790 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) { 00791 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n", 00792 wmw->lpWaveFormat->nAvgBytesPerSec, 00793 wmw->lpWaveFormat->nSamplesPerSec * 00794 wmw->lpWaveFormat->nBlockAlign); 00795 wmw->lpWaveFormat->nAvgBytesPerSec = 00796 wmw->lpWaveFormat->nSamplesPerSec * 00797 wmw->lpWaveFormat->nBlockAlign; 00798 } 00799 } 00800 00801 end = wmw->ckWaveData.cksize; 00802 if (dwFlags & MCI_TO) { 00803 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); 00804 if (position > end) return MCIERR_OUTOFRANGE; 00805 end = position; 00806 } 00807 if (dwFlags & MCI_FROM) { 00808 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); 00809 if (position > end) return MCIERR_OUTOFRANGE; 00810 /* Seek rounds down, so do we. */ 00811 position /= wmw->lpWaveFormat->nBlockAlign; 00812 position *= wmw->lpWaveFormat->nBlockAlign; 00813 wmw->dwPosition = position; 00814 } 00815 if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE; 00816 left = end - wmw->dwPosition; 00817 if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */ 00818 00819 wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */ 00820 wmw->dwStatus = MCI_MODE_PLAY; 00821 00822 if (!(dwFlags & MCI_WAIT)) { 00823 return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags, 00824 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS)); 00825 } 00826 00827 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end); 00828 00829 oldcb = InterlockedExchangePointer(&wmw->hCallback, 00830 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL); 00831 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED); 00832 oldcb = NULL; 00833 00834 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \ 00835 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign) 00836 00837 /* go back to beginning of chunk plus the requested position */ 00838 /* FIXME: I'm not sure this is correct, notably because some data linked to 00839 * the decompression state machine will not be correctly initialized. 00840 * try it this way (other way would be to decompress from 0 up to dwPosition 00841 * and to start sending to hWave when dwPosition is reached) 00842 */ 00843 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */ 00844 00845 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, wmw->wOutput, wmw->lpWaveFormat, 00846 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION); 00847 00848 if (dwRet != 0) { 00849 TRACE("Can't open low level audio device %d\n", dwRet); 00850 dwRet = MCIERR_DEVICE_OPEN; 00851 wmw->hWave = 0; 00852 goto cleanUp; 00853 } 00854 00855 /* make it so that 3 buffers per second are needed */ 00856 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3); 00857 00858 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize); 00859 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR); 00860 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize; 00861 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L; 00862 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L; 00863 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L; 00864 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize; 00865 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || 00866 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) { 00867 dwRet = MCIERR_INTERNAL; 00868 goto cleanUp; 00869 } 00870 00871 whidx = 0; 00872 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 00873 if (!wmw->hEvent) { 00874 dwRet = MCIERR_OUT_OF_MEMORY; 00875 goto cleanUp; 00876 } 00877 wmw->dwEventCount = 1L; /* for first buffer */ 00878 00879 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left); 00880 if (hEvent) SetEvent(hEvent); 00881 00882 /* FIXME: this doesn't work if wmw->dwPosition != 0 */ 00883 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) { 00884 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left)); 00885 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count); 00886 if (count < 1) 00887 break; 00888 /* count is always <= bufsize, so this is correct regarding the 00889 * waveOutPrepareHeader function 00890 */ 00891 waveHdr[whidx].dwBufferLength = count; 00892 waveHdr[whidx].dwFlags &= ~WHDR_DONE; 00893 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n", 00894 &waveHdr[whidx], waveHdr[whidx].dwBufferLength); 00895 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR)); 00896 if (dwRet) { 00897 ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet); 00898 dwRet = MCIERR_HARDWARE; 00899 break; 00900 } 00901 left -= count; 00902 wmw->dwPosition += count; 00903 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition); 00904 /* InterlockedDecrement if and only if waveOutWrite is successful */ 00905 WAVE_mciPlayWaitDone(wmw); 00906 whidx ^= 1; 00907 } 00908 00909 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */ 00910 00911 /* just to get rid of some race conditions between play, stop and pause */ 00912 waveOutReset(wmw->hWave); 00913 00914 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)); 00915 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR)); 00916 00917 cleanUp: 00918 if (dwFlags & MCI_NOTIFY) 00919 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL); 00920 00921 HeapFree(GetProcessHeap(), 0, waveHdr); 00922 00923 if (wmw->hWave) { 00924 waveOutClose(wmw->hWave); 00925 wmw->hWave = 0; 00926 } 00927 CloseHandle(wmw->hEvent); 00928 wmw->hEvent = NULL; 00929 00930 wmw->dwStatus = MCI_MODE_STOP; 00931 00932 /* Let the potentially asynchronous commands support FAILURE notification. */ 00933 if (oldcb) mciDriverNotify(oldcb, wDevID, 00934 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); 00935 00936 return dwRet; 00937 } 00938 00939 /************************************************************************** 00940 * WAVE_mciRecordCallback [internal] 00941 */ 00942 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg, 00943 DWORD_PTR dwInstance, 00944 LPARAM dwParam1, LPARAM dwParam2) 00945 { 00946 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance; 00947 LPWAVEHDR lpWaveHdr; 00948 LONG count = 0; 00949 00950 switch (uMsg) { 00951 case WIM_OPEN: 00952 case WIM_CLOSE: 00953 break; 00954 case WIM_DATA: 00955 lpWaveHdr = (LPWAVEHDR) dwParam1; 00956 00957 InterlockedIncrement(&wmw->dwEventCount); 00958 00959 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded); 00960 00961 lpWaveHdr->dwFlags &= ~WHDR_DONE; 00962 if (count > 0) 00963 wmw->dwPosition += count; 00964 /* else error reporting ?? */ 00965 if (wmw->dwStatus == MCI_MODE_RECORD) 00966 { 00967 /* Only queue up another buffer if we are recording. We could receive this 00968 message also when waveInReset() is called, since it notifies on all wave 00969 buffers that are outstanding. Queueing up more sometimes causes waveInClose 00970 to fail. */ 00971 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr)); 00972 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition); 00973 } 00974 00975 SetEvent(wmw->hEvent); 00976 break; 00977 default: 00978 ERR("Unknown uMsg=%d\n", uMsg); 00979 } 00980 } 00981 00982 /****************************************************************** 00983 * WAVE_mciRecordWaitDone [internal] 00984 */ 00985 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw) 00986 { 00987 for (;;) { 00988 ResetEvent(wmw->hEvent); 00989 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) { 00990 break; 00991 } 00992 InterlockedIncrement(&wmw->dwEventCount); 00993 00994 WaitForSingleObject(wmw->hEvent, INFINITE); 00995 } 00996 } 00997 00998 /************************************************************************** 00999 * WAVE_mciRecord [internal] 01000 */ 01001 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent) 01002 { 01003 LPMCI_RECORD_PARMS lpParms = (void*)pmt; 01004 DWORD end; 01005 DWORD dwRet = MMSYSERR_NOERROR; 01006 LONG bufsize; 01007 LPWAVEHDR waveHdr = NULL; 01008 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 01009 HANDLE oldcb; 01010 01011 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms); 01012 01013 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 01014 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 01015 01016 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) { 01017 /* FIXME: parameters (start/end) in lpParams may not be used */ 01018 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms); 01019 } 01020 01025 if ( !(wmw->dwStatus == MCI_MODE_STOP) && 01026 !((wmw->dwStatus == MCI_MODE_RECORD) && (dwFlags & MCI_WAIT) && !wmw->hWave)) { 01027 return MCIERR_INTERNAL; 01028 } 01029 01030 wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */ 01031 wmw->dwStatus = MCI_MODE_RECORD; 01032 01033 if (!(dwFlags & MCI_WAIT)) { 01034 return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags, 01035 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS)); 01036 } 01037 01038 /* FIXME: we only re-create the RIFF structure from an existing file (if any) 01039 * we don't modify the wave part of an existing file (ie. we always erase an 01040 * existing content, we don't overwrite) 01041 */ 01042 HeapFree(GetProcessHeap(), 0, wmw->lpFileName); 01043 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName); 01044 if (dwRet != 0) return dwRet; 01045 01046 /* new RIFF file, lpWaveFormat now valid */ 01047 dwRet = WAVE_mciCreateRIFFSkeleton(wmw); 01048 if (dwRet != 0) return dwRet; 01049 01050 if (dwFlags & MCI_TO) { 01051 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); 01052 } else end = 0xFFFFFFFF; 01053 if (dwFlags & MCI_FROM) { 01054 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); 01055 if (wmw->ckWaveData.cksize < position) return MCIERR_OUTOFRANGE; 01056 /* Seek rounds down, so do we. */ 01057 position /= wmw->lpWaveFormat->nBlockAlign; 01058 position *= wmw->lpWaveFormat->nBlockAlign; 01059 wmw->dwPosition = position; 01060 } 01061 if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */ 01062 01063 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end); 01064 01065 oldcb = InterlockedExchangePointer(&wmw->hCallback, 01066 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL); 01067 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED); 01068 oldcb = NULL; 01069 01070 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \ 01071 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign) 01072 01073 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize); 01074 01075 /* Go back to the beginning of the chunk plus the requested position */ 01076 /* FIXME: I'm not sure this is correct, notably because some data linked to 01077 * the decompression state machine will not be correctly initialized. 01078 * Try it this way (other way would be to decompress from 0 up to dwPosition 01079 * and to start sending to hWave when dwPosition is reached). 01080 */ 01081 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */ 01082 01083 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, wmw->wInput, wmw->lpWaveFormat, 01084 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION); 01085 01086 if (dwRet != MMSYSERR_NOERROR) { 01087 TRACE("Can't open low level audio device %d\n", dwRet); 01088 dwRet = MCIERR_DEVICE_OPEN; 01089 wmw->hWave = 0; 01090 goto cleanUp; 01091 } 01092 01093 /* make it so that 3 buffers per second are needed */ 01094 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3); 01095 01096 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize); 01097 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR); 01098 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize; 01099 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L; 01100 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L; 01101 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L; 01102 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize; 01103 01104 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || 01105 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) { 01106 dwRet = MCIERR_INTERNAL; 01107 goto cleanUp; 01108 } 01109 01110 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || 01111 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) { 01112 dwRet = MCIERR_INTERNAL; 01113 goto cleanUp; 01114 } 01115 01116 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 01117 wmw->dwEventCount = 1L; /* for first buffer */ 01118 01119 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition); 01120 01121 dwRet = waveInStart(wmw->hWave); 01122 01123 if (hEvent) SetEvent(hEvent); 01124 01125 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) { 01126 WAVE_mciRecordWaitDone(wmw); 01127 } 01128 /* Grab callback before another thread kicks in after we change dwStatus. */ 01129 if (dwFlags & MCI_NOTIFY) { 01130 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL); 01131 dwFlags &= ~MCI_NOTIFY; 01132 } 01133 /* needed so that the callback above won't add again the buffers returned by the reset */ 01134 wmw->dwStatus = MCI_MODE_STOP; 01135 01136 waveInReset(wmw->hWave); 01137 01138 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)); 01139 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR)); 01140 01141 dwRet = 0; 01142 01143 cleanUp: 01144 if (dwFlags & MCI_NOTIFY) 01145 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL); 01146 01147 HeapFree(GetProcessHeap(), 0, waveHdr); 01148 01149 if (wmw->hWave) { 01150 waveInClose(wmw->hWave); 01151 wmw->hWave = 0; 01152 } 01153 CloseHandle(wmw->hEvent); 01154 01155 wmw->dwStatus = MCI_MODE_STOP; 01156 01157 if (oldcb) mciDriverNotify(oldcb, wDevID, 01158 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); 01159 01160 return dwRet; 01161 01162 } 01163 01164 /************************************************************************** 01165 * WAVE_mciPause [internal] 01166 */ 01167 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 01168 { 01169 DWORD dwRet; 01170 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 01171 01172 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 01173 01174 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 01175 01176 switch (wmw->dwStatus) { 01177 case MCI_MODE_PLAY: 01178 dwRet = waveOutPause(wmw->hWave); 01179 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE; 01180 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */ 01181 ERR("waveOutPause error %d\n",dwRet); 01182 dwRet = MCIERR_INTERNAL; 01183 } 01184 break; 01185 case MCI_MODE_RECORD: 01186 dwRet = waveInStop(wmw->hWave); 01187 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE; 01188 else { 01189 ERR("waveInStop error %d\n",dwRet); 01190 dwRet = MCIERR_INTERNAL; 01191 } 01192 break; 01193 case MCI_MODE_PAUSE: 01194 dwRet = MMSYSERR_NOERROR; 01195 break; 01196 default: 01197 dwRet = MCIERR_NONAPPLICABLE_FUNCTION; 01198 } 01199 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms) 01200 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 01201 return dwRet; 01202 } 01203 01204 /************************************************************************** 01205 * WAVE_mciResume [internal] 01206 */ 01207 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 01208 { 01209 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 01210 DWORD dwRet; 01211 01212 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 01213 01214 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 01215 01216 switch (wmw->dwStatus) { 01217 case MCI_MODE_PAUSE: 01218 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */ 01219 if (wmw->fInput) { 01220 dwRet = waveInStart(wmw->hWave); 01221 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD; 01222 else { 01223 ERR("waveInStart error %d\n",dwRet); 01224 dwRet = MCIERR_INTERNAL; 01225 } 01226 } else { 01227 dwRet = waveOutRestart(wmw->hWave); 01228 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY; 01229 else { 01230 ERR("waveOutRestart error %d\n",dwRet); 01231 dwRet = MCIERR_INTERNAL; 01232 } 01233 } 01234 break; 01235 case MCI_MODE_PLAY: 01236 case MCI_MODE_RECORD: 01237 dwRet = MMSYSERR_NOERROR; 01238 break; 01239 default: 01240 dwRet = MCIERR_NONAPPLICABLE_FUNCTION; 01241 } 01242 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms) 01243 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 01244 return dwRet; 01245 } 01246 01247 /************************************************************************** 01248 * WAVE_mciSeek [internal] 01249 */ 01250 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms) 01251 { 01252 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 01253 DWORD position, dwRet; 01254 01255 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 01256 01257 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 01258 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 01259 01260 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO); 01261 if (!position) return MCIERR_MISSING_PARAMETER; 01262 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE; 01263 01264 /* Stop sends MCI_NOTIFY_ABORTED when needed */ 01265 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0); 01266 if (dwRet != MMSYSERR_NOERROR) return dwRet; 01267 01268 if (dwFlags & MCI_TO) { 01269 position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); 01270 if (position > wmw->ckWaveData.cksize) 01271 return MCIERR_OUTOFRANGE; 01272 } else if (dwFlags & MCI_SEEK_TO_START) { 01273 position = 0; 01274 } else { 01275 position = wmw->ckWaveData.cksize; 01276 } 01277 /* Seek rounds down, unless at end */ 01278 if (position != wmw->ckWaveData.cksize) { 01279 position /= wmw->lpWaveFormat->nBlockAlign; 01280 position *= wmw->lpWaveFormat->nBlockAlign; 01281 } 01282 wmw->dwPosition = position; 01283 TRACE("Seeking to position=%u bytes\n", position); 01284 01285 if (dwFlags & MCI_NOTIFY) 01286 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 01287 01288 return MMSYSERR_NOERROR; 01289 } 01290 01291 /************************************************************************** 01292 * WAVE_mciSet [internal] 01293 */ 01294 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_SET_PARMS lpParms) 01295 { 01296 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 01297 01298 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 01299 01300 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 01301 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 01302 01303 if (dwFlags & MCI_SET_TIME_FORMAT) { 01304 switch (lpParms->dwTimeFormat) { 01305 case MCI_FORMAT_MILLISECONDS: 01306 TRACE("MCI_FORMAT_MILLISECONDS !\n"); 01307 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS; 01308 break; 01309 case MCI_FORMAT_BYTES: 01310 TRACE("MCI_FORMAT_BYTES !\n"); 01311 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES; 01312 break; 01313 case MCI_FORMAT_SAMPLES: 01314 TRACE("MCI_FORMAT_SAMPLES !\n"); 01315 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES; 01316 break; 01317 default: 01318 WARN("Bad time format %u!\n", lpParms->dwTimeFormat); 01319 return MCIERR_BAD_TIME_FORMAT; 01320 } 01321 } 01322 if (dwFlags & MCI_SET_VIDEO) { 01323 TRACE("No support for video !\n"); 01324 return MCIERR_UNSUPPORTED_FUNCTION; 01325 } 01326 if (dwFlags & MCI_SET_DOOR_OPEN) { 01327 TRACE("No support for door open !\n"); 01328 return MCIERR_UNSUPPORTED_FUNCTION; 01329 } 01330 if (dwFlags & MCI_SET_DOOR_CLOSED) { 01331 TRACE("No support for door close !\n"); 01332 return MCIERR_UNSUPPORTED_FUNCTION; 01333 } 01334 if (dwFlags & MCI_SET_AUDIO) { 01335 if (dwFlags & MCI_SET_ON) { 01336 TRACE("MCI_SET_ON audio !\n"); 01337 } else if (dwFlags & MCI_SET_OFF) { 01338 TRACE("MCI_SET_OFF audio !\n"); 01339 } else { 01340 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n"); 01341 return MCIERR_BAD_INTEGER; 01342 } 01343 01344 switch (lpParms->dwAudio) 01345 { 01346 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break; 01347 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break; 01348 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break; 01349 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break; 01350 } 01351 } 01352 if (dwFlags & MCI_WAVE_INPUT) { 01353 TRACE("MCI_WAVE_INPUT = %d\n", lpParms->wInput); 01354 if (lpParms->wInput >= waveInGetNumDevs()) 01355 return MCIERR_OUTOFRANGE; 01356 if (wmw->wInput != (WORD)lpParms->wInput) 01357 WAVE_mciStop(wDevID, MCI_WAIT, NULL); 01358 wmw->wInput = lpParms->wInput; 01359 } 01360 if (dwFlags & MCI_WAVE_OUTPUT) { 01361 TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms->wOutput); 01362 if (lpParms->wOutput >= waveOutGetNumDevs()) 01363 return MCIERR_OUTOFRANGE; 01364 if (wmw->wOutput != (WORD)lpParms->wOutput) 01365 WAVE_mciStop(wDevID, MCI_WAIT, NULL); 01366 wmw->wOutput = lpParms->wOutput; 01367 } 01368 if (dwFlags & MCI_WAVE_SET_ANYINPUT) { 01369 TRACE("MCI_WAVE_SET_ANYINPUT\n"); 01370 if (wmw->wInput != (WORD)lpParms->wInput) 01371 WAVE_mciStop(wDevID, MCI_WAIT, NULL); 01372 wmw->wInput = WAVE_MAPPER; 01373 } 01374 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) { 01375 TRACE("MCI_WAVE_SET_ANYOUTPUT\n"); 01376 if (wmw->wOutput != (WORD)lpParms->wOutput) 01377 WAVE_mciStop(wDevID, MCI_WAIT, NULL); 01378 wmw->wOutput = WAVE_MAPPER; 01379 } 01380 /* Set wave format parameters is refused after Open or Record.*/ 01381 if (dwFlags & MCI_WAVE_SET_FORMATTAG) { 01382 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms->wFormatTag); 01383 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 01384 if (lpParms->wFormatTag != WAVE_FORMAT_PCM) 01385 return MCIERR_OUTOFRANGE; 01386 } 01387 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) { 01388 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 01389 wmw->wfxRef.nAvgBytesPerSec = lpParms->nAvgBytesPerSec; 01390 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec); 01391 } 01392 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) { 01393 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 01394 wmw->wfxRef.wBitsPerSample = lpParms->wBitsPerSample; 01395 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample); 01396 } 01397 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) { 01398 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 01399 wmw->wfxRef.nBlockAlign = lpParms->nBlockAlign; 01400 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign); 01401 } 01402 if (dwFlags & MCI_WAVE_SET_CHANNELS) { 01403 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 01404 wmw->wfxRef.nChannels = lpParms->nChannels; 01405 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels); 01406 } 01407 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) { 01408 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; 01409 wmw->wfxRef.nSamplesPerSec = lpParms->nSamplesPerSec; 01410 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec); 01411 } 01412 if (dwFlags & MCI_NOTIFY) 01413 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 01414 return 0; 01415 } 01416 01417 /************************************************************************** 01418 * WAVE_mciSave [internal] 01419 */ 01420 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms) 01421 { 01422 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 01423 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet; 01424 01425 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms); 01426 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 01427 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 01428 01429 if (dwFlags & MCI_WAIT) 01430 { 01431 FIXME("MCI_WAIT not implemented\n"); 01432 } 01433 WAVE_mciStop(wDevID, 0, NULL); 01434 01435 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0); 01436 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0); 01437 01438 ret = mmioClose(wmw->hFile, 0); 01439 wmw->hFile = 0; 01440 01441 /* 01442 If the destination file already exists, it has to be overwritten. (Behaviour 01443 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of 01444 my applications. We are making use of mmioRename, which WILL NOT overwrite 01445 the destination file (which is what Windows does, also verified in Win2K) 01446 So, lets delete the destination file before calling mmioRename. If the 01447 destination file DOESN'T exist, the delete will fail silently. Let's also be 01448 careful not to lose our previous error code. 01449 */ 01450 tmpRet = GetLastError(); 01451 DeleteFileW (lpParms->lpfilename); 01452 SetLastError(tmpRet); 01453 01454 /* FIXME: Open file.wav; Save; must not rename the original file. 01455 * Nor must Save a.wav; Save b.wav rename a. */ 01456 if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) { 01457 ret = MMSYSERR_NOERROR; 01458 } 01459 01460 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY)) 01461 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 01462 01463 if (ret == MMSYSERR_NOERROR) 01464 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename); 01465 01466 return ret; 01467 } 01468 01469 /************************************************************************** 01470 * WAVE_mciStatus [internal] 01471 */ 01472 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms) 01473 { 01474 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 01475 DWORD ret = 0; 01476 01477 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 01478 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 01479 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 01480 if (!(dwFlags & MCI_STATUS_ITEM)) return MCIERR_MISSING_PARAMETER; 01481 01482 if (dwFlags & MCI_STATUS_ITEM) { 01483 switch (lpParms->dwItem) { 01484 case MCI_STATUS_CURRENT_TRACK: 01485 lpParms->dwReturn = 1; 01486 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn); 01487 break; 01488 case MCI_STATUS_LENGTH: 01489 if (!wmw->hFile) { 01490 lpParms->dwReturn = 0; 01491 return MCIERR_UNSUPPORTED_FUNCTION; 01492 } 01493 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */ 01494 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize); 01495 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn); 01496 break; 01497 case MCI_STATUS_MODE: 01498 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus); 01499 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus); 01500 ret = MCI_RESOURCE_RETURNED; 01501 break; 01502 case MCI_STATUS_MEDIA_PRESENT: 01503 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n"); 01504 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 01505 ret = MCI_RESOURCE_RETURNED; 01506 break; 01507 case MCI_STATUS_NUMBER_OF_TRACKS: 01508 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */ 01509 lpParms->dwReturn = 1; 01510 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn); 01511 break; 01512 case MCI_STATUS_POSITION: 01513 if (!wmw->hFile) { 01514 lpParms->dwReturn = 0; 01515 return MCIERR_UNSUPPORTED_FUNCTION; 01516 } 01517 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */ 01518 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, 01519 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition); 01520 TRACE("MCI_STATUS_POSITION %s => %lu\n", 01521 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn); 01522 break; 01523 case MCI_STATUS_READY: 01524 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ? 01525 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE); 01526 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn)); 01527 ret = MCI_RESOURCE_RETURNED; 01528 break; 01529 case MCI_STATUS_TIME_FORMAT: 01530 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat); 01531 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn); 01532 ret = MCI_RESOURCE_RETURNED; 01533 break; 01534 case MCI_WAVE_INPUT: 01535 if (wmw->wInput != (WORD)WAVE_MAPPER) 01536 lpParms->dwReturn = wmw->wInput; 01537 else { 01538 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S); 01539 ret = MCI_RESOURCE_RETURNED; 01540 } 01541 TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw->wInput); 01542 break; 01543 case MCI_WAVE_OUTPUT: 01544 if (wmw->wOutput != (WORD)WAVE_MAPPER) 01545 lpParms->dwReturn = wmw->wOutput; 01546 else { 01547 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S); 01548 ret = MCI_RESOURCE_RETURNED; 01549 } 01550 TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw->wOutput); 01551 break; 01552 /* It is always ok to query wave format parameters, 01553 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */ 01554 case MCI_WAVE_STATUS_FORMATTAG: 01555 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM) 01556 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag; 01557 else { 01558 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_FORMAT_PCM, WAVE_FORMAT_PCM_S); 01559 ret = MCI_RESOURCE_RETURNED; 01560 } 01561 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn); 01562 break; 01563 case MCI_WAVE_STATUS_AVGBYTESPERSEC: 01564 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec; 01565 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn); 01566 break; 01567 case MCI_WAVE_STATUS_BITSPERSAMPLE: 01568 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample; 01569 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn); 01570 break; 01571 case MCI_WAVE_STATUS_BLOCKALIGN: 01572 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign; 01573 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn); 01574 break; 01575 case MCI_WAVE_STATUS_CHANNELS: 01576 lpParms->dwReturn = wmw->lpWaveFormat->nChannels; 01577 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn); 01578 break; 01579 case MCI_WAVE_STATUS_SAMPLESPERSEC: 01580 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec; 01581 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn); 01582 break; 01583 case MCI_WAVE_STATUS_LEVEL: 01584 TRACE("MCI_WAVE_STATUS_LEVEL !\n"); 01585 lpParms->dwReturn = 0xAAAA5555; 01586 break; 01587 default: 01588 WARN("unknown command %08X !\n", lpParms->dwItem); 01589 return MCIERR_UNSUPPORTED_FUNCTION; 01590 } 01591 } 01592 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0) 01593 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 01594 return ret; 01595 } 01596 01597 /************************************************************************** 01598 * WAVE_mciGetDevCaps [internal] 01599 */ 01600 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags, 01601 LPMCI_GETDEVCAPS_PARMS lpParms) 01602 { 01603 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 01604 DWORD ret = 0; 01605 01606 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 01607 01608 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 01609 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; 01610 01611 if (dwFlags & MCI_GETDEVCAPS_ITEM) { 01612 switch(lpParms->dwItem) { 01613 case MCI_GETDEVCAPS_DEVICE_TYPE: 01614 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO); 01615 ret = MCI_RESOURCE_RETURNED; 01616 break; 01617 case MCI_GETDEVCAPS_HAS_AUDIO: 01618 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 01619 ret = MCI_RESOURCE_RETURNED; 01620 break; 01621 case MCI_GETDEVCAPS_HAS_VIDEO: 01622 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 01623 ret = MCI_RESOURCE_RETURNED; 01624 break; 01625 case MCI_GETDEVCAPS_USES_FILES: 01626 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 01627 ret = MCI_RESOURCE_RETURNED; 01628 break; 01629 case MCI_GETDEVCAPS_COMPOUND_DEVICE: 01630 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 01631 ret = MCI_RESOURCE_RETURNED; 01632 break; 01633 case MCI_GETDEVCAPS_CAN_RECORD: 01634 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 01635 ret = MCI_RESOURCE_RETURNED; 01636 break; 01637 case MCI_GETDEVCAPS_CAN_EJECT: 01638 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 01639 ret = MCI_RESOURCE_RETURNED; 01640 break; 01641 case MCI_GETDEVCAPS_CAN_PLAY: 01642 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 01643 ret = MCI_RESOURCE_RETURNED; 01644 break; 01645 case MCI_GETDEVCAPS_CAN_SAVE: 01646 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 01647 ret = MCI_RESOURCE_RETURNED; 01648 break; 01649 case MCI_WAVE_GETDEVCAPS_INPUTS: 01650 lpParms->dwReturn = waveInGetNumDevs(); 01651 break; 01652 case MCI_WAVE_GETDEVCAPS_OUTPUTS: 01653 lpParms->dwReturn = waveOutGetNumDevs(); 01654 break; 01655 default: 01656 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem); 01657 return MCIERR_UNRECOGNIZED_COMMAND; 01658 } 01659 } else { 01660 WARN("No GetDevCaps-Item !\n"); 01661 return MCIERR_UNRECOGNIZED_COMMAND; 01662 } 01663 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0) 01664 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 01665 return ret; 01666 } 01667 01668 /************************************************************************** 01669 * WAVE_mciInfo [internal] 01670 */ 01671 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms) 01672 { 01673 DWORD ret = 0; 01674 LPCWSTR str = 0; 01675 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); 01676 01677 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); 01678 01679 if (!lpParms || !lpParms->lpstrReturn) 01680 return MCIERR_NULL_PARAMETER_BLOCK; 01681 01682 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize); 01683 01684 if (wmw == NULL) { 01685 ret = MCIERR_INVALID_DEVICE_ID; 01686 } else { 01687 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0}; 01688 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0}; 01689 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0}; 01690 01691 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) { 01692 case MCI_INFO_PRODUCT: str = wszAudio; break; 01693 case MCI_INFO_FILE: str = wmw->lpFileName; break; 01694 case MCI_WAVE_INPUT: str = wszWaveIn; break; 01695 case MCI_WAVE_OUTPUT: str = wszWaveOut; break; 01696 default: 01697 WARN("Don't know this info command (%u)\n", dwFlags); 01698 ret = MCIERR_UNRECOGNIZED_KEYWORD; 01699 } 01700 } 01701 if (!ret) { 01702 if (lpParms->dwRetSize) { 01703 WCHAR zero = 0; 01704 /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize 01705 * to the number of characters written, excluding \0. */ 01706 lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize); 01707 } else ret = MCIERR_PARAM_OVERFLOW; 01708 } 01709 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY)) 01710 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); 01711 return ret; 01712 } 01713 01714 /************************************************************************** 01715 * DriverProc (MCIWAVE.@) 01716 */ 01717 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, 01718 LPARAM dwParam1, LPARAM dwParam2) 01719 { 01720 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n", 01721 dwDevID, hDriv, wMsg, dwParam1, dwParam2); 01722 01723 switch (wMsg) { 01724 case DRV_LOAD: return 1; 01725 case DRV_FREE: return 1; 01726 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2); 01727 case DRV_CLOSE: return WAVE_drvClose(dwDevID); 01728 case DRV_ENABLE: return 1; 01729 case DRV_DISABLE: return 1; 01730 case DRV_QUERYCONFIGURE: return 1; 01731 case DRV_CONFIGURE: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1; 01732 case DRV_INSTALL: return DRVCNF_RESTART; 01733 case DRV_REMOVE: return DRVCNF_RESTART; 01734 } 01735 01736 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION; 01737 01738 switch (wMsg) { 01739 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2); 01740 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 01741 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 01742 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL); 01743 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL); 01744 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 01745 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_WAVE_SET_PARMS) dwParam2); 01746 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 01747 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 01748 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2); 01749 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2); 01750 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2); 01751 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2); 01752 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2); 01753 /* commands that should be supported */ 01754 case MCI_LOAD: 01755 case MCI_FREEZE: 01756 case MCI_PUT: 01757 case MCI_REALIZE: 01758 case MCI_UNFREEZE: 01759 case MCI_UPDATE: 01760 case MCI_WHERE: 01761 case MCI_STEP: 01762 case MCI_SPIN: 01763 case MCI_ESCAPE: 01764 case MCI_COPY: 01765 case MCI_CUT: 01766 case MCI_DELETE: 01767 case MCI_PASTE: 01768 FIXME("Unsupported command [%u]\n", wMsg); 01769 break; 01770 case MCI_WINDOW: 01771 TRACE("Unsupported command [%u]\n", wMsg); 01772 break; 01773 /* option which can be silenced */ 01774 case MCI_CONFIGURE: 01775 return 0; 01776 case MCI_OPEN: 01777 case MCI_CLOSE: 01778 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n"); 01779 break; 01780 default: 01781 FIXME("is probably wrong msg [%u]\n", wMsg); 01782 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); 01783 } 01784 return MCIERR_UNRECOGNIZED_COMMAND; 01785 } Generated on Sat May 26 2012 04:23:15 for ReactOS by
1.7.6.1
|