From 3732965c15555f671c6607e876c7821452971b4f Mon Sep 17 00:00:00 2001 From: Francois Jacques Date: Thu, 19 Oct 2000 22:36:54 +0000 Subject: [PATCH] Added asynchronous recording in MCI layer. --- dlls/winmm/mciwave/mciwave.c | 545 ++++++++++++++++++++++++++++------- 1 file changed, 447 insertions(+), 98 deletions(-) diff --git a/dlls/winmm/mciwave/mciwave.c b/dlls/winmm/mciwave/mciwave.c index 9263b12984d..032c593eb3e 100644 --- a/dlls/winmm/mciwave/mciwave.c +++ b/dlls/winmm/mciwave/mciwave.c @@ -4,12 +4,10 @@ * * Copyright 1994 Martin Ayotte * 1999 Eric Pouech - */ -/* - * FIXME: - * - record/play should and must be done asynchronous + * 2000 Francois Jacques */ +#include "winerror.h" #include "windef.h" #include "wingdi.h" #include "winuser.h" @@ -31,11 +29,13 @@ typedef struct { BOOL fInput; /* FALSE = Output, TRUE = Input */ volatile WORD dwStatus; /* one from MCI_MODE_xxxx */ DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */ - DWORD dwFileOffset; /* Offset of chunk in mmio file */ - DWORD dwLength; /* number of bytes in chunk for playing */ - DWORD dwPosition; /* position in bytes in chunk for playing */ + DWORD dwRemaining; /* remaining bytes to play or record */ + DWORD dwPosition; /* position in bytes in chunk */ HANDLE hEvent; /* for synchronization */ DWORD dwEventCount; /* for synchronization */ + BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */ + MMCKINFO ckMainRIFF; /* main RIFF chunk */ + MMCKINFO ckWaveData; /* data chunk */ } WINE_MCIWAVE; /* =================================================================== @@ -172,8 +172,8 @@ static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID) */ static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet) { - DWORD ret = 0; - + DWORD ret = 0; + switch (wmw->dwMciTimeFormat) { case MCI_FORMAT_MILLISECONDS: ret = (val * 1000) / wmw->lpWaveFormat->nAvgBytesPerSec; @@ -245,20 +245,84 @@ static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF) TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize); mmioAscend(wmw->hFile, &mmckInfo, 0); - mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a'); - if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0) { + wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a'); + if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) { TRACE("can't find data chunk\n"); return MCIERR_INVALID_FILE; } TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n", - (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize); + (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize); TRACE("nChannels=%d nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec); - wmw->dwLength = mmckInfo.cksize; - wmw->dwFileOffset = mmckInfo.dwDataOffset; + return 0; } +/************************************************************************** + * WAVE_mciCreateRIFFSkeleton [internal] + */ +static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw) +{ + MMCKINFO ckWaveFormat; + + LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF); + LPMMCKINFO lpckWaveData = &(wmw->ckWaveData); + LPWAVEFORMATEX lpWaveFormat = wmw->lpWaveFormat; + + + HMMIO hmmio = wmw->hFile; + + lpckRIFF->ckid = FOURCC_RIFF; + lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E'); + lpckRIFF->cksize = 0; + + if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckRIFF, MMIO_CREATERIFF)) + goto err; + + ckWaveFormat.fccType = 0; + ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' '); + ckWaveFormat.cksize = 16; + + if (!lpWaveFormat) + { + TRACE("allocating waveformat with default waveformat 11khz/8bit/mono \n"); + + lpWaveFormat = wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpWaveFormat)); + + lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM; + lpWaveFormat->nChannels = 1; /* MONO */ + lpWaveFormat->nSamplesPerSec = 11025; + lpWaveFormat->nAvgBytesPerSec = 11025; + lpWaveFormat->nBlockAlign = 1; + lpWaveFormat->wBitsPerSample = 8; + lpWaveFormat->cbSize = 0; /* don't care */ + } + + if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, &ckWaveFormat, 0)) + goto err; + + /* only the first 16 bytes are serialized */ + if (-1 == mmioWrite(hmmio, (HPCSTR) lpWaveFormat, 16)) + goto err; + + if (MMSYSERR_NOERROR != mmioAscend(hmmio, &ckWaveFormat, 0)) + goto err; + + lpckWaveData->cksize = 0; + lpckWaveData->fccType = 0; + lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a'); + + /* create data chunk */ + if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckWaveData, 0)) + goto err; + + return 0; + +err: + HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat); + return MCIERR_INVALID_FILE; +} + /************************************************************************** * WAVE_mciOpen [internal] */ @@ -267,6 +331,7 @@ static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpO DWORD dwRet = 0; DWORD dwDeviceID; WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID); + CHAR* pszTmpFileName = 0; TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms); if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; @@ -281,12 +346,14 @@ static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpO */ return MCIERR_DEVICE_OPEN; } + wmw->nUseCount++; dwDeviceID = lpOpenParms->wDeviceID; wmw->fInput = FALSE; wmw->hWave = 0; + wmw->dwStatus = MCI_MODE_NOT_READY; TRACE("wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID); @@ -297,46 +364,101 @@ static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpO */ dwRet = MCIERR_UNRECOGNIZED_COMMAND; } else { - LPCSTR lpstrElementName = lpOpenParms->lpstrElementName; - - /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */ - TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName); - if (lpstrElementName && (strlen(lpstrElementName) > 0)) { - wmw->hFile = mmioOpenA((LPSTR)lpstrElementName, NULL, - MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); - if (wmw->hFile == 0) { - WARN("can't find file='%s' !\n", lpstrElementName); - dwRet = MCIERR_FILE_NOT_FOUND; + if (strlen(lpOpenParms->lpstrElementName) > 0) { + lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName; + + /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */ + TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName); + + if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) { + wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL, + MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE); + + if (wmw->hFile == 0) { + WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName); + dwRet = MCIERR_FILE_NOT_FOUND; + } + else + { + LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF; + + /* make sure we're are the beginning of the file */ + mmioSeek(wmw->hFile, 0, SEEK_SET); + + /* first reading of this file. read the waveformat chunk */ + if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) { + dwRet = MCIERR_INVALID_FILE; + } else { + TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n", + (LPSTR)&(lpckMainRIFF->ckid), + (LPSTR) &(lpckMainRIFF->fccType), + (lpckMainRIFF->cksize)); + + if ((lpckMainRIFF->ckid != FOURCC_RIFF) || + lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) { + dwRet = MCIERR_INVALID_FILE; + } else { + dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF); + } + } + } + } + else { + wmw->hFile = 0; } - } else { - wmw->hFile = 0; - } + } + else { + CHAR szTmpPath[MAX_PATH]; + CHAR szPrefix[4] = "TMP\0"; + + pszTmpFileName = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + MAX_PATH * sizeof(*pszTmpFileName)); + + if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) { + WARN("can't retrieve temp path!\n"); + HeapFree(GetProcessHeap(), 0, pszTmpFileName); + return MCIERR_FILE_NOT_FOUND; + } + + if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) { + WARN("can't retrieve temp file name!\n"); + HeapFree(GetProcessHeap(), 0, pszTmpFileName); + return MCIERR_FILE_NOT_FOUND; + } + + wmw->bTemporaryFile = TRUE; + + TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName); + + if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) { + + wmw->hFile = mmioOpenA(pszTmpFileName, NULL, + MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE); + + if (wmw->hFile == 0) { + /* temporary file could not be created. clean filename. */ + HeapFree(GetProcessHeap(), 0, pszTmpFileName); + WARN("can't create file='%s' !\n", pszTmpFileName); + dwRet = MCIERR_FILE_NOT_FOUND; + } + } + } } } + TRACE("hFile=%u\n", wmw->hFile); memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA)); - wmw->wNotifyDeviceID = dwDeviceID; - wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */ - - if (dwRet == 0 && wmw->hFile != 0) { - MMCKINFO ckMainRIFF; - - if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) { - dwRet = MCIERR_INVALID_FILE; - } else { - TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n", - (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize); - if ((ckMainRIFF.ckid != FOURCC_RIFF) || - (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) { - dwRet = MCIERR_INVALID_FILE; - } else { - dwRet = WAVE_mciReadFmt(wmw, &ckMainRIFF); - } - } - } else { - wmw->dwLength = 0; + + if (wmw->bTemporaryFile == TRUE) + { + /* Additional openParms is temporary file's name */ + wmw->openParms.lpstrElementName = pszTmpFileName; } + + wmw->wNotifyDeviceID = dwDeviceID; + if (dwRet == 0) { if (wmw->lpWaveFormat) { switch (wmw->lpWaveFormat->wFormatTag) { @@ -475,6 +597,16 @@ static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpPar wmw->hFile = 0; } } + + /* That string got allocated in mciOpen because no filename was specified + * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was + * allocated by mciOpen, *NOT* the application. + */ + if (wmw->bTemporaryFile) + { + HeapFree(GetProcessHeap(), 0, (CHAR*) wmw->openParms.lpstrElementName); + wmw->openParms.lpstrElementName = NULL; + } HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat); wmw->lpWaveFormat = NULL; @@ -535,26 +667,31 @@ static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) LPWAVEHDR waveHdr = NULL; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); int whidx; - + 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 = FALSE; + if (wmw->fInput) { WARN("cannot play on input device\n"); return MCIERR_NONAPPLICABLE_FUNCTION; } - + if (wmw->hFile == 0) { WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName); return MCIERR_FILE_NOT_FOUND; } - if (wmw->dwStatus == MCI_MODE_PAUSE) { - /* FIXME: parameters (start/end) in lpParams may not be used */ - return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms); - } + if (wmw->dwStatus == MCI_MODE_PAUSE) { + /* 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 @@ -584,18 +721,43 @@ static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) if (end <= wmw->dwPosition) return TRUE; + #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->dwLength = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwLength); + 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 (%ld), setting it to %ld\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 %ld\n", dwRet); + goto cleanUp; + } + + /* go back to begining 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 correcly initialized. * try it this way (other way would be to decompress from 0 up to dwPosition * and to start sending to hWave when dwPosition is reached) */ - mmioSeek(wmw->hFile, wmw->dwFileOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */ + 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 @@ -628,7 +790,7 @@ static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) } whidx = 0; - left = min(wmw->dwLength, end - wmw->dwPosition); + left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition); wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); wmw->dwEventCount = 1L; /* for first buffer */ @@ -687,64 +849,212 @@ cleanUp: return dwRet; } +/************************************************************************** + * WAVE_mciPlayCallback [internal] + */ +static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg, + DWORD dwInstance, + DWORD dwParam1, DWORD dwParam2) +{ + WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance; + LPWAVEHDR lpWaveHdr = NULL; + LONG count = 0; + switch (uMsg) { + case WIM_OPEN: + case WIM_CLOSE: + break; + case WIM_DATA: + lpWaveHdr = (LPWAVEHDR) dwParam1; + + InterlockedIncrement(&wmw->dwEventCount); + + count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded); + + lpWaveHdr->dwFlags &= ~WHDR_DONE; + wmw->dwPosition += count; + wmw->dwRemaining -= count; + + waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr)); + TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition); + + SetEvent(wmw->hEvent); + break; + default: + ERR("Unknown uMsg=%d\n", uMsg); + } +} + +static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw) +{ + for (;;) { + ResetEvent(wmw->hEvent); + if (InterlockedDecrement(&wmw->dwEventCount) >= 0) { + break; + } + InterlockedIncrement(&wmw->dwEventCount); + + WaitForSingleObject(wmw->hEvent, INFINITE); + } +} + /************************************************************************** * WAVE_mciRecord [internal] */ static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms) { - int start, end; - LONG bufsize; - WAVEHDR waveHdr; - DWORD dwRet; + DWORD end; + DWORD dwRet = 0; + LONG bufsize; + LPWAVEHDR waveHdr = NULL; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); - + + 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->fInput) { WARN("cannot record on output device\n"); return MCIERR_NONAPPLICABLE_FUNCTION; } + + if (wmw->dwStatus == MCI_MODE_PAUSE) { + /* FIXME: parameters (start/end) in lpParams may not be used */ + return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms); + } - if (wmw->hFile == 0) { - WARN("can't find file='%s' !\n", - wmw->openParms.lpstrElementName); - return MCIERR_FILE_NOT_FOUND; + /** 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 + * 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; } - start = 1; end = 99999; - if (dwFlags & MCI_FROM) { - start = lpParms->dwFrom; - TRACE("MCI_FROM=%d \n", start); - } - if (dwFlags & MCI_TO) { - end = lpParms->dwTo; - TRACE("MCI_TO=%d \n", end); - } - bufsize = 64000; - waveHdr.lpData = HeapAlloc(GetProcessHeap(), 0, bufsize); - waveHdr.dwBufferLength = bufsize; - waveHdr.dwUser = 0L; - waveHdr.dwFlags = 0L; - waveHdr.dwLoops = 0L; - dwRet = waveInPrepareHeader(wmw->hWave, &waveHdr, sizeof(WAVEHDR)); - for (;;) { /* FIXME: I don't see any waveInAddBuffer ? */ - waveHdr.dwBytesRecorded = 0; - dwRet = waveInStart(wmw->hWave); - TRACE("waveInStart => lpWaveHdr=%p dwBytesRecorded=%lu\n", - &waveHdr, waveHdr.dwBytesRecorded); - if (waveHdr.dwBytesRecorded == 0) break; + wmw->dwStatus = MCI_MODE_RECORD; + + if (!(dwFlags & MCI_WAIT)) { + return MCI_SendCommandAsync(wmw->wNotifyDeviceID, MCI_RECORD, dwFlags, + (DWORD)lpParms, sizeof(MCI_RECORD_PARMS)); } - dwRet = waveInUnprepareHeader(wmw->hWave, &waveHdr, sizeof(WAVEHDR)); - HeapFree(GetProcessHeap(), 0, waveHdr.lpData); - if (dwFlags & MCI_NOTIFY) { + if (!wmw->lpWaveFormat) + { + /* new RIFF file */ + dwRet = WAVE_mciCreateRIFFSkeleton(wmw); + } + + 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("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end); + + if (end <= wmw->dwPosition) + { + return TRUE; + } + +#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 begining 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 correcly initialized. + * try it this way (other way would be to decompress from 0 up to dwPosition + * and to start sending to hWave when dwPosition is reached) + */ + 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 = waveInOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat, + (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION); + + if (dwRet != 0) { + TRACE("Can't open low level audio device %ld\n", dwRet); + dwRet = MCIERR_DEVICE_OPEN; + wmw->hWave = 0; + goto cleanUp; + } + + /* make it so that 3 buffers per second are needed */ + bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3); + + waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize); + waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR); + waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize; + waveHdr[0].dwUser = waveHdr[1].dwUser = 0L; + waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L; + waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L; + waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize; + + if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || + waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) { + dwRet = MCIERR_INTERNAL; + goto cleanUp; + } + + if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || + waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) { + dwRet = MCIERR_INTERNAL; + goto cleanUp; + } + + wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + wmw->dwEventCount = 1L; /* for first buffer */ + + wmw->dwRemaining = end - wmw->dwPosition; + + TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, wmw->dwRemaining); + + dwRet = waveInStart(wmw->hWave); + + while ( wmw->dwRemaining > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) { + WAVE_mciRecordWaitDone(wmw); + } + + waveInReset(wmw->hWave); + waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)); + waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR)); + + dwRet = 0; + +cleanUp: + HeapFree(GetProcessHeap(), 0, waveHdr); + + if (wmw->hWave) { + waveInClose(wmw->hWave); + wmw->hWave = 0; + } + CloseHandle(wmw->hEvent); + + if (lpParms && (dwFlags & MCI_NOTIFY)) { mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), - wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL); + wmw->wNotifyDeviceID, + dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); } - return 0; + + wmw->dwStatus = MCI_MODE_STOP; + + return dwRet; + } /************************************************************************** @@ -811,7 +1121,7 @@ static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms) if (dwFlags & MCI_SEEK_TO_START) { wmw->dwPosition = 0; } else if (dwFlags & MCI_SEEK_TO_END) { - wmw->dwPosition = wmw->dwLength; + wmw->dwPosition = wmw->ckWaveData.cksize; } else if (dwFlags & MCI_TO) { wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); } else { @@ -912,6 +1222,45 @@ static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms) return 0; } +/************************************************************************** + * WAVE_mciSave [internal] + */ +static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms) +{ + WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); + DWORD ret = MCIERR_FILE_NOT_SAVED; + WPARAM wparam = MCI_NOTIFY_FAILURE; + + TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms); + if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; + if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; + + if (dwFlags & MCI_WAIT) + { + FIXME("MCI_WAIT not implemented\n"); + } + + ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0); + ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0); + + + ret = mmioClose(wmw->hFile, 0); + + if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) { + ret = ERROR_SUCCESS; + } + + if (dwFlags & MCI_NOTIFY) { + if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL; + + mciDriverNotify( (HWND) LOWORD(lpParms->dwCallback), + wmw->wNotifyDeviceID, + wparam); + } + + return ret; +} + /************************************************************************** * WAVE_mciStatus [internal] */ @@ -936,7 +1285,7 @@ static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpPar return MCIERR_UNSUPPORTED_FUNCTION; } /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */ - lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength, &ret); + lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret); TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn); break; case MCI_STATUS_MODE: @@ -1209,9 +1558,9 @@ LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg, case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2); case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2); case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2); + case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2); /* commands that should be supported */ case MCI_LOAD: - case MCI_SAVE: case MCI_FREEZE: case MCI_PUT: case MCI_REALIZE: