diff --git a/reactos/dll/win32/mciwave/mciwave.c b/reactos/dll/win32/mciwave/mciwave.c index c533e857797..24e75ee9dac 100644 --- a/reactos/dll/win32/mciwave/mciwave.c +++ b/reactos/dll/win32/mciwave/mciwave.c @@ -1,9 +1,10 @@ /* - * Sample Wine Driver for MCI wave forms + * Wine Driver for MCI wave forms * * Copyright 1994 Martin Ayotte * 1999,2000,2005 Eric Pouech * 2000 Francois Jacques + * 2009 Jörg Höhle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,6 +21,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include #include #include "windef.h" @@ -38,11 +40,12 @@ typedef struct { UINT wDevID; HANDLE hWave; int nUseCount; /* Incremented for each shared open */ - BOOL fShareable; /* TRUE if first open was shareable */ HMMIO hFile; /* mmio file handle open as Element */ - MCI_WAVE_OPEN_PARMSW openParms; + MCIDEVICEID wNotifyDeviceID; /* MCI device ID with a pending notification */ + HANDLE hCallback; /* Callback handle for pending notification */ + LPWSTR lpFileName; /* Name of file (if any) */ WAVEFORMATEX wfxRef; - LPWAVEFORMATEX lpWaveFormat; + LPWAVEFORMATEX lpWaveFormat; /* Points to wfxRef until set by OPEN or RECORD */ BOOL fInput; /* FALSE = Output, TRUE = Input */ volatile WORD dwStatus; /* one from MCI_MODE_xxxx */ DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */ @@ -62,9 +65,12 @@ typedef struct { * =================================================================== * =================================================================== */ +typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt); + struct SCA { + async_cmd cmd; + HANDLE evt; UINT wDevID; - UINT wMsg; DWORD_PTR dwParam1; DWORD_PTR dwParam2; }; @@ -77,32 +83,29 @@ static DWORD CALLBACK MCI_SCAStarter(LPVOID arg) struct SCA* sca = (struct SCA*)arg; DWORD ret; - TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n", - sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2); - ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2); - TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n", - sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2); + TRACE("In thread before async command (%08x,%08lx,%08lx)\n", + sca->wDevID, sca->dwParam1, sca->dwParam2); + ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt); + TRACE("In thread after async command (%08x,%08lx,%08lx)\n", + sca->wDevID, sca->dwParam1, sca->dwParam2); HeapFree(GetProcessHeap(), 0, sca); - ExitThread(ret); - WARN("Should not happen ? what's wrong\n"); - /* should not go after this point */ return ret; } /************************************************************************** * MCI_SendCommandAsync [internal] */ -static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD_PTR dwParam1, +static DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1, DWORD_PTR dwParam2, UINT size) { - HANDLE handle; + HANDLE handles[2]; struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size); if (sca == 0) return MCIERR_OUT_OF_MEMORY; sca->wDevID = wDevID; - sca->wMsg = wMsg; + sca->cmd = cmd; sca->dwParam1 = dwParam1; if (size && dwParam2) { @@ -115,12 +118,22 @@ static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD_PTR dwParam1, sca->dwParam2 = dwParam2; } - if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) { + if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL || + (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) { WARN("Couldn't allocate thread for async command handling, sending synchronously\n"); + if (handles[1]) CloseHandle(handles[1]); + sca->evt = NULL; return MCI_SCAStarter(&sca); } - SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL); - CloseHandle(handle); + + SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL); + /* wait until either: + * - the thread has finished (handles[0], likely an error) + * - init phase of async command is done (handles[1]) + */ + WaitForMultipleObjects(2, handles, FALSE, INFINITE); + CloseHandle(handles[0]); + CloseHandle(handles[1]); return 0; } @@ -189,6 +202,24 @@ static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID) return wmw; } +/************************************************************************** + * WAVE_mciNotify [internal] + * + * Notifications in MCI work like a 1-element queue. + * Each new notification request supersedes the previous one. + * This affects Play and Record; other commands are immediate. + */ +static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus) +{ + /* We simply save one parameter by not passing the wDevID local + * to the command. They are the same (via mciGetDriverData). + */ + MCIDEVICEID wDevID = wmw->wNotifyDeviceID; + HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL); + if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED); + mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus); +} + /************************************************************************** * WAVE_ConvertByteToTimeFormat [internal] */ @@ -203,8 +234,8 @@ static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD case MCI_FORMAT_BYTES: ret = val; break; - case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */ - ret = (val * 8) / (wmw->lpWaveFormat->wBitsPerSample ? wmw->lpWaveFormat->wBitsPerSample : 1); + case MCI_FORMAT_SAMPLES: + ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec); break; default: WARN("Bad time format %u!\n", wmw->dwMciTimeFormat); @@ -223,13 +254,13 @@ static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val) switch (wmw->dwMciTimeFormat) { case MCI_FORMAT_MILLISECONDS: - ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000; + ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000); break; case MCI_FORMAT_BYTES: ret = val; break; - case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */ - ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8; + case MCI_FORMAT_SAMPLES: + ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec); break; default: WARN("Bad time format %u!\n", wmw->dwMciTimeFormat); @@ -245,6 +276,7 @@ static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF) { MMCKINFO mmckInfo; long r; + LPWAVEFORMATEX pwfx; mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' '); if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0) @@ -252,20 +284,28 @@ static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF) TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n", (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize); - wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize); - if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM; - r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize); - if (r < sizeof(WAVEFORMAT)) - return MCIERR_INVALID_FILE; + pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize); + if (!pwfx) return MCIERR_OUT_OF_MEMORY; - TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag); - TRACE("nChannels=%d\n", wmw->lpWaveFormat->nChannels); - TRACE("nSamplesPerSec=%d\n", wmw->lpWaveFormat->nSamplesPerSec); - TRACE("nAvgBytesPerSec=%d\n", wmw->lpWaveFormat->nAvgBytesPerSec); - TRACE("nBlockAlign=%d\n", wmw->lpWaveFormat->nBlockAlign); - TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample); + r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize); + if (r < sizeof(PCMWAVEFORMAT)) { + HeapFree(GetProcessHeap(), 0, pwfx); + return MCIERR_INVALID_FILE; + } + TRACE("wFormatTag=%04X !\n", pwfx->wFormatTag); + TRACE("nChannels=%d\n", pwfx->nChannels); + TRACE("nSamplesPerSec=%d\n", pwfx->nSamplesPerSec); + TRACE("nAvgBytesPerSec=%d\n", pwfx->nAvgBytesPerSec); + TRACE("nBlockAlign=%d\n", pwfx->nBlockAlign); + TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample); if (r >= (long)sizeof(WAVEFORMATEX)) - TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize); + TRACE("cbSize=%u !\n", pwfx->cbSize); + if ((pwfx->wFormatTag != WAVE_FORMAT_PCM) + && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) { + HeapFree(GetProcessHeap(), 0, pwfx); + return MCIERR_INVALID_FILE; + } + wmw->lpWaveFormat = pwfx; mmioAscend(wmw->hFile, &mmckInfo, 0); wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a'); @@ -275,28 +315,26 @@ static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF) } TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n", (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize); - TRACE("nChannels=%d nSamplesPerSec=%d\n", - wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec); - return 0; } /************************************************************************** - * WAVE_mciDefaultFmt [internal] + * WAVE_mciDefaultFmt [internal] + * + * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef + * until either Open File or Record. It becomes immutable afterwards, + * i.e. Set wave format or channels etc. is subsequently refused. */ -static DWORD WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw) +static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw) { - wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, sizeof(*wmw->lpWaveFormat)); - if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM; - + wmw->lpWaveFormat = &wmw->wfxRef; wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM; wmw->lpWaveFormat->nChannels = 1; - wmw->lpWaveFormat->nSamplesPerSec = 44000; - wmw->lpWaveFormat->nAvgBytesPerSec = 44000; + wmw->lpWaveFormat->nSamplesPerSec = 11025; + wmw->lpWaveFormat->nAvgBytesPerSec = 11025; wmw->lpWaveFormat->nBlockAlign = 1; wmw->lpWaveFormat->wBitsPerSample = 8; - - return 0; + wmw->lpWaveFormat->cbSize = 0; } /************************************************************************** @@ -319,24 +357,41 @@ static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw) ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' '); ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT); - if (!wmw->lpWaveFormat) - { - wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat)); - if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM; - *wmw->lpWaveFormat = wmw->wfxRef; + /* Set wave format accepts PCM only, however open an + * existing ADPCM file, record into it and the MCI will + * happily save back in that format. */ + if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) { + if (wmw->lpWaveFormat->nBlockAlign != + wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) { + WORD size = wmw->lpWaveFormat->nChannels * + wmw->lpWaveFormat->wBitsPerSample/8; + WARN("Incorrect nBlockAlign (%d), setting it to %d\n", + wmw->lpWaveFormat->nBlockAlign, size); + wmw->lpWaveFormat->nBlockAlign = size; + } + if (wmw->lpWaveFormat->nAvgBytesPerSec != + wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) { + DWORD speed = wmw->lpWaveFormat->nSamplesPerSec * + wmw->lpWaveFormat->nBlockAlign; + WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n", + wmw->lpWaveFormat->nAvgBytesPerSec, speed); + wmw->lpWaveFormat->nAvgBytesPerSec = speed; + } + } + if (wmw->lpWaveFormat == &wmw->wfxRef) { + LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX)); + if (!pwfx) return MCIERR_OUT_OF_MEMORY; + /* Set wave format accepts PCM only so the size is known. */ + assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM); + *pwfx = wmw->wfxRef; + wmw->lpWaveFormat = pwfx; } - - /* we can only record PCM files... there is no way in the MCI API to specify - * the necessary data to initialize the extra bytes of the WAVEFORMATEX - * structure - */ - if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM) - goto err; if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0)) goto err; - if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT))) + if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag) + ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize)) goto err; if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0)) @@ -353,8 +408,7 @@ static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw) return 0; err: - HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat); - wmw->lpWaveFormat = NULL; + /* mciClose takes care of wmw->lpWaveFormat. */ return MCIERR_INVALID_FILE; } @@ -371,6 +425,7 @@ static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName) if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) { WARN("can't retrieve temp path!\n"); + *pszTmpFileName = NULL; return MCIERR_FILE_NOT_FOUND; } @@ -400,16 +455,16 @@ static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName) return dwRet; } -static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename) +static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename) { LRESULT dwRet = MMSYSERR_NOERROR; - WCHAR* fn; + LPWSTR fn; fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR)); if (!fn) return MCIERR_OUT_OF_MEMORY; strcpyW(fn, filename); - HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName); - wmw->openParms.lpstrElementName = fn; + HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName); + wmw->lpFileName = fn; if (strlenW(filename) > 0) { /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */ @@ -463,7 +518,7 @@ static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_P if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; if (dwFlags & MCI_OPEN_SHAREABLE) - return MCIERR_HARDWARE; + return MCIERR_UNSUPPORTED_FUNCTION; if (wmw->nUseCount > 0) { /* The driver is already opened on this channel @@ -478,11 +533,13 @@ static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_P wmw->hWave = 0; wmw->dwStatus = MCI_MODE_NOT_READY; wmw->hFile = 0; - memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA)); - /* will be set by WAVE_mciOpenFile */ - wmw->openParms.lpstrElementName = NULL; + wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */ + wmw->hCallback = NULL; + WAVE_mciDefaultFmt(wmw); TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID); + /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */ + wmw->wNotifyDeviceID = wDevID; if (dwFlags & MCI_OPEN_ELEMENT) { if (dwFlags & MCI_OPEN_ELEMENT_ID) { @@ -494,32 +551,15 @@ static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_P dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName); } } - TRACE("hFile=%p\n", wmw->hFile); - if (dwRet == 0 && !wmw->lpWaveFormat) - dwRet = WAVE_mciDefaultFmt(wmw); - if (dwRet == 0) { - if (wmw->lpWaveFormat) { - switch (wmw->lpWaveFormat->wFormatTag) { - case WAVE_FORMAT_PCM: - if (wmw->lpWaveFormat->nAvgBytesPerSec != - wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) { - WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n", - wmw->lpWaveFormat->nAvgBytesPerSec, - wmw->lpWaveFormat->nSamplesPerSec * - wmw->lpWaveFormat->nBlockAlign); - wmw->lpWaveFormat->nAvgBytesPerSec = - wmw->lpWaveFormat->nSamplesPerSec * - wmw->lpWaveFormat->nBlockAlign; - } - break; - } - } wmw->dwPosition = 0; wmw->dwStatus = MCI_MODE_STOP; + + if (dwFlags & MCI_NOTIFY) + WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); } else { wmw->nUseCount--; if (wmw->hFile != 0) @@ -532,46 +572,38 @@ static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_P /************************************************************************** * WAVE_mciCue [internal] */ -static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM dwParam, LPMCI_GENERIC_PARMS lpParms) +static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) { - /* - FIXME - - This routine is far from complete. At the moment only a check is done on the - MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that - is the default. - - The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT - are ignored - */ - - DWORD dwRet; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); - FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms); + TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); - if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; + /* Tests on systems without sound drivers show that Cue, like + * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE. + * The first Cue Notify does not immediately return the + * notification, as if a player or recorder thread is started. + * PAUSE mode is reported when successful, but this mode is + * different from the normal Pause, because a) Pause then returns + * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is + * still accepted, returning the original notification as ABORTED. + * I.e. Cue allows subsequent format changes, unlike Record or + * Open file, closes winmm if the format changes and stops this + * thread. + * Wine creates one player or recorder thread per async. Play or + * Record command. Notification behaviour suggests that MS-W* + * reuses a single thread to improve response times. Having Cue + * start this thread early helps to improve Play/Record's initial + * response time. In effect, Cue is a performance hint, which + * justifies our almost no-op implementation. + */ - /* FIXME */ - /* always close elements ? */ - if (wmw->hFile != 0) { - mmioClose(wmw->hFile, 0); - wmw->hFile = 0; - } + if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; + if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION; - dwRet = MMSYSERR_NOERROR; /* assume success */ + if ((dwFlags & MCI_NOTIFY) && lpParms) + WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL); - if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) { - dwRet = waveOutClose(wmw->hWave); - if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL; - wmw->fInput = TRUE; - } else if (wmw->fInput) { - dwRet = waveInClose(wmw->hWave); - if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL; - wmw->fInput = FALSE; - } - wmw->hWave = 0; - return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL; + return MMSYSERR_NOERROR; } /************************************************************************** @@ -586,6 +618,11 @@ static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; + if (wmw->dwStatus != MCI_MODE_STOP) { + HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL); + if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED); + } + /* wait for playback thread (if any) to exit before processing further */ switch (wmw->dwStatus) { case MCI_MODE_PAUSE: @@ -602,15 +639,11 @@ static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS break; } - wmw->dwPosition = 0; - /* sanity resets */ wmw->dwStatus = MCI_MODE_STOP; - if ((dwFlags & MCI_NOTIFY) && lpParms) { - mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), - wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL); - } + if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet) + WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); return dwRet; } @@ -628,6 +661,7 @@ static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARM if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; if (wmw->dwStatus != MCI_MODE_STOP) { + /* mciStop handles MCI_NOTIFY_ABORTED */ dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms); } @@ -640,15 +674,15 @@ static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARM } } - HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat); - wmw->lpWaveFormat = NULL; - HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName); - wmw->openParms.lpstrElementName = NULL; + if (wmw->lpWaveFormat != &wmw->wfxRef) + HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat); + wmw->lpWaveFormat = &wmw->wfxRef; + HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName); + wmw->lpFileName = NULL; if ((dwFlags & MCI_NOTIFY) && lpParms) { - mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), - wmw->openParms.wDeviceID, - (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE); + WAVE_mciNotify(lpParms->dwCallback, wmw, + (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE); } return 0; @@ -678,9 +712,7 @@ static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg, } /****************************************************************** - * WAVE_mciPlayWaitDone - * - * + * WAVE_mciPlayWaitDone [internal] */ static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw) { @@ -698,28 +730,28 @@ static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw) /************************************************************************** * WAVE_mciPlay [internal] */ -static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) +static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent) { + LPMCI_PLAY_PARMS lpParms = (void*)pmt; DWORD end; LONG bufsize, count, left; - DWORD dwRet = 0; + DWORD dwRet; LPWAVEHDR waveHdr = NULL; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); + HANDLE oldcb; int whidx; - TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); + TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms); if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; - wmw->fInput = FALSE; - if (wmw->hFile == 0) { - WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName)); + WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName)); return MCIERR_FILE_NOT_FOUND; } - if (wmw->dwStatus == MCI_MODE_PAUSE) { + if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput) { /* FIXME: parameters (start/end) in lpParams may not be used */ return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms); } @@ -732,56 +764,65 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lp return MCIERR_INTERNAL; } + if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) { + if (wmw->lpWaveFormat->nBlockAlign != + wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) { + WARN("Incorrect nBlockAlign (%d), setting it to %d\n", + wmw->lpWaveFormat->nBlockAlign, + wmw->lpWaveFormat->nChannels * + wmw->lpWaveFormat->wBitsPerSample/8); + wmw->lpWaveFormat->nBlockAlign = + wmw->lpWaveFormat->nChannels * + wmw->lpWaveFormat->wBitsPerSample/8; + } + if (wmw->lpWaveFormat->nAvgBytesPerSec != + wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) { + WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n", + wmw->lpWaveFormat->nAvgBytesPerSec, + wmw->lpWaveFormat->nSamplesPerSec * + wmw->lpWaveFormat->nBlockAlign); + wmw->lpWaveFormat->nAvgBytesPerSec = + wmw->lpWaveFormat->nSamplesPerSec * + wmw->lpWaveFormat->nBlockAlign; + } + } + + end = wmw->ckWaveData.cksize; + if (lpParms && (dwFlags & MCI_TO)) { + DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); + if (position > end) return MCIERR_OUTOFRANGE; + end = position; + } + if (lpParms && (dwFlags & MCI_FROM)) { + DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); + if (position > end) return MCIERR_OUTOFRANGE; + /* Seek rounds down, so do we. */ + position /= wmw->lpWaveFormat->nBlockAlign; + position *= wmw->lpWaveFormat->nBlockAlign; + wmw->dwPosition = position; + } + if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE; + left = end - wmw->dwPosition; + if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */ + + wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */ wmw->dwStatus = MCI_MODE_PLAY; if (!(dwFlags & MCI_WAIT)) { - return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags, + return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags, (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS)); } - end = 0xFFFFFFFF; - if (lpParms && (dwFlags & MCI_FROM)) { - wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); - } - if (lpParms && (dwFlags & MCI_TO)) { - end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); - } - TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end); - if (end <= wmw->dwPosition) - return TRUE; - + oldcb = InterlockedExchangePointer(&wmw->hCallback, + (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL); + if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED); + oldcb = NULL; #define WAVE_ALIGN_ON_BLOCK(wmw,v) \ ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign) - wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition); - wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize); - - if (dwRet == 0) { - if (wmw->lpWaveFormat) { - switch (wmw->lpWaveFormat->wFormatTag) { - case WAVE_FORMAT_PCM: - if (wmw->lpWaveFormat->nAvgBytesPerSec != - wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) { - WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n", - wmw->lpWaveFormat->nAvgBytesPerSec, - wmw->lpWaveFormat->nSamplesPerSec * - wmw->lpWaveFormat->nBlockAlign); - wmw->lpWaveFormat->nAvgBytesPerSec = - wmw->lpWaveFormat->nSamplesPerSec * - wmw->lpWaveFormat->nBlockAlign; - } - break; - } - } - } else { - TRACE("can't retrieve wave format %d\n", dwRet); - goto cleanUp; - } - - /* go back to beginning of chunk plus the requested position */ /* FIXME: I'm not sure this is correct, notably because some data linked to * the decompression state machine will not be correctly initialized. @@ -790,9 +831,6 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lp */ mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */ - /* By default the device will be opened for output, the MCI_CUE function is there to - * change from output to input and back - */ /* FIXME: how to choose between several output channels ? here mapper is forced */ dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat, (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION); @@ -821,11 +859,11 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lp } whidx = 0; - left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition); wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); wmw->dwEventCount = 1L; /* for first buffer */ TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left); + if (hEvent) SetEvent(hEvent); /* FIXME: this doesn't work if wmw->dwPosition != 0 */ while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) { @@ -838,14 +876,18 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lp */ waveHdr[whidx].dwBufferLength = count; waveHdr[whidx].dwFlags &= ~WHDR_DONE; - TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n", - &waveHdr[whidx], waveHdr[whidx].dwBufferLength, - waveHdr[whidx].dwBytesRecorded); + TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n", + &waveHdr[whidx], waveHdr[whidx].dwBufferLength); dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR)); + if (dwRet) { + ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet); + dwRet = MCIERR_HARDWARE; + break; + } left -= count; wmw->dwPosition += count; TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition); - + /* InterlockedDecrement if and only if waveOutWrite is successful */ WAVE_mciPlayWaitDone(wmw); whidx ^= 1; } @@ -858,9 +900,10 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lp waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)); waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR)); - dwRet = 0; - cleanUp: + if (dwFlags & MCI_NOTIFY) + oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL); + HeapFree(GetProcessHeap(), 0, waveHdr); if (wmw->hWave) { @@ -869,19 +912,17 @@ cleanUp: } CloseHandle(wmw->hEvent); - if (lpParms && (dwFlags & MCI_NOTIFY)) { - mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), - wmw->openParms.wDeviceID, - dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); - } - wmw->dwStatus = MCI_MODE_STOP; + /* Let the potentically asynchronous commands support FAILURE notification. */ + if (oldcb) mciDriverNotify(oldcb, wDevID, + dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); + return dwRet; } /************************************************************************** - * WAVE_mciPlayCallback [internal] + * WAVE_mciRecordCallback [internal] */ static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, @@ -924,8 +965,7 @@ static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg, } /****************************************************************** - * bWAVE_mciRecordWaitDone - * + * WAVE_mciRecordWaitDone [internal] */ static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw) { @@ -943,41 +983,39 @@ static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw) /************************************************************************** * WAVE_mciRecord [internal] */ -static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms) +static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent) { + LPMCI_RECORD_PARMS lpParms = (void*)pmt; DWORD end; DWORD dwRet = MMSYSERR_NOERROR; LONG bufsize; LPWAVEHDR waveHdr = NULL; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); + HANDLE oldcb; - TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); + TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms); if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; - /* FIXME : since there is no way to determine in which mode the device is - * open (recording/playback) automatically switch from a mode to another - */ - wmw->fInput = TRUE; - - if (wmw->dwStatus == MCI_MODE_PAUSE) { + if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) { /* FIXME: parameters (start/end) in lpParams may not be used */ return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms); } /** This function will be called again by a thread when async is used. - * We have to set MCI_MODE_PLAY before we do this so that the app can spin + * We have to set MCI_MODE_RECORD before we do this so that the app can spin * on MCI_STATUS, so we have to allow it here if we're not going to start this thread. */ if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) { return MCIERR_INTERNAL; } + wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */ wmw->dwStatus = MCI_MODE_RECORD; if (!(dwFlags & MCI_WAIT)) { - return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags, + return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags, (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS)); } @@ -985,34 +1023,37 @@ static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARM * we don't modify the wave part of an existing file (ie. we always erase an * existing content, we don't overwrite) */ - HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName); - dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName); + HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName); + dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName); if (dwRet != 0) return dwRet; - /* new RIFF file */ + /* new RIFF file, lpWaveFormat now valid */ dwRet = WAVE_mciCreateRIFFSkeleton(wmw); - if (dwRet != 0) return dwRet; /* FIXME: we leak resources */ - - end = 0xFFFFFFFF; - if (lpParms && (dwFlags & MCI_FROM)) { - wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); - } + if (dwRet != 0) return dwRet; if (lpParms && (dwFlags & MCI_TO)) { end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); + } else end = 0xFFFFFFFF; + if (lpParms && (dwFlags & MCI_FROM)) { + DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); + if (wmw->ckWaveData.cksize < position) return MCIERR_OUTOFRANGE; + /* Seek rounds down, so do we. */ + position /= wmw->lpWaveFormat->nBlockAlign; + position *= wmw->lpWaveFormat->nBlockAlign; + wmw->dwPosition = position; } + if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */ TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end); - if (end <= wmw->dwPosition) - { - return TRUE; - } + oldcb = InterlockedExchangePointer(&wmw->hCallback, + (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL); + if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED); + oldcb = NULL; #define WAVE_ALIGN_ON_BLOCK(wmw,v) \ ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign) - wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition); wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize); /* Go back to the beginning of the chunk plus the requested position */ @@ -1067,10 +1108,16 @@ static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARM dwRet = waveInStart(wmw->hWave); + if (hEvent) SetEvent(hEvent); + while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) { WAVE_mciRecordWaitDone(wmw); } - + /* Grab callback before another thread kicks in after we change dwStatus. */ + if (dwFlags & MCI_NOTIFY) { + oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL); + dwFlags &= ~MCI_NOTIFY; + } /* needed so that the callback above won't add again the buffers returned by the reset */ wmw->dwStatus = MCI_MODE_STOP; @@ -1082,6 +1129,9 @@ static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARM dwRet = 0; cleanUp: + if (dwFlags & MCI_NOTIFY) + oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL); + HeapFree(GetProcessHeap(), 0, waveHdr); if (wmw->hWave) { @@ -1090,14 +1140,11 @@ cleanUp: } CloseHandle(wmw->hEvent); - if (lpParms && (dwFlags & MCI_NOTIFY)) { - mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), - wmw->openParms.wDeviceID, - dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); - } - wmw->dwStatus = MCI_MODE_STOP; + if (oldcb) mciDriverNotify(oldcb, wDevID, + dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); + return dwRet; } @@ -1112,17 +1159,34 @@ static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARM TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); - if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; - if (wmw->dwStatus == MCI_MODE_PLAY) { - wmw->dwStatus = MCI_MODE_PAUSE; + switch (wmw->dwStatus) { + case MCI_MODE_PLAY: + dwRet = waveOutPause(wmw->hWave); + if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE; + else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */ + ERR("waveOutPause error %d\n",dwRet); + dwRet = MCIERR_INTERNAL; + } + break; + case MCI_MODE_RECORD: + dwRet = waveInStop(wmw->hWave); + if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE; + else { + ERR("waveInStop error %d\n",dwRet); + dwRet = MCIERR_INTERNAL; + } + break; + case MCI_MODE_PAUSE: + dwRet = MMSYSERR_NOERROR; + break; + default: + dwRet = MCIERR_NONAPPLICABLE_FUNCTION; } - - if (wmw->fInput) dwRet = waveInStop(wmw->hWave); - else dwRet = waveOutPause(wmw->hWave); - - return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL; + if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms) + WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); + return dwRet; } /************************************************************************** @@ -1131,19 +1195,41 @@ static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARM static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) { WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); - DWORD dwRet = 0; + DWORD dwRet; TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; - if (wmw->dwStatus == MCI_MODE_PAUSE) { - wmw->dwStatus = MCI_MODE_PLAY; + switch (wmw->dwStatus) { + case MCI_MODE_PAUSE: + /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */ + if (wmw->fInput) { + dwRet = waveInStart(wmw->hWave); + if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD; + else { + ERR("waveInStart error %d\n",dwRet); + dwRet = MCIERR_INTERNAL; + } + } else { + dwRet = waveOutRestart(wmw->hWave); + if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY; + else { + ERR("waveOutRestart error %d\n",dwRet); + dwRet = MCIERR_INTERNAL; + } + } + break; + case MCI_MODE_PLAY: + case MCI_MODE_RECORD: + dwRet = MMSYSERR_NOERROR; + break; + default: + dwRet = MCIERR_NONAPPLICABLE_FUNCTION; } - - if (wmw->fInput) dwRet = waveInStart(wmw->hWave); - else dwRet = waveOutRestart(wmw->hWave); - return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL; + if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms) + WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); + return dwRet; } /************************************************************************** @@ -1151,37 +1237,43 @@ static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PAR */ static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms) { - DWORD ret = 0; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); + DWORD position, dwRet; TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); - if (lpParms == NULL) { - ret = MCIERR_NULL_PARAMETER_BLOCK; - } else if (wmw == NULL) { - ret = MCIERR_INVALID_DEVICE_ID; + if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; + if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; + + position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO); + if (!position) return MCIERR_MISSING_PARAMETER; + if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE; + + /* Stop sends MCI_NOTIFY_ABORTED when needed */ + dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0); + if (dwRet != MMSYSERR_NOERROR) return dwRet; + + if (dwFlags & MCI_TO) { + position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); + if (position > wmw->ckWaveData.cksize) + return MCIERR_OUTOFRANGE; + } else if (dwFlags & MCI_SEEK_TO_START) { + position = 0; } else { - WAVE_mciStop(wDevID, MCI_WAIT, 0); - - if (dwFlags & MCI_SEEK_TO_START) { - wmw->dwPosition = 0; - } else if (dwFlags & MCI_SEEK_TO_END) { - wmw->dwPosition = wmw->ckWaveData.cksize; - } else if (dwFlags & MCI_TO) { - wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); - } else { - WARN("dwFlag doesn't tell where to seek to...\n"); - return MCIERR_MISSING_PARAMETER; - } - - TRACE("Seeking to position=%u bytes\n", wmw->dwPosition); - - if (dwFlags & MCI_NOTIFY) { - mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), - wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL); - } + position = wmw->ckWaveData.cksize; } - return ret; + /* Seek rounds down, unless at end */ + if (position != wmw->ckWaveData.cksize) { + position /= wmw->lpWaveFormat->nBlockAlign; + position *= wmw->lpWaveFormat->nBlockAlign; + } + wmw->dwPosition = position; + TRACE("Seeking to position=%u bytes\n", position); + + if (dwFlags & MCI_NOTIFY) + WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); + + return MMSYSERR_NOERROR; } /************************************************************************** @@ -1253,30 +1345,40 @@ static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpPa TRACE("MCI_WAVE_SET_ANYINPUT !\n"); if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) TRACE("MCI_WAVE_SET_ANYOUTPUT !\n"); + /* Set wave format parameters is refused after Open or Record.*/ + if (dwFlags & MCI_WAVE_SET_FORMATTAG) { + TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag); + if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; + if (((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag != WAVE_FORMAT_PCM) + return MCIERR_OUTOFRANGE; + } if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) { + if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec; TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec); } if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) { + if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample; TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample); } if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) { + if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign; TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign); } if (dwFlags & MCI_WAVE_SET_CHANNELS) { + if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels; TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels); } - if (dwFlags & MCI_WAVE_SET_FORMATTAG) { - wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag; - TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag); - } if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) { + if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION; wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec; TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec); } + if (dwFlags & MCI_NOTIFY) + WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); return 0; } @@ -1287,7 +1389,6 @@ static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW l { WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet; - WPARAM wparam = MCI_NOTIFY_FAILURE; TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms); if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; @@ -1318,16 +1419,14 @@ static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW l DeleteFileW (lpParms->lpfilename); SetLastError(tmpRet); - if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) { + /* FIXME: Open file.wav; Save; must not rename the original file. + * Nor must Save a.wav; Save b.wav rename a. */ + if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) { ret = MMSYSERR_NOERROR; } - if (dwFlags & MCI_NOTIFY) { - if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL; - - mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), - wmw->openParms.wDeviceID, wparam); - } + if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY)) + WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); if (ret == MMSYSERR_NOERROR) ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename); @@ -1413,71 +1512,47 @@ static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARM lpParms->dwReturn = id; } else { lpParms->dwReturn = 0; - ret = MCIERR_WAVE_INPUTUNSPECIFIED; + ret = MCIERR_WAVE_OUTPUTUNSPECIFIED; } } break; + /* It is always ok to query wave format parameters, + * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */ case MCI_WAVE_STATUS_AVGBYTESPERSEC: - if (!wmw->hFile) { - lpParms->dwReturn = 0; - return MCIERR_UNSUPPORTED_FUNCTION; - } lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec; - TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn); + TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn); break; case MCI_WAVE_STATUS_BITSPERSAMPLE: - if (!wmw->hFile) { - lpParms->dwReturn = 0; - return MCIERR_UNSUPPORTED_FUNCTION; - } lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample; - TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn); + TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn); break; case MCI_WAVE_STATUS_BLOCKALIGN: - if (!wmw->hFile) { - lpParms->dwReturn = 0; - return MCIERR_UNSUPPORTED_FUNCTION; - } lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign; - TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn); + TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn); break; case MCI_WAVE_STATUS_CHANNELS: - if (!wmw->hFile) { - lpParms->dwReturn = 0; - return MCIERR_UNSUPPORTED_FUNCTION; - } lpParms->dwReturn = wmw->lpWaveFormat->nChannels; - TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn); + TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn); break; case MCI_WAVE_STATUS_FORMATTAG: - if (!wmw->hFile) { - lpParms->dwReturn = 0; - return MCIERR_UNSUPPORTED_FUNCTION; - } lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag; - TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn); + TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn); + break; + case MCI_WAVE_STATUS_SAMPLESPERSEC: + lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec; + TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn); break; case MCI_WAVE_STATUS_LEVEL: TRACE("MCI_WAVE_STATUS_LEVEL !\n"); lpParms->dwReturn = 0xAAAA5555; break; - case MCI_WAVE_STATUS_SAMPLESPERSEC: - if (!wmw->hFile) { - lpParms->dwReturn = 0; - return MCIERR_UNSUPPORTED_FUNCTION; - } - lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec; - TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn); - break; default: WARN("unknown command %08X !\n", lpParms->dwItem); return MCIERR_UNRECOGNIZED_COMMAND; } } - if (dwFlags & MCI_NOTIFY) { - mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), - wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL); - } + if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0) + WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); return ret; } @@ -1534,10 +1609,10 @@ static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags, ret = MCI_RESOURCE_RETURNED; break; case MCI_WAVE_GETDEVCAPS_INPUTS: - lpParms->dwReturn = 1; + lpParms->dwReturn = waveInGetNumDevs(); break; case MCI_WAVE_GETDEVCAPS_OUTPUTS: - lpParms->dwReturn = 1; + lpParms->dwReturn = waveOutGetNumDevs(); break; default: FIXME("Unknown capability (%08x) !\n", lpParms->dwItem); @@ -1547,6 +1622,8 @@ static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags, WARN("No GetDevCaps-Item !\n"); return MCIERR_UNRECOGNIZED_COMMAND; } + if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0) + WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); return ret; } @@ -1561,9 +1638,10 @@ static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW l TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms); - if (lpParms == NULL || lpParms->lpstrReturn == NULL) { - ret = MCIERR_NULL_PARAMETER_BLOCK; - } else if (wmw == NULL) { + if (!lpParms || !lpParms->lpstrReturn) + return MCIERR_NULL_PARAMETER_BLOCK; + + if (wmw == NULL) { ret = MCIERR_INVALID_DEVICE_ID; } else { static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0}; @@ -1574,7 +1652,7 @@ static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW l switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) { case MCI_INFO_PRODUCT: str = wszAudio; break; - case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break; + case MCI_INFO_FILE: str = wmw->lpFileName; break; case MCI_WAVE_INPUT: str = wszWaveIn; break; case MCI_WAVE_OUTPUT: str = wszWaveOut; break; default: @@ -1591,7 +1669,8 @@ static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW l } else { lpParms->lpstrReturn[0] = 0; } - + if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY)) + WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL); return ret; } @@ -1612,7 +1691,7 @@ LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, case DRV_ENABLE: return 1; case DRV_DISABLE: return 1; case DRV_QUERYCONFIGURE: return 1; - case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1; + case DRV_CONFIGURE: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1; case DRV_INSTALL: return DRVCNF_RESTART; case DRV_REMOVE: return DRVCNF_RESTART; } @@ -1623,8 +1702,8 @@ LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2); case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); - case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2); - case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2); + case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL); + case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL); case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2); case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);