Sweden-Number/dlls/winmm/mciwave/mciwave.c

1590 lines
50 KiB
C

/* -*- tab-width: 8; c-basic-offset: 4 -*- */
/*
* Sample Wine Driver for MCI wave forms
*
* Copyright 1994 Martin Ayotte
* 1999 Eric Pouech
* 2000 Francois Jacques
*/
#include "winerror.h"
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "mmddk.h"
#include "digitalv.h"
#include "heap.h"
#include "debugtools.h"
DEFAULT_DEBUG_CHANNEL(mciwave);
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_PARMSA openParms;
LPWAVEFORMATEX lpWaveFormat;
BOOL fInput; /* FALSE = Output, TRUE = Input */
volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
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;
/* ===================================================================
* ===================================================================
* FIXME: should be using the new mmThreadXXXX functions from WINMM
* instead of those
* it would require to add a wine internal flag to mmThreadCreate
* in order to pass a 32 bit function instead of a 16 bit one
* ===================================================================
* =================================================================== */
struct SCA {
UINT wDevID;
UINT wMsg;
DWORD dwParam1;
DWORD dwParam2;
BOOL allocatedCopy;
};
/**************************************************************************
* MCI_SCAStarter [internal]
*/
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);
if (sca->allocatedCopy)
HeapFree(GetProcessHeap(), 0, (LPVOID)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 dwParam1,
DWORD dwParam2, UINT size)
{
struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA));
if (sca == 0)
return MCIERR_OUT_OF_MEMORY;
sca->wDevID = wDevID;
sca->wMsg = wMsg;
sca->dwParam1 = dwParam1;
if (size) {
sca->dwParam2 = (DWORD)HeapAlloc(GetProcessHeap(), 0, size);
if (sca->dwParam2 == 0) {
HeapFree(GetProcessHeap(), 0, sca);
return MCIERR_OUT_OF_MEMORY;
}
sca->allocatedCopy = TRUE;
/* copy structure passed by program in dwParam2 to be sure
* we can still use it whatever the program does
*/
memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
} else {
sca->dwParam2 = dwParam2;
sca->allocatedCopy = FALSE;
}
if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
return MCI_SCAStarter(&sca);
}
return 0;
}
/*======================================================================*
* MCI WAVE implemantation *
*======================================================================*/
static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
/**************************************************************************
* MCIWAVE_drvOpen [internal]
*/
static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
{
WINE_MCIWAVE* wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
if (!wmw)
return 0;
wmw->wDevID = modp->wDeviceID;
mciSetDriverData(wmw->wDevID, (DWORD)wmw);
modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
return modp->wDeviceID;
}
/**************************************************************************
* MCIWAVE_drvClose [internal]
*/
static DWORD WAVE_drvClose(DWORD dwDevID)
{
WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
if (wmw) {
HeapFree(GetProcessHeap(), 0, wmw);
mciSetDriverData(dwDevID, 0);
return 1;
}
return 0;
}
/**************************************************************************
* WAVE_mciGetOpenDev [internal]
*/
static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
{
WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
if (wmw == NULL || wmw->nUseCount == 0) {
WARN("Invalid wDevID=%u\n", wDevID);
return 0;
}
return wmw;
}
/**************************************************************************
* WAVE_ConvertByteToTimeFormat [internal]
*/
static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
{
DWORD ret = 0;
switch (wmw->dwMciTimeFormat) {
case MCI_FORMAT_MILLISECONDS:
ret = (val * 1000) / wmw->lpWaveFormat->nAvgBytesPerSec;
break;
case MCI_FORMAT_BYTES:
ret = val;
break;
case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
break;
default:
WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
}
TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
*lpRet = 0;
return ret;
}
/**************************************************************************
* WAVE_ConvertTimeFormatToByte [internal]
*/
static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
{
DWORD ret = 0;
switch (wmw->dwMciTimeFormat) {
case MCI_FORMAT_MILLISECONDS:
ret = (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;
break;
default:
WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
}
TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
return ret;
}
/**************************************************************************
* WAVE_mciReadFmt [internal]
*/
static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
{
MMCKINFO mmckInfo;
long r;
mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
return MCIERR_INVALID_FILE;
TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
(LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
if (r < sizeof(WAVEFORMAT))
return MCIERR_INVALID_FILE;
TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
if (r >= (long)sizeof(WAVEFORMATEX))
TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
mmioAscend(wmw->hFile, &mmckInfo, 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)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
TRACE("nChannels=%d nSamplesPerSec=%ld\n",
wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
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]
*/
static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
{
DWORD dwRet = 0;
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;
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
if (dwFlags & MCI_OPEN_SHAREABLE)
return MCIERR_HARDWARE;
if (wmw->nUseCount > 0) {
/* The driver is already opened on this channel
* Wave driver cannot be shared
*/
return MCIERR_DEVICE_OPEN;
}
wmw->nUseCount++;
wmw->fInput = FALSE;
wmw->hWave = 0;
wmw->dwStatus = MCI_MODE_NOT_READY;
TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
if (dwFlags & MCI_OPEN_ELEMENT) {
if (dwFlags & MCI_OPEN_ELEMENT_ID) {
/* could it be that (DWORD)lpOpenParms->lpstrElementName
* contains the hFile value ?
*/
dwRet = MCIERR_UNRECOGNIZED_COMMAND;
} else {
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 {
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));
if (wmw->bTemporaryFile == TRUE)
{
/* Additional openParms is temporary file's name */
wmw->openParms.lpstrElementName = pszTmpFileName;
}
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;
}
}
wmw->dwPosition = 0;
wmw->dwStatus = MCI_MODE_STOP;
} else {
wmw->nUseCount--;
if (wmw->hFile != 0)
mmioClose(wmw->hFile, 0);
wmw->hFile = 0;
}
return dwRet;
}
/**************************************************************************
* WAVE_mciCue [internal]
*/
static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, 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);
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
/* always close elements ? */
if (wmw->hFile != 0) {
mmioClose(wmw->hFile, 0);
wmw->hFile = 0;
}
dwRet = MMSYSERR_NOERROR; /* assume success */
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;
}
/**************************************************************************
* WAVE_mciStop [internal]
*/
static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{
DWORD dwRet = 0;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
/* wait for playback thread (if any) to exit before processing further */
switch (wmw->dwStatus) {
case MCI_MODE_PAUSE:
case MCI_MODE_PLAY:
case MCI_MODE_RECORD:
{
int oldStat = wmw->dwStatus;
wmw->dwStatus = MCI_MODE_NOT_READY;
if (oldStat == MCI_MODE_PAUSE)
dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
}
while (wmw->dwStatus != MCI_MODE_STOP)
Sleep(10);
break;
}
wmw->dwPosition = 0;
/* sanity resets */
wmw->dwStatus = MCI_MODE_STOP;
if ((dwFlags & MCI_NOTIFY) && lpParms) {
mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
}
return dwRet;
}
/**************************************************************************
* WAVE_mciClose [internal]
*/
static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{
DWORD dwRet = 0;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
if (wmw->dwStatus != MCI_MODE_STOP) {
dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
}
wmw->nUseCount--;
if (wmw->nUseCount == 0) {
if (wmw->hFile != 0) {
mmioClose(wmw->hFile, 0);
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;
if ((dwFlags & MCI_NOTIFY) && lpParms) {
mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
wmw->openParms.wDeviceID,
(dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
}
return 0;
}
/**************************************************************************
* WAVE_mciPlayCallback [internal]
*/
static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
DWORD dwInstance,
DWORD dwParam1, DWORD dwParam2)
{
WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
switch (uMsg) {
case WOM_OPEN:
case WOM_CLOSE:
break;
case WOM_DONE:
InterlockedIncrement(&wmw->dwEventCount);
TRACE("Returning waveHdr=%lx\n", dwParam1);
SetEvent(wmw->hEvent);
break;
default:
ERR("Unknown uMsg=%d\n", uMsg);
}
}
static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
{
for (;;) {
ResetEvent(wmw->hEvent);
if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
break;
}
InterlockedIncrement(&wmw->dwEventCount);
WaitForSingleObject(wmw->hEvent, INFINITE);
}
}
/**************************************************************************
* WAVE_mciPlay [internal]
*/
static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
{
DWORD end;
LONG bufsize, count, left;
DWORD dwRet = 0;
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);
}
/** 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_PLAY) && (dwFlags & MCI_WAIT))) {
return MCIERR_INTERNAL;
}
wmw->dwStatus = MCI_MODE_PLAY;
if (!(dwFlags & MCI_WAIT)) {
return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
(DWORD)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=%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);
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->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(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
(DWORD)WAVE_mciPlayCallback, (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 (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
dwRet = MCIERR_INTERNAL;
goto cleanUp;
}
whidx = 0;
left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
wmw->dwEventCount = 1L; /* for first buffer */
TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
/* FIXME: this doesn't work if wmw->dwPosition != 0 */
while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
if (count < 1)
break;
/* count is always <= bufsize, so this is correct regarding the
* waveOutPrepareHeader function
*/
waveHdr[whidx].dwBufferLength = count;
waveHdr[whidx].dwFlags &= ~WHDR_DONE;
TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
&waveHdr[whidx], waveHdr[whidx].dwBufferLength,
waveHdr[whidx].dwBytesRecorded);
dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
left -= count;
wmw->dwPosition += count;
TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
WAVE_mciPlayWaitDone(wmw);
whidx ^= 1;
}
WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
/* just to get rid of some race conditions between play, stop and pause */
waveOutReset(wmw->hWave);
waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
dwRet = 0;
cleanUp:
HeapFree(GetProcessHeap(), 0, waveHdr);
if (wmw->hWave) {
waveOutClose(wmw->hWave);
wmw->hWave = 0;
}
CloseHandle(wmw->hEvent);
if (lpParms && (dwFlags & MCI_NOTIFY)) {
mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
wmw->openParms.wDeviceID,
dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
}
wmw->dwStatus = MCI_MODE_STOP;
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)
{
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);
}
/** 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;
}
wmw->dwStatus = MCI_MODE_RECORD;
if (!(dwFlags & MCI_WAIT)) {
return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
(DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
}
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->openParms.wDeviceID,
dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
}
wmw->dwStatus = MCI_MODE_STOP;
return dwRet;
}
/**************************************************************************
* WAVE_mciPause [internal]
*/
static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{
DWORD dwRet;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE("(%u, %08lX, %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;
}
if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
else dwRet = waveOutPause(wmw->hWave);
return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
}
/**************************************************************************
* WAVE_mciResume [internal]
*/
static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
DWORD dwRet = 0;
TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
if (wmw->dwStatus == MCI_MODE_PAUSE) {
wmw->dwStatus = MCI_MODE_PLAY;
}
if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
else dwRet = waveOutRestart(wmw->hWave);
return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
}
/**************************************************************************
* WAVE_mciSeek [internal]
*/
static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
{
DWORD ret = 0;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (lpParms == NULL) {
ret = MCIERR_NULL_PARAMETER_BLOCK;
} else if (wmw == NULL) {
ret = MCIERR_INVALID_DEVICE_ID;
} 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=%lu bytes\n", wmw->dwPosition);
if (dwFlags & MCI_NOTIFY) {
mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
}
}
return ret;
}
/**************************************************************************
* WAVE_mciSet [internal]
*/
static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
{
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE("(%u, %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_SET_TIME_FORMAT) {
switch (lpParms->dwTimeFormat) {
case MCI_FORMAT_MILLISECONDS:
TRACE("MCI_FORMAT_MILLISECONDS !\n");
wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
break;
case MCI_FORMAT_BYTES:
TRACE("MCI_FORMAT_BYTES !\n");
wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
break;
case MCI_FORMAT_SAMPLES:
TRACE("MCI_FORMAT_SAMPLES !\n");
wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
break;
default:
WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
return MCIERR_BAD_TIME_FORMAT;
}
}
if (dwFlags & MCI_SET_VIDEO) {
TRACE("No support for video !\n");
return MCIERR_UNSUPPORTED_FUNCTION;
}
if (dwFlags & MCI_SET_DOOR_OPEN) {
TRACE("No support for door open !\n");
return MCIERR_UNSUPPORTED_FUNCTION;
}
if (dwFlags & MCI_SET_DOOR_CLOSED) {
TRACE("No support for door close !\n");
return MCIERR_UNSUPPORTED_FUNCTION;
}
if (dwFlags & MCI_SET_AUDIO) {
if (dwFlags & MCI_SET_ON) {
TRACE("MCI_SET_ON audio !\n");
} else if (dwFlags & MCI_SET_OFF) {
TRACE("MCI_SET_OFF audio !\n");
} else {
WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
return MCIERR_BAD_INTEGER;
}
if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
TRACE("MCI_SET_AUDIO_ALL !\n");
if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
TRACE("MCI_SET_AUDIO_LEFT !\n");
if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
TRACE("MCI_SET_AUDIO_RIGHT !\n");
}
if (dwFlags & MCI_WAVE_INPUT)
TRACE("MCI_WAVE_INPUT !\n");
if (dwFlags & MCI_WAVE_OUTPUT)
TRACE("MCI_WAVE_OUTPUT !\n");
if (dwFlags & MCI_WAVE_SET_ANYINPUT)
TRACE("MCI_WAVE_SET_ANYINPUT !\n");
if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
if (dwFlags & MCI_WAVE_SET_CHANNELS)
TRACE("MCI_WAVE_SET_CHANNELS !\n");
if (dwFlags & MCI_WAVE_SET_FORMATTAG)
TRACE("MCI_WAVE_SET_FORMATTAG !\n");
if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
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->openParms.wDeviceID, wparam);
}
return ret;
}
/**************************************************************************
* WAVE_mciStatus [internal]
*/
static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
{
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
DWORD ret = 0;
TRACE("(%u, %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_STATUS_ITEM) {
switch (lpParms->dwItem) {
case MCI_STATUS_CURRENT_TRACK:
lpParms->dwReturn = 1;
TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
break;
case MCI_STATUS_LENGTH:
if (!wmw->hFile) {
lpParms->dwReturn = 0;
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->ckWaveData.cksize, &ret);
TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
break;
case MCI_STATUS_MODE:
TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_STATUS_MEDIA_PRESENT:
TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_STATUS_NUMBER_OF_TRACKS:
/* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
lpParms->dwReturn = 1;
TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
break;
case MCI_STATUS_POSITION:
if (!wmw->hFile) {
lpParms->dwReturn = 0;
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,
(dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
&ret);
TRACE("MCI_STATUS_POSITION %s => %lu\n",
(dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
break;
case MCI_STATUS_READY:
lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_STATUS_TIME_FORMAT:
lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_WAVE_INPUT:
TRACE("MCI_WAVE_INPUT !\n");
lpParms->dwReturn = 0;
ret = MCIERR_WAVE_INPUTUNSPECIFIED;
break;
case MCI_WAVE_OUTPUT:
TRACE("MCI_WAVE_OUTPUT !\n");
{
UINT id;
if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
lpParms->dwReturn = id;
} else {
lpParms->dwReturn = 0;
ret = MCIERR_WAVE_INPUTUNSPECIFIED;
}
}
break;
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);
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);
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);
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);
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);
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 %08lX !\n", lpParms->dwItem);
return MCIERR_UNRECOGNIZED_COMMAND;
}
}
if (dwFlags & MCI_NOTIFY) {
mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
}
return ret;
}
/**************************************************************************
* WAVE_mciGetDevCaps [internal]
*/
static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
LPMCI_GETDEVCAPS_PARMS lpParms)
{
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
DWORD ret = 0;
TRACE("(%u, %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_GETDEVCAPS_ITEM) {
switch(lpParms->dwItem) {
case MCI_GETDEVCAPS_DEVICE_TYPE:
lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_GETDEVCAPS_HAS_AUDIO:
lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_GETDEVCAPS_HAS_VIDEO:
lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_GETDEVCAPS_USES_FILES:
lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_GETDEVCAPS_COMPOUND_DEVICE:
lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_GETDEVCAPS_CAN_RECORD:
lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_GETDEVCAPS_CAN_EJECT:
lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_GETDEVCAPS_CAN_PLAY:
lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_GETDEVCAPS_CAN_SAVE:
lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
ret = MCI_RESOURCE_RETURNED;
break;
case MCI_WAVE_GETDEVCAPS_INPUTS:
lpParms->dwReturn = 1;
break;
case MCI_WAVE_GETDEVCAPS_OUTPUTS:
lpParms->dwReturn = 1;
break;
default:
FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
return MCIERR_UNRECOGNIZED_COMMAND;
}
} else {
WARN("No GetDevCaps-Item !\n");
return MCIERR_UNRECOGNIZED_COMMAND;
}
return ret;
}
/**************************************************************************
* WAVE_mciInfo [internal]
*/
static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
{
DWORD ret = 0;
LPCSTR str = 0;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
ret = MCIERR_NULL_PARAMETER_BLOCK;
} else if (wmw == NULL) {
ret = MCIERR_INVALID_DEVICE_ID;
} else {
TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
case MCI_INFO_PRODUCT:
str = "Wine's audio player";
break;
case MCI_INFO_FILE:
str = wmw->openParms.lpstrElementName;
break;
case MCI_WAVE_INPUT:
str = "Wine Wave In";
break;
case MCI_WAVE_OUTPUT:
str = "Wine Wave Out";
break;
default:
WARN("Don't know this info command (%lu)\n", dwFlags);
ret = MCIERR_UNRECOGNIZED_COMMAND;
}
}
if (str) {
if (strlen(str) + 1 > lpParms->dwRetSize) {
ret = MCIERR_PARAM_OVERFLOW;
} else {
lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
}
} else {
lpParms->lpstrReturn[0] = 0;
}
return ret;
}
/**************************************************************************
* MCIWAVE_DriverProc [sample driver]
*/
LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
DWORD dwParam1, DWORD dwParam2)
{
TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
dwDevID, hDriv, wMsg, dwParam1, dwParam2);
switch(wMsg) {
case DRV_LOAD: return 1;
case DRV_FREE: return 1;
case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
case DRV_CLOSE: return WAVE_drvClose(dwDevID);
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_INSTALL: return DRVCNF_RESTART;
case DRV_REMOVE: return DRVCNF_RESTART;
case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) 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_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);
case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
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_FREEZE:
case MCI_PUT:
case MCI_REALIZE:
case MCI_UNFREEZE:
case MCI_UPDATE:
case MCI_WHERE:
case MCI_STEP:
case MCI_SPIN:
case MCI_ESCAPE:
case MCI_COPY:
case MCI_CUT:
case MCI_DELETE:
case MCI_PASTE:
FIXME("Unsupported yet command [%lu]\n", wMsg);
break;
case MCI_WINDOW:
TRACE("Unsupported command [%lu]\n", wMsg);
break;
/* option which can be silenced */
case MCI_CONFIGURE:
return 0;
case MCI_OPEN:
case MCI_CLOSE:
ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
break;
default:
FIXME("is probably wrong msg [%lu]\n", wMsg);
return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
}
return MCIERR_UNRECOGNIZED_COMMAND;
}