wine/multimedia/mciwave.c
Eric Pouech c11b167f62 Added support for MCI AVI driver
Added some fixes mixer functions
Fixed MCI string comparison (thanks to Lionel ULMER)
No longer using 16 bit USER functions (use their 32 bit counter part)
Reindenting.
Added function sndPlaySound32W
1999-02-13 12:38:09 +00:00

955 lines
32 KiB
C

/* -*- tab-width: 8; c-basic-offset: 4 -*- */
/*
* Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
*
* Copyright 1994 Martin Ayotte
*/
/*
* FIXME:
* - record/play should and must be done asynchronous
* - segmented/linear pointer problems (lpData in waveheaders,W*_DONE cbs)
*/
#define EMULATE_SB16
#define DEBUG_MCIWAVE
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "wine/winuser16.h"
#include "user.h"
#include "driver.h"
#include "mmsystem.h"
#include "heap.h"
#include "ldt.h"
#include "debug.h"
#include "multimedia.h"
#define MAX_MCIWAVEDRV (1)
typedef struct {
int nUseCount; /* Incremented for each shared open */
BOOL16 fShareable; /* TRUE if first open was shareable */
WORD wNotifyDeviceID;/* MCI device ID with a pending notification */
HANDLE16 hCallback; /* Callback handle for pending notification */
HMMIO32 hFile; /* mmio file handle open as Element */
MCI_WAVE_OPEN_PARMS32A openParms;
WAVEOPENDESC waveDesc;
PCMWAVEFORMAT WaveFormat;
WAVEHDR WaveHdr;
BOOL16 fInput; /* FALSE = Output, TRUE = Input */
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 */
} WINE_MCIWAVE;
static WINE_MCIWAVE MCIWaveDev[MAX_MCIWAVEDRV];
/*======================================================================*
* MCI WAVE implemantation *
*======================================================================*/
/**************************************************************************
* WAVE_mciGetOpenDev [internal]
*/
static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT16 wDevID)
{
if (wDevID >= MAX_MCIWAVEDRV || MCIWaveDev[wDevID].nUseCount == 0) {
WARN(mciwave, "Invalid wDevID=%u\n", wDevID);
return 0;
}
return &MCIWaveDev[wDevID];
}
static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val)
{
DWORD ret = 0;
switch (wmw->dwMciTimeFormat) {
case MCI_FORMAT_MILLISECONDS:
ret = (val * 1000) / wmw->WaveFormat.wf.nAvgBytesPerSec;
break;
case MCI_FORMAT_BYTES:
ret = val;
break;
case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
ret = (val * 8) / wmw->WaveFormat.wBitsPerSample;
break;
default:
WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
}
TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
return ret;
}
static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
{
DWORD ret = 0;
switch (wmw->dwMciTimeFormat) {
case MCI_FORMAT_MILLISECONDS:
ret = (val * wmw->WaveFormat.wf.nAvgBytesPerSec) / 1000;
break;
case MCI_FORMAT_BYTES:
ret = val;
break;
case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
ret = (val * wmw->WaveFormat.wBitsPerSample) / 8;
break;
default:
WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
}
TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
return ret;
}
static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
{
MMCKINFO mmckInfo;
mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
return MCIERR_INVALID_FILE;
TRACE(mciwave, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
(LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
if (mmioRead32(wmw->hFile, (HPSTR)&wmw->WaveFormat,
(long)sizeof(PCMWAVEFORMAT)) != (long)sizeof(PCMWAVEFORMAT))
return MCIERR_INVALID_FILE;
TRACE(mciwave, "wFormatTag=%04X !\n", wmw->WaveFormat.wf.wFormatTag);
TRACE(mciwave, "nChannels=%d \n", wmw->WaveFormat.wf.nChannels);
TRACE(mciwave, "nSamplesPerSec=%ld\n", wmw->WaveFormat.wf.nSamplesPerSec);
TRACE(mciwave, "nAvgBytesPerSec=%ld\n", wmw->WaveFormat.wf.nAvgBytesPerSec);
TRACE(mciwave, "nBlockAlign=%d \n", wmw->WaveFormat.wf.nBlockAlign);
TRACE(mciwave, "wBitsPerSample=%u !\n", wmw->WaveFormat.wBitsPerSample);
mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
return MCIERR_INVALID_FILE;
TRACE(mciwave,"Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
(LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
TRACE(mciwave, "nChannels=%d nSamplesPerSec=%ld\n",
wmw->WaveFormat.wf.nChannels, wmw->WaveFormat.wf.nSamplesPerSec);
wmw->dwLength = mmckInfo.cksize;
wmw->dwFileOffset = mmioSeek32(wmw->hFile, 0, SEEK_CUR); /* >= 0 */
return 0;
}
/**************************************************************************
* WAVE_mciOpen [internal]
*/
static DWORD WAVE_mciOpen(UINT16 wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMS32A lpOpenParms)
{
DWORD dwRet = 0;
DWORD dwDeviceID;
WINE_MCIWAVE* wmw;
TRACE(mciwave,"(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
if (wDevID >= MAX_MCIWAVEDRV) {
WARN(mciwave, "Invalid wDevID=%u\n", wDevID);
return MCIERR_INVALID_DEVICE_ID;
}
wmw = &MCIWaveDev[wDevID];
if (wmw->nUseCount > 0) {
/* The driver already open on this channel */
/* If the driver was opened shareable before and this open specifies */
/* shareable then increment the use count */
if (wmw->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
++wmw->nUseCount;
else
return MCIERR_MUST_USE_SHAREABLE;
} else {
wmw->nUseCount = 1;
wmw->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
}
dwDeviceID = lpOpenParms->wDeviceID;
wmw->fInput = FALSE;
TRACE(mciwave, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
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 {
LPCSTR lpstrElementName = lpOpenParms->lpstrElementName;
/*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
TRACE(mciwave,"MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
wmw->hFile = mmioOpen32A(lpstrElementName, NULL,
MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_EXCLUSIVE);
if (wmw->hFile == 0) {
WARN(mciwave, "can't find file='%s' !\n", lpstrElementName);
dwRet = MCIERR_FILE_NOT_FOUND;
}
} else {
wmw->hFile = 0;
}
}
}
TRACE(mciwave,"hFile=%u\n", wmw->hFile);
memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMS32A));
wmw->wNotifyDeviceID = dwDeviceID;
wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
wmw->waveDesc.hWave = 0;
if (dwRet == 0 && wmw->hFile != 0) {
MMCKINFO ckMainRIFF;
if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) {
dwRet = MCIERR_INVALID_FILE;
} else {
TRACE(mciwave, "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 (dwRet == 0) {
wmw->WaveFormat.wf.nAvgBytesPerSec =
wmw->WaveFormat.wf.nSamplesPerSec * wmw->WaveFormat.wf.nBlockAlign;
wmw->waveDesc.lpFormat = (LPWAVEFORMAT)&wmw->WaveFormat;
wmw->dwPosition = 0;
/* By default the device will be opened for output, the MCI_CUE function is there to
* change from output to input and back
*/
dwRet = wodMessage(wDevID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
wmw->dwStatus = MCI_MODE_STOP;
} else {
wmw->nUseCount--;
if (wmw->hFile != 0)
mmioClose32(wmw->hFile, 0);
wmw->hFile = 0;
}
return 0;
}
/**************************************************************************
* WAVE_mciCue [internal]
*/
static DWORD WAVE_mciCue(UINT16 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);
TRACE(mciwave,"(%u, %08lX, %p);\n", wDevID, dwParam, lpParms);
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
/* always close elements ? */
if (wmw->hFile != 0) {
mmioClose32(wmw->hFile, 0);
wmw->hFile = 0;
}
dwRet = MMSYSERR_NOERROR; /* assume success */
if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
dwRet = wodMessage(wDevID, WODM_CLOSE, 0, 0L, 0L);
if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
dwRet = widMessage(wDevID, WIDM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
wmw->fInput = TRUE;
} else if (wmw->fInput) {
dwRet = widMessage(wDevID, WIDM_CLOSE, 0, 0L, 0L);
if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
dwRet = wodMessage(wDevID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
wmw->fInput = FALSE;
}
return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
}
/**************************************************************************
* WAVE_mciStop [internal]
*/
static DWORD WAVE_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{
DWORD dwRet;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
wmw->dwStatus = MCI_MODE_STOP;
wmw->dwPosition = 0;
TRACE(mciwave, "wmw->dwStatus=%d\n", wmw->dwStatus);
if (wmw->fInput)
dwRet = widMessage(wDevID, WIDM_STOP, 0, dwFlags, (DWORD)lpParms);
else
dwRet = wodMessage(wDevID, WODM_STOP, 0, dwFlags, (DWORD)lpParms);
if (dwFlags & MCI_NOTIFY) {
TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
}
return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
}
/**************************************************************************
* WAVE_mciClose [internal]
*/
static DWORD WAVE_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{
DWORD dwRet;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
if (wmw->dwStatus != MCI_MODE_STOP) {
WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
}
wmw->dwStatus = MCI_MODE_STOP;
wmw->nUseCount--;
if (wmw->nUseCount == 0) {
if (wmw->hFile != 0) {
mmioClose32(wmw->hFile, 0);
wmw->hFile = 0;
}
if (wmw->fInput) dwRet = widMessage(wDevID, WIDM_CLOSE, 0, 0L, 0L);
else dwRet = wodMessage(wDevID, WODM_CLOSE, 0, 0L, 0L);
if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
}
if (lpParms && (dwFlags & MCI_NOTIFY)) {
TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
}
return 0;
}
/**************************************************************************
* WAVE_mciPlay [internal]
*/
static DWORD WAVE_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
{
DWORD end;
LONG bufsize, count;
HGLOBAL16 hData;
DWORD dwRet;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
if (wmw->fInput) {
WARN(mciwave, "cannot play on input device\n");
return MCIERR_NONAPPLICABLE_FUNCTION;
}
if (wmw->hFile == 0) {
WARN(mciwave, "Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
return MCIERR_FILE_NOT_FOUND;
}
if (!(dwFlags & MCI_WAIT)) {
/** FIXME: I'm not 100% sure that wNotifyDeviceID is the right value in all cases ??? */
return MCI_SendCommandAsync32(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags, (DWORD)lpParms);
}
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(mciwave, "Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
/* go back to begining of chunk */
mmioSeek32(wmw->hFile, wmw->dwFileOffset, SEEK_SET); /* >= 0 */
/* at 22050 bytes per sec => 30 ms by block */
bufsize = 10240;
hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
wmw->WaveHdr.lpData = (LPSTR)GlobalLock16(hData);
wmw->dwStatus = MCI_MODE_PLAY;
/* FIXME: this doesn't work if wmw->dwPosition != 0 */
while (wmw->dwStatus != MCI_MODE_STOP) {
wmw->WaveHdr.dwUser = 0L;
wmw->WaveHdr.dwFlags = 0L;
wmw->WaveHdr.dwLoops = 0L;
dwRet = wodMessage(wDevID, WODM_PREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
count = mmioRead32(wmw->hFile, wmw->WaveHdr.lpData, bufsize);
TRACE(mciwave,"mmioRead bufsize=%ld count=%ld\n", bufsize, count);
if (count < 1) break;
wmw->WaveHdr.dwBufferLength = count;
wmw->WaveHdr.dwBytesRecorded = 0;
TRACE(mciwave,"before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
&wmw->WaveHdr, wmw->WaveHdr.dwBufferLength, wmw->WaveHdr.dwBytesRecorded);
dwRet = wodMessage(wDevID, WODM_WRITE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
/* FIXME: should use callback mechanisms from audio driver */
#if 1
while (!(wmw->WaveHdr.dwFlags & WHDR_DONE))
Sleep(1);
#endif
wmw->dwPosition += count;
TRACE(mciwave,"after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
dwRet = wodMessage(wDevID, WODM_UNPREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
}
if (wmw->WaveHdr.lpData != NULL) {
GlobalUnlock16(hData);
GlobalFree16(hData);
wmw->WaveHdr.lpData = NULL;
}
wmw->dwStatus = MCI_MODE_STOP;
if (lpParms && (dwFlags & MCI_NOTIFY)) {
TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
}
return 0;
}
/**************************************************************************
* WAVE_mciRecord [internal]
*/
static DWORD WAVE_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
{
int start, end;
LONG bufsize;
HGLOBAL16 hData;
LPWAVEHDR lpWaveHdr;
DWORD dwRet;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
if (!wmw->fInput) {
WARN(mciwave, "cannot record on output device\n");
return MCIERR_NONAPPLICABLE_FUNCTION;
}
if (wmw->hFile == 0) {
WARN(mciwave, "can't find file='%s' !\n",
wmw->openParms.lpstrElementName);
return MCIERR_FILE_NOT_FOUND;
}
start = 1; end = 99999;
if (dwFlags & MCI_FROM) {
start = lpParms->dwFrom;
TRACE(mciwave, "MCI_FROM=%d \n", start);
}
if (dwFlags & MCI_TO) {
end = lpParms->dwTo;
TRACE(mciwave,"MCI_TO=%d \n", end);
}
bufsize = 64000;
lpWaveHdr = &wmw->WaveHdr;
hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
lpWaveHdr->lpData = (LPSTR)GlobalLock16(hData);
lpWaveHdr->dwBufferLength = bufsize;
lpWaveHdr->dwUser = 0L;
lpWaveHdr->dwFlags = 0L;
lpWaveHdr->dwLoops = 0L;
dwRet=widMessage(wDevID,WIDM_PREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
TRACE(mciwave,"after WIDM_PREPARE \n");
while (TRUE) {
lpWaveHdr->dwBytesRecorded = 0;
dwRet = widMessage(wDevID, WIDM_START, 0, 0L, 0L);
TRACE(mciwave, "after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
lpWaveHdr, lpWaveHdr->dwBytesRecorded);
if (lpWaveHdr->dwBytesRecorded == 0) break;
}
TRACE(mciwave,"before WIDM_UNPREPARE \n");
dwRet = widMessage(wDevID,WIDM_UNPREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
TRACE(mciwave,"after WIDM_UNPREPARE \n");
if (lpWaveHdr->lpData != NULL) {
GlobalUnlock16(hData);
GlobalFree16(hData);
lpWaveHdr->lpData = NULL;
}
if (dwFlags & MCI_NOTIFY) {
TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
}
return 0;
}
/**************************************************************************
* WAVE_mciPause [internal]
*/
static DWORD WAVE_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{
DWORD dwRet;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE(mciwave, "(%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 = widMessage(wDevID, WIDM_PAUSE, 0, dwFlags, (DWORD)lpParms);
else dwRet = wodMessage(wDevID, WODM_PAUSE, 0, dwFlags, (DWORD)lpParms);
return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
}
/**************************************************************************
* WAVE_mciResume [internal]
*/
static DWORD WAVE_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
DWORD dwRet = 0;
TRACE(mciwave, "(%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_PAUSE) {
wmw->dwStatus = MCI_MODE_PLAY;
}
#if 0
if (wmw->fInput) dwRet = widMessage(wDevID, WIDM_PLAY, 0, dwFlags, (DWORD)lpParms);
else dwRet = wodMessage(wDevID, WODM_PLAY, 0, dwFlags, (DWORD)lpParms);
return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
#else
return dwRet;
#endif
}
/**************************************************************************
* WAVE_mciSeek [internal]
*/
static DWORD WAVE_mciSeek(UINT16 wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
{
DWORD ret = 0;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE(mciwave, "(%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 = 0xFFFFFFFF; /* fixme */
} else if (dwFlags & MCI_TO) {
wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
} else {
WARN(mciwave, "dwFlag doesn't tell where to seek to...\n");
return MCIERR_MISSING_PARAMETER;
}
TRACE(mciwave, "Seeking to position=%lu bytes\n", wmw->dwPosition);
if (dwFlags & MCI_NOTIFY) {
TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
}
}
return ret;
}
/**************************************************************************
* WAVE_mciSet [internal]
*/
static DWORD WAVE_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
{
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE(mciwave, "(%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(mciwave, "MCI_FORMAT_MILLISECONDS !\n");
wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
break;
case MCI_FORMAT_BYTES:
TRACE(mciwave, "MCI_FORMAT_BYTES !\n");
wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
break;
case MCI_FORMAT_SAMPLES:
TRACE(mciwave, "MCI_FORMAT_SAMPLES !\n");
wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
break;
default:
WARN(mciwave,"Bad time format %lu!\n", lpParms->dwTimeFormat);
return MCIERR_BAD_TIME_FORMAT;
}
}
if (dwFlags & MCI_SET_VIDEO) {
TRACE(mciwave, "No support for video !\n");
return MCIERR_UNSUPPORTED_FUNCTION;
}
if (dwFlags & MCI_SET_DOOR_OPEN) {
TRACE(mciwave, "No support for door open !\n");
return MCIERR_UNSUPPORTED_FUNCTION;
}
if (dwFlags & MCI_SET_DOOR_CLOSED) {
TRACE(mciwave, "No support for door close !\n");
return MCIERR_UNSUPPORTED_FUNCTION;
}
if (dwFlags & MCI_SET_AUDIO) {
if (dwFlags & MCI_SET_ON) {
TRACE(mciwave, "MCI_SET_ON audio !\n");
} else if (dwFlags & MCI_SET_OFF) {
TRACE(mciwave, "MCI_SET_OFF audio !\n");
} else {
WARN(mciwave, "MCI_SET_AUDIO without SET_ON or SET_OFF\n");
return MCIERR_BAD_INTEGER;
}
if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
TRACE(mciwave, "MCI_SET_AUDIO_ALL !\n");
if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
TRACE(mciwave, "MCI_SET_AUDIO_LEFT !\n");
if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
TRACE(mciwave, "MCI_SET_AUDIO_RIGHT !\n");
}
if (dwFlags & MCI_WAVE_INPUT)
TRACE(mciwave, "MCI_WAVE_INPUT !\n");
if (dwFlags & MCI_WAVE_OUTPUT)
TRACE(mciwave, "MCI_WAVE_OUTPUT !\n");
if (dwFlags & MCI_WAVE_SET_ANYINPUT)
TRACE(mciwave, "MCI_WAVE_SET_ANYINPUT !\n");
if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
TRACE(mciwave, "MCI_WAVE_SET_ANYOUTPUT !\n");
if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
TRACE(mciwave, "MCI_WAVE_SET_AVGBYTESPERSEC !\n");
if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
TRACE(mciwave, "MCI_WAVE_SET_BITSPERSAMPLE !\n");
if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
TRACE(mciwave, "MCI_WAVE_SET_BLOCKALIGN !\n");
if (dwFlags & MCI_WAVE_SET_CHANNELS)
TRACE(mciwave, "MCI_WAVE_SET_CHANNELS !\n");
if (dwFlags & MCI_WAVE_SET_FORMATTAG)
TRACE(mciwave, "MCI_WAVE_SET_FORMATTAG !\n");
if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
TRACE(mciwave, "MCI_WAVE_SET_SAMPLESPERSEC !\n");
return 0;
}
/**************************************************************************
* WAVE_mciStatus [internal]
*/
static DWORD WAVE_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
{
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE(mciwave, "(%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(mciwave, "MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
break;
case MCI_STATUS_LENGTH:
/* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength);
TRACE(mciwave, "MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
break;
case MCI_STATUS_MODE:
lpParms->dwReturn = wmw->dwStatus;
TRACE(mciwave, "MCI_STATUS_MODE => %lu\n", lpParms->dwReturn);
break;
case MCI_STATUS_MEDIA_PRESENT:
TRACE(mciwave, "MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
lpParms->dwReturn = TRUE;
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(mciwave, "MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
break;
case MCI_STATUS_POSITION:
/* 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);
TRACE(mciwave, "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);
TRACE(mciwave,"MCI_STATUS_READY => %lu!\n", lpParms->dwReturn);
break;
case MCI_STATUS_TIME_FORMAT:
lpParms->dwReturn = wmw->dwMciTimeFormat;
TRACE(mciwave, "MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
break;
case MCI_WAVE_INPUT:
TRACE(mciwave,"MCI_WAVE_INPUT !\n");
lpParms->dwReturn = 0;
break;
case MCI_WAVE_OUTPUT:
TRACE(mciwave,"MCI_WAVE_OUTPUT !\n");
lpParms->dwReturn = 0;
break;
case MCI_WAVE_STATUS_AVGBYTESPERSEC:
lpParms->dwReturn = wmw->WaveFormat.wf.nAvgBytesPerSec;
TRACE(mciwave,"MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
break;
case MCI_WAVE_STATUS_BITSPERSAMPLE:
lpParms->dwReturn = wmw->WaveFormat.wBitsPerSample;
TRACE(mciwave,"MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
break;
case MCI_WAVE_STATUS_BLOCKALIGN:
lpParms->dwReturn = wmw->WaveFormat.wf.nBlockAlign;
TRACE(mciwave,"MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
break;
case MCI_WAVE_STATUS_CHANNELS:
lpParms->dwReturn = wmw->WaveFormat.wf.nChannels;
TRACE(mciwave,"MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
break;
case MCI_WAVE_STATUS_FORMATTAG:
lpParms->dwReturn = wmw->WaveFormat.wf.wFormatTag;
TRACE(mciwave,"MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
break;
case MCI_WAVE_STATUS_LEVEL:
TRACE(mciwave,"MCI_WAVE_STATUS_LEVEL !\n");
lpParms->dwReturn = 0xAAAA5555;
break;
case MCI_WAVE_STATUS_SAMPLESPERSEC:
lpParms->dwReturn = wmw->WaveFormat.wf.nSamplesPerSec;
TRACE(mciwave,"MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
break;
default:
WARN(mciwave,"unknown command %08lX !\n", lpParms->dwItem);
return MCIERR_UNRECOGNIZED_COMMAND;
}
}
if (dwFlags & MCI_NOTIFY) {
TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
}
return 0;
}
/**************************************************************************
* WAVE_mciGetDevCaps [internal]
*/
static DWORD WAVE_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags,
LPMCI_GETDEVCAPS_PARMS lpParms)
{
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE(mciwave, "(%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 = MCI_DEVTYPE_WAVEFORM_AUDIO;
break;
case MCI_GETDEVCAPS_HAS_AUDIO:
lpParms->dwReturn = TRUE;
break;
case MCI_GETDEVCAPS_HAS_VIDEO:
lpParms->dwReturn = FALSE;
break;
case MCI_GETDEVCAPS_USES_FILES:
lpParms->dwReturn = TRUE;
break;
case MCI_GETDEVCAPS_COMPOUND_DEVICE:
lpParms->dwReturn = TRUE;
break;
case MCI_GETDEVCAPS_CAN_RECORD:
lpParms->dwReturn = TRUE;
break;
case MCI_GETDEVCAPS_CAN_EJECT:
lpParms->dwReturn = FALSE;
break;
case MCI_GETDEVCAPS_CAN_PLAY:
lpParms->dwReturn = TRUE;
break;
case MCI_GETDEVCAPS_CAN_SAVE:
lpParms->dwReturn = TRUE;
break;
case MCI_WAVE_GETDEVCAPS_INPUTS:
lpParms->dwReturn = 1;
break;
case MCI_WAVE_GETDEVCAPS_OUTPUTS:
lpParms->dwReturn = 1;
break;
default:
TRACE(mciwave, "Unknown capability (%08lx) !\n", lpParms->dwItem);
return MCIERR_UNRECOGNIZED_COMMAND;
}
}
return 0;
}
/**************************************************************************
* WAVE_mciInfo [internal]
*/
static DWORD WAVE_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS16 lpParms)
{
DWORD ret = 0;
LPCSTR str = 0;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
TRACE(mciwave, "(%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(mciwave, "buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
switch(dwFlags) {
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(mciwave, "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 {
lstrcpyn32A(lpParms->lpstrReturn, str, lpParms->dwRetSize);
}
} else {
lpParms->lpstrReturn[0] = 0;
}
return ret;
}
/**************************************************************************
* WAVE_DriverProc32 [sample driver]
*/
LONG MCIWAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
DWORD dwParam1, DWORD dwParam2)
{
TRACE(mciwave,"(%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 1;
case DRV_CLOSE: return 1;
case DRV_ENABLE: return 1;
case DRV_DISABLE: return 1;
case DRV_QUERYCONFIGURE: return 1;
case DRV_CONFIGURE: MessageBox32A(0, "Sample MultiMedia Linux Driver !", "MMLinux 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_PARMS32A)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_PARMS16) dwParam2);
case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
case MCI_LOAD:
case MCI_SAVE:
case MCI_FREEZE:
case MCI_PUT:
case MCI_REALIZE:
case MCI_UNFREEZE:
case MCI_UPDATE:
case MCI_WHERE:
case MCI_WINDOW:
case MCI_STEP:
case MCI_SPIN:
case MCI_ESCAPE:
case MCI_COPY:
case MCI_CUT:
case MCI_DELETE:
case MCI_PASTE:
WARN(mciwave, "Unsupported command=%s\n", MCI_CommandToString(wMsg));
break;
case MCI_OPEN:
case MCI_CLOSE:
FIXME(mciwave, "Shouldn't receive a MCI_OPEN or CLOSE message\n");
break;
default:
FIXME(mciwave, "is probably wrong msg=%s\n", MCI_CommandToString(wMsg));
return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
}
return MCIERR_UNRECOGNIZED_COMMAND;
}