Added asynchronous recording in MCI layer.

This commit is contained in:
Francois Jacques 2000-10-19 22:36:54 +00:00 committed by Alexandre Julliard
parent d747b76cb6
commit 3732965c15

View file

@ -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: