diff --git a/reactos/baseaddress.rbuild b/reactos/baseaddress.rbuild
index 18f2361ccfa..2036254cac9 100644
--- a/reactos/baseaddress.rbuild
+++ b/reactos/baseaddress.rbuild
@@ -43,6 +43,10 @@
+
+
+
+
@@ -119,6 +123,7 @@
+
diff --git a/reactos/boot/bootdata/packages/reactos.dff b/reactos/boot/bootdata/packages/reactos.dff
index 2faa17c49ed..b578b6ebbf8 100644
--- a/reactos/boot/bootdata/packages/reactos.dff
+++ b/reactos/boot/bootdata/packages/reactos.dff
@@ -292,6 +292,11 @@ dll\win32\localui\localui.dll 1
dll\win32\lsasrv\lsasrv.dll 1
dll\win32\lz32\lz32.dll 1
dll\win32\mapi32\mapi32.dll 1
+dll\win32\mciavi32\mciavi32.dll 1
+dll\win32\mcicda\mcicda.dll 1
+dll\win32\mciqtz32\mciqtz32.dll 1
+dll\win32\mciseq\mciseq.dll 1
+dll\win32\mciwave\mciwave.dll 1
dll\win32\mlang\mlang.dll 1
dll\win32\mmdrv\mmdrv.dll 1
dll\win32\modemui\modemui.dll 1
diff --git a/reactos/dll/win32/mciavi32/info.c b/reactos/dll/win32/mciavi32/info.c
new file mode 100644
index 00000000000..61b79b71dc1
--- /dev/null
+++ b/reactos/dll/win32/mciavi32/info.c
@@ -0,0 +1,473 @@
+/*
+ * Digital video MCI Wine Driver
+ *
+ * Copyright 1999, 2000 Eric POUECH
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include
+#include "private_mciavi.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mciavi);
+
+/**************************************************************************
+ * MCIAVI_ConvertFrameToTimeFormat [internal]
+ */
+static DWORD MCIAVI_ConvertFrameToTimeFormat(WINE_MCIAVI* wma, DWORD val, LPDWORD lpRet)
+{
+ DWORD ret = 0;
+
+ switch (wma->dwMciTimeFormat) {
+ case MCI_FORMAT_MILLISECONDS:
+ ret = (val * wma->mah.dwMicroSecPerFrame) / 1000;
+ break;
+ case MCI_FORMAT_FRAMES:
+ ret = val;
+ break;
+ default:
+ WARN("Bad time format %u!\n", wma->dwMciTimeFormat);
+ }
+ TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wma->dwMciTimeFormat, ret);
+ *lpRet = 0;
+ return ret;
+}
+
+/**************************************************************************
+ * MCIAVI_ConvertTimeFormatToFrame [internal]
+ */
+DWORD MCIAVI_ConvertTimeFormatToFrame(WINE_MCIAVI* wma, DWORD val)
+{
+ DWORD ret = 0;
+
+ switch (wma->dwMciTimeFormat) {
+ case MCI_FORMAT_MILLISECONDS:
+ ret = (val * 1000) / wma->mah.dwMicroSecPerFrame;
+ break;
+ case MCI_FORMAT_FRAMES:
+ ret = val;
+ break;
+ default:
+ WARN("Bad time format %u!\n", wma->dwMciTimeFormat);
+ }
+ TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wma->dwMciTimeFormat, ret);
+ return ret;
+}
+
+/***************************************************************************
+ * MCIAVI_mciGetDevCaps [internal]
+ */
+DWORD MCIAVI_mciGetDevCaps(UINT wDevID, DWORD dwFlags, LPMCI_GETDEVCAPS_PARMS lpParms)
+{
+ WINE_MCIAVI* wma = MCIAVI_mciGetOpenDev(wDevID);
+ DWORD ret;
+
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (dwFlags & MCI_GETDEVCAPS_ITEM) {
+ switch (lpParms->dwItem) {
+ case MCI_GETDEVCAPS_DEVICE_TYPE:
+ TRACE("MCI_GETDEVCAPS_DEVICE_TYPE !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_DIGITAL_VIDEO, MCI_DEVTYPE_DIGITAL_VIDEO);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_HAS_AUDIO:
+ TRACE("MCI_GETDEVCAPS_HAS_AUDIO !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_HAS_VIDEO:
+ TRACE("MCI_GETDEVCAPS_HAS_VIDEO !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_USES_FILES:
+ TRACE("MCI_GETDEVCAPS_USES_FILES !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_COMPOUND_DEVICE:
+ TRACE("MCI_GETDEVCAPS_COMPOUND_DEVICE !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_CAN_EJECT:
+ TRACE("MCI_GETDEVCAPS_CAN_EJECT !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_CAN_PLAY:
+ TRACE("MCI_GETDEVCAPS_CAN_PLAY !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_CAN_RECORD:
+ TRACE("MCI_GETDEVCAPS_CAN_RECORD !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_CAN_SAVE:
+ TRACE("MCI_GETDEVCAPS_CAN_SAVE !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ default:
+ FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
+ ret = MCIERR_UNRECOGNIZED_COMMAND;
+ break;
+ }
+ } else {
+ WARN("No GetDevCaps-Item !\n");
+ ret = MCIERR_UNRECOGNIZED_COMMAND;
+ }
+
+ LeaveCriticalSection(&wma->cs);
+ return ret;
+}
+
+/***************************************************************************
+ * MCIAVI_mciInfo [internal]
+ */
+DWORD MCIAVI_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_DGV_INFO_PARMSW lpParms)
+{
+ LPCWSTR str = 0;
+ WINE_MCIAVI* wma = MCIAVI_mciGetOpenDev(wDevID);
+ DWORD ret = 0;
+ static const WCHAR wszAviPlayer[] = {'W','i','n','e','\'','s',' ','A','V','I',' ','p','l','a','y','e','r',0};
+
+ if (lpParms == NULL || lpParms->lpstrReturn == NULL)
+ return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
+
+ EnterCriticalSection(&wma->cs);
+
+ if (dwFlags & MCI_INFO_PRODUCT)
+ str = wszAviPlayer;
+ else if (dwFlags & MCI_INFO_FILE)
+ str = wma->lpFileName;
+ else {
+ WARN("Don't know this info command (%u)\n", dwFlags);
+ ret = MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ if (str) {
+ if (strlenW(str) + 1 > lpParms->dwRetSize) {
+ ret = MCIERR_PARAM_OVERFLOW;
+ } else {
+ lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
+ }
+ } else {
+ lpParms->lpstrReturn[0] = 0;
+ }
+
+ LeaveCriticalSection(&wma->cs);
+ return ret;
+}
+
+/***************************************************************************
+ * MCIAVI_mciSet [internal]
+ */
+DWORD MCIAVI_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SET_PARMS lpParms)
+{
+ WINE_MCIAVI* wma = MCIAVI_mciGetOpenDev(wDevID);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (dwFlags & MCI_SET_TIME_FORMAT) {
+ switch (lpParms->dwTimeFormat) {
+ case MCI_FORMAT_MILLISECONDS:
+ TRACE("MCI_FORMAT_MILLISECONDS !\n");
+ wma->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
+ break;
+ case MCI_FORMAT_FRAMES:
+ TRACE("MCI_FORMAT_FRAMES !\n");
+ wma->dwMciTimeFormat = MCI_FORMAT_FRAMES;
+ break;
+ default:
+ WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_BAD_TIME_FORMAT;
+ }
+ }
+
+ if (dwFlags & MCI_SET_DOOR_OPEN) {
+ TRACE("No support for door open !\n");
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_UNSUPPORTED_FUNCTION;
+ }
+ if (dwFlags & MCI_SET_DOOR_CLOSED) {
+ TRACE("No support for door close !\n");
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_UNSUPPORTED_FUNCTION;
+ }
+
+ if (dwFlags & MCI_SET_ON) {
+ char buffer[256];
+
+ strcpy(buffer, "MCI_SET_ON:");
+
+ if (dwFlags & MCI_SET_VIDEO) {
+ strncat(buffer, " video", sizeof(buffer)-sizeof("MCI_SET_ON:"));
+ wma->dwSet |= 4;
+ }
+ if (dwFlags & MCI_SET_AUDIO) {
+ strncat(buffer, " audio", sizeof(buffer)-sizeof("MCI_SET_ON:"));
+ switch (lpParms->dwAudio) {
+ case MCI_SET_AUDIO_ALL:
+ strncat(buffer, " all", sizeof(buffer)-sizeof("MCI_SET_ON:"));
+ wma->dwSet |= 3;
+ break;
+ case MCI_SET_AUDIO_LEFT:
+ strncat(buffer, " left", sizeof(buffer)-sizeof("MCI_SET_ON:"));
+ wma->dwSet |= 1;
+ break;
+ case MCI_SET_AUDIO_RIGHT:
+ strncat(buffer, " right", sizeof(buffer)-sizeof("MCI_SET_ON:"));
+ wma->dwSet |= 2;
+ break;
+ default:
+ WARN("Unknown audio channel %u\n", lpParms->dwAudio);
+ break;
+ }
+ }
+ if (dwFlags & MCI_DGV_SET_SEEK_EXACTLY) {
+ strncat(buffer, " seek_exactly", sizeof(buffer));
+ }
+ FIXME("%s\n", buffer);
+ }
+
+ if (dwFlags & MCI_SET_OFF) {
+ char buffer[256];
+
+ strcpy(buffer, "MCI_SET_OFF:");
+ if (dwFlags & MCI_SET_VIDEO) {
+ strncat(buffer, " video", sizeof(buffer)-sizeof("MCI_SET_OFF:"));
+ wma->dwSet &= ~4;
+ }
+ if (dwFlags & MCI_SET_AUDIO) {
+ strncat(buffer, " audio", sizeof(buffer)-sizeof("MCI_SET_OFF:"));
+ switch (lpParms->dwAudio) {
+ case MCI_SET_AUDIO_ALL:
+ strncat(buffer, " all", sizeof(buffer)-sizeof("MCI_SET_OFF:"));
+ wma->dwSet &= ~3;
+ break;
+ case MCI_SET_AUDIO_LEFT:
+ strncat(buffer, " left", sizeof(buffer)-sizeof("MCI_SET_OFF:"));
+ wma->dwSet &= ~2;
+ break;
+ case MCI_SET_AUDIO_RIGHT:
+ strncat(buffer, " right", sizeof(buffer)-sizeof("MCI_SET_OFF:"));
+ wma->dwSet &= ~2;
+ break;
+ default:
+ WARN("Unknown audio channel %u\n", lpParms->dwAudio);
+ break;
+ }
+ }
+ if (dwFlags & MCI_DGV_SET_SEEK_EXACTLY) {
+ strncat(buffer, " seek_exactly", sizeof(buffer)-strlen(buffer)-1);
+ }
+ FIXME("%s\n", buffer);
+ }
+ if (dwFlags & MCI_DGV_SET_FILEFORMAT) {
+ LPCSTR str = "save";
+ if (dwFlags & MCI_DGV_SET_STILL)
+ str = "capture";
+
+ switch (lpParms->dwFileFormat) {
+ case MCI_DGV_FF_AVI: FIXME("Setting file format (%s) to 'AVI'\n", str); break;
+ case MCI_DGV_FF_AVSS: FIXME("Setting file format (%s) to 'AVSS'\n", str); break;
+ case MCI_DGV_FF_DIB: FIXME("Setting file format (%s) to 'DIB'\n", str); break;
+ case MCI_DGV_FF_JFIF: FIXME("Setting file format (%s) to 'JFIF'\n", str); break;
+ case MCI_DGV_FF_JPEG: FIXME("Setting file format (%s) to 'JPEG'\n", str); break;
+ case MCI_DGV_FF_MPEG: FIXME("Setting file format (%s) to 'MPEG'\n", str); break;
+ case MCI_DGV_FF_RDIB: FIXME("Setting file format (%s) to 'RLE DIB'\n", str); break;
+ case MCI_DGV_FF_RJPEG: FIXME("Setting file format (%s) to 'RJPEG'\n", str); break;
+ default: FIXME("Setting unknown file format (%s): %d\n", str, lpParms->dwFileFormat);
+ }
+ }
+
+ if (dwFlags & MCI_DGV_SET_SPEED) {
+ FIXME("Setting speed to %d\n", lpParms->dwSpeed);
+ }
+
+ LeaveCriticalSection(&wma->cs);
+ return 0;
+}
+
+/***************************************************************************
+ * MCIAVI_mciStatus [internal]
+ */
+DWORD MCIAVI_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_DGV_STATUS_PARMSW lpParms)
+{
+ WINE_MCIAVI* wma = MCIAVI_mciGetOpenDev(wDevID);
+ DWORD ret = 0;
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ 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 (!wma->hFile) {
+ lpParms->dwReturn = 0;
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_UNSUPPORTED_FUNCTION;
+ }
+ /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
+ lpParms->dwReturn = MCIAVI_ConvertFrameToTimeFormat(wma, wma->mah.dwTotalFrames, &ret);
+ TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
+ break;
+ case MCI_STATUS_MODE:
+ lpParms->dwReturn = MAKEMCIRESOURCE(wma->dwStatus, wma->dwStatus);
+ ret = MCI_RESOURCE_RETURNED;
+ TRACE("MCI_STATUS_MODE => 0x%04x\n", LOWORD(lpParms->dwReturn));
+ 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:
+ lpParms->dwReturn = 1;
+ TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
+ break;
+ case MCI_STATUS_POSITION:
+ if (!wma->hFile) {
+ lpParms->dwReturn = 0;
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_UNSUPPORTED_FUNCTION;
+ }
+ /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
+ lpParms->dwReturn = MCIAVI_ConvertFrameToTimeFormat(wma,
+ (dwFlags & MCI_STATUS_START) ? 0 : wma->dwCurrVideoFrame,
+ &ret);
+ TRACE("MCI_STATUS_POSITION %s => %lu\n",
+ (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
+ break;
+ case MCI_STATUS_READY:
+ lpParms->dwReturn = (wma->dwStatus == MCI_MODE_NOT_READY) ?
+ MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ TRACE("MCI_STATUS_READY = %u\n", LOWORD(lpParms->dwReturn));
+ break;
+ case MCI_STATUS_TIME_FORMAT:
+ lpParms->dwReturn = MAKEMCIRESOURCE(wma->dwMciTimeFormat,
+ wma->dwMciTimeFormat + MCI_FORMAT_RETURN_BASE);
+ TRACE("MCI_STATUS_TIME_FORMAT => %u\n", LOWORD(lpParms->dwReturn));
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+#if 0
+ case MCI_AVI_STATUS_AUDIO_BREAKS:
+ case MCI_AVI_STATUS_FRAMES_SKIPPED:
+ case MCI_AVI_STATUS_LAST_PLAY_SPEED:
+ case MCI_DGV_STATUS_AUDIO:
+ case MCI_DGV_STATUS_AUDIO_INPUT:
+ case MCI_DGV_STATUS_AUDIO_RECORD:
+ case MCI_DGV_STATUS_AUDIO_SOURCE:
+ case MCI_DGV_SETAUDIO_AVERAGE:
+ case MCI_DGV_SETAUDIO_LEFT:
+ case MCI_DGV_SETAUDIO_RIGHT:
+ case MCI_DGV_SETAUDIO_STEREO:
+ case MCI_DGV_STATUS_AUDIO_STREAM:
+ case MCI_DGV_STATUS_AVGBYTESPERSEC:
+ case MCI_DGV_STATUS_BASS:
+ case MCI_DGV_STATUS_BITSPERPEL:
+ case MCI_DGV_STATUS_BITSPERSAMPLE:
+ case MCI_DGV_STATUS_BLOCKALIGN:
+ case MCI_DGV_STATUS_BRIGHTNESS:
+ case MCI_DGV_STATUS_COLOR:
+ case MCI_DGV_STATUS_CONTRAST:
+ case MCI_DGV_STATUS_FILEFORMAT:
+ case MCI_DGV_STATUS_FILE_MODE:
+ case MCI_DGV_STATUS_FILE_COMPLETION:
+ case MCI_DGV_STATUS_FORWARD:
+ case MCI_DGV_STATUS_FRAME_RATE:
+ case MCI_DGV_STATUS_GAMMA:
+#endif
+ case MCI_DGV_STATUS_HPAL:
+ lpParms->dwReturn = 0;
+ TRACE("MCI_DGV_STATUS_HPAL => %lx\n", lpParms->dwReturn);
+ break;
+ case MCI_DGV_STATUS_HWND:
+ lpParms->dwReturn = (DWORD_PTR)wma->hWndPaint;
+ TRACE("MCI_DGV_STATUS_HWND => %p\n", wma->hWndPaint);
+ break;
+#if 0
+ case MCI_DGV_STATUS_KEY_COLOR:
+ case MCI_DGV_STATUS_KEY_INDEX:
+ case MCI_DGV_STATUS_MONITOR:
+ case MCI_DGV_MONITOR_FILE:
+ case MCI_DGV_MONITOR_INPUT:
+ case MCI_DGV_STATUS_MONITOR_METHOD:
+ case MCI_DGV_STATUS_PAUSE_MODE:
+ case MCI_DGV_STATUS_SAMPLESPERSECOND:
+ case MCI_DGV_STATUS_SEEK_EXACTLY:
+ case MCI_DGV_STATUS_SHARPNESS:
+ case MCI_DGV_STATUS_SIZE:
+ case MCI_DGV_STATUS_SMPTE:
+ case MCI_DGV_STATUS_SPEED:
+ case MCI_DGV_STATUS_STILL_FILEFORMAT:
+ case MCI_DGV_STATUS_TINT:
+ case MCI_DGV_STATUS_TREBLE:
+ case MCI_DGV_STATUS_UNSAVED:
+ case MCI_DGV_STATUS_VIDEO:
+ case MCI_DGV_STATUS_VIDEO_RECORD:
+ case MCI_DGV_STATUS_VIDEO_SOURCE:
+ case MCI_DGV_STATUS_VIDEO_SRC_NUM:
+ case MCI_DGV_STATUS_VIDEO_STREAM:
+ case MCI_DGV_STATUS_VOLUME:
+ case MCI_DGV_STATUS_WINDOW_VISIBLE:
+ case MCI_DGV_STATUS_WINDOW_MINIMIZED:
+ case MCI_DGV_STATUS_WINDOW_MAXIMIZED:
+ case MCI_STATUS_MEDIA_PRESENT:
+#endif
+ default:
+ FIXME("Unknown command %08X !\n", lpParms->dwItem);
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ } else {
+ WARN("No Status-Item!\n");
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+
+ if (dwFlags & MCI_NOTIFY) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wDevID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ LeaveCriticalSection(&wma->cs);
+ return ret;
+}
diff --git a/reactos/dll/win32/mciavi32/mciavi.c b/reactos/dll/win32/mciavi32/mciavi.c
new file mode 100644
index 00000000000..4e5faac7c0a
--- /dev/null
+++ b/reactos/dll/win32/mciavi32/mciavi.c
@@ -0,0 +1,1269 @@
+/*
+ * Digital video MCI Wine Driver
+ *
+ * Copyright 1999, 2000 Eric POUECH
+ * Copyright 2003 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/* TODO list :
+ * - handling of palettes
+ * - recording (which input devices ?), a cam recorder ?
+ * - lots of messages still need to be handled (cf FIXME)
+ * - synchronization between audio and video (especially for interleaved
+ * files)
+ * - robustness when reading file can be enhanced
+ * - better move the AVI handling part to avifile DLL and make use of it
+ * - some files appear to have more than one audio stream (we only play the
+ * first one)
+ * - some files contain an index of audio/video frame. Better use it,
+ * instead of rebuilding it
+ * - stopping while playing a file with sound blocks until all buffered
+ * audio is played... still should be stopped ASAP
+ */
+
+#include
+#include "private_mciavi.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mciavi);
+
+static DWORD MCIAVI_mciStop(UINT, DWORD, LPMCI_GENERIC_PARMS);
+
+/*======================================================================*
+ * MCI AVI implementation *
+ *======================================================================*/
+
+HINSTANCE MCIAVI_hInstance = 0;
+
+/***********************************************************************
+ * DllMain (MCIAVI.0)
+ */
+BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
+{
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hInstDLL);
+ MCIAVI_hInstance = hInstDLL;
+ break;
+ }
+ return TRUE;
+}
+
+/**************************************************************************
+ * MCIAVI_drvOpen [internal]
+ */
+static DWORD MCIAVI_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
+{
+ WINE_MCIAVI* wma;
+ static const WCHAR mciAviWStr[] = {'M','C','I','A','V','I',0};
+
+ TRACE("%s, %p\n", debugstr_w(str), modp);
+
+ /* session instance */
+ if (!modp) return 0xFFFFFFFF;
+
+ if (!MCIAVI_RegisterClass()) return 0;
+
+ wma = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIAVI));
+ if (!wma)
+ return 0;
+
+ InitializeCriticalSection(&wma->cs);
+ wma->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINE_MCIAVI.cs");
+ wma->ack_event = CreateEventW(NULL, FALSE, FALSE, NULL);
+ wma->hStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ wma->wDevID = modp->wDeviceID;
+ wma->wCommandTable = mciLoadCommandResource(MCIAVI_hInstance, mciAviWStr, 0);
+ modp->wCustomCommandTable = wma->wCommandTable;
+ modp->wType = MCI_DEVTYPE_DIGITAL_VIDEO;
+ mciSetDriverData(wma->wDevID, (DWORD_PTR)wma);
+
+ return modp->wDeviceID;
+}
+
+/**************************************************************************
+ * MCIAVI_drvClose [internal]
+ */
+static DWORD MCIAVI_drvClose(DWORD dwDevID)
+{
+ WINE_MCIAVI *wma;
+
+ TRACE("%04x\n", dwDevID);
+
+ /* finish all outstanding things */
+ MCIAVI_mciClose(dwDevID, MCI_WAIT, NULL);
+
+ wma = (WINE_MCIAVI*)mciGetDriverData(dwDevID);
+
+ if (wma) {
+ MCIAVI_UnregisterClass();
+
+ EnterCriticalSection(&wma->cs);
+
+ mciSetDriverData(dwDevID, 0);
+ mciFreeCommandResource(wma->wCommandTable);
+
+ CloseHandle(wma->ack_event);
+ CloseHandle(wma->hStopEvent);
+
+ LeaveCriticalSection(&wma->cs);
+ wma->cs.DebugInfo->Spare[0] = 0;
+ DeleteCriticalSection(&wma->cs);
+
+ HeapFree(GetProcessHeap(), 0, wma);
+ return 1;
+ }
+ return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
+}
+
+/**************************************************************************
+ * MCIAVI_drvConfigure [internal]
+ */
+static DWORD MCIAVI_drvConfigure(DWORD dwDevID)
+{
+ WINE_MCIAVI *wma;
+
+ TRACE("%04x\n", dwDevID);
+
+ MCIAVI_mciStop(dwDevID, MCI_WAIT, NULL);
+
+ wma = (WINE_MCIAVI*)mciGetDriverData(dwDevID);
+
+ if (wma) {
+ MessageBoxA(0, "Sample AVI Wine Driver !", "MM-Wine Driver", MB_OK);
+ return 1;
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * MCIAVI_mciGetOpenDev [internal]
+ */
+WINE_MCIAVI* MCIAVI_mciGetOpenDev(UINT wDevID)
+{
+ WINE_MCIAVI* wma = (WINE_MCIAVI*)mciGetDriverData(wDevID);
+
+ if (wma == NULL || wma->nUseCount == 0) {
+ WARN("Invalid wDevID=%u\n", wDevID);
+ return 0;
+ }
+ return wma;
+}
+
+static void MCIAVI_CleanUp(WINE_MCIAVI* wma)
+{
+ /* to prevent handling in WindowProc */
+ wma->dwStatus = MCI_MODE_NOT_READY;
+ if (wma->hFile) {
+ mmioClose(wma->hFile, 0);
+ wma->hFile = 0;
+
+ HeapFree(GetProcessHeap(), 0, wma->lpFileName);
+ wma->lpFileName = NULL;
+
+ HeapFree(GetProcessHeap(), 0, wma->lpVideoIndex);
+ wma->lpVideoIndex = NULL;
+ HeapFree(GetProcessHeap(), 0, wma->lpAudioIndex);
+ wma->lpAudioIndex = NULL;
+ if (wma->hic) ICClose(wma->hic);
+ wma->hic = 0;
+ HeapFree(GetProcessHeap(), 0, wma->inbih);
+ wma->inbih = NULL;
+ HeapFree(GetProcessHeap(), 0, wma->outbih);
+ wma->outbih = NULL;
+ HeapFree(GetProcessHeap(), 0, wma->indata);
+ wma->indata = NULL;
+ HeapFree(GetProcessHeap(), 0, wma->outdata);
+ wma->outdata = NULL;
+ if (wma->hbmFrame) DeleteObject(wma->hbmFrame);
+ wma->hbmFrame = 0;
+ if (wma->hWnd) DestroyWindow(wma->hWnd);
+ wma->hWnd = 0;
+
+ HeapFree(GetProcessHeap(), 0, wma->lpWaveFormat);
+ wma->lpWaveFormat = 0;
+
+ memset(&wma->mah, 0, sizeof(wma->mah));
+ memset(&wma->ash_video, 0, sizeof(wma->ash_video));
+ memset(&wma->ash_audio, 0, sizeof(wma->ash_audio));
+ wma->dwCurrVideoFrame = wma->dwCurrAudioBlock = 0;
+ wma->dwCachedFrame = -1;
+ }
+}
+
+/***************************************************************************
+ * MCIAVI_mciOpen [internal]
+ */
+static DWORD MCIAVI_mciOpen(UINT wDevID, DWORD dwFlags,
+ LPMCI_DGV_OPEN_PARMSW lpOpenParms)
+{
+ WINE_MCIAVI *wma;
+ LRESULT dwRet = 0;
+
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = (WINE_MCIAVI *)mciGetDriverData(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (wma->nUseCount > 0) {
+ /* The driver is already open on this channel */
+ /* If the driver was opened shareable before and this open specifies */
+ /* shareable then increment the use count */
+ if (wma->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
+ ++wma->nUseCount;
+ else
+ {
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_MUST_USE_SHAREABLE;
+ }
+ } else {
+ wma->nUseCount = 1;
+ wma->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
+ }
+
+ wma->dwStatus = MCI_MODE_NOT_READY;
+
+ 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 (strlenW(lpOpenParms->lpstrElementName) > 0) {
+ /* FIXME : what should be done id wma->hFile is already != 0, or the driver is playin' */
+ TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(lpOpenParms->lpstrElementName));
+
+ if (lpOpenParms->lpstrElementName && (strlenW(lpOpenParms->lpstrElementName) > 0))
+ {
+ wma->lpFileName = HeapAlloc(GetProcessHeap(), 0, (strlenW(lpOpenParms->lpstrElementName) + 1) * sizeof(WCHAR));
+ strcpyW(wma->lpFileName, lpOpenParms->lpstrElementName);
+
+ wma->hFile = mmioOpenW(lpOpenParms->lpstrElementName, NULL,
+ MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
+
+ if (wma->hFile == 0) {
+ WARN("can't find file=%s!\n", debugstr_w(lpOpenParms->lpstrElementName));
+ dwRet = MCIERR_FILE_NOT_FOUND;
+ } else {
+ if (!MCIAVI_GetInfo(wma))
+ dwRet = MCIERR_INVALID_FILE;
+ else if (!MCIAVI_OpenVideo(wma))
+ dwRet = MCIERR_CANNOT_LOAD_DRIVER;
+ else if (!MCIAVI_CreateWindow(wma, dwFlags, lpOpenParms))
+ dwRet = MCIERR_CREATEWINDOW;
+ }
+ }
+ } else {
+ FIXME("Don't record yet\n");
+ dwRet = MCIERR_UNSUPPORTED_FUNCTION;
+ }
+ }
+
+ if (dwRet == 0) {
+ TRACE("lpOpenParms->wDeviceID = %04x\n", lpOpenParms->wDeviceID);
+
+ wma->dwStatus = MCI_MODE_STOP;
+ wma->dwMciTimeFormat = MCI_FORMAT_FRAMES;
+ } else {
+ MCIAVI_CleanUp(wma);
+ }
+
+ LeaveCriticalSection(&wma->cs);
+ return dwRet;
+}
+
+/***************************************************************************
+ * MCIAVI_mciClose [internal]
+ */
+DWORD MCIAVI_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+ DWORD dwRet = 0;
+
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (wma->nUseCount == 1) {
+ if (wma->dwStatus != MCI_MODE_STOP)
+ dwRet = MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+ MCIAVI_CleanUp(wma);
+
+ if ((dwFlags & MCI_NOTIFY) && lpParms) {
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wDevID,
+ MCI_NOTIFY_SUCCESSFUL);
+ }
+ LeaveCriticalSection(&wma->cs);
+ return dwRet;
+ }
+ wma->nUseCount--;
+
+ LeaveCriticalSection(&wma->cs);
+ return dwRet;
+}
+
+static DWORD MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms);
+
+struct MCIAVI_play_data
+{
+ MCIDEVICEID wDevID;
+ DWORD flags;
+ MCI_PLAY_PARMS params;
+};
+
+/*
+ * MCIAVI_mciPlay_thread
+ *
+ * FIXME: probably should use a common worker thread created at the driver
+ * load time and queue all async commands to it.
+ */
+static DWORD WINAPI MCIAVI_mciPlay_thread(LPVOID arg)
+{
+ struct MCIAVI_play_data *data = (struct MCIAVI_play_data *)arg;
+ DWORD ret;
+
+ TRACE("In thread before async play command (id %08x, flags %08x)\n", data->wDevID, data->flags);
+ ret = MCIAVI_mciPlay(data->wDevID, data->flags | MCI_WAIT, &data->params);
+ TRACE("In thread after async play command (id %08x, flags %08x)\n", data->wDevID, data->flags);
+
+ HeapFree(GetProcessHeap(), 0, data);
+ return ret;
+}
+
+/*
+ * MCIAVI_mciPlay_async
+ */
+static DWORD MCIAVI_mciPlay_async(WINE_MCIAVI *wma, DWORD dwFlags, LPMCI_PLAY_PARMS lpParams)
+{
+ HANDLE handle, ack_event = wma->ack_event;
+ struct MCIAVI_play_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(struct MCIAVI_play_data));
+
+ if (!data) return MCIERR_OUT_OF_MEMORY;
+
+ data->wDevID = wma->wDevID;
+ data->flags = dwFlags;
+ data->params = *lpParams;
+
+ if (!(handle = CreateThread(NULL, 0, MCIAVI_mciPlay_thread, data, 0, NULL)))
+ {
+ WARN("Couldn't create thread for async play, playing synchronously\n");
+ return MCIAVI_mciPlay_thread(data);
+ }
+ SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
+ CloseHandle(handle);
+ /* wait until the thread starts up, so the app could see a changed status */
+ WaitForSingleObject(ack_event, INFINITE);
+ TRACE("Async play has started\n");
+ return 0;
+}
+
+/***************************************************************************
+ * MCIAVI_mciPlay [internal]
+ */
+static DWORD MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+ DWORD tc;
+ DWORD frameTime;
+ DWORD delta;
+ DWORD dwRet;
+ LPWAVEHDR waveHdr = NULL;
+ unsigned i, nHdr = 0;
+ DWORD dwFromFrame, dwToFrame;
+
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (!wma->hFile)
+ {
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_FILE_NOT_FOUND;
+ }
+ if (!wma->hWndPaint)
+ {
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_NO_WINDOW;
+ }
+
+ LeaveCriticalSection(&wma->cs);
+
+ if (!(dwFlags & MCI_WAIT))
+ return MCIAVI_mciPlay_async(wma, dwFlags, lpParms);
+
+ if (!(GetWindowLongW(wma->hWndPaint, GWL_STYLE) & WS_VISIBLE))
+ ShowWindow(wma->hWndPaint, SW_SHOWNA);
+
+ EnterCriticalSection(&wma->cs);
+
+ dwFromFrame = wma->dwCurrVideoFrame;
+ dwToFrame = wma->dwPlayableVideoFrames - 1;
+
+ if (lpParms && (dwFlags & MCI_FROM)) {
+ dwFromFrame = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwFrom);
+ }
+ if (lpParms && (dwFlags & MCI_TO)) {
+ dwToFrame = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwTo);
+ }
+ if (dwToFrame >= wma->dwPlayableVideoFrames)
+ dwToFrame = wma->dwPlayableVideoFrames - 1;
+
+ TRACE("Playing from frame=%u to frame=%u\n", dwFromFrame, dwToFrame);
+
+ wma->dwCurrVideoFrame = dwFromFrame;
+ wma->dwToVideoFrame = dwToFrame;
+
+ /* if already playing exit */
+ if (wma->dwStatus == MCI_MODE_PLAY)
+ {
+ LeaveCriticalSection(&wma->cs);
+ SetEvent(wma->ack_event);
+ return 0;
+ }
+
+ if (wma->dwToVideoFrame <= wma->dwCurrVideoFrame)
+ {
+ dwRet = 0;
+ SetEvent(wma->ack_event);
+ goto mci_play_done;
+ }
+
+ wma->dwStatus = MCI_MODE_PLAY;
+ /* signal the state change */
+ SetEvent(wma->ack_event);
+
+ if (dwFlags & (MCI_DGV_PLAY_REPEAT|MCI_DGV_PLAY_REVERSE|MCI_MCIAVI_PLAY_WINDOW|MCI_MCIAVI_PLAY_FULLSCREEN))
+ FIXME("Unsupported flag %08x\n", dwFlags);
+
+ /* time is in microseconds, we should convert it to milliseconds */
+ frameTime = (wma->mah.dwMicroSecPerFrame + 500) / 1000;
+
+ if (wma->lpWaveFormat) {
+ if (MCIAVI_OpenAudio(wma, &nHdr, &waveHdr) != 0)
+ {
+ /* can't play audio */
+ HeapFree(GetProcessHeap(), 0, wma->lpWaveFormat);
+ wma->lpWaveFormat = NULL;
+ }
+ else
+ /* fill the queue with as many wave headers as possible */
+ MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr);
+ }
+
+ while (wma->dwStatus == MCI_MODE_PLAY)
+ {
+ HDC hDC;
+ DWORD ret;
+
+ tc = GetTickCount();
+
+ hDC = wma->hWndPaint ? GetDC(wma->hWndPaint) : 0;
+ if (hDC)
+ {
+ MCIAVI_PaintFrame(wma, hDC);
+ ReleaseDC(wma->hWndPaint, hDC);
+ }
+
+ if (wma->lpWaveFormat) {
+ HANDLE events[2];
+
+ events[0] = wma->hStopEvent;
+ events[1] = wma->hEvent;
+
+ MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr);
+ delta = GetTickCount() - tc;
+
+ LeaveCriticalSection(&wma->cs);
+ ret = WaitForMultipleObjects(2, events, FALSE, (delta >= frameTime) ? 0 : frameTime - delta);
+ EnterCriticalSection(&wma->cs);
+
+ if (ret == WAIT_OBJECT_0 || wma->dwStatus != MCI_MODE_PLAY) break;
+ }
+
+ delta = GetTickCount() - tc;
+ if (delta < frameTime)
+ delta = frameTime - delta;
+ else
+ delta = 0;
+
+ LeaveCriticalSection(&wma->cs);
+ ret = WaitForMultipleObjects(1, &wma->hStopEvent, FALSE, delta);
+ EnterCriticalSection(&wma->cs);
+ if (ret == WAIT_OBJECT_0) break;
+
+ if (wma->dwCurrVideoFrame < dwToFrame)
+ wma->dwCurrVideoFrame++;
+ else
+ break;
+ }
+
+ if (wma->lpWaveFormat) {
+ while (wma->dwEventCount != nHdr - 1)
+ {
+ LeaveCriticalSection(&wma->cs);
+ Sleep(100);
+ EnterCriticalSection(&wma->cs);
+ }
+
+ /* just to get rid of some race conditions between play, stop and pause */
+ LeaveCriticalSection(&wma->cs);
+ waveOutReset(wma->hWave);
+ EnterCriticalSection(&wma->cs);
+
+ for (i = 0; i < nHdr; i++)
+ waveOutUnprepareHeader(wma->hWave, &waveHdr[i], sizeof(WAVEHDR));
+ }
+
+ dwRet = 0;
+
+ if (wma->lpWaveFormat) {
+ HeapFree(GetProcessHeap(), 0, waveHdr);
+
+ if (wma->hWave) {
+ LeaveCriticalSection(&wma->cs);
+ waveOutClose(wma->hWave);
+ EnterCriticalSection(&wma->cs);
+ wma->hWave = 0;
+ }
+ CloseHandle(wma->hEvent);
+ }
+
+mci_play_done:
+ wma->dwStatus = MCI_MODE_STOP;
+
+ if (lpParms && (dwFlags & MCI_NOTIFY)) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wDevID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ LeaveCriticalSection(&wma->cs);
+ return dwRet;
+}
+
+/***************************************************************************
+ * MCIAVI_mciRecord [internal]
+ */
+static DWORD MCIAVI_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_DGV_RECORD_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08X, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+ wma->dwStatus = MCI_MODE_RECORD;
+ LeaveCriticalSection(&wma->cs);
+ return 0;
+}
+
+/***************************************************************************
+ * MCIAVI_mciStop [internal]
+ */
+static DWORD MCIAVI_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+ DWORD dwRet = 0;
+
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ TRACE("current status %04x\n", wma->dwStatus);
+
+ switch (wma->dwStatus) {
+ case MCI_MODE_PLAY:
+ case MCI_MODE_RECORD:
+ LeaveCriticalSection(&wma->cs);
+ SetEvent(wma->hStopEvent);
+ EnterCriticalSection(&wma->cs);
+ /* fall through */
+ case MCI_MODE_PAUSE:
+ /* Since our wave notification callback takes the lock,
+ * we must release it before resetting the device */
+ LeaveCriticalSection(&wma->cs);
+ dwRet = waveOutReset(wma->hWave);
+ EnterCriticalSection(&wma->cs);
+ /* fall through */
+ default:
+ do /* one more chance for an async thread to finish */
+ {
+ LeaveCriticalSection(&wma->cs);
+ Sleep(10);
+ EnterCriticalSection(&wma->cs);
+ } while (wma->dwStatus != MCI_MODE_STOP);
+
+ break;
+
+ case MCI_MODE_NOT_READY:
+ break;
+ }
+
+ if ((dwFlags & MCI_NOTIFY) && lpParms) {
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wDevID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ LeaveCriticalSection(&wma->cs);
+ return dwRet;
+}
+
+/***************************************************************************
+ * MCIAVI_mciPause [internal]
+ */
+static DWORD MCIAVI_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (wma->dwStatus == MCI_MODE_PLAY)
+ wma->dwStatus = MCI_MODE_PAUSE;
+
+ if (wma->lpWaveFormat) {
+ LeaveCriticalSection(&wma->cs);
+ return waveOutPause(wma->hWave);
+ }
+
+ LeaveCriticalSection(&wma->cs);
+ return 0;
+}
+
+/***************************************************************************
+ * MCIAVI_mciResume [internal]
+ */
+static DWORD MCIAVI_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (wma->dwStatus == MCI_MODE_PAUSE)
+ wma->dwStatus = MCI_MODE_PLAY;
+
+ if (wma->lpWaveFormat) {
+ LeaveCriticalSection(&wma->cs);
+ return waveOutRestart(wma->hWave);
+ }
+
+ LeaveCriticalSection(&wma->cs);
+ return 0;
+}
+
+/***************************************************************************
+ * MCIAVI_mciSeek [internal]
+ */
+static DWORD MCIAVI_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (dwFlags & MCI_SEEK_TO_START) {
+ wma->dwCurrVideoFrame = 0;
+ } else if (dwFlags & MCI_SEEK_TO_END) {
+ wma->dwCurrVideoFrame = wma->dwPlayableVideoFrames - 1;
+ } else if (dwFlags & MCI_TO) {
+ if (lpParms->dwTo > wma->dwPlayableVideoFrames - 1)
+ lpParms->dwTo = wma->dwPlayableVideoFrames - 1;
+ wma->dwCurrVideoFrame = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwTo);
+ } else {
+ WARN("dwFlag doesn't tell where to seek to...\n");
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_MISSING_PARAMETER;
+ }
+
+ TRACE("Seeking to frame=%u bytes\n", wma->dwCurrVideoFrame);
+
+ if (dwFlags & MCI_NOTIFY) {
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wDevID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ LeaveCriticalSection(&wma->cs);
+ return 0;
+}
+
+/*****************************************************************************
+ * MCIAVI_mciLoad [internal]
+ */
+static DWORD MCIAVI_mciLoad(UINT wDevID, DWORD dwFlags, LPMCI_DGV_LOAD_PARMSW lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciSave [internal]
+ */
+static DWORD MCIAVI_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SAVE_PARMSW lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciFreeze [internal]
+ */
+static DWORD MCIAVI_mciFreeze(UINT wDevID, DWORD dwFlags, LPMCI_DGV_RECT_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciRealize [internal]
+ */
+static DWORD MCIAVI_mciRealize(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciUnFreeze [internal]
+ */
+static DWORD MCIAVI_mciUnFreeze(UINT wDevID, DWORD dwFlags, LPMCI_DGV_RECT_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciUpdate [internal]
+ */
+static DWORD MCIAVI_mciUpdate(UINT wDevID, DWORD dwFlags, LPMCI_DGV_UPDATE_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ TRACE("%04x, %08x, %p\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (dwFlags & MCI_DGV_UPDATE_HDC)
+ MCIAVI_PaintFrame(wma, lpParms->hDC);
+
+ LeaveCriticalSection(&wma->cs);
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciStep [internal]
+ */
+static DWORD MCIAVI_mciStep(UINT wDevID, DWORD dwFlags, LPMCI_DGV_STEP_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciCopy [internal]
+ */
+static DWORD MCIAVI_mciCopy(UINT wDevID, DWORD dwFlags, LPMCI_DGV_COPY_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciCut [internal]
+ */
+static DWORD MCIAVI_mciCut(UINT wDevID, DWORD dwFlags, LPMCI_DGV_CUT_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciDelete [internal]
+ */
+static DWORD MCIAVI_mciDelete(UINT wDevID, DWORD dwFlags, LPMCI_DGV_DELETE_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciPaste [internal]
+ */
+static DWORD MCIAVI_mciPaste(UINT wDevID, DWORD dwFlags, LPMCI_DGV_PASTE_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciCue [internal]
+ */
+static DWORD MCIAVI_mciCue(UINT wDevID, DWORD dwFlags, LPMCI_DGV_CUE_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciCapture [internal]
+ */
+static DWORD MCIAVI_mciCapture(UINT wDevID, DWORD dwFlags, LPMCI_DGV_CAPTURE_PARMSW lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciMonitor [internal]
+ */
+static DWORD MCIAVI_mciMonitor(UINT wDevID, DWORD dwFlags, LPMCI_DGV_MONITOR_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciReserve [internal]
+ */
+static DWORD MCIAVI_mciReserve(UINT wDevID, DWORD dwFlags, LPMCI_DGV_RESERVE_PARMSW lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciSetAudio [internal]
+ */
+static DWORD MCIAVI_mciSetAudio(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SETAUDIO_PARMSW lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciSignal [internal]
+ */
+static DWORD MCIAVI_mciSignal(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SIGNAL_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciSetVideo [internal]
+ */
+static DWORD MCIAVI_mciSetVideo(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SETVIDEO_PARMSW lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciQuality [internal]
+ */
+static DWORD MCIAVI_mciQuality(UINT wDevID, DWORD dwFlags, LPMCI_DGV_QUALITY_PARMSW lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciList [internal]
+ */
+static DWORD MCIAVI_mciList(UINT wDevID, DWORD dwFlags, LPMCI_DGV_LIST_PARMSW lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciUndo [internal]
+ */
+static DWORD MCIAVI_mciUndo(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciConfigure [internal]
+ */
+static DWORD MCIAVI_mciConfigure(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciRestore [internal]
+ */
+static DWORD MCIAVI_mciRestore(UINT wDevID, DWORD dwFlags, LPMCI_DGV_RESTORE_PARMSW lpParms)
+{
+ WINE_MCIAVI *wma;
+
+ FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
+
+ MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wma = MCIAVI_mciGetOpenDev(wDevID);
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ return 0;
+}
+
+/*======================================================================*
+ * MCI AVI entry points *
+ *======================================================================*/
+
+/**************************************************************************
+ * DriverProc (MCIAVI.@)
+ */
+LRESULT CALLBACK MCIAVI_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
+ LPARAM dwParam1, LPARAM dwParam2)
+{
+ TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
+ dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+
+ switch (wMsg) {
+ case DRV_LOAD: return 1;
+ case DRV_FREE: return 1;
+ case DRV_OPEN: return MCIAVI_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
+ case DRV_CLOSE: return MCIAVI_drvClose(dwDevID);
+ case DRV_ENABLE: return 1;
+ case DRV_DISABLE: return 1;
+ case DRV_QUERYCONFIGURE: return 1;
+ case DRV_CONFIGURE: return MCIAVI_drvConfigure(dwDevID);
+ case DRV_INSTALL: return DRVCNF_RESTART;
+ case DRV_REMOVE: return DRVCNF_RESTART;
+ }
+
+ /* session instance */
+ if (dwDevID == 0xFFFFFFFF) return 1;
+
+ switch (wMsg) {
+ case MCI_OPEN_DRIVER: return MCIAVI_mciOpen (dwDevID, dwParam1, (LPMCI_DGV_OPEN_PARMSW) dwParam2);
+ case MCI_CLOSE_DRIVER: return MCIAVI_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_PLAY: return MCIAVI_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
+ case MCI_RECORD: return MCIAVI_mciRecord (dwDevID, dwParam1, (LPMCI_DGV_RECORD_PARMS) dwParam2);
+ case MCI_STOP: return MCIAVI_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_SET: return MCIAVI_mciSet (dwDevID, dwParam1, (LPMCI_DGV_SET_PARMS) dwParam2);
+ case MCI_PAUSE: return MCIAVI_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_RESUME: return MCIAVI_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_STATUS: return MCIAVI_mciStatus (dwDevID, dwParam1, (LPMCI_DGV_STATUS_PARMSW) dwParam2);
+ case MCI_GETDEVCAPS: return MCIAVI_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
+ case MCI_INFO: return MCIAVI_mciInfo (dwDevID, dwParam1, (LPMCI_DGV_INFO_PARMSW) dwParam2);
+ case MCI_SEEK: return MCIAVI_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
+ case MCI_PUT: return MCIAVI_mciPut (dwDevID, dwParam1, (LPMCI_DGV_PUT_PARMS) dwParam2);
+ case MCI_WINDOW: return MCIAVI_mciWindow (dwDevID, dwParam1, (LPMCI_DGV_WINDOW_PARMSW) dwParam2);
+ case MCI_LOAD: return MCIAVI_mciLoad (dwDevID, dwParam1, (LPMCI_DGV_LOAD_PARMSW) dwParam2);
+ case MCI_SAVE: return MCIAVI_mciSave (dwDevID, dwParam1, (LPMCI_DGV_SAVE_PARMSW) dwParam2);
+ case MCI_FREEZE: return MCIAVI_mciFreeze (dwDevID, dwParam1, (LPMCI_DGV_RECT_PARMS) dwParam2);
+ case MCI_REALIZE: return MCIAVI_mciRealize (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_UNFREEZE: return MCIAVI_mciUnFreeze (dwDevID, dwParam1, (LPMCI_DGV_RECT_PARMS) dwParam2);
+ case MCI_UPDATE: return MCIAVI_mciUpdate (dwDevID, dwParam1, (LPMCI_DGV_UPDATE_PARMS) dwParam2);
+ case MCI_WHERE: return MCIAVI_mciWhere (dwDevID, dwParam1, (LPMCI_DGV_RECT_PARMS) dwParam2);
+ case MCI_STEP: return MCIAVI_mciStep (dwDevID, dwParam1, (LPMCI_DGV_STEP_PARMS) dwParam2);
+ case MCI_COPY: return MCIAVI_mciCopy (dwDevID, dwParam1, (LPMCI_DGV_COPY_PARMS) dwParam2);
+ case MCI_CUT: return MCIAVI_mciCut (dwDevID, dwParam1, (LPMCI_DGV_CUT_PARMS) dwParam2);
+ case MCI_DELETE: return MCIAVI_mciDelete (dwDevID, dwParam1, (LPMCI_DGV_DELETE_PARMS) dwParam2);
+ case MCI_PASTE: return MCIAVI_mciPaste (dwDevID, dwParam1, (LPMCI_DGV_PASTE_PARMS) dwParam2);
+ case MCI_CUE: return MCIAVI_mciCue (dwDevID, dwParam1, (LPMCI_DGV_CUE_PARMS) dwParam2);
+ /* Digital Video specific */
+ case MCI_CAPTURE: return MCIAVI_mciCapture (dwDevID, dwParam1, (LPMCI_DGV_CAPTURE_PARMSW) dwParam2);
+ case MCI_MONITOR: return MCIAVI_mciMonitor (dwDevID, dwParam1, (LPMCI_DGV_MONITOR_PARMS) dwParam2);
+ case MCI_RESERVE: return MCIAVI_mciReserve (dwDevID, dwParam1, (LPMCI_DGV_RESERVE_PARMSW) dwParam2);
+ case MCI_SETAUDIO: return MCIAVI_mciSetAudio (dwDevID, dwParam1, (LPMCI_DGV_SETAUDIO_PARMSW) dwParam2);
+ case MCI_SIGNAL: return MCIAVI_mciSignal (dwDevID, dwParam1, (LPMCI_DGV_SIGNAL_PARMS) dwParam2);
+ case MCI_SETVIDEO: return MCIAVI_mciSetVideo (dwDevID, dwParam1, (LPMCI_DGV_SETVIDEO_PARMSW) dwParam2);
+ case MCI_QUALITY: return MCIAVI_mciQuality (dwDevID, dwParam1, (LPMCI_DGV_QUALITY_PARMSW) dwParam2);
+ case MCI_LIST: return MCIAVI_mciList (dwDevID, dwParam1, (LPMCI_DGV_LIST_PARMSW) dwParam2);
+ case MCI_UNDO: return MCIAVI_mciUndo (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_CONFIGURE: return MCIAVI_mciConfigure (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_RESTORE: return MCIAVI_mciRestore (dwDevID, dwParam1, (LPMCI_DGV_RESTORE_PARMSW) dwParam2);
+
+ case MCI_SPIN:
+ case MCI_ESCAPE:
+ WARN("Unsupported command [%u]\n", wMsg);
+ break;
+ case MCI_OPEN:
+ case MCI_CLOSE:
+ FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n");
+ break;
+ default:
+ TRACE("Sending msg [%u] to default driver proc\n", wMsg);
+ return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+ }
+ return MCIERR_UNRECOGNIZED_COMMAND;
+}
diff --git a/reactos/dll/win32/mciavi32/mciavi32.rbuild b/reactos/dll/win32/mciavi32/mciavi32.rbuild
new file mode 100644
index 00000000000..c8dae4ba182
--- /dev/null
+++ b/reactos/dll/win32/mciavi32/mciavi32.rbuild
@@ -0,0 +1,18 @@
+
+
+ .
+ include/reactos/wine
+
+ info.c
+ mciavi.c
+ mmoutput.c
+ wnd.c
+ mciavi_res.rc
+ wine
+ msvfw32
+ winmm
+ user32
+ gdi32
+ kernel32
+ ntdll
+
diff --git a/reactos/dll/win32/mciavi32/mciavi32.spec b/reactos/dll/win32/mciavi32/mciavi32.spec
new file mode 100644
index 00000000000..ae74ec4d427
--- /dev/null
+++ b/reactos/dll/win32/mciavi32/mciavi32.spec
@@ -0,0 +1 @@
+@ stdcall -private DriverProc(long long long long long) MCIAVI_DriverProc
diff --git a/reactos/dll/win32/mciavi32/mciavi_res.rc b/reactos/dll/win32/mciavi32/mciavi_res.rc
new file mode 100644
index 00000000000..5f6d973e54a
--- /dev/null
+++ b/reactos/dll/win32/mciavi32/mciavi_res.rc
@@ -0,0 +1,565 @@
+/* MciAvi resources
+ *
+ * Copyright (c) 2000, Eric Pouech
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "mmddk.h"
+
+MCIAVI RCDATA
+BEGIN
+L"play\0", 0x00000806L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"from\0", 0x00000004L, MCI_INTEGER,
+L"to\0", 0x00000008L, MCI_INTEGER,
+L"repeat\0", 0x00010000L, MCI_FLAG,
+L"reverse\0", 0x00020000L, MCI_FLAG,
+L"window\0", 0x01000000L, MCI_FLAG,
+L"fullscreen by 2\0", 0x04000000L, MCI_FLAG,
+L"fullscreen\0", 0x02000000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"capability\0", 0x0000080bL, MCI_COMMAND_HEAD,
+L"\0", 0x00000002L, MCI_RETURN,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00000100L, MCI_CONSTANT,
+L"can record\0", 0x00000001L, MCI_INTEGER,
+L"has audio\0", 0x00000002L, MCI_INTEGER,
+L"has video\0", 0x00000003L, MCI_INTEGER,
+L"uses files\0", 0x00000005L, MCI_INTEGER,
+L"compound device\0", 0x00000006L, MCI_INTEGER,
+L"device type\0", 0x00000004L, MCI_INTEGER,
+L"can eject\0", 0x00000007L, MCI_INTEGER,
+L"can play\0", 0x00000008L, MCI_INTEGER,
+L"can save\0", 0x00000009L, MCI_INTEGER,
+L"can lock\0", 0x00004000L, MCI_INTEGER,
+L"can reverse\0", 0x00004004L, MCI_INTEGER,
+L"can stretch input\0", 0x00004008L, MCI_INTEGER,
+L"can stretch\0", 0x00004001L, MCI_INTEGER,
+L"can test\0", 0x00004009L, MCI_INTEGER,
+L"has still\0", 0x00004005L, MCI_INTEGER,
+L"can freeze\0", 0x00004002L, MCI_INTEGER,
+L"uses palettes\0", 0x00004006L, MCI_INTEGER,
+L"windows\0", 0x00004003L, MCI_INTEGER,
+L"maximum play rate\0", 0x0000400aL, MCI_INTEGER,
+L"minimum play rate\0", 0x0000400bL, MCI_INTEGER,
+L"can colorkey\0", 0x00004100L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"capture\0", 0x00000870L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"as\0", 0x00010000L, MCI_STRING,
+L"at\0", 0x00020000L, MCI_RECT,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"close\0", 0x00000804L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"configure\0", 0x0000087aL, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"copy\0", 0x00000852L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"from\0", 0x00000004L, MCI_INTEGER,
+L"to\0", 0x00000008L, MCI_INTEGER,
+L"at\0", 0x00010000L, MCI_RECT,
+L"audio stream\0", 0x00020000L, MCI_INTEGER,
+L"video stream\0", 0x00040000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"cue\0", 0x00000830L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"input\0", 0x00010000L, MCI_FLAG,
+L"output\0", 0x00020000L, MCI_FLAG,
+L"to\0", 0x00000008L, MCI_INTEGER,
+L"noshow\0", 0x00040000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"cut\0", 0x00000851L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"from\0", 0x00000004L, MCI_INTEGER,
+L"to\0", 0x00000008L, MCI_INTEGER,
+L"at\0", 0x00010000L, MCI_RECT,
+L"audio stream\0", 0x00020000L, MCI_INTEGER,
+L"video stream\0", 0x00040000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"delete\0", 0x00000856L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"from\0", 0x00000004L, MCI_INTEGER,
+L"to\0", 0x00000008L, MCI_INTEGER,
+L"at\0", 0x00010000L, MCI_RECT,
+L"audio stream\0", 0x00020000L, MCI_INTEGER,
+L"video stream\0", 0x00040000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"freeze\0", 0x00000844L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"at\0", 0x00010000L, MCI_RECT,
+L"outside\0", 0x00020000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"info\0", 0x0000080aL, MCI_COMMAND_HEAD,
+L"\0", 0x00000001L, MCI_RETURN,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"file\0", 0x00000200L, MCI_FLAG,
+L"product\0", 0x00000100L, MCI_FLAG,
+L"window text\0", 0x00010000L, MCI_FLAG,
+L"usage\0", 0x00004000L, MCI_FLAG,
+L"version\0", 0x00000400L, MCI_FLAG,
+L"\0", 0x00020000L, MCI_CONSTANT,
+L"audio algorithm\0", 0x00004004L, MCI_INTEGER,
+L"audio quality\0", 0x00004001L, MCI_INTEGER,
+L"still algorithm\0", 0x00004005L, MCI_INTEGER,
+L"still quality\0", 0x00004002L, MCI_INTEGER,
+L"video algorithm\0", 0x00004006L, MCI_INTEGER,
+L"video quality\0", 0x00004003L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"list\0", 0x00000878L, MCI_COMMAND_HEAD,
+L"\0", 0x00000001L, MCI_RETURN,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"count\0", 0x00020000L, MCI_FLAG,
+L"number\0", 0x00040000L, MCI_INTEGER,
+L"\0", 0x00010000L, MCI_CONSTANT,
+L"audio algorithm\0", 0x00004000L, MCI_INTEGER,
+L"audio quality\0", 0x00004001L, MCI_INTEGER,
+L"audio stream\0", 0x00004002L, MCI_INTEGER,
+L"still algorithm\0", 0x00004003L, MCI_INTEGER,
+L"still quality\0", 0x00004004L, MCI_INTEGER,
+L"video algorithm\0", 0x00004005L, MCI_INTEGER,
+L"video quality\0", 0x00004006L, MCI_INTEGER,
+L"video source\0", 0x00004008L, MCI_INTEGER,
+L"video stream\0", 0x00004007L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"algorithm\0", 0x00080000L, MCI_STRING,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"load\0", 0x00000850L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00000100L, MCI_STRING,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"monitor\0", 0x00000871L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00020000L, MCI_CONSTANT,
+L"input\0", 0x00004000L, MCI_INTEGER,
+L"file\0", 0x00004001L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"method\0", 0x00010000L, MCI_CONSTANT,
+L"pre\0", 0x0000a000L, MCI_INTEGER,
+L"post\0", 0x0000a001L, MCI_INTEGER,
+L"direct\0", 0x0000a002L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"open\0", 0x00000803L, MCI_COMMAND_HEAD,
+L"\0", 0x00000002L, MCI_RETURN,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"type\0", 0x00002000L, MCI_STRING,
+L"element\0", 0x00000200L, MCI_STRING,
+L"alias\0", 0x00000400L, MCI_STRING,
+L"shareable\0", 0x00000100L, MCI_FLAG,
+L"style\0", 0x00010000L, MCI_CONSTANT,
+L"overlapped\0", 0x00cf0000L, MCI_INTEGER,
+L"popup\0", 0x80880000L, MCI_INTEGER,
+L"child\0", 0x40000000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"parent\0", 0x00020000L, MCI_INTEGER,
+L"nostatic\0", 0x00040000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"paste\0", 0x00000853L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"to\0", 0x00000008L, MCI_INTEGER,
+L"at\0", 0x00010000L, MCI_RECT,
+L"insert\0", 0x00080000L, MCI_FLAG,
+L"overwrite\0", 0x00100000L, MCI_FLAG,
+L"audio stream\0", 0x00020000L, MCI_INTEGER,
+L"video stream\0", 0x00040000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"pause\0", 0x00000809L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"put\0", 0x00000842L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"at\0", 0x00010000L, MCI_RECT,
+L"source\0", 0x00020000L, MCI_FLAG,
+L"destination\0", 0x00040000L, MCI_FLAG,
+L"frame\0", 0x00080000L, MCI_FLAG,
+L"video\0", 0x00100000L, MCI_FLAG,
+L"window\0", 0x00200000L, MCI_FLAG,
+L"client\0", 0x00400000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"quality\0", 0x00000877L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00010000L, MCI_CONSTANT,
+L"audio\0", 0x00004000L, MCI_INTEGER,
+L"still\0", 0x00004001L, MCI_INTEGER,
+L"video\0", 0x00004002L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"name\0", 0x00020000L, MCI_STRING,
+L"algorithm\0", 0x00040000L, MCI_STRING,
+L"dialog\0", 0x00080000L, MCI_FLAG,
+L"handle\0", 0x00100000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"realize\0", 0x00000840L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"normal\0", 0x00010000L, MCI_FLAG,
+L"background\0", 0x00020000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"record\0", 0x0000080fL, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"from\0", 0x00000004L, MCI_INTEGER,
+L"to\0", 0x00000008L, MCI_INTEGER,
+L"insert\0", 0x00000100L, MCI_FLAG,
+L"overwrite\0", 0x00000200L, MCI_FLAG,
+L"at\0", 0x00010000L, MCI_RECT,
+L"hold\0", 0x00020000L, MCI_FLAG,
+L"audio stream\0", 0x00040000L, MCI_INTEGER,
+L"video stream\0", 0x00080000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"reserve\0", 0x00000872L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"in\0", 0x00010000L, MCI_STRING,
+L"size\0", 0x00020000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"restore\0", 0x0000087bL, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"from\0", 0x00010000L, MCI_STRING,
+L"at\0", 0x00020000L, MCI_RECT,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"resume\0", 0x00000855L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"save\0", 0x00000813L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00000100L, MCI_STRING,
+L"at\0", 0x00010000L, MCI_RECT,
+L"abort\0", 0x00020000L, MCI_FLAG,
+L"keepreserve\0", 0x00040000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"seek\0", 0x00000807L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"to start\0", 0x00000100L, MCI_FLAG,
+L"to end\0", 0x00000200L, MCI_FLAG,
+L"to\0", 0x00000008L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"set\0", 0x0000080dL, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"time format\0", 0x00000400L, MCI_CONSTANT,
+L"frames\0", 0x00000003L, MCI_INTEGER,
+L"milliseconds\0", 0x00000000L, MCI_INTEGER,
+L"ms\0", 0x00000000L, MCI_INTEGER,
+L"bytes\0", 0x00000008L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"door open\0", 0x00000100L, MCI_FLAG,
+L"door closed\0", 0x00000200L, MCI_FLAG,
+L"audio\0", 0x00000800L, MCI_CONSTANT,
+L"all\0", 0x00000000L, MCI_INTEGER,
+L"left\0", 0x00000001L, MCI_INTEGER,
+L"right\0", 0x00000002L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"video\0", 0x00001000L, MCI_FLAG,
+L"seek exactly\0", 0x00010000L, MCI_FLAG,
+L"file format\0", 0x00080000L, MCI_CONSTANT,
+L"avss\0", 0x00004000L, MCI_INTEGER,
+L"avi\0", 0x00004001L, MCI_INTEGER,
+L"dib\0", 0x00004002L, MCI_INTEGER,
+L"rdib\0", 0x00004003L, MCI_INTEGER,
+L"jpeg\0", 0x00004004L, MCI_INTEGER,
+L"rjpeg\0", 0x00004005L, MCI_INTEGER,
+L"jfif\0", 0x00004006L, MCI_INTEGER,
+L"mpeg\0", 0x00004007L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"still\0", 0x00040000L, MCI_FLAG,
+L"on\0", 0x00002000L, MCI_FLAG,
+L"off\0", 0x00004000L, MCI_FLAG,
+L"speed\0", 0x00020000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"setaudio\0", 0x00000873L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00800000L, MCI_CONSTANT,
+L"bass\0", 0x00004001L, MCI_INTEGER,
+L"treble\0", 0x00004000L, MCI_INTEGER,
+L"volume\0", 0x00004002L, MCI_INTEGER,
+L"stream\0", 0x00004003L, MCI_INTEGER,
+L"source\0", 0x00004004L, MCI_INTEGER,
+L"samplespersec\0", 0x00004005L, MCI_INTEGER,
+L"bytespersec\0", 0x00004006L, MCI_INTEGER,
+L"alignment\0", 0x00004007L, MCI_INTEGER,
+L"bitspersample\0", 0x00004008L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"to\0", 0x01000000L, MCI_CONSTANT,
+L"left\0", 0x00000001L, MCI_INTEGER,
+L"right\0", 0x00000002L, MCI_INTEGER,
+L"average\0", 0x00004000L, MCI_INTEGER,
+L"stereo\0", 0x00000000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"over\0", 0x00010000L, MCI_INTEGER,
+L"algorithm\0", 0x00040000L, MCI_STRING,
+L"quality\0", 0x00080000L, MCI_STRING,
+L"record\0", 0x00100000L, MCI_FLAG,
+L"left\0", 0x00200000L, MCI_FLAG,
+L"clocktime\0", 0x00020000L, MCI_FLAG,
+L"right\0", 0x00400000L, MCI_FLAG,
+L"on\0", 0x00002000L, MCI_FLAG,
+L"off\0", 0x00004000L, MCI_FLAG,
+L"input\0", 0x02000000L, MCI_FLAG,
+L"output\0", 0x04000000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"setvideo\0", 0x00000876L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00100000L, MCI_CONSTANT,
+L"frame rate\0", 0x00004008L, MCI_INTEGER,
+L"brightness\0", 0x00004000L, MCI_INTEGER,
+L"color\0", 0x00004001L, MCI_INTEGER,
+L"contrast\0", 0x00004002L, MCI_INTEGER,
+L"tint\0", 0x00004003L, MCI_INTEGER,
+L"sharpness\0", 0x00004004L, MCI_INTEGER,
+L"gamma\0", 0x00004005L, MCI_INTEGER,
+L"palette handle\0", 0x00004007L, MCI_INTEGER,
+L"stream\0", 0x00004006L, MCI_INTEGER,
+L"source\0", 0x00004009L, MCI_INTEGER,
+L"key index\0", 0x0000400aL, MCI_INTEGER,
+L"key color\0", 0x0000400bL, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"to\0", 0x01000000L, MCI_CONSTANT,
+L"ntsc\0", 0x00004000L, MCI_INTEGER,
+L"rgb\0", 0x00004001L, MCI_INTEGER,
+L"svideo\0", 0x00004002L, MCI_INTEGER,
+L"pal\0", 0x00004003L, MCI_INTEGER,
+L"secam\0", 0x00004004L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"quality\0", 0x00010000L, MCI_STRING,
+L"algorithm\0", 0x00020000L, MCI_STRING,
+L"number\0", 0x00080000L, MCI_INTEGER,
+L"over\0", 0x00200000L, MCI_INTEGER,
+L"record\0", 0x00400000L, MCI_FLAG,
+L"still\0", 0x00800000L, MCI_FLAG,
+L"clocktime\0", 0x00040000L, MCI_FLAG,
+L"on\0", 0x00002000L, MCI_FLAG,
+L"off\0", 0x00004000L, MCI_FLAG,
+L"input\0", 0x02000000L, MCI_FLAG,
+L"output\0", 0x04000000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"signal\0", 0x00000875L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"at\0", 0x00010000L, MCI_INTEGER,
+L"every\0", 0x00020000L, MCI_INTEGER,
+L"uservalue\0", 0x00040000L, MCI_INTEGER,
+L"cancel\0", 0x00080000L, MCI_FLAG,
+L"return position\0", 0x00100000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"status\0", 0x00000814L, MCI_COMMAND_HEAD,
+L"\0", 0x00000002L, MCI_RETURN,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00000100L, MCI_CONSTANT,
+L"frames skipped\0", 0x00008001L, MCI_INTEGER,
+L"play speed\0", 0x00008002L, MCI_INTEGER,
+L"audio breaks\0", 0x00008003L, MCI_INTEGER,
+L"alignment\0", 0x00004029L, MCI_INTEGER,
+L"audio input\0", 0x00004000L, MCI_INTEGER,
+L"audio record\0", 0x0000401aL, MCI_INTEGER,
+L"audio source\0", 0x00004009L, MCI_INTEGER,
+L"audio stream\0", 0x0000402dL, MCI_INTEGER,
+L"bass\0", 0x0000400fL, MCI_INTEGER,
+L"bitsperpel\0", 0x0000402bL, MCI_INTEGER,
+L"bitspersample\0", 0x0000402aL, MCI_INTEGER,
+L"brightness\0", 0x00004005L, MCI_INTEGER,
+L"bytespersec\0", 0x00004028L, MCI_INTEGER,
+L"color\0", 0x00004006L, MCI_INTEGER,
+L"contrast\0", 0x00004007L, MCI_INTEGER,
+L"current track\0", 0x00000008L, MCI_INTEGER,
+L"file format\0", 0x00004008L, MCI_INTEGER,
+L"still file format\0", 0x0000401dL, MCI_INTEGER,
+L"file mode\0", 0x0000401fL, MCI_INTEGER,
+L"file completion\0", 0x00004020L, MCI_INTEGER,
+L"forward\0", 0x0000402cL, MCI_INTEGER,
+L"gamma\0", 0x0000400aL, MCI_INTEGER,
+L"length\0", 0x00000001L, MCI_INTEGER,
+L"media present\0", 0x00000005L, MCI_INTEGER,
+L"mode\0", 0x00000004L, MCI_INTEGER,
+L"monitor method\0", 0x0000400cL, MCI_INTEGER,
+L"monitor\0", 0x0000400bL, MCI_INTEGER,
+L"number of tracks\0", 0x00000003L, MCI_INTEGER,
+L"palette handle\0", 0x00004004L, MCI_INTEGER,
+L"pause mode\0", 0x00004026L, MCI_INTEGER,
+L"position\0", 0x00000002L, MCI_INTEGER,
+L"ready\0", 0x00000007L, MCI_INTEGER,
+L"frame rate\0", 0x0000400eL, MCI_INTEGER,
+L"reserved size\0", 0x00004010L, MCI_INTEGER,
+L"samplespersec\0", 0x00004027L, MCI_INTEGER,
+L"seek exactly\0", 0x00004011L, MCI_INTEGER,
+L"sharpness\0", 0x00004012L, MCI_INTEGER,
+L"smpte\0", 0x00004013L, MCI_INTEGER,
+L"speed\0", 0x00004003L, MCI_INTEGER,
+L"time format\0", 0x00000006L, MCI_INTEGER,
+L"tint\0", 0x00004015L, MCI_INTEGER,
+L"treble\0", 0x00004016L, MCI_INTEGER,
+L"unsaved\0", 0x00004017L, MCI_INTEGER,
+L"video key color\0", 0x00004025L, MCI_INTEGER,
+L"video key index\0", 0x00004024L, MCI_INTEGER,
+L"video source\0", 0x0000401bL, MCI_INTEGER,
+L"video source number\0", 0x0000401eL, MCI_INTEGER,
+L"video record\0", 0x0000401cL, MCI_INTEGER,
+L"video stream\0", 0x0000402eL, MCI_INTEGER,
+L"volume\0", 0x00004019L, MCI_INTEGER,
+L"window handle\0", 0x00004001L, MCI_INTEGER,
+L"window visible\0", 0x00004021L, MCI_INTEGER,
+L"window minimized\0", 0x00004022L, MCI_INTEGER,
+L"window maximized\0", 0x00004023L, MCI_INTEGER,
+L"video streams\0", 0x00004100L, MCI_INTEGER,
+L"video bitrate\0", 0x00004101L, MCI_INTEGER,
+L"video maxbitrate\0", 0x00004102L, MCI_INTEGER,
+L"audio streams\0", 0x00004103L, MCI_INTEGER,
+L"audio bitrate\0", 0x00004104L, MCI_INTEGER,
+L"video brush\0", 0x00004105L, MCI_INTEGER,
+L"audio\0", 0x00004014L, MCI_INTEGER,
+L"video\0", 0x00004018L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"track\0", 0x00000010L, MCI_INTEGER,
+L"start\0", 0x00000200L, MCI_FLAG,
+L"left\0", 0x00080000L, MCI_FLAG,
+L"right\0", 0x00100000L, MCI_FLAG,
+L"nominal\0", 0x00020000L, MCI_FLAG,
+L"record\0", 0x01000000L, MCI_FLAG,
+L"input\0", 0x00400000L, MCI_FLAG,
+L"output\0", 0x00800000L, MCI_FLAG,
+L"disk space\0", 0x00200000L, MCI_STRING,
+L"reference\0", 0x00040000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"step\0", 0x0000080eL, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"reverse\0", 0x00010000L, MCI_FLAG,
+L"by\0", 0x00020000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"stop\0", 0x00000808L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"hold\0", 0x00010000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"undo\0", 0x00000879L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"unfreeze\0", 0x00000845L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"at\0", 0x00010000L, MCI_RECT,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"update\0", 0x00000854L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"at\0", 0x00010000L, MCI_RECT,
+L"hdc\0", 0x00020000L, MCI_INTEGER,
+L"paint\0", 0x00040000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"where\0", 0x00000843L, MCI_COMMAND_HEAD,
+L"\0", 0x00000007L, MCI_RETURN,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"source\0", 0x00020000L, MCI_FLAG,
+L"destination\0", 0x00040000L, MCI_FLAG,
+L"frame\0", 0x00080000L, MCI_FLAG,
+L"video\0", 0x00100000L, MCI_FLAG,
+L"window\0", 0x00200000L, MCI_FLAG,
+L"max\0", 0x00400000L, MCI_FLAG,
+L"min\0", 0x00800000L, MCI_FLAG,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"window\0", 0x00000841L, MCI_COMMAND_HEAD,
+L"notify\0", 0x00000001L, MCI_FLAG,
+L"wait\0", 0x00000002L, MCI_FLAG,
+L"test\0", 0x00000020L, MCI_FLAG,
+L"handle\0", 0x00010000L, MCI_CONSTANT,
+L"default\0", 0x00000000L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"state\0", 0x00040000L, MCI_CONSTANT,
+L"hide\0", 0x00000000L, MCI_INTEGER,
+L"minimize\0", 0x00000006L, MCI_INTEGER,
+L"restore\0", 0x00000009L, MCI_INTEGER,
+L"show maximized\0", 0x00000003L, MCI_INTEGER,
+L"show min noactive\0", 0x00000007L, MCI_INTEGER,
+L"show minimized\0", 0x00000002L, MCI_INTEGER,
+L"show na\0", 0x00000008L, MCI_INTEGER,
+L"show noactivate\0", 0x00000004L, MCI_INTEGER,
+L"show normal\0", 0x00000001L, MCI_INTEGER,
+L"show\0", 0x00000005L, MCI_INTEGER,
+L"no action\0", 0x00000008L, MCI_INTEGER,
+L"minimized\0", 0x00000002L, MCI_INTEGER,
+L"\0", 0x00000000L, MCI_END_CONSTANT,
+L"text\0", 0x00080000L, MCI_STRING,
+L"\0", 0x00000000L, MCI_END_COMMAND,
+L"\0", 0x00000000L, MCI_END_COMMAND_LIST
+END
diff --git a/reactos/dll/win32/mciavi32/mmoutput.c b/reactos/dll/win32/mciavi32/mmoutput.c
new file mode 100644
index 00000000000..f7118ff6ca3
--- /dev/null
+++ b/reactos/dll/win32/mciavi32/mmoutput.c
@@ -0,0 +1,650 @@
+/*
+ * Digital video MCI Wine Driver
+ *
+ * Copyright 1999, 2000 Eric POUECH
+ * Copyright 2003 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "private_mciavi.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mciavi);
+
+static BOOL MCIAVI_GetInfoAudio(WINE_MCIAVI* wma, const MMCKINFO* mmckList, MMCKINFO *mmckStream)
+{
+ MMCKINFO mmckInfo;
+
+ TRACE("ash.fccType='%c%c%c%c'\n", LOBYTE(LOWORD(wma->ash_audio.fccType)),
+ HIBYTE(LOWORD(wma->ash_audio.fccType)),
+ LOBYTE(HIWORD(wma->ash_audio.fccType)),
+ HIBYTE(HIWORD(wma->ash_audio.fccType)));
+ if (wma->ash_audio.fccHandler) /* not all streams specify a handler */
+ TRACE("ash.fccHandler='%c%c%c%c'\n", LOBYTE(LOWORD(wma->ash_audio.fccHandler)),
+ HIBYTE(LOWORD(wma->ash_audio.fccHandler)),
+ LOBYTE(HIWORD(wma->ash_audio.fccHandler)),
+ HIBYTE(HIWORD(wma->ash_audio.fccHandler)));
+ else
+ TRACE("ash.fccHandler=0, no handler specified\n");
+ TRACE("ash.dwFlags=%d\n", wma->ash_audio.dwFlags);
+ TRACE("ash.wPriority=%d\n", wma->ash_audio.wPriority);
+ TRACE("ash.wLanguage=%d\n", wma->ash_audio.wLanguage);
+ TRACE("ash.dwInitialFrames=%d\n", wma->ash_audio.dwInitialFrames);
+ TRACE("ash.dwScale=%d\n", wma->ash_audio.dwScale);
+ TRACE("ash.dwRate=%d\n", wma->ash_audio.dwRate);
+ TRACE("ash.dwStart=%d\n", wma->ash_audio.dwStart);
+ TRACE("ash.dwLength=%d\n", wma->ash_audio.dwLength);
+ TRACE("ash.dwSuggestedBufferSize=%d\n", wma->ash_audio.dwSuggestedBufferSize);
+ TRACE("ash.dwQuality=%d\n", wma->ash_audio.dwQuality);
+ TRACE("ash.dwSampleSize=%d\n", wma->ash_audio.dwSampleSize);
+ TRACE("ash.rcFrame=(%d,%d,%d,%d)\n", wma->ash_audio.rcFrame.top, wma->ash_audio.rcFrame.left,
+ wma->ash_audio.rcFrame.bottom, wma->ash_audio.rcFrame.right);
+
+ /* rewind to the start of the stream */
+ mmioAscend(wma->hFile, mmckStream, 0);
+
+ mmckInfo.ckid = ckidSTREAMFORMAT;
+ if (mmioDescend(wma->hFile, &mmckInfo, mmckList, MMIO_FINDCHUNK) != 0) {
+ WARN("Can't find 'strf' chunk\n");
+ return FALSE;
+ }
+ if (mmckInfo.cksize < sizeof(WAVEFORMAT)) {
+ WARN("Size of strf chunk (%d) < audio format struct\n", mmckInfo.cksize);
+ return FALSE;
+ }
+ wma->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
+ if (!wma->lpWaveFormat) {
+ WARN("Can't alloc WaveFormat\n");
+ return FALSE;
+ }
+
+ mmioRead(wma->hFile, (LPSTR)wma->lpWaveFormat, mmckInfo.cksize);
+
+ TRACE("waveFormat.wFormatTag=%d\n", wma->lpWaveFormat->wFormatTag);
+ TRACE("waveFormat.nChannels=%d\n", wma->lpWaveFormat->nChannels);
+ TRACE("waveFormat.nSamplesPerSec=%d\n", wma->lpWaveFormat->nSamplesPerSec);
+ TRACE("waveFormat.nAvgBytesPerSec=%d\n", wma->lpWaveFormat->nAvgBytesPerSec);
+ TRACE("waveFormat.nBlockAlign=%d\n", wma->lpWaveFormat->nBlockAlign);
+ TRACE("waveFormat.wBitsPerSample=%d\n", wma->lpWaveFormat->wBitsPerSample);
+ if (mmckInfo.cksize >= sizeof(WAVEFORMATEX))
+ TRACE("waveFormat.cbSize=%d\n", wma->lpWaveFormat->cbSize);
+
+ return TRUE;
+}
+
+static BOOL MCIAVI_GetInfoVideo(WINE_MCIAVI* wma, const MMCKINFO* mmckList, MMCKINFO* mmckStream)
+{
+ MMCKINFO mmckInfo;
+
+ TRACE("ash.fccType='%c%c%c%c'\n", LOBYTE(LOWORD(wma->ash_video.fccType)),
+ HIBYTE(LOWORD(wma->ash_video.fccType)),
+ LOBYTE(HIWORD(wma->ash_video.fccType)),
+ HIBYTE(HIWORD(wma->ash_video.fccType)));
+ TRACE("ash.fccHandler='%c%c%c%c'\n", LOBYTE(LOWORD(wma->ash_video.fccHandler)),
+ HIBYTE(LOWORD(wma->ash_video.fccHandler)),
+ LOBYTE(HIWORD(wma->ash_video.fccHandler)),
+ HIBYTE(HIWORD(wma->ash_video.fccHandler)));
+ TRACE("ash.dwFlags=%d\n", wma->ash_video.dwFlags);
+ TRACE("ash.wPriority=%d\n", wma->ash_video.wPriority);
+ TRACE("ash.wLanguage=%d\n", wma->ash_video.wLanguage);
+ TRACE("ash.dwInitialFrames=%d\n", wma->ash_video.dwInitialFrames);
+ TRACE("ash.dwScale=%d\n", wma->ash_video.dwScale);
+ TRACE("ash.dwRate=%d\n", wma->ash_video.dwRate);
+ TRACE("ash.dwStart=%d\n", wma->ash_video.dwStart);
+ TRACE("ash.dwLength=%d\n", wma->ash_video.dwLength);
+ TRACE("ash.dwSuggestedBufferSize=%d\n", wma->ash_video.dwSuggestedBufferSize);
+ TRACE("ash.dwQuality=%d\n", wma->ash_video.dwQuality);
+ TRACE("ash.dwSampleSize=%d\n", wma->ash_video.dwSampleSize);
+ TRACE("ash.rcFrame=(%d,%d,%d,%d)\n", wma->ash_video.rcFrame.top, wma->ash_video.rcFrame.left,
+ wma->ash_video.rcFrame.bottom, wma->ash_video.rcFrame.right);
+
+ /* rewind to the start of the stream */
+ mmioAscend(wma->hFile, mmckStream, 0);
+
+ mmckInfo.ckid = ckidSTREAMFORMAT;
+ if (mmioDescend(wma->hFile, &mmckInfo, mmckList, MMIO_FINDCHUNK) != 0) {
+ WARN("Can't find 'strf' chunk\n");
+ return FALSE;
+ }
+
+ wma->inbih = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
+ if (!wma->inbih) {
+ WARN("Can't alloc input BIH\n");
+ return FALSE;
+ }
+
+ mmioRead(wma->hFile, (LPSTR)wma->inbih, mmckInfo.cksize);
+
+ TRACE("bih.biSize=%d\n", wma->inbih->biSize);
+ TRACE("bih.biWidth=%d\n", wma->inbih->biWidth);
+ TRACE("bih.biHeight=%d\n", wma->inbih->biHeight);
+ TRACE("bih.biPlanes=%d\n", wma->inbih->biPlanes);
+ TRACE("bih.biBitCount=%d\n", wma->inbih->biBitCount);
+ TRACE("bih.biCompression=%x\n", wma->inbih->biCompression);
+ TRACE("bih.biSizeImage=%d\n", wma->inbih->biSizeImage);
+ TRACE("bih.biXPelsPerMeter=%d\n", wma->inbih->biXPelsPerMeter);
+ TRACE("bih.biYPelsPerMeter=%d\n", wma->inbih->biYPelsPerMeter);
+ TRACE("bih.biClrUsed=%d\n", wma->inbih->biClrUsed);
+ TRACE("bih.biClrImportant=%d\n", wma->inbih->biClrImportant);
+
+ wma->source.left = 0;
+ wma->source.top = 0;
+ wma->source.right = wma->inbih->biWidth;
+ wma->source.bottom = wma->inbih->biHeight;
+
+ wma->dest = wma->source;
+
+ return TRUE;
+}
+
+struct AviListBuild {
+ DWORD numVideoFrames;
+ DWORD numAudioAllocated;
+ DWORD numAudioBlocks;
+ DWORD inVideoSize;
+ DWORD inAudioSize;
+};
+
+static BOOL MCIAVI_AddFrame(WINE_MCIAVI* wma, LPMMCKINFO mmck,
+ struct AviListBuild* alb)
+{
+ const BYTE *p;
+ DWORD stream_n;
+ DWORD twocc;
+
+ if (mmck->ckid == ckidAVIPADDING) return TRUE;
+
+ p = (const BYTE *)&mmck->ckid;
+
+ if (!isxdigit(p[0]) || !isxdigit(p[1]))
+ {
+ WARN("wrongly encoded stream #\n");
+ return FALSE;
+ }
+
+ stream_n = (p[0] <= '9') ? (p[0] - '0') : (tolower(p[0]) - 'a' + 10);
+ stream_n <<= 4;
+ stream_n |= (p[1] <= '9') ? (p[1] - '0') : (tolower(p[1]) - 'a' + 10);
+
+ TRACE("ckid %4.4s (stream #%d)\n", (LPSTR)&mmck->ckid, stream_n);
+
+ /* Some (rare?) AVI files have video streams name XXYY where XX = stream number and YY = TWOCC
+ * of the last 2 characters of the biCompression member of the BITMAPINFOHEADER structure.
+ * Ex: fccHandler = IV32 & biCompression = IV32 => stream name = XX32
+ * fccHandler = MSVC & biCompression = CRAM => stream name = XXAM
+ * Another possibility is that these TWOCC are simply ignored.
+ * Default to cktypeDIBcompressed when this case happens.
+ */
+ twocc = TWOCCFromFOURCC(mmck->ckid);
+ if (twocc == TWOCCFromFOURCC(wma->inbih->biCompression))
+ twocc = cktypeDIBcompressed;
+
+ switch (twocc) {
+ case cktypeDIBbits:
+ case cktypeDIBcompressed:
+ case cktypePALchange:
+ if (stream_n != wma->video_stream_n)
+ {
+ TRACE("data belongs to another video stream #%d\n", stream_n);
+ return FALSE;
+ }
+
+ TRACE("Adding video frame[%d]: %d bytes\n",
+ alb->numVideoFrames, mmck->cksize);
+
+ if (alb->numVideoFrames < wma->dwPlayableVideoFrames) {
+ wma->lpVideoIndex[alb->numVideoFrames].dwOffset = mmck->dwDataOffset;
+ wma->lpVideoIndex[alb->numVideoFrames].dwSize = mmck->cksize;
+ if (alb->inVideoSize < mmck->cksize)
+ alb->inVideoSize = mmck->cksize;
+ alb->numVideoFrames++;
+ } else {
+ WARN("Too many video frames\n");
+ }
+ break;
+ case cktypeWAVEbytes:
+ if (stream_n != wma->audio_stream_n)
+ {
+ TRACE("data belongs to another audio stream #%d\n", stream_n);
+ return FALSE;
+ }
+
+ TRACE("Adding audio frame[%d]: %d bytes\n",
+ alb->numAudioBlocks, mmck->cksize);
+ if (wma->lpWaveFormat) {
+ if (alb->numAudioBlocks >= alb->numAudioAllocated) {
+ alb->numAudioAllocated += 32;
+ if (!wma->lpAudioIndex)
+ wma->lpAudioIndex = HeapAlloc(GetProcessHeap(), 0,
+ alb->numAudioAllocated * sizeof(struct MMIOPos));
+ else
+ wma->lpAudioIndex = HeapReAlloc(GetProcessHeap(), 0, wma->lpAudioIndex,
+ alb->numAudioAllocated * sizeof(struct MMIOPos));
+ if (!wma->lpAudioIndex) return FALSE;
+ }
+ wma->lpAudioIndex[alb->numAudioBlocks].dwOffset = mmck->dwDataOffset;
+ wma->lpAudioIndex[alb->numAudioBlocks].dwSize = mmck->cksize;
+ if (alb->inAudioSize < mmck->cksize)
+ alb->inAudioSize = mmck->cksize;
+ alb->numAudioBlocks++;
+ } else {
+ WARN("Wave chunk without wave format... discarding\n");
+ }
+ break;
+ default:
+ WARN("Unknown frame type %4.4s\n", (LPSTR)&mmck->ckid);
+ break;
+ }
+ return TRUE;
+}
+
+BOOL MCIAVI_GetInfo(WINE_MCIAVI* wma)
+{
+ MMCKINFO ckMainRIFF;
+ MMCKINFO mmckHead;
+ MMCKINFO mmckList;
+ MMCKINFO mmckInfo;
+ AVIStreamHeader strh;
+ struct AviListBuild alb;
+ DWORD stream_n;
+
+ if (mmioDescend(wma->hFile, &ckMainRIFF, NULL, 0) != 0) {
+ WARN("Can't find 'RIFF' chunk\n");
+ return FALSE;
+ }
+
+ if ((ckMainRIFF.ckid != FOURCC_RIFF) || (ckMainRIFF.fccType != formtypeAVI)) {
+ WARN("Can't find 'AVI ' chunk\n");
+ return FALSE;
+ }
+
+ mmckHead.fccType = listtypeAVIHEADER;
+ if (mmioDescend(wma->hFile, &mmckHead, &ckMainRIFF, MMIO_FINDLIST) != 0) {
+ WARN("Can't find 'hdrl' list\n");
+ return FALSE;
+ }
+
+ mmckInfo.ckid = ckidAVIMAINHDR;
+ if (mmioDescend(wma->hFile, &mmckInfo, &mmckHead, MMIO_FINDCHUNK) != 0) {
+ WARN("Can't find 'avih' chunk\n");
+ return FALSE;
+ }
+
+ mmioRead(wma->hFile, (LPSTR)&wma->mah, sizeof(wma->mah));
+
+ TRACE("mah.dwMicroSecPerFrame=%d\n", wma->mah.dwMicroSecPerFrame);
+ TRACE("mah.dwMaxBytesPerSec=%d\n", wma->mah.dwMaxBytesPerSec);
+ TRACE("mah.dwPaddingGranularity=%d\n", wma->mah.dwPaddingGranularity);
+ TRACE("mah.dwFlags=%d\n", wma->mah.dwFlags);
+ TRACE("mah.dwTotalFrames=%d\n", wma->mah.dwTotalFrames);
+ TRACE("mah.dwInitialFrames=%d\n", wma->mah.dwInitialFrames);
+ TRACE("mah.dwStreams=%d\n", wma->mah.dwStreams);
+ TRACE("mah.dwSuggestedBufferSize=%d\n", wma->mah.dwSuggestedBufferSize);
+ TRACE("mah.dwWidth=%d\n", wma->mah.dwWidth);
+ TRACE("mah.dwHeight=%d\n", wma->mah.dwHeight);
+
+ mmioAscend(wma->hFile, &mmckInfo, 0);
+
+ TRACE("Start of streams\n");
+ wma->video_stream_n = 0;
+ wma->audio_stream_n = 0;
+
+ for (stream_n = 0; stream_n < wma->mah.dwStreams; stream_n++)
+ {
+ MMCKINFO mmckStream;
+
+ mmckList.fccType = listtypeSTREAMHEADER;
+ if (mmioDescend(wma->hFile, &mmckList, &mmckHead, MMIO_FINDLIST) != 0)
+ break;
+
+ mmckStream.ckid = ckidSTREAMHEADER;
+ if (mmioDescend(wma->hFile, &mmckStream, &mmckList, MMIO_FINDCHUNK) != 0)
+ {
+ WARN("Can't find 'strh' chunk\n");
+ continue;
+ }
+
+ mmioRead(wma->hFile, (LPSTR)&strh, sizeof(strh));
+
+ TRACE("Stream #%d fccType %4.4s\n", stream_n, (LPSTR)&strh.fccType);
+
+ if (strh.fccType == streamtypeVIDEO)
+ {
+ TRACE("found video stream\n");
+ if (wma->inbih)
+ WARN("ignoring another video stream\n");
+ else
+ {
+ wma->ash_audio = strh;
+
+ if (!MCIAVI_GetInfoVideo(wma, &mmckList, &mmckStream))
+ return FALSE;
+ wma->video_stream_n = stream_n;
+ }
+ }
+ else if (strh.fccType == streamtypeAUDIO)
+ {
+ TRACE("found audio stream\n");
+ if (wma->lpWaveFormat)
+ WARN("ignoring another audio stream\n");
+ else
+ {
+ wma->ash_video = strh;
+
+ if (!MCIAVI_GetInfoAudio(wma, &mmckList, &mmckStream))
+ return FALSE;
+ wma->audio_stream_n = stream_n;
+ }
+ }
+ else
+ TRACE("Unsupported stream type %4.4s\n", (LPSTR)&strh.fccType);
+
+ mmioAscend(wma->hFile, &mmckList, 0);
+ }
+
+ TRACE("End of streams\n");
+
+ mmioAscend(wma->hFile, &mmckHead, 0);
+
+ /* no need to read optional JUNK chunk */
+
+ mmckList.fccType = listtypeAVIMOVIE;
+ if (mmioDescend(wma->hFile, &mmckList, &ckMainRIFF, MMIO_FINDLIST) != 0) {
+ WARN("Can't find 'movi' list\n");
+ return FALSE;
+ }
+
+ wma->dwPlayableVideoFrames = wma->mah.dwTotalFrames;
+ wma->lpVideoIndex = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ wma->dwPlayableVideoFrames * sizeof(struct MMIOPos));
+ if (!wma->lpVideoIndex) {
+ WARN("Can't alloc video index array\n");
+ return FALSE;
+ }
+ wma->dwPlayableAudioBlocks = 0;
+ wma->lpAudioIndex = NULL;
+
+ alb.numAudioBlocks = alb.numVideoFrames = 0;
+ alb.inVideoSize = alb.inAudioSize = 0;
+ alb.numAudioAllocated = 0;
+
+ while (mmioDescend(wma->hFile, &mmckInfo, &mmckList, 0) == 0) {
+ if (mmckInfo.fccType == listtypeAVIRECORD) {
+ MMCKINFO tmp;
+
+ while (mmioDescend(wma->hFile, &tmp, &mmckInfo, 0) == 0) {
+ MCIAVI_AddFrame(wma, &tmp, &alb);
+ mmioAscend(wma->hFile, &tmp, 0);
+ }
+ } else {
+ MCIAVI_AddFrame(wma, &mmckInfo, &alb);
+ }
+
+ mmioAscend(wma->hFile, &mmckInfo, 0);
+ }
+ if (alb.numVideoFrames != wma->dwPlayableVideoFrames) {
+ WARN("Found %d video frames (/%d), reducing playable frames\n",
+ alb.numVideoFrames, wma->dwPlayableVideoFrames);
+ wma->dwPlayableVideoFrames = alb.numVideoFrames;
+ }
+ wma->dwPlayableAudioBlocks = alb.numAudioBlocks;
+
+ if (alb.inVideoSize > wma->ash_video.dwSuggestedBufferSize) {
+ WARN("inVideoSize=%d suggestedSize=%d\n", alb.inVideoSize, wma->ash_video.dwSuggestedBufferSize);
+ wma->ash_video.dwSuggestedBufferSize = alb.inVideoSize;
+ }
+ if (alb.inAudioSize > wma->ash_audio.dwSuggestedBufferSize) {
+ WARN("inAudioSize=%d suggestedSize=%d\n", alb.inAudioSize, wma->ash_audio.dwSuggestedBufferSize);
+ wma->ash_audio.dwSuggestedBufferSize = alb.inAudioSize;
+ }
+
+ wma->indata = HeapAlloc(GetProcessHeap(), 0, wma->ash_video.dwSuggestedBufferSize);
+ if (!wma->indata) {
+ WARN("Can't alloc input buffer\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL MCIAVI_OpenVideo(WINE_MCIAVI* wma)
+{
+ HDC hDC;
+ DWORD outSize;
+ FOURCC fcc = wma->ash_video.fccHandler;
+
+ TRACE("fcc %4.4s\n", (LPSTR)&fcc);
+
+ wma->dwCachedFrame = -1;
+
+ /* get the right handle */
+ if (fcc == mmioFOURCC('C','R','A','M')) fcc = mmioFOURCC('M','S','V','C');
+
+ /* try to get a decompressor for that type */
+ wma->hic = ICLocate(ICTYPE_VIDEO, fcc, wma->inbih, NULL, ICMODE_DECOMPRESS);
+ if (!wma->hic) {
+ /* check for builtin DIB compressions */
+ fcc = wma->inbih->biCompression;
+ if ((fcc == mmioFOURCC('D','I','B',' ')) ||
+ (fcc == mmioFOURCC('R','L','E',' ')) ||
+ (fcc == BI_RGB) || (fcc == BI_RLE8) ||
+ (fcc == BI_RLE4) || (fcc == BI_BITFIELDS))
+ goto paint_frame;
+
+ WARN("Can't locate codec for the file\n");
+ return FALSE;
+ }
+
+ outSize = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
+
+ wma->outbih = HeapAlloc(GetProcessHeap(), 0, outSize);
+ if (!wma->outbih) {
+ WARN("Can't alloc output BIH\n");
+ return FALSE;
+ }
+ if (!ICGetDisplayFormat(wma->hic, wma->inbih, wma->outbih, 0, 0, 0)) {
+ WARN("Can't open decompressor\n");
+ return FALSE;
+ }
+
+ TRACE("bih.biSize=%d\n", wma->outbih->biSize);
+ TRACE("bih.biWidth=%d\n", wma->outbih->biWidth);
+ TRACE("bih.biHeight=%d\n", wma->outbih->biHeight);
+ TRACE("bih.biPlanes=%d\n", wma->outbih->biPlanes);
+ TRACE("bih.biBitCount=%d\n", wma->outbih->biBitCount);
+ TRACE("bih.biCompression=%x\n", wma->outbih->biCompression);
+ TRACE("bih.biSizeImage=%d\n", wma->outbih->biSizeImage);
+ TRACE("bih.biXPelsPerMeter=%d\n", wma->outbih->biXPelsPerMeter);
+ TRACE("bih.biYPelsPerMeter=%d\n", wma->outbih->biYPelsPerMeter);
+ TRACE("bih.biClrUsed=%d\n", wma->outbih->biClrUsed);
+ TRACE("bih.biClrImportant=%d\n", wma->outbih->biClrImportant);
+
+ wma->outdata = HeapAlloc(GetProcessHeap(), 0, wma->outbih->biSizeImage);
+ if (!wma->outdata) {
+ WARN("Can't alloc output buffer\n");
+ return FALSE;
+ }
+
+ if (ICSendMessage(wma->hic, ICM_DECOMPRESS_BEGIN,
+ (DWORD_PTR)wma->inbih, (DWORD_PTR)wma->outbih) != ICERR_OK) {
+ WARN("Can't begin decompression\n");
+ return FALSE;
+ }
+
+paint_frame:
+ hDC = wma->hWndPaint ? GetDC(wma->hWndPaint) : 0;
+ if (hDC)
+ {
+ MCIAVI_PaintFrame(wma, hDC);
+ ReleaseDC(wma->hWndPaint, hDC);
+ }
+ return TRUE;
+}
+
+static void CALLBACK MCIAVI_waveCallback(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
+ DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+ WINE_MCIAVI *wma = MCIAVI_mciGetOpenDev(dwInstance);
+
+ if (!wma) return;
+
+ EnterCriticalSection(&wma->cs);
+
+ switch (uMsg) {
+ case WOM_OPEN:
+ case WOM_CLOSE:
+ break;
+ case WOM_DONE:
+ InterlockedIncrement(&wma->dwEventCount);
+ TRACE("Returning waveHdr=%lx\n", dwParam1);
+ SetEvent(wma->hEvent);
+ break;
+ default:
+ ERR("Unknown uMsg=%d\n", uMsg);
+ }
+
+ LeaveCriticalSection(&wma->cs);
+}
+
+DWORD MCIAVI_OpenAudio(WINE_MCIAVI* wma, unsigned* nHdr, LPWAVEHDR* pWaveHdr)
+{
+ DWORD dwRet;
+ LPWAVEHDR waveHdr;
+ unsigned i;
+
+ dwRet = waveOutOpen((HWAVEOUT *)&wma->hWave, WAVE_MAPPER, wma->lpWaveFormat,
+ (DWORD_PTR)MCIAVI_waveCallback, wma->wDevID, CALLBACK_FUNCTION);
+ if (dwRet != 0) {
+ TRACE("Can't open low level audio device %d\n", dwRet);
+ dwRet = MCIERR_DEVICE_OPEN;
+ wma->hWave = 0;
+ goto cleanUp;
+ }
+
+ /* FIXME: should set up a heuristic to compute the number of wave headers
+ * to be used...
+ */
+ *nHdr = 7;
+ waveHdr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ *nHdr * (sizeof(WAVEHDR) + wma->ash_audio.dwSuggestedBufferSize));
+ if (!waveHdr) {
+ TRACE("Can't alloc wave headers\n");
+ dwRet = MCIERR_DEVICE_OPEN;
+ goto cleanUp;
+ }
+
+ for (i = 0; i < *nHdr; i++) {
+ /* other fields are zero:ed on allocation */
+ waveHdr[i].lpData = (char*)waveHdr +
+ *nHdr * sizeof(WAVEHDR) + i * wma->ash_audio.dwSuggestedBufferSize;
+ waveHdr[i].dwBufferLength = wma->ash_audio.dwSuggestedBufferSize;
+ if (waveOutPrepareHeader(wma->hWave, &waveHdr[i], sizeof(WAVEHDR))) {
+ dwRet = MCIERR_INTERNAL;
+ goto cleanUp;
+ }
+ }
+
+ if (wma->dwCurrVideoFrame != 0 && wma->lpWaveFormat) {
+ FIXME("Should recompute dwCurrAudioBlock, except unsynchronized sound & video\n");
+ }
+ wma->dwCurrAudioBlock = 0;
+
+ wma->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ wma->dwEventCount = *nHdr - 1;
+ *pWaveHdr = waveHdr;
+ cleanUp:
+ return dwRet;
+}
+
+void MCIAVI_PlayAudioBlocks(WINE_MCIAVI* wma, unsigned nHdr, LPWAVEHDR waveHdr)
+{
+ if (!wma->lpAudioIndex)
+ return;
+ TRACE("%d (ec=%u)\n", wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset, wma->dwEventCount);
+
+ /* push as many blocks as possible => audio gets priority */
+ while (wma->dwStatus != MCI_MODE_STOP && wma->dwStatus != MCI_MODE_NOT_READY &&
+ wma->dwCurrAudioBlock < wma->dwPlayableAudioBlocks) {
+ unsigned whidx = wma->dwCurrAudioBlock % nHdr;
+
+ ResetEvent(wma->hEvent);
+ if (InterlockedDecrement(&wma->dwEventCount) < 0 ||
+ !wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset)
+ {
+ InterlockedIncrement(&wma->dwEventCount);
+ break;
+ }
+
+ mmioSeek(wma->hFile, wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset, SEEK_SET);
+ mmioRead(wma->hFile, waveHdr[whidx].lpData, wma->lpAudioIndex[wma->dwCurrAudioBlock].dwSize);
+
+ waveHdr[whidx].dwFlags &= ~WHDR_DONE;
+ waveHdr[whidx].dwBufferLength = wma->lpAudioIndex[wma->dwCurrAudioBlock].dwSize;
+ waveOutWrite(wma->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
+ wma->dwCurrAudioBlock++;
+ }
+}
+
+LRESULT MCIAVI_PaintFrame(WINE_MCIAVI* wma, HDC hDC)
+{
+ void* pBitmapData;
+ LPBITMAPINFO pBitmapInfo;
+
+ if (!hDC || !wma->inbih)
+ return TRUE;
+
+ TRACE("Painting frame %u (cached %u)\n", wma->dwCurrVideoFrame, wma->dwCachedFrame);
+
+ if (wma->dwCurrVideoFrame != wma->dwCachedFrame)
+ {
+ if (!wma->lpVideoIndex[wma->dwCurrVideoFrame].dwOffset)
+ return FALSE;
+
+ if (wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize)
+ {
+ mmioSeek(wma->hFile, wma->lpVideoIndex[wma->dwCurrVideoFrame].dwOffset, SEEK_SET);
+ mmioRead(wma->hFile, wma->indata, wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize);
+
+ wma->inbih->biSizeImage = wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize;
+
+ if (wma->hic && ICDecompress(wma->hic, 0, wma->inbih, wma->indata,
+ wma->outbih, wma->outdata) != ICERR_OK)
+ {
+ WARN("Decompression error\n");
+ return FALSE;
+ }
+ }
+
+ wma->dwCachedFrame = wma->dwCurrVideoFrame;
+ }
+
+ if (wma->hic) {
+ pBitmapData = wma->outdata;
+ pBitmapInfo = (LPBITMAPINFO)wma->outbih;
+ } else {
+ pBitmapData = wma->indata;
+ pBitmapInfo = (LPBITMAPINFO)wma->inbih;
+ }
+
+ StretchDIBits(hDC,
+ wma->dest.left, wma->dest.top,
+ wma->dest.right - wma->dest.left, wma->dest.bottom - wma->dest.top,
+ wma->source.left, wma->source.top,
+ wma->source.right - wma->source.left, wma->source.bottom - wma->source.top,
+ pBitmapData, pBitmapInfo, DIB_RGB_COLORS, SRCCOPY);
+
+ return TRUE;
+}
diff --git a/reactos/dll/win32/mciavi32/private_mciavi.h b/reactos/dll/win32/mciavi32/private_mciavi.h
new file mode 100644
index 00000000000..548254c5a4f
--- /dev/null
+++ b/reactos/dll/win32/mciavi32/private_mciavi.h
@@ -0,0 +1,113 @@
+/*
+ * Digital video MCI Wine Driver
+ *
+ * Copyright 1999, 2000 Eric POUECH
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef __WINE_PRIVATE_MCIAVI_H
+#define __WINE_PRIVATE_MCIAVI_H
+
+#include
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "mmddk.h"
+#include "digitalv.h"
+#include "vfw.h"
+#include "wownt32.h"
+#include "mciavi.h"
+
+struct MMIOPos {
+ DWORD dwOffset;
+ DWORD dwSize;
+};
+
+typedef struct {
+ MCIDEVICEID wDevID;
+ int nUseCount; /* Incremented for each shared open */
+ BOOL fShareable; /* TRUE if first open was shareable */
+ WORD wCommandTable; /* custom MCI command table */
+ DWORD dwStatus; /* One of MCI_MODE_XXX */
+ LPWSTR lpFileName;
+ DWORD dwMciTimeFormat; /* current time format */
+ DWORD dwSet; /* what's turned on: video & audio l&r */
+ /* information on the loaded AVI file */
+ HMMIO hFile; /* mmio file handle open as Element */
+ DWORD video_stream_n, audio_stream_n; /* stream #s */
+ MainAVIHeader mah;
+ AVIStreamHeader ash_video;
+ AVIStreamHeader ash_audio;
+ LPBITMAPINFOHEADER inbih;
+ struct MMIOPos* lpVideoIndex;
+ LPWAVEFORMATEX lpWaveFormat;
+ struct MMIOPos* lpAudioIndex;
+ /* computed data from the file */
+ DWORD dwPlayableVideoFrames; /* max number of frames to be played. Takes care of truncated files and audio skew */
+ DWORD dwPlayableAudioBlocks;
+ /* data for the AVI decompressor */
+ HIC hic;
+ LPBITMAPINFOHEADER outbih;
+ LPVOID indata;
+ LPVOID outdata;
+ HBITMAP hbmFrame;
+ /* data for playing the audio part */
+ HANDLE hWave;
+ HANDLE hEvent; /* for synchronization */
+ LONG dwEventCount; /* for synchronization */
+ /* data for play back */
+ HWND hWnd, hWndPaint;
+ DWORD dwCachedFrame; /* buffered frame */
+ DWORD dwCurrVideoFrame; /* video frame to display and current position */
+ DWORD dwToVideoFrame; /* play to */
+ DWORD dwCurrAudioBlock; /* current audio block being played */
+ RECT source, dest;
+ /* data for the background mechanism */
+ CRITICAL_SECTION cs;
+ HANDLE hStopEvent;
+ HANDLE ack_event; /* acknowledge that an async command has started */
+} WINE_MCIAVI;
+
+extern HINSTANCE MCIAVI_hInstance;
+
+/* info.c */
+DWORD MCIAVI_ConvertTimeFormatToFrame(WINE_MCIAVI* wma, DWORD val);
+DWORD MCIAVI_mciGetDevCaps(UINT wDevID, DWORD dwFlags, LPMCI_GETDEVCAPS_PARMS lpParms);
+DWORD MCIAVI_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_DGV_INFO_PARMSW lpParms);
+DWORD MCIAVI_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SET_PARMS lpParms);
+DWORD MCIAVI_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_DGV_STATUS_PARMSW lpParms);
+
+/* mmoutput.c */
+BOOL MCIAVI_GetInfo(WINE_MCIAVI* wma);
+DWORD MCIAVI_OpenAudio(WINE_MCIAVI* wma, unsigned* nHdr, LPWAVEHDR* pWaveHdr);
+BOOL MCIAVI_OpenVideo(WINE_MCIAVI* wma);
+void MCIAVI_PlayAudioBlocks(WINE_MCIAVI* wma, unsigned nHdr, LPWAVEHDR waveHdr);
+LRESULT MCIAVI_PaintFrame(WINE_MCIAVI* wma, HDC hDC);
+
+/* mciavi.c */
+WINE_MCIAVI* MCIAVI_mciGetOpenDev(UINT wDevID);
+DWORD MCIAVI_mciClose(UINT, DWORD, LPMCI_GENERIC_PARMS);
+
+/* wnd.c */
+BOOL MCIAVI_RegisterClass(void);
+BOOL MCIAVI_UnregisterClass(void);
+BOOL MCIAVI_CreateWindow(WINE_MCIAVI* wma, DWORD dwFlags, LPMCI_DGV_OPEN_PARMSW lpOpenParms);
+DWORD MCIAVI_mciPut(UINT wDevID, DWORD dwFlags, LPMCI_DGV_PUT_PARMS lpParms);
+DWORD MCIAVI_mciWhere(UINT wDevID, DWORD dwFlags, LPMCI_DGV_RECT_PARMS lpParms);
+DWORD MCIAVI_mciWindow(UINT wDevID, DWORD dwFlags, LPMCI_DGV_WINDOW_PARMSW lpParms);
+
+#endif /* __WINE_PRIVATE_MCIAVI_H */
diff --git a/reactos/dll/win32/mciavi32/wnd.c b/reactos/dll/win32/mciavi32/wnd.c
new file mode 100644
index 00000000000..36f56e6293a
--- /dev/null
+++ b/reactos/dll/win32/mciavi32/wnd.c
@@ -0,0 +1,308 @@
+/*
+ * Digital video MCI Wine Driver
+ *
+ * Copyright 1999, 2000 Eric POUECH
+ * Copyright 2003 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include
+#include "private_mciavi.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mciavi);
+
+static const WCHAR mciaviW[] = {'M','C','I','A','V','I',0};
+
+static LRESULT WINAPI MCIAVI_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n", hWnd, uMsg, wParam, lParam);
+
+ switch (uMsg) {
+ case WM_CREATE:
+ SetWindowLongW(hWnd, 0, (LPARAM)((CREATESTRUCTW *)lParam)->lpCreateParams);
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+
+ case WM_DESTROY:
+ MCIAVI_mciClose(GetWindowLongW(hWnd, 0), MCI_WAIT, NULL);
+ SetWindowLongW(hWnd, 0, 0);
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+
+ case WM_ERASEBKGND:
+ {
+ RECT rect;
+ GetClientRect(hWnd, &rect);
+ FillRect((HDC)wParam, &rect, GetStockObject(BLACK_BRUSH));
+ }
+ return 1;
+
+ case WM_PAINT:
+ {
+ WINE_MCIAVI *wma = (WINE_MCIAVI *)mciGetDriverData(GetWindowLongW(hWnd, 0));
+
+ if (!wma)
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+
+ EnterCriticalSection(&wma->cs);
+
+ /* the animation isn't playing, don't paint */
+ if (wma->dwStatus == MCI_MODE_NOT_READY)
+ {
+ LeaveCriticalSection(&wma->cs);
+ /* default paint handling */
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+ }
+
+ if (wParam)
+ MCIAVI_PaintFrame(wma, (HDC)wParam);
+ else
+ {
+ PAINTSTRUCT ps;
+ BeginPaint(hWnd, &ps);
+ MCIAVI_PaintFrame(wma, ps.hdc);
+ EndPaint(hWnd, &ps);
+ }
+
+ LeaveCriticalSection(&wma->cs);
+ }
+ return 1;
+
+ default:
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+ }
+}
+
+BOOL MCIAVI_UnregisterClass(void)
+{
+ return UnregisterClassW(mciaviW, MCIAVI_hInstance);
+}
+
+BOOL MCIAVI_RegisterClass(void)
+{
+ WNDCLASSW wndClass;
+
+ ZeroMemory(&wndClass, sizeof(WNDCLASSW));
+ wndClass.style = CS_DBLCLKS;
+ wndClass.lpfnWndProc = MCIAVI_WindowProc;
+ wndClass.cbWndExtra = sizeof(MCIDEVICEID);
+ wndClass.hInstance = MCIAVI_hInstance;
+ wndClass.hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
+ wndClass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
+ wndClass.lpszClassName = mciaviW;
+
+ if (RegisterClassW(&wndClass)) return TRUE;
+ if (GetLastError() == ERROR_CLASS_ALREADY_EXISTS) return TRUE;
+
+ return FALSE;
+}
+
+BOOL MCIAVI_CreateWindow(WINE_MCIAVI* wma, DWORD dwFlags, LPMCI_DGV_OPEN_PARMSW lpOpenParms)
+{
+ static const WCHAR captionW[] = {'W','i','n','e',' ','M','C','I','-','A','V','I',' ','p','l','a','y','e','r',0};
+ HWND hParent = 0;
+ DWORD dwStyle = WS_OVERLAPPEDWINDOW;
+ RECT rc;
+
+ /* what should be done ? */
+ if (wma->hWnd) return TRUE;
+
+ if (dwFlags & MCI_DGV_OPEN_PARENT) hParent = lpOpenParms->hWndParent;
+ if (dwFlags & MCI_DGV_OPEN_WS) dwStyle = lpOpenParms->dwStyle;
+
+ rc.left = rc.top = 0;
+ rc.right = (wma->hic ? wma->outbih : wma->inbih)->biWidth;
+ rc.bottom = (wma->hic ? wma->outbih : wma->inbih)->biHeight;
+ AdjustWindowRect(&rc, dwStyle, FALSE);
+ if (!(dwStyle & (WS_CHILD|WS_POPUP))) /* overlapped window ? */
+ {
+ rc.right -= rc.left;
+ rc.bottom -= rc.top;
+ rc.left = rc.top = CW_USEDEFAULT;
+ }
+
+ wma->hWnd = CreateWindowW(mciaviW, captionW,
+ dwStyle, rc.left, rc.top,
+ rc.right, rc.bottom,
+ hParent, 0, MCIAVI_hInstance,
+ ULongToPtr(wma->wDevID));
+ wma->hWndPaint = wma->hWnd;
+ return wma->hWnd != 0;
+}
+
+/***************************************************************************
+ * MCIAVI_mciPut [internal]
+ */
+DWORD MCIAVI_mciPut(UINT wDevID, DWORD dwFlags, LPMCI_DGV_PUT_PARMS lpParms)
+{
+ WINE_MCIAVI* wma = MCIAVI_mciGetOpenDev(wDevID);
+ RECT rc;
+
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (dwFlags & MCI_DGV_RECT) {
+ /* In MCI, RECT structure is used differently: rc.right = width & rc.bottom = height
+ * So convert input MCI RECT into a normal RECT */
+ rc.left = lpParms->rc.left;
+ rc.top = lpParms->rc.top;
+ rc.right = lpParms->rc.left + lpParms->rc.right;
+ rc.bottom = lpParms->rc.top + lpParms->rc.bottom;
+ } else {
+ GetClientRect(wma->hWndPaint, &rc);
+ }
+
+ if (dwFlags & MCI_DGV_PUT_CLIENT) {
+ FIXME("PUT_CLIENT %s\n", wine_dbgstr_rect(&rc));
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ if (dwFlags & MCI_DGV_PUT_DESTINATION) {
+ TRACE("PUT_DESTINATION %s\n", wine_dbgstr_rect(&rc));
+ wma->dest = rc;
+ }
+ if (dwFlags & MCI_DGV_PUT_FRAME) {
+ FIXME("PUT_FRAME %s\n", wine_dbgstr_rect(&rc));
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ if (dwFlags & MCI_DGV_PUT_SOURCE) {
+ TRACE("PUT_SOURCE %s\n", wine_dbgstr_rect(&rc));
+ wma->source = rc;
+ }
+ if (dwFlags & MCI_DGV_PUT_VIDEO) {
+ FIXME("PUT_VIDEO %s\n", wine_dbgstr_rect(&rc));
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ if (dwFlags & MCI_DGV_PUT_WINDOW) {
+ TRACE("PUT_WINDOW %s\n", wine_dbgstr_rect(&rc));
+ SetWindowPos(wma->hWndPaint, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER);
+ }
+ LeaveCriticalSection(&wma->cs);
+ return 0;
+}
+
+/******************************************************************************
+ * MCIAVI_mciWhere [internal]
+ */
+DWORD MCIAVI_mciWhere(UINT wDevID, DWORD dwFlags, LPMCI_DGV_RECT_PARMS lpParms)
+{
+ WINE_MCIAVI* wma = MCIAVI_mciGetOpenDev(wDevID);
+ RECT rc;
+
+ TRACE("(%04x, %08x, %p)\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (dwFlags & MCI_DGV_WHERE_DESTINATION) {
+ if (dwFlags & MCI_DGV_WHERE_MAX) {
+ GetClientRect(wma->hWndPaint, &rc);
+ TRACE("WHERE_DESTINATION_MAX %s\n", wine_dbgstr_rect(&rc));
+ } else {
+ TRACE("WHERE_DESTINATION %s\n", wine_dbgstr_rect(&wma->dest));
+ rc = wma->dest;
+ }
+ }
+ if (dwFlags & MCI_DGV_WHERE_FRAME) {
+ if (dwFlags & MCI_DGV_WHERE_MAX)
+ FIXME("MCI_DGV_WHERE_FRAME_MAX\n");
+ else
+ FIXME("MCI_DGV_WHERE_FRAME\n");
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ if (dwFlags & MCI_DGV_WHERE_SOURCE) {
+ if (dwFlags & MCI_DGV_WHERE_MAX) {
+ rc.left = 0;
+ rc.top = 0;
+ rc.right = wma->inbih->biWidth;
+ rc.bottom = wma->inbih->biHeight;
+ TRACE("WHERE_SOURCE_MAX %s\n", wine_dbgstr_rect(&rc));
+ } else {
+ TRACE("WHERE_SOURCE %s\n", wine_dbgstr_rect(&wma->source));
+ rc = wma->source;
+ }
+ }
+ if (dwFlags & MCI_DGV_WHERE_VIDEO) {
+ if (dwFlags & MCI_DGV_WHERE_MAX)
+ FIXME("WHERE_VIDEO_MAX\n");
+ else
+ FIXME("WHERE_VIDEO\n");
+ LeaveCriticalSection(&wma->cs);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ if (dwFlags & MCI_DGV_WHERE_WINDOW) {
+ if (dwFlags & MCI_DGV_WHERE_MAX) {
+ GetWindowRect(GetDesktopWindow(), &rc);
+ TRACE("WHERE_WINDOW_MAX %s\n", wine_dbgstr_rect(&rc));
+ } else {
+ GetWindowRect(wma->hWndPaint, &rc);
+ TRACE("WHERE_WINDOW %s\n", wine_dbgstr_rect(&rc));
+ }
+ }
+
+ /* In MCI, RECT structure is used differently: rc.right = width & rc.bottom = height
+ * So convert the normal RECT into a MCI RECT before returning */
+ lpParms->rc.left = rc.left;
+ lpParms->rc.top = rc.top;
+ lpParms->rc.right = rc.right - rc.left;
+ lpParms->rc.bottom = rc.bottom - rc.top;
+
+ LeaveCriticalSection(&wma->cs);
+ return 0;
+}
+
+/***************************************************************************
+ * MCIAVI_mciWindow [internal]
+ */
+DWORD MCIAVI_mciWindow(UINT wDevID, DWORD dwFlags, LPMCI_DGV_WINDOW_PARMSW lpParms)
+{
+ WINE_MCIAVI* wma = MCIAVI_mciGetOpenDev(wDevID);
+
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wma == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ EnterCriticalSection(&wma->cs);
+
+ if (dwFlags & MCI_DGV_WINDOW_HWND) {
+ if (IsWindow(lpParms->hWnd))
+ {
+ TRACE("Setting hWnd to %p\n", lpParms->hWnd);
+ if (wma->hWnd) ShowWindow(wma->hWnd, SW_HIDE);
+ wma->hWndPaint = (lpParms->hWnd == MCI_DGV_WINDOW_DEFAULT) ? wma->hWnd : lpParms->hWnd;
+ }
+ }
+ if (dwFlags & MCI_DGV_WINDOW_STATE) {
+ TRACE("Setting nCmdShow to %d\n", lpParms->nCmdShow);
+ ShowWindow(wma->hWndPaint, lpParms->nCmdShow);
+ }
+ if (dwFlags & MCI_DGV_WINDOW_TEXT) {
+ TRACE("Setting caption to %s\n", debugstr_w(lpParms->lpstrText));
+ SetWindowTextW(wma->hWndPaint, lpParms->lpstrText);
+ }
+
+ LeaveCriticalSection(&wma->cs);
+ return 0;
+}
diff --git a/reactos/dll/win32/mcicda/mcicda.c b/reactos/dll/win32/mcicda/mcicda.c
new file mode 100644
index 00000000000..d5e52c7fec2
--- /dev/null
+++ b/reactos/dll/win32/mcicda/mcicda.c
@@ -0,0 +1,1332 @@
+/*
+ * MCI driver for audio CD (MCICDA)
+ *
+ * Copyright 1994 Martin Ayotte
+ * Copyright 1998-99 Eric Pouech
+ * Copyright 2000 Andreas Mohr
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include
+#include
+#include
+
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "wownt32.h"
+#include "mmddk.h"
+#include "winioctl.h"
+#define __NTDDSTOR_H /* ROS HACK */
+#include "ntddcdrm.h"
+#include "winternl.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "dsound.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
+
+#define CDFRAMES_PERSEC 75
+#define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
+#define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
+#define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
+
+/* Defined by red-book standard; do not change! */
+#define RAW_SECTOR_SIZE (2352)
+
+/* Must be >= RAW_SECTOR_SIZE */
+#define CDDA_FRAG_SIZE (32768)
+/* Must be >= 2 */
+#define CDDA_FRAG_COUNT (3)
+
+typedef struct {
+ UINT wDevID;
+ int nUseCount; /* Incremented for each shared open */
+ BOOL fShareable; /* TRUE if first open was shareable */
+ WORD wNotifyDeviceID; /* MCI device ID with a pending notification */
+ HANDLE hCallback; /* Callback handle for pending notification */
+ DWORD dwTimeFormat;
+ HANDLE handle;
+
+ /* The following are used for digital playback only */
+ HANDLE hThread;
+ HANDLE stopEvent;
+ DWORD start, end;
+
+ IDirectSound *dsObj;
+ IDirectSoundBuffer *dsBuf;
+
+ CRITICAL_SECTION cs;
+} WINE_MCICDAUDIO;
+
+/*-----------------------------------------------------------------------*/
+
+typedef HRESULT(WINAPI*LPDIRECTSOUNDCREATE)(LPCGUID,LPDIRECTSOUND*,LPUNKNOWN);
+static LPDIRECTSOUNDCREATE pDirectSoundCreate;
+
+static DWORD CALLBACK MCICDA_playLoop(void *ptr)
+{
+ WINE_MCICDAUDIO *wmcda = (WINE_MCICDAUDIO*)ptr;
+ DWORD lastPos, curPos, endPos, br;
+ void *cdData;
+ DWORD lockLen, fragLen;
+ DSBCAPS caps;
+ RAW_READ_INFO rdInfo;
+ HRESULT hr = DS_OK;
+
+ memset(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(caps);
+ hr = IDirectSoundBuffer_GetCaps(wmcda->dsBuf, &caps);
+
+ fragLen = caps.dwBufferBytes/CDDA_FRAG_COUNT;
+ curPos = lastPos = 0;
+ endPos = ~0u;
+ while (SUCCEEDED(hr) && endPos != lastPos &&
+ WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0) {
+ hr = IDirectSoundBuffer_GetCurrentPosition(wmcda->dsBuf, &curPos, NULL);
+ if ((curPos-lastPos+caps.dwBufferBytes)%caps.dwBufferBytes < fragLen) {
+ Sleep(1);
+ continue;
+ }
+
+ EnterCriticalSection(&wmcda->cs);
+ rdInfo.DiskOffset.QuadPart = wmcda->start<<11;
+ rdInfo.SectorCount = min(fragLen/RAW_SECTOR_SIZE, wmcda->end-wmcda->start);
+ rdInfo.TrackMode = CDDA;
+
+ hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0);
+ if (hr == DSERR_BUFFERLOST) {
+ if(FAILED(IDirectSoundBuffer_Restore(wmcda->dsBuf)) ||
+ FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING))) {
+ LeaveCriticalSection(&wmcda->cs);
+ break;
+ }
+ hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0);
+ }
+
+ if (SUCCEEDED(hr)) {
+ if (rdInfo.SectorCount > 0) {
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ, &rdInfo, sizeof(rdInfo), cdData, lockLen, &br, NULL))
+ WARN("CD read failed at sector %d: 0x%x\n", wmcda->start, GetLastError());
+ }
+ if (rdInfo.SectorCount*RAW_SECTOR_SIZE < lockLen) {
+ if(endPos == ~0u) endPos = lastPos;
+ memset((BYTE*)cdData + rdInfo.SectorCount*RAW_SECTOR_SIZE, 0,
+ lockLen - rdInfo.SectorCount*RAW_SECTOR_SIZE);
+ }
+ hr = IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0);
+ }
+
+ lastPos += fragLen;
+ lastPos %= caps.dwBufferBytes;
+ wmcda->start += rdInfo.SectorCount;
+
+ LeaveCriticalSection(&wmcda->cs);
+ }
+ IDirectSoundBuffer_Stop(wmcda->dsBuf);
+ SetEvent(wmcda->stopEvent);
+
+ EnterCriticalSection(&wmcda->cs);
+ if (wmcda->hCallback) {
+ mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID,
+ FAILED(hr) ? MCI_NOTIFY_FAILURE :
+ ((endPos!=lastPos) ? MCI_NOTIFY_ABORTED :
+ MCI_NOTIFY_SUCCESSFUL));
+ wmcda->hCallback = NULL;
+ }
+ LeaveCriticalSection(&wmcda->cs);
+
+ ExitThread(0);
+ return 0;
+}
+
+
+
+/**************************************************************************
+ * MCICDA_drvOpen [internal]
+ */
+static DWORD MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
+{
+ static HMODULE dsHandle;
+ WINE_MCICDAUDIO* wmcda;
+
+ if (!modp) return 0xFFFFFFFF;
+
+ wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
+
+ if (!wmcda)
+ return 0;
+
+ if (!dsHandle) {
+ dsHandle = LoadLibraryA("dsound.dll");
+ if(dsHandle)
+ pDirectSoundCreate = (LPDIRECTSOUNDCREATE)GetProcAddress(dsHandle, "DirectSoundCreate");
+ }
+
+ wmcda->wDevID = modp->wDeviceID;
+ mciSetDriverData(wmcda->wDevID, (DWORD_PTR)wmcda);
+ modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
+ modp->wType = MCI_DEVTYPE_CD_AUDIO;
+ InitializeCriticalSection(&wmcda->cs);
+ return modp->wDeviceID;
+}
+
+/**************************************************************************
+ * MCICDA_drvClose [internal]
+ */
+static DWORD MCICDA_drvClose(DWORD dwDevID)
+{
+ WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
+
+ if (wmcda) {
+ DeleteCriticalSection(&wmcda->cs);
+ HeapFree(GetProcessHeap(), 0, wmcda);
+ mciSetDriverData(dwDevID, 0);
+ }
+ return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
+}
+
+/**************************************************************************
+ * MCICDA_GetOpenDrv [internal]
+ */
+static WINE_MCICDAUDIO* MCICDA_GetOpenDrv(UINT wDevID)
+{
+ WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
+
+ if (wmcda == NULL || wmcda->nUseCount == 0) {
+ WARN("Invalid wDevID=%u\n", wDevID);
+ return 0;
+ }
+ return wmcda;
+}
+
+/**************************************************************************
+ * MCICDA_GetStatus [internal]
+ */
+static DWORD MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
+{
+ CDROM_SUB_Q_DATA_FORMAT fmt;
+ SUB_Q_CHANNEL_DATA data;
+ DWORD br;
+ DWORD mode = MCI_MODE_NOT_READY;
+
+ fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
+ if(wmcda->hThread != 0) {
+ DWORD status;
+ HRESULT hr;
+
+ hr = IDirectSoundBuffer_GetStatus(wmcda->dsBuf, &status);
+ if(SUCCEEDED(hr)) {
+ if(!(status&DSBSTATUS_PLAYING)) {
+ if(WaitForSingleObject(wmcda->stopEvent, 0) == WAIT_OBJECT_0)
+ mode = MCI_MODE_STOP;
+ else
+ mode = MCI_MODE_PAUSE;
+ }
+ else
+ mode = MCI_MODE_PLAY;
+ }
+ }
+ else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
+ &data, sizeof(data), &br, NULL)) {
+ if (GetLastError() == ERROR_NOT_READY) mode = MCI_MODE_OPEN;
+ } else {
+ switch (data.CurrentPosition.Header.AudioStatus)
+ {
+ case AUDIO_STATUS_IN_PROGRESS: mode = MCI_MODE_PLAY; break;
+ case AUDIO_STATUS_PAUSED: mode = MCI_MODE_PAUSE; break;
+ case AUDIO_STATUS_NO_STATUS:
+ case AUDIO_STATUS_PLAY_COMPLETE: mode = MCI_MODE_STOP; break;
+ case AUDIO_STATUS_PLAY_ERROR:
+ case AUDIO_STATUS_NOT_SUPPORTED:
+ default:
+ break;
+ }
+ }
+ return mode;
+}
+
+/**************************************************************************
+ * MCICDA_GetError [internal]
+ */
+static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
+{
+ switch (GetLastError())
+ {
+ case ERROR_NOT_READY: return MCIERR_DEVICE_NOT_READY;
+ case ERROR_IO_DEVICE: return MCIERR_HARDWARE;
+ default:
+ FIXME("Unknown mode %u\n", GetLastError());
+ }
+ return MCIERR_DRIVER_INTERNAL;
+}
+
+/**************************************************************************
+ * MCICDA_CalcFrame [internal]
+ */
+static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
+{
+ DWORD dwFrame = 0;
+ UINT wTrack;
+ CDROM_TOC toc;
+ DWORD br;
+ BYTE* addr;
+
+ TRACE("(%p, %08X, %u);\n", wmcda, wmcda->dwTimeFormat, dwTime);
+
+ switch (wmcda->dwTimeFormat) {
+ case MCI_FORMAT_MILLISECONDS:
+ dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
+ TRACE("MILLISECONDS %u\n", dwFrame);
+ break;
+ case MCI_FORMAT_MSF:
+ TRACE("MSF %02u:%02u:%02u\n",
+ MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
+ dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
+ dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
+ dwFrame += MCI_MSF_FRAME(dwTime);
+ break;
+ case MCI_FORMAT_TMSF:
+ default: /* unknown format ! force TMSF ! ... */
+ wTrack = MCI_TMSF_TRACK(dwTime);
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &br, NULL))
+ return 0;
+ if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
+ return 0;
+ TRACE("MSF %02u-%02u:%02u:%02u\n",
+ MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
+ MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
+ addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
+ TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
+ wTrack, addr[1], addr[2], addr[3]);
+ dwFrame = CDFRAMES_PERMIN * (addr[1] + MCI_TMSF_MINUTE(dwTime)) +
+ CDFRAMES_PERSEC * (addr[2] + MCI_TMSF_SECOND(dwTime)) +
+ addr[3] + MCI_TMSF_FRAME(dwTime);
+ break;
+ }
+ return dwFrame;
+}
+
+/**************************************************************************
+ * MCICDA_CalcTime [internal]
+ */
+static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
+{
+ DWORD dwTime = 0;
+ UINT wTrack;
+ UINT wMinutes;
+ UINT wSeconds;
+ UINT wFrames;
+ CDROM_TOC toc;
+ DWORD br;
+
+ TRACE("(%p, %08X, %u);\n", wmcda, tf, dwFrame);
+
+ switch (tf) {
+ case MCI_FORMAT_MILLISECONDS:
+ dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
+ TRACE("MILLISECONDS %u\n", dwTime);
+ *lpRet = 0;
+ break;
+ case MCI_FORMAT_MSF:
+ wMinutes = dwFrame / CDFRAMES_PERMIN;
+ wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
+ wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
+ dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
+ TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n",
+ wMinutes, wSeconds, wFrames, dwTime);
+ *lpRet = MCI_COLONIZED3_RETURN;
+ break;
+ case MCI_FORMAT_TMSF:
+ default: /* unknown format ! force TMSF ! ... */
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &br, NULL))
+ return 0;
+ if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
+ dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
+ ERR("Out of range value %u [%u,%u]\n",
+ dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
+ FRAME_OF_TOC(toc, toc.LastTrack + 1));
+ *lpRet = 0;
+ return 0;
+ }
+ for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
+ if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
+ break;
+ }
+ wTrack--;
+ dwFrame -= FRAME_OF_TOC(toc, wTrack);
+ wMinutes = dwFrame / CDFRAMES_PERMIN;
+ wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
+ wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
+ dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
+ TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
+ *lpRet = MCI_COLONIZED4_RETURN;
+ break;
+ }
+ return dwTime;
+}
+
+static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
+static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
+
+/**************************************************************************
+ * MCICDA_Open [internal]
+ */
+static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms)
+{
+ DWORD dwDeviceID;
+ DWORD ret = MCIERR_HARDWARE;
+ WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
+ WCHAR root[7], drive = 0;
+ unsigned int count;
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpOpenParms);
+
+ if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ dwDeviceID = lpOpenParms->wDeviceID;
+
+ if (wmcda->nUseCount > 0) {
+ /* The driver is already open on this channel */
+ /* If the driver was opened shareable before and this open specifies */
+ /* shareable then increment the use count */
+ if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
+ ++wmcda->nUseCount;
+ else
+ return MCIERR_MUST_USE_SHAREABLE;
+ } else {
+ wmcda->nUseCount = 1;
+ wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
+ }
+ if (dwFlags & MCI_OPEN_ELEMENT) {
+ if (dwFlags & MCI_OPEN_ELEMENT_ID) {
+ WARN("MCI_OPEN_ELEMENT_ID %p! Abort\n", lpOpenParms->lpstrElementName);
+ return MCIERR_NO_ELEMENT_ALLOWED;
+ }
+ TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName));
+ if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
+ (lpOpenParms->lpstrElementName[2] && lpOpenParms->lpstrElementName[2] != '\\'))
+ {
+ WARN("MCI_OPEN_ELEMENT unsupported format: %s\n",
+ debugstr_w(lpOpenParms->lpstrElementName));
+ ret = MCIERR_NO_ELEMENT_ALLOWED;
+ goto the_error;
+ }
+ drive = toupper(lpOpenParms->lpstrElementName[0]);
+ root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
+ if (GetDriveTypeW(root) != DRIVE_CDROM)
+ {
+ ret = MCIERR_INVALID_DEVICE_NAME;
+ goto the_error;
+ }
+ }
+ else
+ {
+ /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
+ root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
+ for (count = 0; root[0] <= 'Z'; root[0]++)
+ {
+ if (GetDriveTypeW(root) == DRIVE_CDROM && ++count >= dwDeviceID)
+ {
+ drive = root[0];
+ break;
+ }
+ }
+ if (!drive)
+ {
+ ret = MCIERR_INVALID_DEVICE_ID;
+ goto the_error;
+ }
+ }
+
+ wmcda->wNotifyDeviceID = dwDeviceID;
+ wmcda->dwTimeFormat = MCI_FORMAT_MSF;
+
+ /* now, open the handle */
+ root[0] = root[1] = '\\'; root[2] = '.'; root[3] = '\\'; root[4] = drive; root[5] = ':'; root[6] = '\0';
+ wmcda->handle = CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+ if (wmcda->handle != 0)
+ return 0;
+
+ the_error:
+ --wmcda->nUseCount;
+ return ret;
+}
+
+/**************************************************************************
+ * MCICDA_Close [internal]
+ */
+static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwParam, lpParms);
+
+ if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (--wmcda->nUseCount == 0) {
+ CloseHandle(wmcda->handle);
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * MCICDA_GetDevCaps [internal]
+ */
+static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
+ LPMCI_GETDEVCAPS_PARMS lpParms)
+{
+ DWORD ret = 0;
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ if (dwFlags & MCI_GETDEVCAPS_ITEM) {
+ TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms->dwItem);
+
+ switch (lpParms->dwItem) {
+ case MCI_GETDEVCAPS_CAN_RECORD:
+ lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
+ 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_DEVICE_TYPE:
+ lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_USES_FILES:
+ lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_COMPOUND_DEVICE:
+ lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_CAN_EJECT:
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ 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(FALSE, MCI_FALSE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ default:
+ ERR("Unsupported %x devCaps item\n", lpParms->dwItem);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ } else {
+ TRACE("No GetDevCaps-Item !\n");
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ TRACE("lpParms->dwReturn=%08X;\n", lpParms->dwReturn);
+ return ret;
+}
+
+static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
+{
+ unsigned long serial = 0;
+ int i;
+ WORD wMagic;
+ DWORD dwStart, dwEnd;
+
+ /*
+ * wMagic collects the wFrames from track 1
+ * dwStart, dwEnd collect the beginning and end of the disc respectively, in
+ * frames.
+ * There it is collected for correcting the serial when there are less than
+ * 3 tracks.
+ */
+ wMagic = toc->TrackData[0].Address[3];
+ dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
+
+ for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
+ serial += (toc->TrackData[i].Address[1] << 16) |
+ (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
+ }
+ dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
+
+ if (toc->LastTrack - toc->FirstTrack + 1 < 3)
+ serial += wMagic + (dwEnd - dwStart);
+
+ return serial;
+}
+
+
+/**************************************************************************
+ * MCICDA_Info [internal]
+ */
+static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
+{
+ LPCWSTR str = NULL;
+ WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
+ DWORD ret = 0;
+ WCHAR buffer[16];
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL || lpParms->lpstrReturn == NULL)
+ return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
+
+ if (dwFlags & MCI_INFO_PRODUCT) {
+ static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
+ str = wszAudioCd;
+ } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
+ ret = MCIERR_NO_IDENTITY;
+ } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
+ DWORD res = 0;
+ CDROM_TOC toc;
+ DWORD br;
+ static const WCHAR wszLu[] = {'%','l','u',0};
+
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &br, NULL)) {
+ return MCICDA_GetError(wmcda);
+ }
+
+ res = CDROM_Audio_GetSerial(&toc);
+ sprintfW(buffer, wszLu, res);
+ str = buffer;
+ } else {
+ WARN("Don't know this info command (%u)\n", dwFlags);
+ ret = MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ if (str) {
+ if (lpParms->dwRetSize <= strlenW(str)) {
+ lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
+ ret = MCIERR_PARAM_OVERFLOW;
+ } else {
+ strcpyW(lpParms->lpstrReturn, str);
+ }
+ } else {
+ *lpParms->lpstrReturn = 0;
+ }
+ TRACE("=> %s (%d)\n", debugstr_w(lpParms->lpstrReturn), ret);
+ return ret;
+}
+
+/**************************************************************************
+ * MCICDA_Status [internal]
+ */
+static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
+{
+ WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
+ DWORD ret = 0;
+ CDROM_SUB_Q_DATA_FORMAT fmt;
+ SUB_Q_CHANNEL_DATA data;
+ CDROM_TOC toc;
+ DWORD br;
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (dwFlags & MCI_NOTIFY) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ if (dwFlags & MCI_STATUS_ITEM) {
+ TRACE("dwItem = %x\n", lpParms->dwItem);
+ switch (lpParms->dwItem) {
+ case MCI_STATUS_CURRENT_TRACK:
+ fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
+ &data, sizeof(data), &br, NULL))
+ {
+ return MCICDA_GetError(wmcda);
+ }
+ lpParms->dwReturn = data.CurrentPosition.TrackNumber;
+ TRACE("CURRENT_TRACK=%lu\n", lpParms->dwReturn);
+ break;
+ case MCI_STATUS_LENGTH:
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &br, NULL)) {
+ WARN("error reading TOC !\n");
+ return MCICDA_GetError(wmcda);
+ }
+ if (dwFlags & MCI_TRACK) {
+ TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms->dwTrack);
+ if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
+ return MCIERR_OUTOFRANGE;
+ lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
+ FRAME_OF_TOC(toc, lpParms->dwTrack);
+ /* Windows returns one frame less than the total track length for the
+ last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
+ if (lpParms->dwTrack == toc.LastTrack)
+ lpParms->dwReturn--;
+ } else {
+ /* Sum of the lengths of all of the tracks. Inherits the
+ 'off by one frame' behavior from the length of the last track.
+ See above comment. */
+ lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
+ FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
+ }
+ lpParms->dwReturn = MCICDA_CalcTime(wmcda,
+ (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
+ ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
+ lpParms->dwReturn,
+ &ret);
+ TRACE("LENGTH=%lu\n", lpParms->dwReturn);
+ break;
+ case MCI_STATUS_MODE:
+ lpParms->dwReturn = MCICDA_GetStatus(wmcda);
+ TRACE("MCI_STATUS_MODE=%08lX\n", lpParms->dwReturn);
+ lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_STATUS_MEDIA_PRESENT:
+ lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
+ MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_STATUS_NUMBER_OF_TRACKS:
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &br, NULL)) {
+ WARN("error reading TOC !\n");
+ return MCICDA_GetError(wmcda);
+ }
+ lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
+ TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu\n", lpParms->dwReturn);
+ if (lpParms->dwReturn == (WORD)-1)
+ return MCICDA_GetError(wmcda);
+ break;
+ case MCI_STATUS_POSITION:
+ if (dwFlags & MCI_STATUS_START) {
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &br, NULL)) {
+ WARN("error reading TOC !\n");
+ return MCICDA_GetError(wmcda);
+ }
+ lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
+ TRACE("get MCI_STATUS_START !\n");
+ } else if (dwFlags & MCI_TRACK) {
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &br, NULL)) {
+ WARN("error reading TOC !\n");
+ return MCICDA_GetError(wmcda);
+ }
+ if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
+ return MCIERR_OUTOFRANGE;
+ lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
+ TRACE("get MCI_TRACK #%u !\n", lpParms->dwTrack);
+ } else {
+ fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
+ &data, sizeof(data), &br, NULL)) {
+ return MCICDA_GetError(wmcda);
+ }
+ lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
+ }
+ lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
+ TRACE("MCI_STATUS_POSITION=%08lX\n", lpParms->dwReturn);
+ break;
+ case MCI_STATUS_READY:
+ TRACE("MCI_STATUS_READY !\n");
+ switch (MCICDA_GetStatus(wmcda))
+ {
+ case MCI_MODE_NOT_READY:
+ case MCI_MODE_OPEN:
+ lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
+ break;
+ default:
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ break;
+ }
+ TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_STATUS_TIME_FORMAT:
+ lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat);
+ TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case 4001: /* FIXME: for bogus FullCD */
+ case MCI_CDA_STATUS_TYPE_TRACK:
+ if (!(dwFlags & MCI_TRACK))
+ ret = MCIERR_MISSING_PARAMETER;
+ else {
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &br, NULL)) {
+ WARN("error reading TOC !\n");
+ return MCICDA_GetError(wmcda);
+ }
+ if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
+ ret = MCIERR_OUTOFRANGE;
+ else
+ lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
+ MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
+ }
+ TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%ld\n", lpParms->dwTrack, lpParms->dwReturn);
+ break;
+ default:
+ FIXME("unknown command %08X !\n", lpParms->dwItem);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ } else {
+ WARN("not MCI_STATUS_ITEM !\n");
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * MCICDA_SkipDataTracks [internal]
+ */
+static DWORD MCICDA_SkipDataTracks(WINE_MCICDAUDIO* wmcda,DWORD *frame)
+{
+ int i;
+ DWORD br;
+ CDROM_TOC toc;
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &br, NULL)) {
+ WARN("error reading TOC !\n");
+ return MCICDA_GetError(wmcda);
+ }
+ /* Locate first track whose starting frame is bigger than frame */
+ for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
+ if ( FRAME_OF_TOC(toc, i) > *frame ) break;
+ if (i <= toc.FirstTrack && i>toc.LastTrack+1) {
+ i = 0; /* requested address is out of range: go back to start */
+ *frame = FRAME_OF_TOC(toc,toc.FirstTrack);
+ }
+ else
+ i--;
+ /* i points to last track whose start address is not greater than frame.
+ * Now skip non-audio tracks */
+ for(;i<=toc.LastTrack+1;i++)
+ if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) )
+ break;
+ /* The frame will be an address in the next audio track or
+ * address of lead-out. */
+ if ( FRAME_OF_TOC(toc, i) > *frame )
+ *frame = FRAME_OF_TOC(toc, i);
+ return 0;
+}
+
+/**************************************************************************
+ * MCICDA_Play [internal]
+ */
+static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
+{
+ WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
+ DWORD ret = 0, start, end;
+ DWORD br;
+ CDROM_PLAY_AUDIO_MSF play;
+ CDROM_SUB_Q_DATA_FORMAT fmt;
+ SUB_Q_CHANNEL_DATA data;
+ CDROM_TOC toc;
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL)
+ return MCIERR_NULL_PARAMETER_BLOCK;
+
+ if (wmcda == NULL)
+ return MCIERR_INVALID_DEVICE_ID;
+
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &br, NULL)) {
+ WARN("error reading TOC !\n");
+ return MCICDA_GetError(wmcda);
+ }
+
+ if (dwFlags & MCI_FROM) {
+ start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
+ if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
+ return ret;
+ TRACE("MCI_FROM=%08X -> %u\n", lpParms->dwFrom, start);
+ } else {
+ fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
+ &data, sizeof(data), &br, NULL)) {
+ return MCICDA_GetError(wmcda);
+ }
+ start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
+ if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
+ return ret;
+ }
+ if (dwFlags & MCI_TO) {
+ end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
+ TRACE("MCI_TO=%08X -> %u\n", lpParms->dwTo, end);
+ } else {
+ end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
+ }
+ TRACE("Playing from %u to %u\n", start, end);
+
+ if (wmcda->hThread != 0) {
+ SetEvent(wmcda->stopEvent);
+ WaitForSingleObject(wmcda->hThread, INFINITE);
+
+ CloseHandle(wmcda->hThread);
+ wmcda->hThread = 0;
+ CloseHandle(wmcda->stopEvent);
+ wmcda->stopEvent = 0;
+
+ IDirectSoundBuffer_Stop(wmcda->dsBuf);
+ IDirectSoundBuffer_Release(wmcda->dsBuf);
+ wmcda->dsBuf = NULL;
+ IDirectSound_Release(wmcda->dsObj);
+ wmcda->dsObj = NULL;
+ }
+ else if(wmcda->hCallback) {
+ mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID,
+ MCI_NOTIFY_ABORTED);
+ wmcda->hCallback = NULL;
+ }
+
+ if ((dwFlags&MCI_NOTIFY))
+ wmcda->hCallback = HWND_32(LOWORD(lpParms->dwCallback));
+
+ if (pDirectSoundCreate) {
+ WAVEFORMATEX format;
+ DSBUFFERDESC desc;
+ DWORD lockLen;
+ void *cdData;
+ HRESULT hr;
+
+ hr = pDirectSoundCreate(NULL, &wmcda->dsObj, NULL);
+ if (SUCCEEDED(hr)) {
+ IDirectSound_SetCooperativeLevel(wmcda->dsObj, GetDesktopWindow(), DSSCL_PRIORITY);
+
+ /* The "raw" frame is relative to the start of the first track */
+ wmcda->start = start - FRAME_OF_TOC(toc, toc.FirstTrack);
+ wmcda->end = end - FRAME_OF_TOC(toc, toc.FirstTrack);
+
+ memset(&format, 0, sizeof(format));
+ format.wFormatTag = WAVE_FORMAT_PCM;
+ format.nChannels = 2;
+ format.nSamplesPerSec = 44100;
+ format.wBitsPerSample = 16;
+ format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
+ format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.dwSize = sizeof(desc);
+ desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
+ desc.dwBufferBytes = (CDDA_FRAG_SIZE - (CDDA_FRAG_SIZE%RAW_SECTOR_SIZE)) * CDDA_FRAG_COUNT;
+ desc.lpwfxFormat = &format;
+
+ hr = IDirectSound_CreateSoundBuffer(wmcda->dsObj, &desc, &wmcda->dsBuf, NULL);
+ }
+ if (SUCCEEDED(hr)) {
+ hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, 0, 0, &cdData, &lockLen,
+ NULL, NULL, DSBLOCK_ENTIREBUFFER);
+ }
+ if (SUCCEEDED(hr)) {
+ RAW_READ_INFO rdInfo;
+ int readok;
+
+ rdInfo.DiskOffset.QuadPart = wmcda->start<<11;
+ rdInfo.SectorCount = min(desc.dwBufferBytes/RAW_SECTOR_SIZE,
+ wmcda->end-wmcda->start);
+ rdInfo.TrackMode = CDDA;
+
+ readok = DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ,
+ &rdInfo, sizeof(rdInfo), cdData, lockLen,
+ &br, NULL);
+ IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0);
+
+ if (readok) {
+ wmcda->start += rdInfo.SectorCount;
+ wmcda->stopEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+ }
+ if (wmcda->stopEvent != 0)
+ wmcda->hThread = CreateThread(NULL, 0, MCICDA_playLoop, wmcda, 0, &br);
+ if (wmcda->hThread != 0) {
+ hr = IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING);
+ if (SUCCEEDED(hr))
+ return ret;
+
+ SetEvent(wmcda->stopEvent);
+ WaitForSingleObject(wmcda->hThread, INFINITE);
+ CloseHandle(wmcda->hThread);
+ wmcda->hThread = 0;
+ }
+ }
+
+ if (wmcda->stopEvent != 0) {
+ CloseHandle(wmcda->stopEvent);
+ wmcda->stopEvent = 0;
+ }
+ if (wmcda->dsBuf) {
+ IDirectSoundBuffer_Release(wmcda->dsBuf);
+ wmcda->dsBuf = NULL;
+ }
+ if (wmcda->dsObj) {
+ IDirectSound_Release(wmcda->dsObj);
+ wmcda->dsObj = NULL;
+ }
+ }
+
+ play.StartingM = start / CDFRAMES_PERMIN;
+ play.StartingS = (start / CDFRAMES_PERSEC) % 60;
+ play.StartingF = start % CDFRAMES_PERSEC;
+ play.EndingM = end / CDFRAMES_PERMIN;
+ play.EndingS = (end / CDFRAMES_PERSEC) % 60;
+ play.EndingF = end % CDFRAMES_PERSEC;
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
+ NULL, 0, &br, NULL)) {
+ wmcda->hCallback = NULL;
+ ret = MCIERR_HARDWARE;
+ } else if (dwFlags & MCI_NOTIFY) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ /*
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ */
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * MCICDA_Stop [internal]
+ */
+static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
+ DWORD br;
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (wmcda->hThread != 0) {
+ SetEvent(wmcda->stopEvent);
+ WaitForSingleObject(wmcda->hThread, INFINITE);
+
+ CloseHandle(wmcda->hThread);
+ wmcda->hThread = 0;
+ CloseHandle(wmcda->stopEvent);
+ wmcda->stopEvent = 0;
+
+ IDirectSoundBuffer_Release(wmcda->dsBuf);
+ wmcda->dsBuf = NULL;
+ IDirectSound_Release(wmcda->dsObj);
+ wmcda->dsObj = NULL;
+ }
+ else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
+ return MCIERR_HARDWARE;
+
+ if (wmcda->hCallback) {
+ mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED);
+ wmcda->hCallback = NULL;
+ }
+
+ if (lpParms && (dwFlags & MCI_NOTIFY)) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * MCICDA_Pause [internal]
+ */
+static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
+ DWORD br;
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (wmcda->hThread != 0) {
+ /* Don't bother calling stop if the playLoop thread has already stopped */
+ if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 &&
+ FAILED(IDirectSoundBuffer_Stop(wmcda->dsBuf)))
+ return MCIERR_HARDWARE;
+ }
+ else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
+ return MCIERR_HARDWARE;
+
+ EnterCriticalSection(&wmcda->cs);
+ if (wmcda->hCallback) {
+ mciDriverNotify(wmcda->hCallback, wmcda->wNotifyDeviceID, MCI_NOTIFY_SUPERSEDED);
+ wmcda->hCallback = NULL;
+ }
+ LeaveCriticalSection(&wmcda->cs);
+
+ if (lpParms && (dwFlags & MCI_NOTIFY)) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * MCICDA_Resume [internal]
+ */
+static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
+ DWORD br;
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (wmcda->hThread != 0) {
+ /* Don't restart if the playLoop thread has already stopped */
+ if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 &&
+ FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING)))
+ return MCIERR_HARDWARE;
+ }
+ else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
+ return MCIERR_HARDWARE;
+
+ if (lpParms && (dwFlags & MCI_NOTIFY)) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * MCICDA_Seek [internal]
+ */
+static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
+{
+ DWORD at;
+ WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
+ CDROM_SEEK_AUDIO_MSF seek;
+ DWORD br, ret;
+ CDROM_TOC toc;
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &br, NULL)) {
+ WARN("error reading TOC !\n");
+ return MCICDA_GetError(wmcda);
+ }
+ switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
+ case MCI_SEEK_TO_START:
+ TRACE("Seeking to start\n");
+ at = FRAME_OF_TOC(toc,toc.FirstTrack);
+ if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
+ return ret;
+ break;
+ case MCI_SEEK_TO_END:
+ TRACE("Seeking to end\n");
+ at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
+ if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
+ return ret;
+ break;
+ case MCI_TO:
+ TRACE("Seeking to %u\n", lpParms->dwTo);
+ at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
+ if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
+ return ret;
+ break;
+ default:
+ TRACE("Unknown seek action %08lX\n",
+ (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
+ return MCIERR_UNSUPPORTED_FUNCTION;
+ }
+
+ if (wmcda->hThread != 0) {
+ EnterCriticalSection(&wmcda->cs);
+ wmcda->start = at - FRAME_OF_TOC(toc, toc.FirstTrack);
+ /* Flush remaining data, or just let it play into the new data? */
+ LeaveCriticalSection(&wmcda->cs);
+ }
+ else {
+ seek.M = at / CDFRAMES_PERMIN;
+ seek.S = (at / CDFRAMES_PERSEC) % 60;
+ seek.F = at % CDFRAMES_PERSEC;
+ if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
+ NULL, 0, &br, NULL))
+ return MCIERR_HARDWARE;
+ }
+
+ if (dwFlags & MCI_NOTIFY) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * MCICDA_SetDoor [internal]
+ */
+static DWORD MCICDA_SetDoor(UINT wDevID, BOOL open)
+{
+ WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
+ DWORD br;
+
+ TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
+
+ if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (!DeviceIoControl(wmcda->handle,
+ (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
+ NULL, 0, NULL, 0, &br, NULL))
+ return MCIERR_HARDWARE;
+
+ return 0;
+}
+
+/**************************************************************************
+ * MCICDA_Set [internal]
+ */
+static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
+{
+ WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (dwFlags & MCI_SET_DOOR_OPEN) {
+ MCICDA_SetDoor(wDevID, TRUE);
+ }
+ if (dwFlags & MCI_SET_DOOR_CLOSED) {
+ MCICDA_SetDoor(wDevID, FALSE);
+ }
+
+ /* only functions which require valid lpParms below this line ! */
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ /*
+ TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
+ TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
+ */
+ if (dwFlags & MCI_SET_TIME_FORMAT) {
+ switch (lpParms->dwTimeFormat) {
+ case MCI_FORMAT_MILLISECONDS:
+ TRACE("MCI_FORMAT_MILLISECONDS !\n");
+ break;
+ case MCI_FORMAT_MSF:
+ TRACE("MCI_FORMAT_MSF !\n");
+ break;
+ case MCI_FORMAT_TMSF:
+ TRACE("MCI_FORMAT_TMSF !\n");
+ break;
+ default:
+ WARN("bad time format !\n");
+ return MCIERR_BAD_TIME_FORMAT;
+ }
+ wmcda->dwTimeFormat = lpParms->dwTimeFormat;
+ }
+ if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
+ if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
+ if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
+ if (dwFlags & MCI_NOTIFY) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
+ lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * DriverProc (MCICDA.@)
+ */
+LRESULT CALLBACK MCICDA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
+ LPARAM dwParam1, LPARAM dwParam2)
+{
+ switch(wMsg) {
+ case DRV_LOAD: return 1;
+ case DRV_FREE: return 1;
+ case DRV_OPEN: return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
+ case DRV_CLOSE: return MCICDA_drvClose(dwDevID);
+ case DRV_ENABLE: return 1;
+ case DRV_DISABLE: return 1;
+ case DRV_QUERYCONFIGURE: return 1;
+ case DRV_CONFIGURE: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
+ case DRV_INSTALL: return DRVCNF_RESTART;
+ case DRV_REMOVE: return DRVCNF_RESTART;
+ }
+
+ if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
+
+ switch (wMsg) {
+ case MCI_OPEN_DRIVER: return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
+ case MCI_CLOSE_DRIVER: return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
+ case MCI_GETDEVCAPS: return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
+ case MCI_INFO: return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2);
+ case MCI_STATUS: return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
+ case MCI_SET: return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
+ case MCI_PLAY: return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
+ case MCI_STOP: return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
+ case MCI_PAUSE: return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
+ case MCI_RESUME: return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
+ case MCI_SEEK: return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
+ /* commands that should report an error as they are not supported in
+ * the native version */
+ case MCI_SET_DOOR_CLOSED:
+ case MCI_SET_DOOR_OPEN:
+ 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_STEP:
+ case MCI_SPIN:
+ case MCI_ESCAPE:
+ case MCI_COPY:
+ case MCI_CUT:
+ case MCI_DELETE:
+ case MCI_PASTE:
+ case MCI_WINDOW:
+ TRACE("Unsupported command [0x%x]\n", wMsg);
+ break;
+ case MCI_OPEN:
+ case MCI_CLOSE:
+ ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
+ break;
+ default:
+ TRACE("Sending msg [0x%x] to default driver proc\n", wMsg);
+ return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+ }
+ return MCIERR_UNRECOGNIZED_COMMAND;
+}
+
+/*-----------------------------------------------------------------------*/
diff --git a/reactos/dll/win32/mcicda/mcicda.rbuild b/reactos/dll/win32/mcicda/mcicda.rbuild
new file mode 100644
index 00000000000..f4ec529818c
--- /dev/null
+++ b/reactos/dll/win32/mcicda/mcicda.rbuild
@@ -0,0 +1,12 @@
+
+
+ .
+ include/reactos/wine
+
+ mcicda.c
+ wine
+ winmm
+ user32
+ kernel32
+ ntdll
+
diff --git a/reactos/dll/win32/mcicda/mcicda.spec b/reactos/dll/win32/mcicda/mcicda.spec
new file mode 100644
index 00000000000..d3cb5133a1b
--- /dev/null
+++ b/reactos/dll/win32/mcicda/mcicda.spec
@@ -0,0 +1 @@
+@ stdcall -private DriverProc(long long long long long) MCICDA_DriverProc
diff --git a/reactos/dll/win32/mciqtz32/mciqtz.c b/reactos/dll/win32/mciqtz32/mciqtz.c
new file mode 100644
index 00000000000..6d272b58b6d
--- /dev/null
+++ b/reactos/dll/win32/mciqtz32/mciqtz.c
@@ -0,0 +1,63 @@
+/*
+ * DirectShow MCI Driver
+ *
+ * Copyright 2009 Christian Costa
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include
+#include "windef.h"
+#include "winbase.h"
+#include "mmddk.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mciqtz);
+
+/*======================================================================*
+ * MCI QTZ implementation *
+ *======================================================================*/
+
+HINSTANCE MCIQTZ_hInstance = 0;
+
+/***********************************************************************
+ * DllMain (MCIQTZ.0)
+ */
+BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
+{
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hInstDLL);
+ MCIQTZ_hInstance = hInstDLL;
+ break;
+ }
+ return TRUE;
+}
+
+/*======================================================================*
+ * MCI QTZ entry points *
+ *======================================================================*/
+
+/**************************************************************************
+ * DriverProc (MCIQTZ.@)
+ */
+LRESULT CALLBACK MCIQTZ_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
+ LPARAM dwParam1, LPARAM dwParam2)
+{
+ FIXME("(%08lX, %p, %08X, %08lX, %08lX): Stub!\n",
+ dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+
+ return MCIERR_UNRECOGNIZED_COMMAND;
+}
diff --git a/reactos/dll/win32/mciqtz32/mciqtz32.rbuild b/reactos/dll/win32/mciqtz32/mciqtz32.rbuild
new file mode 100644
index 00000000000..20656a8a8e5
--- /dev/null
+++ b/reactos/dll/win32/mciqtz32/mciqtz32.rbuild
@@ -0,0 +1,11 @@
+
+
+ .
+ include/reactos/wine
+
+ mciqtz.c
+ version.rc
+ wine
+ kernel32
+ ntdll
+
diff --git a/reactos/dll/win32/mciqtz32/mciqtz32.spec b/reactos/dll/win32/mciqtz32/mciqtz32.spec
new file mode 100644
index 00000000000..ba507d764f0
--- /dev/null
+++ b/reactos/dll/win32/mciqtz32/mciqtz32.spec
@@ -0,0 +1 @@
+@ stdcall -private DriverProc(long long long long long) MCIQTZ_DriverProc
diff --git a/reactos/dll/win32/mciqtz32/version.rc b/reactos/dll/win32/mciqtz32/version.rc
new file mode 100644
index 00000000000..94c6078bd27
--- /dev/null
+++ b/reactos/dll/win32/mciqtz32/version.rc
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2009 Christian Costa
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define WINE_OLESELFREGISTER
+#define WINE_FILEDESCRIPTION_STR "Wine DirectShow MCI Driver"
+#define WINE_FILENAME_STR "mciqtz32.dll"
+#define WINE_FILEVERSION 6,5,1,900
+#define WINE_FILEVERSION_STR "6.5.1.900"
+#define WINE_PRODUCTVERSION 6,5,1,900
+#define WINE_PRODUCTVERSION_STR "6.5"
+
+#include "wine/wine_common_ver.rc"
diff --git a/reactos/dll/win32/mciseq/mcimidi.c b/reactos/dll/win32/mciseq/mcimidi.c
new file mode 100644
index 00000000000..47cb792362c
--- /dev/null
+++ b/reactos/dll/win32/mciseq/mcimidi.c
@@ -0,0 +1,1708 @@
+/*
+ * Sample MIDI Wine Driver for Linux
+ *
+ * Copyright 1994 Martin Ayotte
+ * Copyright 1999 Eric Pouech
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/* TODO:
+ * + implement it correctly
+ * + finish asynchronous commands (especially for reading/record)
+ * + better implement non waiting command (without the MCI_WAIT flag).
+ * + implement the recording features
+ */
+
+#include
+#include
+#include
+#include
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "wownt32.h"
+#include "mmddk.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mcimidi);
+
+#define MIDI_NOTEOFF 0x80
+#define MIDI_NOTEON 0x90
+
+typedef struct {
+ DWORD dwFirst; /* offset in file of track */
+ DWORD dwLast; /* number of bytes in file of track */
+ DWORD dwIndex; /* current index in file (dwFirst <= dwIndex < dwLast) */
+ DWORD dwLength; /* number of pulses in this track */
+ DWORD dwEventPulse; /* current pulse # (event) pointed by dwIndex */
+ DWORD dwEventData; /* current data (event) pointed by dwIndex */
+ WORD wEventLength; /* current length (event) pointed by dwIndex */
+ WORD wStatus : 1, /* 1 : playing, 0 : done */
+ wTrackNr : 7,
+ wLastCommand : 8; /* last MIDI command on track */
+} MCI_MIDITRACK;
+
+typedef struct tagWINE_MCIMIDI {
+ UINT wDevID; /* the MCI one */
+ HMIDI hMidi;
+ int nUseCount; /* Incremented for each shared open */
+ WORD wNotifyDeviceID; /* MCI device ID with a pending notification */
+ HANDLE hCallback; /* Callback handle for pending notification */
+ HMMIO hFile; /* mmio file handle open as Element */
+ LPWSTR lpstrElementName; /* Name of file (if any) */
+ LPWSTR lpstrCopyright;
+ LPWSTR lpstrName;
+ WORD dwStatus; /* one from MCI_MODE_xxxx */
+ DWORD dwMciTimeFormat; /* One of the supported MCI_FORMAT_xxxx */
+ WORD wFormat; /* Format of MIDI hFile (0, 1 or 2) */
+ WORD nTracks; /* Number of tracks in hFile */
+ WORD nDivision; /* Number of division in hFile PPQN or SMPTE */
+ WORD wStartedPlaying;
+ DWORD dwTempo; /* Tempo (# of 1/4 note per second */
+ MCI_MIDITRACK* tracks; /* Content of each track */
+ DWORD dwPulse;
+ DWORD dwPositionMS;
+ DWORD dwStartTicks;
+} WINE_MCIMIDI;
+
+/* ===================================================================
+ * ===================================================================
+ * 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
+ * ===================================================================
+ * =================================================================== */
+
+struct SCA {
+ UINT wDevID;
+ UINT wMsg;
+ DWORD_PTR dwParam1;
+ DWORD_PTR dwParam2;
+};
+
+/* EPP DWORD WINAPI mciSendCommandA(UINT wDevID, UINT wMsg, DWORD dwParam1, DWORD dwParam2); */
+
+/**************************************************************************
+ * MCI_SCAStarter [internal]
+ */
+static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
+{
+ struct SCA* 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);
+ HeapFree(GetProcessHeap(), 0, sca);
+ ExitThread(ret);
+ WARN("Should not happen ? what's wrong\n");
+ /* should not go after this point */
+ return ret;
+}
+
+/**************************************************************************
+ * MCI_SendCommandAsync [internal]
+ */
+static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD_PTR dwParam1,
+ DWORD_PTR dwParam2, UINT size)
+{
+ HANDLE handle;
+ struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
+
+ if (sca == 0)
+ return MCIERR_OUT_OF_MEMORY;
+
+ sca->wDevID = wDevID;
+ sca->wMsg = wMsg;
+ sca->dwParam1 = dwParam1;
+
+ if (size && dwParam2) {
+ sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
+ /* 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;
+ }
+
+ if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
+ WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
+ return MCI_SCAStarter(&sca);
+ }
+ SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
+ CloseHandle(handle);
+ return 0;
+}
+
+/*======================================================================*
+ * MCI MIDI implementation *
+ *======================================================================*/
+
+static DWORD MIDI_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
+
+/**************************************************************************
+ * MIDI_drvOpen [internal]
+ */
+static DWORD MIDI_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
+{
+ WINE_MCIMIDI* wmm;
+
+ if (!modp) return 0xFFFFFFFF;
+
+ wmm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIMIDI));
+
+ if (!wmm)
+ return 0;
+
+ wmm->wDevID = modp->wDeviceID;
+ mciSetDriverData(wmm->wDevID, (DWORD_PTR)wmm);
+ modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
+ modp->wType = MCI_DEVTYPE_SEQUENCER;
+ return modp->wDeviceID;
+}
+
+/**************************************************************************
+ * MCIMIDI_drvClose [internal]
+ */
+static DWORD MIDI_drvClose(DWORD dwDevID)
+{
+ WINE_MCIMIDI* wmm = (WINE_MCIMIDI*)mciGetDriverData(dwDevID);
+
+ if (wmm) {
+ HeapFree(GetProcessHeap(), 0, wmm);
+ mciSetDriverData(dwDevID, 0);
+ return 1;
+ }
+ return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
+}
+
+/**************************************************************************
+ * MIDI_mciGetOpenDev [internal]
+ */
+static WINE_MCIMIDI* MIDI_mciGetOpenDev(UINT wDevID)
+{
+ WINE_MCIMIDI* wmm = (WINE_MCIMIDI*)mciGetDriverData(wDevID);
+
+ if (wmm == NULL || wmm->nUseCount == 0) {
+ WARN("Invalid wDevID=%u\n", wDevID);
+ return 0;
+ }
+ return wmm;
+}
+
+/**************************************************************************
+ * MIDI_mciReadByte [internal]
+ */
+static DWORD MIDI_mciReadByte(WINE_MCIMIDI* wmm, BYTE *lpbyt)
+{
+ DWORD ret = 0;
+
+ if (lpbyt == NULL ||
+ mmioRead(wmm->hFile, (HPSTR)lpbyt, sizeof(BYTE)) != (long)sizeof(BYTE)) {
+ WARN("Error reading wmm=%p\n", wmm);
+ ret = MCIERR_INVALID_FILE;
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ * MIDI_mciReadWord [internal]
+ */
+static DWORD MIDI_mciReadWord(WINE_MCIMIDI* wmm, LPWORD lpw)
+{
+ BYTE hibyte, lobyte;
+ DWORD ret = MCIERR_INVALID_FILE;
+
+ if (lpw != NULL &&
+ MIDI_mciReadByte(wmm, &hibyte) == 0 &&
+ MIDI_mciReadByte(wmm, &lobyte) == 0) {
+ *lpw = ((WORD)hibyte << 8) + lobyte;
+ ret = 0;
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * MIDI_mciReadLong [internal]
+ */
+static DWORD MIDI_mciReadLong(WINE_MCIMIDI* wmm, LPDWORD lpdw)
+{
+ WORD hiword, loword;
+ DWORD ret = MCIERR_INVALID_FILE;
+
+ if (lpdw != NULL &&
+ MIDI_mciReadWord(wmm, &hiword) == 0 &&
+ MIDI_mciReadWord(wmm, &loword) == 0) {
+ *lpdw = MAKELONG(loword, hiword);
+ ret = 0;
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * MIDI_mciReadVaryLen [internal]
+ */
+static WORD MIDI_mciReadVaryLen(WINE_MCIMIDI* wmm, LPDWORD lpdw)
+{
+ BYTE byte;
+ DWORD value = 0;
+ WORD ret = 0;
+
+ if (lpdw == NULL) {
+ ret = MCIERR_INVALID_FILE;
+ } else {
+ do {
+ if (MIDI_mciReadByte(wmm, &byte) != 0) {
+ return 0;
+ }
+ value = (value << 7) + (byte & 0x7F);
+ ret++;
+ } while (byte & 0x80);
+ *lpdw = value;
+ /*
+ TRACE("val=%08X\n", value);
+ */
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * MIDI_mciReadNextEvent [internal]
+ */
+static DWORD MIDI_mciReadNextEvent(WINE_MCIMIDI* wmm, MCI_MIDITRACK* mmt)
+{
+ BYTE b1, b2 = 0, b3;
+ WORD hw = 0;
+ DWORD evtPulse;
+ DWORD evtLength;
+ DWORD tmp;
+
+ if (mmioSeek(wmm->hFile, mmt->dwIndex, SEEK_SET) != mmt->dwIndex) {
+ WARN("Can't seek at %08X\n", mmt->dwIndex);
+ return MCIERR_INVALID_FILE;
+ }
+ evtLength = MIDI_mciReadVaryLen(wmm, &evtPulse) + 1; /* > 0 */
+ MIDI_mciReadByte(wmm, &b1);
+ switch (b1) {
+ case 0xF0:
+ case 0xF7:
+ evtLength += MIDI_mciReadVaryLen(wmm, &tmp);
+ evtLength += tmp;
+ break;
+ case 0xFF:
+ MIDI_mciReadByte(wmm, &b2); evtLength++;
+
+ evtLength += MIDI_mciReadVaryLen(wmm, &tmp);
+ if (evtLength >= 0x10000u) {
+ /* this limitation shouldn't be a problem */
+ WARN("Ouch !! Implementation limitation to 64k bytes for a MIDI event is overflowed\n");
+ hw = 0xFFFF;
+ } else {
+ hw = LOWORD(evtLength);
+ }
+ evtLength += tmp;
+ break;
+ default:
+ if (b1 & 0x80) { /* use running status ? */
+ mmt->wLastCommand = b1;
+ MIDI_mciReadByte(wmm, &b2); evtLength++;
+ } else {
+ b2 = b1;
+ b1 = mmt->wLastCommand;
+ }
+ switch ((b1 >> 4) & 0x07) {
+ case 0: case 1: case 2: case 3: case 6:
+ MIDI_mciReadByte(wmm, &b3); evtLength++;
+ hw = b3;
+ break;
+ case 4: case 5:
+ break;
+ case 7:
+ WARN("Strange indeed b1=0x%02x\n", b1);
+ }
+ break;
+ }
+ if (mmt->dwIndex + evtLength > mmt->dwLast)
+ return MCIERR_INTERNAL;
+
+ mmt->dwEventPulse += evtPulse;
+ mmt->dwEventData = (hw << 16) + (b2 << 8) + b1;
+ mmt->wEventLength = evtLength;
+
+ /*
+ TRACE("[%u] => pulse=%08x(%08x), data=%08x, length=%u\n",
+ mmt->wTrackNr, mmt->dwEventPulse, evtPulse,
+ mmt->dwEventData, mmt->wEventLength);
+ */
+ return 0;
+}
+
+/**************************************************************************
+ * MIDI_mciReadMTrk [internal]
+ */
+static DWORD MIDI_mciReadMTrk(WINE_MCIMIDI* wmm, MCI_MIDITRACK* mmt)
+{
+ DWORD toberead;
+ FOURCC fourcc;
+
+ if (mmioRead(wmm->hFile, (HPSTR)&fourcc, (long)sizeof(FOURCC)) !=
+ (long)sizeof(FOURCC)) {
+ return MCIERR_INVALID_FILE;
+ }
+
+ if (fourcc != mmioFOURCC('M', 'T', 'r', 'k')) {
+ WARN("Can't synchronize on 'MTrk' !\n");
+ return MCIERR_INVALID_FILE;
+ }
+
+ if (MIDI_mciReadLong(wmm, &toberead) != 0) {
+ return MCIERR_INVALID_FILE;
+ }
+ mmt->dwFirst = mmioSeek(wmm->hFile, 0, SEEK_CUR); /* >= 0 */
+ mmt->dwLast = mmt->dwFirst + toberead;
+
+ /* compute # of pulses in this track */
+ mmt->dwIndex = mmt->dwFirst;
+ mmt->dwEventPulse = 0;
+
+ while (MIDI_mciReadNextEvent(wmm, mmt) == 0 && LOWORD(mmt->dwEventData) != 0x2FFF) {
+ char buf[1024];
+ WORD len;
+
+ mmt->dwIndex += mmt->wEventLength;
+
+ switch (LOWORD(mmt->dwEventData)) {
+ case 0x02FF:
+ case 0x03FF:
+ /* position after meta data header */
+ mmioSeek(wmm->hFile, mmt->dwIndex + HIWORD(mmt->dwEventData), SEEK_SET);
+ len = mmt->wEventLength - HIWORD(mmt->dwEventData);
+
+ if (len >= sizeof(buf)) {
+ WARN("Buffer for text is too small (%u are needed)\n", len);
+ len = sizeof(buf) - 1;
+ }
+ if (mmioRead(wmm->hFile, buf, len) == len) {
+ buf[len] = 0; /* end string in case */
+ switch (HIBYTE(LOWORD(mmt->dwEventData))) {
+ case 0x02:
+ if (wmm->lpstrCopyright) {
+ WARN("Two copyright notices (%s|%s)\n", debugstr_w(wmm->lpstrCopyright), buf);
+ } else {
+ len = MultiByteToWideChar( CP_ACP, 0, buf, -1, NULL, 0 );
+ wmm->lpstrCopyright = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ len = MultiByteToWideChar( CP_ACP, 0, buf, -1, wmm->lpstrCopyright, len );
+ }
+ break;
+ case 0x03:
+ if (wmm->lpstrName) {
+ WARN("Two names (%s|%s)\n", debugstr_w(wmm->lpstrName), buf);
+ } else {
+ len = MultiByteToWideChar( CP_ACP, 0, buf, -1, NULL, 0 );
+ wmm->lpstrName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ len = MultiByteToWideChar( CP_ACP, 0, buf, -1, wmm->lpstrName, len );
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ mmt->dwLength = mmt->dwEventPulse;
+
+ TRACE("Track %u has %u bytes and %u pulses\n", mmt->wTrackNr, toberead, mmt->dwLength);
+
+ /* reset track data */
+ mmt->wStatus = 1; /* ok, playing */
+ mmt->dwIndex = mmt->dwFirst;
+ mmt->dwEventPulse = 0;
+
+ if (mmioSeek(wmm->hFile, 0, SEEK_CUR) != mmt->dwLast) {
+ WARN("Ouch, out of sync seek=%u track=%u\n",
+ mmioSeek(wmm->hFile, 0, SEEK_CUR), mmt->dwLast);
+ /* position at end of this track, to be ready to read next track */
+ mmioSeek(wmm->hFile, mmt->dwLast, SEEK_SET);
+ }
+
+ return 0;
+}
+
+/**************************************************************************
+ * MIDI_mciReadMThd [internal]
+ */
+static DWORD MIDI_mciReadMThd(WINE_MCIMIDI* wmm, DWORD dwOffset)
+{
+ DWORD toberead;
+ FOURCC fourcc;
+ WORD nt;
+
+ TRACE("(%p, %08X);\n", wmm, dwOffset);
+
+ if (mmioSeek(wmm->hFile, dwOffset, SEEK_SET) != dwOffset) {
+ WARN("Can't seek at %08X begin of 'MThd'\n", dwOffset);
+ return MCIERR_INVALID_FILE;
+ }
+ if (mmioRead(wmm->hFile, (HPSTR)&fourcc,
+ (long) sizeof(FOURCC)) != (long) sizeof(FOURCC))
+ return MCIERR_INVALID_FILE;
+
+ if (fourcc != mmioFOURCC('M', 'T', 'h', 'd')) {
+ WARN("Can't synchronize on 'MThd' !\n");
+ return MCIERR_INVALID_FILE;
+ }
+
+ if (MIDI_mciReadLong(wmm, &toberead) != 0 || toberead < 3 * sizeof(WORD))
+ return MCIERR_INVALID_FILE;
+
+ if (MIDI_mciReadWord(wmm, &wmm->wFormat) != 0 ||
+ MIDI_mciReadWord(wmm, &wmm->nTracks) != 0 ||
+ MIDI_mciReadWord(wmm, &wmm->nDivision) != 0) {
+ return MCIERR_INVALID_FILE;
+ }
+
+ TRACE("toberead=0x%08X, wFormat=0x%04X nTracks=0x%04X nDivision=0x%04X\n",
+ toberead, wmm->wFormat, wmm->nTracks, wmm->nDivision);
+
+ /* MS doc says that the MIDI MCI time format must be put by default to the format
+ * stored in the MIDI file...
+ */
+ if (wmm->nDivision > 0x8000) {
+ /* eric.pouech@lemel.fr 98/11
+ * I did not check this very code (pulses are expressed as SMPTE sub-frames).
+ * In about 40 MB of MIDI files I have, none was SMPTE based...
+ * I'm just wondering if this is widely used :-). So, if someone has one of
+ * these files, I'd like to know about it.
+ */
+ FIXME("Handling SMPTE time in MIDI files has not been tested\n"
+ "Please report to comp.emulators.ms-windows.wine with MIDI file !\n");
+
+ switch (HIBYTE(wmm->nDivision)) {
+ case 0xE8: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_24; break; /* -24 */
+ case 0xE7: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_25; break; /* -25 */
+ case 0xE3: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30DROP; break; /* -29 */ /* is the MCI constant correct ? */
+ case 0xE2: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30; break; /* -30 */
+ default:
+ WARN("Unsupported number of frames %d\n", -(char)HIBYTE(wmm->nDivision));
+ return MCIERR_INVALID_FILE;
+ }
+ switch (LOBYTE(wmm->nDivision)) {
+ case 4: /* MIDI Time Code */
+ case 8:
+ case 10:
+ case 80: /* SMPTE bit resolution */
+ case 100:
+ default:
+ WARN("Unsupported number of sub-frames %d\n", LOBYTE(wmm->nDivision));
+ return MCIERR_INVALID_FILE;
+ }
+ } else if (wmm->nDivision == 0) {
+ WARN("Number of division is 0, can't support that !!\n");
+ return MCIERR_INVALID_FILE;
+ } else {
+ wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
+ }
+
+ switch (wmm->wFormat) {
+ case 0:
+ if (wmm->nTracks != 1) {
+ WARN("Got type 0 file whose number of track is not 1. Setting it to 1\n");
+ wmm->nTracks = 1;
+ }
+ break;
+ case 1:
+ case 2:
+ break;
+ default:
+ WARN("Handling MIDI files which format = %d is not (yet) supported\n"
+ "Please report with MIDI file !\n", wmm->wFormat);
+ return MCIERR_INVALID_FILE;
+ }
+
+ if (wmm->nTracks & 0x8000) {
+ /* this shouldn't be a problem... */
+ WARN("Ouch !! Implementation limitation to 32k tracks per MIDI file is overflowed\n");
+ wmm->nTracks = 0x7FFF;
+ }
+
+ if ((wmm->tracks = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_MIDITRACK) * wmm->nTracks)) == NULL) {
+ return MCIERR_OUT_OF_MEMORY;
+ }
+
+ toberead -= 3 * sizeof(WORD);
+ if (toberead > 0) {
+ TRACE("Size of MThd > 6, skipping %d extra bytes\n", toberead);
+ mmioSeek(wmm->hFile, toberead, SEEK_CUR);
+ }
+
+ for (nt = 0; nt < wmm->nTracks; nt++) {
+ wmm->tracks[nt].wTrackNr = nt;
+ if (MIDI_mciReadMTrk(wmm, &wmm->tracks[nt]) != 0) {
+ WARN("Can't read 'MTrk' header\n");
+ return MCIERR_INVALID_FILE;
+ }
+ }
+
+ wmm->dwTempo = 500000;
+
+ return 0;
+}
+
+/**************************************************************************
+ * MIDI_ConvertPulseToMS [internal]
+ */
+static DWORD MIDI_ConvertPulseToMS(WINE_MCIMIDI* wmm, DWORD pulse)
+{
+ DWORD ret = 0;
+
+ /* FIXME: this function may return false values since the tempo (wmm->dwTempo)
+ * may change during file playing
+ */
+ if (wmm->nDivision == 0) {
+ FIXME("Shouldn't happen. wmm->nDivision = 0\n");
+ } else if (wmm->nDivision > 0x8000) { /* SMPTE, unchecked FIXME? */
+ int nf = -(char)HIBYTE(wmm->nDivision); /* number of frames */
+ int nsf = LOBYTE(wmm->nDivision); /* number of sub-frames */
+ ret = (pulse * 1000) / (nf * nsf);
+ } else {
+ ret = (DWORD)((double)pulse * ((double)wmm->dwTempo / 1000) /
+ (double)wmm->nDivision);
+ }
+
+ /*
+ TRACE("pulse=%u tempo=%u division=%u=0x%04x => ms=%u\n",
+ pulse, wmm->dwTempo, wmm->nDivision, wmm->nDivision, ret);
+ */
+
+ return ret;
+}
+
+#define TIME_MS_IN_ONE_HOUR (60*60*1000)
+#define TIME_MS_IN_ONE_MINUTE (60*1000)
+#define TIME_MS_IN_ONE_SECOND (1000)
+
+/**************************************************************************
+ * MIDI_ConvertTimeFormatToMS [internal]
+ */
+static DWORD MIDI_ConvertTimeFormatToMS(WINE_MCIMIDI* wmm, DWORD val)
+{
+ DWORD ret = 0;
+
+ switch (wmm->dwMciTimeFormat) {
+ case MCI_FORMAT_MILLISECONDS:
+ ret = val;
+ break;
+ case MCI_FORMAT_SMPTE_24:
+ ret =
+ (HIBYTE(HIWORD(val)) * 125) / 3 + LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND +
+ HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR;
+ break;
+ case MCI_FORMAT_SMPTE_25:
+ ret =
+ HIBYTE(HIWORD(val)) * 40 + LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND +
+ HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR;
+ break;
+ case MCI_FORMAT_SMPTE_30:
+ ret =
+ (HIBYTE(HIWORD(val)) * 100) / 3 + LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND +
+ HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR;
+ break;
+ default:
+ WARN("Bad time format %u!\n", wmm->dwMciTimeFormat);
+ }
+ /*
+ TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmm->dwMciTimeFormat, ret);
+ */
+ return ret;
+}
+
+/**************************************************************************
+ * MIDI_ConvertMSToTimeFormat [internal]
+ */
+static DWORD MIDI_ConvertMSToTimeFormat(WINE_MCIMIDI* wmm, DWORD _val)
+{
+ DWORD ret = 0, val = _val;
+ DWORD h, m, s, f;
+
+ switch (wmm->dwMciTimeFormat) {
+ case MCI_FORMAT_MILLISECONDS:
+ ret = val;
+ break;
+ case MCI_FORMAT_SMPTE_24:
+ case MCI_FORMAT_SMPTE_25:
+ case MCI_FORMAT_SMPTE_30:
+ h = val / TIME_MS_IN_ONE_HOUR;
+ m = (val -= h * TIME_MS_IN_ONE_HOUR) / TIME_MS_IN_ONE_MINUTE;
+ s = (val -= m * TIME_MS_IN_ONE_MINUTE) / TIME_MS_IN_ONE_SECOND;
+ switch (wmm->dwMciTimeFormat) {
+ case MCI_FORMAT_SMPTE_24:
+ /* one frame is 1000/24 val long, 1000/24 == 125/3 */
+ f = (val * 3) / 125; val -= (f * 125) / 3;
+ break;
+ case MCI_FORMAT_SMPTE_25:
+ /* one frame is 1000/25 ms long, 1000/25 == 40 */
+ f = val / 40; val -= f * 40;
+ break;
+ case MCI_FORMAT_SMPTE_30:
+ /* one frame is 1000/30 ms long, 1000/30 == 100/3 */
+ f = (val * 3) / 100; val -= (f * 100) / 3;
+ break;
+ default:
+ FIXME("There must be some bad bad programmer\n");
+ f = 0;
+ }
+ /* val contains the number of ms which cannot make a complete frame */
+ /* FIXME: is this correct ? programs seem to be happy with that */
+ ret = (f << 24) | (s << 16) | (m << 8) | (h << 0);
+ break;
+ default:
+ WARN("Bad time format %u!\n", wmm->dwMciTimeFormat);
+ }
+ /*
+ TRACE("val=%u [tf=%u] => ret=%u=0x%08x\n", _val, wmm->dwMciTimeFormat, ret, ret);
+ */
+ return ret;
+}
+
+/**************************************************************************
+ * MIDI_GetMThdLengthMS [internal]
+ */
+static DWORD MIDI_GetMThdLengthMS(WINE_MCIMIDI* wmm)
+{
+ WORD nt;
+ DWORD ret = 0;
+
+ for (nt = 0; nt < wmm->nTracks; nt++) {
+ if (wmm->wFormat == 2) {
+ ret += wmm->tracks[nt].dwLength;
+ } else if (wmm->tracks[nt].dwLength > ret) {
+ ret = wmm->tracks[nt].dwLength;
+ }
+ }
+ /* FIXME: this is wrong if there is a tempo change inside the file */
+ return MIDI_ConvertPulseToMS(wmm, ret);
+}
+
+/**************************************************************************
+ * MIDI_mciOpen [internal]
+ */
+static DWORD MIDI_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpParms)
+{
+ DWORD dwRet = 0;
+ DWORD dwDeviceID;
+ WINE_MCIMIDI* wmm = (WINE_MCIMIDI*)mciGetDriverData(wDevID);
+
+ TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID;
+ if (dwFlags & MCI_OPEN_SHAREABLE)
+ return MCIERR_HARDWARE;
+
+ if (wmm->nUseCount > 0) {
+ /* The driver is already opened on this channel
+ * MIDI sequencer cannot be shared
+ */
+ return MCIERR_DEVICE_OPEN;
+ }
+ wmm->nUseCount++;
+
+ wmm->hFile = 0;
+ wmm->hMidi = 0;
+ wmm->lpstrElementName = NULL;
+ dwDeviceID = lpParms->wDeviceID;
+
+ TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, dwDeviceID);
+ /* lpParms->wDeviceID = wDevID;*/
+
+ if (dwFlags & MCI_OPEN_ELEMENT) {
+ TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(lpParms->lpstrElementName));
+ if (lpParms->lpstrElementName && strlenW(lpParms->lpstrElementName) > 0) {
+ wmm->hFile = mmioOpenW((LPWSTR)lpParms->lpstrElementName, NULL,
+ MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
+ if (wmm->hFile == 0) {
+ WARN("Can't find file %s!\n", debugstr_w(lpParms->lpstrElementName));
+ wmm->nUseCount--;
+ return MCIERR_FILE_NOT_FOUND;
+ }
+ wmm->lpstrElementName = HeapAlloc(GetProcessHeap(), 0,
+ (strlenW(lpParms->lpstrElementName) + 1) * sizeof(WCHAR));
+ strcpyW(wmm->lpstrElementName, lpParms->lpstrElementName);
+ }
+ }
+ TRACE("hFile=%p\n", wmm->hFile);
+
+ wmm->lpstrCopyright = NULL;
+ wmm->lpstrName = NULL;
+
+ wmm->wNotifyDeviceID = dwDeviceID;
+ wmm->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
+ /* spec says it should be the default format from the MIDI file... */
+ wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
+
+ if (wmm->hFile != 0) {
+ MMCKINFO ckMainRIFF;
+ MMCKINFO mmckInfo;
+ DWORD dwOffset = 0;
+
+ if (mmioDescend(wmm->hFile, &ckMainRIFF, NULL, 0) != 0) {
+ dwRet = MCIERR_INVALID_FILE;
+ } else {
+ TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
+ (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
+
+ if (ckMainRIFF.ckid == FOURCC_RIFF && ckMainRIFF.fccType == mmioFOURCC('R', 'M', 'I', 'D')) {
+ mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
+ mmioSeek(wmm->hFile, ckMainRIFF.dwDataOffset + ((ckMainRIFF.cksize + 1) & ~1), SEEK_SET);
+ if (mmioDescend(wmm->hFile, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK) == 0) {
+ TRACE("... is a 'RMID' file\n");
+ dwOffset = mmckInfo.dwDataOffset;
+ } else {
+ dwRet = MCIERR_INVALID_FILE;
+ }
+ }
+ if (dwRet == 0 && MIDI_mciReadMThd(wmm, dwOffset) != 0) {
+ WARN("Can't read 'MThd' header\n");
+ dwRet = MCIERR_INVALID_FILE;
+ }
+ }
+ } else {
+ TRACE("hFile==0, setting #tracks to 0; is this correct ?\n");
+ wmm->nTracks = 0;
+ wmm->wFormat = 0;
+ wmm->nDivision = 1;
+ }
+ if (dwRet != 0) {
+ wmm->nUseCount--;
+ if (wmm->hFile != 0)
+ mmioClose(wmm->hFile, 0);
+ wmm->hFile = 0;
+ } else {
+ wmm->dwPositionMS = 0;
+ wmm->dwStatus = MCI_MODE_STOP;
+ }
+ return dwRet;
+}
+
+/**************************************************************************
+ * MIDI_mciStop [internal]
+ */
+static DWORD MIDI_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIMIDI* wmm = MIDI_mciGetOpenDev(wDevID);
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (wmm->dwStatus != MCI_MODE_STOP) {
+ int oldstat = wmm->dwStatus;
+
+ wmm->dwStatus = MCI_MODE_NOT_READY;
+ if (oldstat == MCI_MODE_PAUSE)
+ midiOutReset((HMIDIOUT)wmm->hMidi);
+
+ while (wmm->dwStatus != MCI_MODE_STOP)
+ Sleep(10);
+ }
+
+ /* sanity reset */
+ wmm->dwStatus = MCI_MODE_STOP;
+
+ TRACE("wmm->dwStatus=%d\n", wmm->dwStatus);
+
+ if (lpParms && (dwFlags & MCI_NOTIFY)) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * MIDI_mciClose [internal]
+ */
+static DWORD MIDI_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIMIDI* wmm = MIDI_mciGetOpenDev(wDevID);
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (wmm->dwStatus != MCI_MODE_STOP) {
+ MIDI_mciStop(wDevID, MCI_WAIT, lpParms);
+ }
+
+ wmm->nUseCount--;
+ if (wmm->nUseCount == 0) {
+ if (wmm->hFile != 0) {
+ mmioClose(wmm->hFile, 0);
+ wmm->hFile = 0;
+ TRACE("hFile closed !\n");
+ }
+ HeapFree(GetProcessHeap(), 0, wmm->tracks);
+ HeapFree(GetProcessHeap(), 0, wmm->lpstrElementName);
+ HeapFree(GetProcessHeap(), 0, wmm->lpstrCopyright);
+ HeapFree(GetProcessHeap(), 0, wmm->lpstrName);
+ } else {
+ TRACE("Shouldn't happen... nUseCount=%d\n", wmm->nUseCount);
+ return MCIERR_INTERNAL;
+ }
+
+ if (lpParms && (dwFlags & MCI_NOTIFY)) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * MIDI_mciFindNextEvent [internal]
+ */
+static MCI_MIDITRACK* MIDI_mciFindNextEvent(WINE_MCIMIDI* wmm, LPDWORD hiPulse)
+{
+ WORD cnt, nt;
+ MCI_MIDITRACK* mmt;
+
+ *hiPulse = 0xFFFFFFFFul;
+ cnt = 0xFFFFu;
+ for (nt = 0; nt < wmm->nTracks; nt++) {
+ mmt = &wmm->tracks[nt];
+
+ if (mmt->wStatus == 0)
+ continue;
+ if (mmt->dwEventPulse < *hiPulse) {
+ *hiPulse = mmt->dwEventPulse;
+ cnt = nt;
+ }
+ }
+ return (cnt == 0xFFFFu) ? 0 /* no more event on all tracks */
+ : &wmm->tracks[cnt];
+}
+
+/**************************************************************************
+ * MIDI_mciPlay [internal]
+ */
+static DWORD MIDI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
+{
+ DWORD dwStartMS, dwEndMS;
+ DWORD dwRet = 0;
+ WORD doPlay, nt;
+ MCI_MIDITRACK* mmt;
+ DWORD hiPulse;
+ WINE_MCIMIDI* wmm = MIDI_mciGetOpenDev(wDevID);
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (wmm->hFile == 0) {
+ WARN("Can't play: no file %s!\n", debugstr_w(wmm->lpstrElementName));
+ return MCIERR_FILE_NOT_FOUND;
+ }
+
+ if (wmm->dwStatus != MCI_MODE_STOP) {
+ if (wmm->dwStatus == MCI_MODE_PAUSE) {
+ /* FIXME: parameters (start/end) in lpParams may not be used */
+ return MIDI_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
+ }
+ WARN("Can't play: device is not stopped !\n");
+ return MCIERR_INTERNAL;
+ }
+
+ if (!(dwFlags & MCI_WAIT)) {
+ /** FIXME: I'm not 100% sure that wNotifyDeviceID is the right value in all cases ??? */
+ return MCI_SendCommandAsync(wmm->wNotifyDeviceID, MCI_PLAY, dwFlags, (DWORD_PTR)lpParms, sizeof(LPMCI_PLAY_PARMS));
+ }
+
+ if (lpParms && (dwFlags & MCI_FROM)) {
+ dwStartMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwFrom);
+ } else {
+ dwStartMS = wmm->dwPositionMS;
+ }
+
+ if (lpParms && (dwFlags & MCI_TO)) {
+ dwEndMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwTo);
+ } else {
+ dwEndMS = 0xFFFFFFFFul;
+ }
+
+ TRACE("Playing from %u to %u\n", dwStartMS, dwEndMS);
+
+ /* init tracks */
+ for (nt = 0; nt < wmm->nTracks; nt++) {
+ mmt = &wmm->tracks[nt];
+
+ mmt->wStatus = 1; /* ok, playing */
+ mmt->dwIndex = mmt->dwFirst;
+ if (wmm->wFormat == 2 && nt > 0) {
+ mmt->dwEventPulse = wmm->tracks[nt - 1].dwLength;
+ } else {
+ mmt->dwEventPulse = 0;
+ }
+ MIDI_mciReadNextEvent(wmm, mmt); /* FIXME == 0 */
+ }
+
+ dwRet = midiOutOpen((LPHMIDIOUT)&wmm->hMidi, MIDIMAPPER, 0L, 0L, CALLBACK_NULL);
+ /* dwRet = midiInOpen(&wmm->hMidi, MIDIMAPPER, 0L, 0L, CALLBACK_NULL);*/
+ if (dwRet != MMSYSERR_NOERROR) {
+ return dwRet;
+ }
+
+ wmm->dwPulse = 0;
+ wmm->dwTempo = 500000;
+ wmm->dwStatus = MCI_MODE_PLAY;
+ wmm->dwPositionMS = 0;
+ wmm->wStartedPlaying = FALSE;
+
+ while (wmm->dwStatus != MCI_MODE_STOP && wmm->dwStatus != MCI_MODE_NOT_READY) {
+ /* it seems that in case of multi-threading, gcc is optimizing just a little bit
+ * too much. Tell gcc not to optimize status value using volatile.
+ */
+ while (((volatile WINE_MCIMIDI*)wmm)->dwStatus == MCI_MODE_PAUSE);
+
+ doPlay = (wmm->dwPositionMS >= dwStartMS && wmm->dwPositionMS <= dwEndMS);
+
+ TRACE("wmm->dwStatus=%d, doPlay=%c\n", wmm->dwStatus, doPlay ? 'T' : 'F');
+
+ if ((mmt = MIDI_mciFindNextEvent(wmm, &hiPulse)) == NULL)
+ break; /* no more event on tracks */
+
+ /* if starting playing, then set StartTicks to the value it would have had
+ * if play had started at position 0
+ */
+ if (doPlay && !wmm->wStartedPlaying) {
+ wmm->dwStartTicks = GetTickCount() - MIDI_ConvertPulseToMS(wmm, wmm->dwPulse);
+ wmm->wStartedPlaying = TRUE;
+ TRACE("Setting dwStartTicks to %u\n", wmm->dwStartTicks);
+ }
+
+ if (hiPulse > wmm->dwPulse) {
+ wmm->dwPositionMS += MIDI_ConvertPulseToMS(wmm, hiPulse - wmm->dwPulse);
+ if (doPlay) {
+ DWORD togo = wmm->dwStartTicks + wmm->dwPositionMS;
+ DWORD tc = GetTickCount();
+
+ TRACE("Pulses hi=0x%08x <> cur=0x%08x\n", hiPulse, wmm->dwPulse);
+ TRACE("Wait until %u => %u ms\n",
+ tc - wmm->dwStartTicks, togo - wmm->dwStartTicks);
+ if (tc < togo)
+ Sleep(togo - tc);
+ }
+ wmm->dwPulse = hiPulse;
+ }
+
+ switch (LOBYTE(LOWORD(mmt->dwEventData))) {
+ case 0xF0:
+ case 0xF7: /* sysex events */
+ {
+ FIXME("Not handling SysEx events (yet)\n");
+ }
+ break;
+ case 0xFF:
+ /* position after meta data header */
+ mmioSeek(wmm->hFile, mmt->dwIndex + HIWORD(mmt->dwEventData), SEEK_SET);
+ switch (HIBYTE(LOWORD(mmt->dwEventData))) {
+ case 0x00: /* 16-bit sequence number */
+ if (TRACE_ON(mcimidi)) {
+ WORD twd;
+
+ MIDI_mciReadWord(wmm, &twd); /* == 0 */
+ TRACE("Got sequence number %u\n", twd);
+ }
+ break;
+ case 0x01: /* any text */
+ case 0x02: /* Copyright Message text */
+ case 0x03: /* Sequence/Track Name text */
+ case 0x04: /* Instrument Name text */
+ case 0x05: /* Lyric text */
+ case 0x06: /* Marker text */
+ case 0x07: /* Cue-point text */
+ if (TRACE_ON(mcimidi)) {
+ char buf[1024];
+ WORD len = mmt->wEventLength - HIWORD(mmt->dwEventData);
+ static const char* const info[8] = {"", "Text", "Copyright", "Seq/Trk name",
+ "Instrument", "Lyric", "Marker", "Cue-point"};
+ WORD idx = HIBYTE(LOWORD(mmt->dwEventData));
+
+ if (len >= sizeof(buf)) {
+ WARN("Buffer for text is too small (%u are needed)\n", len);
+ len = sizeof(buf) - 1;
+ }
+ if (mmioRead(wmm->hFile, buf, len) == len) {
+ buf[len] = 0; /* end string in case */
+ TRACE("%s => \"%s\"\n", (idx < 8 ) ? info[idx] : "", buf);
+ } else {
+ WARN("Couldn't read data for %s\n", (idx < 8) ? info[idx] : "");
+ }
+ }
+ break;
+ case 0x20:
+ /* MIDI channel (cc) */
+ if (FIXME_ON(mcimidi)) {
+ BYTE bt;
+
+ MIDI_mciReadByte(wmm, &bt); /* == 0 */
+ FIXME("NIY: MIDI channel=%u, track=%u\n", bt, mmt->wTrackNr);
+ }
+ break;
+ case 0x21:
+ /* MIDI port (pp) */
+ if (FIXME_ON(mcimidi)) {
+ BYTE bt;
+
+ MIDI_mciReadByte(wmm, &bt); /* == 0 */
+ FIXME("NIY: MIDI port=%u, track=%u\n", bt, mmt->wTrackNr);
+ }
+ break;
+ case 0x2F: /* end of track */
+ mmt->wStatus = 0;
+ break;
+ case 0x51:/* set tempo */
+ /* Tempo is expressed in -seconds per midi quarter note
+ * for format 1 MIDI files, this can only be present on track #0
+ */
+ if (mmt->wTrackNr != 0 && wmm->wFormat == 1) {
+ WARN("For format #1 MIDI files, tempo can only be changed on track #0 (%u)\n", mmt->wTrackNr);
+ } else {
+ BYTE tbt;
+ DWORD value = 0;
+
+ MIDI_mciReadByte(wmm, &tbt); value = ((DWORD)tbt) << 16;
+ MIDI_mciReadByte(wmm, &tbt); value |= ((DWORD)tbt) << 8;
+ MIDI_mciReadByte(wmm, &tbt); value |= ((DWORD)tbt) << 0;
+ TRACE("Setting tempo to %d (BPM=%d)\n", wmm->dwTempo, (value) ? (60000000 / value) : 0);
+ wmm->dwTempo = value;
+ }
+ break;
+ case 0x54: /* (hour) (min) (second) (frame) (fractional-frame) - SMPTE track start */
+ if (mmt->wTrackNr != 0 && wmm->wFormat == 1) {
+ WARN("For format #1 MIDI files, SMPTE track start can only be expressed on track #0 (%u)\n", mmt->wTrackNr);
+ } if (mmt->dwEventPulse != 0) {
+ WARN("SMPTE track start can only be expressed at start of track (%u)\n", mmt->dwEventPulse);
+ } else {
+ BYTE h, m, s, f, ff;
+
+ MIDI_mciReadByte(wmm, &h);
+ MIDI_mciReadByte(wmm, &m);
+ MIDI_mciReadByte(wmm, &s);
+ MIDI_mciReadByte(wmm, &f);
+ MIDI_mciReadByte(wmm, &ff);
+ FIXME("NIY: SMPTE track start %u:%u:%u %u.%u\n", h, m, s, f, ff);
+ }
+ break;
+ case 0x58: /* file rhythm */
+ if (TRACE_ON(mcimidi)) {
+ BYTE num, den, cpmc, _32npqn;
+
+ MIDI_mciReadByte(wmm, &num);
+ MIDI_mciReadByte(wmm, &den); /* to notate e.g. 6/8 */
+ MIDI_mciReadByte(wmm, &cpmc); /* number of MIDI clocks per metronome click */
+ MIDI_mciReadByte(wmm, &_32npqn); /* number of notated 32nd notes per MIDI quarter note */
+
+ TRACE("%u/%u, clock per metronome click=%u, 32nd notes by 1/4 note=%u\n", num, 1 << den, cpmc, _32npqn);
+ }
+ break;
+ case 0x59: /* key signature */
+ if (TRACE_ON(mcimidi)) {
+ BYTE sf, mm;
+
+ MIDI_mciReadByte(wmm, &sf);
+ MIDI_mciReadByte(wmm, &mm);
+
+ if (sf >= 0x80) TRACE("%d flats\n", -(char)sf);
+ else if (sf > 0) TRACE("%d sharps\n", (char)sf);
+ else TRACE("Key of C\n");
+ TRACE("Mode: %s\n", (mm == 0) ? "major" : "minor");
+ }
+ break;
+ default:
+ WARN("Unknown MIDI meta event %02x. Skipping...\n", HIBYTE(LOWORD(mmt->dwEventData)));
+ break;
+ }
+ break;
+ default:
+ if (doPlay) {
+ dwRet = midiOutShortMsg((HMIDIOUT)wmm->hMidi, mmt->dwEventData);
+ } else {
+ switch (LOBYTE(LOWORD(mmt->dwEventData)) & 0xF0) {
+ case MIDI_NOTEON:
+ case MIDI_NOTEOFF:
+ dwRet = 0;
+ break;
+ default:
+ dwRet = midiOutShortMsg((HMIDIOUT)wmm->hMidi, mmt->dwEventData);
+ }
+ }
+ }
+ mmt->dwIndex += mmt->wEventLength;
+ if (mmt->dwIndex < mmt->dwFirst || mmt->dwIndex >= mmt->dwLast) {
+ mmt->wStatus = 0;
+ }
+ if (mmt->wStatus) {
+ MIDI_mciReadNextEvent(wmm, mmt);
+ }
+ }
+
+ midiOutReset((HMIDIOUT)wmm->hMidi);
+
+ dwRet = midiOutClose((HMIDIOUT)wmm->hMidi);
+ /* to restart playing at beginning when it's over */
+ wmm->dwPositionMS = 0;
+
+ if (lpParms && (dwFlags & MCI_NOTIFY)) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+
+ wmm->dwStatus = MCI_MODE_STOP;
+ return dwRet;
+}
+
+/**************************************************************************
+ * MIDI_mciRecord [internal]
+ */
+static DWORD MIDI_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
+{
+ int start, end;
+ MIDIHDR midiHdr;
+ WINE_MCIMIDI* wmm = MIDI_mciGetOpenDev(wDevID);
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmm == 0) return MCIERR_INVALID_DEVICE_ID;
+
+ if (wmm->hFile == 0) {
+ WARN("Can't find file=%s!\n", debugstr_w(wmm->lpstrElementName));
+ return MCIERR_FILE_NOT_FOUND;
+ }
+ start = 1; end = 99999;
+ if (lpParms && (dwFlags & MCI_FROM)) {
+ start = lpParms->dwFrom;
+ TRACE("MCI_FROM=%d\n", start);
+ }
+ if (lpParms && (dwFlags & MCI_TO)) {
+ end = lpParms->dwTo;
+ TRACE("MCI_TO=%d\n", end);
+ }
+ midiHdr.lpData = HeapAlloc(GetProcessHeap(), 0, 1200);
+ if (!midiHdr.lpData)
+ return MCIERR_OUT_OF_MEMORY;
+ midiHdr.dwBufferLength = 1024;
+ midiHdr.dwUser = 0L;
+ midiHdr.dwFlags = 0L;
+ midiInPrepareHeader((HMIDIIN)wmm->hMidi, &midiHdr, sizeof(MIDIHDR));
+ TRACE("After MIDM_PREPARE\n");
+ wmm->dwStatus = MCI_MODE_RECORD;
+ /* FIXME: there is no buffer added */
+ while (wmm->dwStatus != MCI_MODE_STOP) {
+ TRACE("wmm->dwStatus=%p %d\n",
+ &wmm->dwStatus, wmm->dwStatus);
+ midiHdr.dwBytesRecorded = 0;
+ midiInStart((HMIDIIN)wmm->hMidi);
+ TRACE("midiInStart => dwBytesRecorded=%u\n", midiHdr.dwBytesRecorded);
+ if (midiHdr.dwBytesRecorded == 0) break;
+ }
+ TRACE("Before MIDM_UNPREPARE\n");
+ midiInUnprepareHeader((HMIDIIN)wmm->hMidi, &midiHdr, sizeof(MIDIHDR));
+ TRACE("After MIDM_UNPREPARE\n");
+ HeapFree(GetProcessHeap(), 0, midiHdr.lpData);
+ midiHdr.lpData = NULL;
+ wmm->dwStatus = MCI_MODE_STOP;
+ if (lpParms && (dwFlags & MCI_NOTIFY)) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * MIDI_mciPause [internal]
+ */
+static DWORD MIDI_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIMIDI* wmm = MIDI_mciGetOpenDev(wDevID);
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (wmm->dwStatus == MCI_MODE_PLAY) {
+ /* stop all notes */
+ unsigned chn;
+ for (chn = 0; chn < 16; chn++)
+ midiOutShortMsg((HMIDIOUT)(wmm->hMidi), 0x78B0 | chn);
+ wmm->dwStatus = MCI_MODE_PAUSE;
+ }
+ if (lpParms && (dwFlags & MCI_NOTIFY)) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+
+ return 0;
+}
+
+/**************************************************************************
+ * MIDI_mciResume [internal]
+ */
+static DWORD MIDI_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIMIDI* wmm = MIDI_mciGetOpenDev(wDevID);
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (wmm->dwStatus == MCI_MODE_PAUSE) {
+ wmm->wStartedPlaying = FALSE;
+ wmm->dwStatus = MCI_MODE_PLAY;
+ }
+ if (lpParms && (dwFlags & MCI_NOTIFY)) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * MIDI_mciSet [internal]
+ */
+static DWORD MIDI_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
+{
+ WINE_MCIMIDI* wmm = MIDI_mciGetOpenDev(wDevID);
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (dwFlags & MCI_SET_TIME_FORMAT) {
+ switch (lpParms->dwTimeFormat) {
+ case MCI_FORMAT_MILLISECONDS:
+ TRACE("MCI_FORMAT_MILLISECONDS !\n");
+ wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
+ break;
+ case MCI_FORMAT_SMPTE_24:
+ TRACE("MCI_FORMAT_SMPTE_24 !\n");
+ wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_24;
+ break;
+ case MCI_FORMAT_SMPTE_25:
+ TRACE("MCI_FORMAT_SMPTE_25 !\n");
+ wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_25;
+ break;
+ case MCI_FORMAT_SMPTE_30:
+ TRACE("MCI_FORMAT_SMPTE_30 !\n");
+ wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30;
+ break;
+ default:
+ WARN("Bad time format %u!\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;
+ }
+
+ switch (lpParms->dwAudio)
+ {
+ case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
+ case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
+ case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
+ default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
+ }
+ }
+
+ if (dwFlags & MCI_SEQ_SET_MASTER)
+ TRACE("MCI_SEQ_SET_MASTER !\n");
+ if (dwFlags & MCI_SEQ_SET_SLAVE)
+ TRACE("MCI_SEQ_SET_SLAVE !\n");
+ if (dwFlags & MCI_SEQ_SET_OFFSET)
+ TRACE("MCI_SEQ_SET_OFFSET !\n");
+ if (dwFlags & MCI_SEQ_SET_PORT)
+ TRACE("MCI_SEQ_SET_PORT !\n");
+ if (dwFlags & MCI_SEQ_SET_TEMPO)
+ TRACE("MCI_SEQ_SET_TEMPO !\n");
+ return 0;
+}
+
+/**************************************************************************
+ * MIDI_mciStatus [internal]
+ */
+static DWORD MIDI_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
+{
+ WINE_MCIMIDI* wmm = MIDI_mciGetOpenDev(wDevID);
+ DWORD ret = 0;
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (dwFlags & MCI_STATUS_ITEM) {
+ switch (lpParms->dwItem) {
+ case MCI_STATUS_CURRENT_TRACK:
+ /* FIXME in Format 2 */
+ lpParms->dwReturn = 1;
+ TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
+ break;
+ case MCI_STATUS_LENGTH:
+ if ((dwFlags & MCI_TRACK) && wmm->wFormat == 2) {
+ if (lpParms->dwTrack >= wmm->nTracks)
+ return MCIERR_BAD_INTEGER;
+ /* FIXME: this is wrong if there is a tempo change inside the file */
+ lpParms->dwReturn = MIDI_ConvertPulseToMS(wmm, wmm->tracks[lpParms->dwTrack].dwLength);
+ } else {
+ lpParms->dwReturn = MIDI_GetMThdLengthMS(wmm);
+ }
+ lpParms->dwReturn = MIDI_ConvertMSToTimeFormat(wmm, lpParms->dwReturn);
+ TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
+ break;
+ case MCI_STATUS_MODE:
+ TRACE("MCI_STATUS_MODE => %u\n", wmm->dwStatus);
+ lpParms->dwReturn = MAKEMCIRESOURCE(wmm->dwStatus, wmm->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:
+ lpParms->dwReturn = (wmm->wFormat == 2) ? wmm->nTracks : 1;
+ TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
+ break;
+ case MCI_STATUS_POSITION:
+ /* FIXME: do I need to use MCI_TRACK ? */
+ lpParms->dwReturn = MIDI_ConvertMSToTimeFormat(wmm,
+ (dwFlags & MCI_STATUS_START) ? 0 : wmm->dwPositionMS);
+ TRACE("MCI_STATUS_POSITION %s => %lu\n",
+ (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
+ break;
+ case MCI_STATUS_READY:
+ lpParms->dwReturn = (wmm->dwStatus == MCI_MODE_NOT_READY) ?
+ MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ TRACE("MCI_STATUS_READY = %u\n", LOWORD(lpParms->dwReturn));
+ break;
+ case MCI_STATUS_TIME_FORMAT:
+ lpParms->dwReturn = MAKEMCIRESOURCE(wmm->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmm->dwMciTimeFormat);
+ TRACE("MCI_STATUS_TIME_FORMAT => %u\n", LOWORD(lpParms->dwReturn));
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_SEQ_STATUS_DIVTYPE:
+ TRACE("MCI_SEQ_STATUS_DIVTYPE !\n");
+ if (wmm->nDivision > 0x8000) {
+ switch (wmm->nDivision) {
+ case 0xE8: lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_24; break; /* -24 */
+ case 0xE7: lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_25; break; /* -25 */
+ case 0xE3: lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_30DROP; break; /* -29 */ /* is the MCI constant correct ? */
+ case 0xE2: lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_30; break; /* -30 */
+ default: FIXME("There is a bad bad programmer\n");
+ }
+ } else {
+ lpParms->dwReturn = MCI_SEQ_DIV_PPQN;
+ }
+ lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn,lpParms->dwReturn);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_SEQ_STATUS_MASTER:
+ TRACE("MCI_SEQ_STATUS_MASTER !\n");
+ lpParms->dwReturn = 0;
+ break;
+ case MCI_SEQ_STATUS_SLAVE:
+ TRACE("MCI_SEQ_STATUS_SLAVE !\n");
+ lpParms->dwReturn = 0;
+ break;
+ case MCI_SEQ_STATUS_OFFSET:
+ TRACE("MCI_SEQ_STATUS_OFFSET !\n");
+ lpParms->dwReturn = 0;
+ break;
+ case MCI_SEQ_STATUS_PORT:
+ TRACE("MCI_SEQ_STATUS_PORT (%u)!\n", wmm->wDevID);
+ lpParms->dwReturn = MIDI_MAPPER;
+ break;
+ case MCI_SEQ_STATUS_TEMPO:
+ TRACE("MCI_SEQ_STATUS_TEMPO !\n");
+ lpParms->dwReturn = wmm->dwTempo;
+ break;
+ default:
+ FIXME("Unknown command %08X !\n", lpParms->dwItem);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ } else {
+ WARN("No Status-Item!\n");
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ if (dwFlags & MCI_NOTIFY) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * MIDI_mciGetDevCaps [internal]
+ */
+static DWORD MIDI_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
+ LPMCI_GETDEVCAPS_PARMS lpParms)
+{
+ WINE_MCIMIDI* wmm = MIDI_mciGetOpenDev(wDevID);
+ DWORD ret;
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (dwFlags & MCI_GETDEVCAPS_ITEM) {
+ switch (lpParms->dwItem) {
+ case MCI_GETDEVCAPS_DEVICE_TYPE:
+ TRACE("MCI_GETDEVCAPS_DEVICE_TYPE !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_SEQUENCER, MCI_DEVTYPE_SEQUENCER);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_HAS_AUDIO:
+ TRACE("MCI_GETDEVCAPS_HAS_AUDIO !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_HAS_VIDEO:
+ TRACE("MCI_GETDEVCAPS_HAS_VIDEO !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_USES_FILES:
+ TRACE("MCI_GETDEVCAPS_USES_FILES !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_COMPOUND_DEVICE:
+ TRACE("MCI_GETDEVCAPS_COMPOUND_DEVICE !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_CAN_EJECT:
+ TRACE("MCI_GETDEVCAPS_CAN_EJECT !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_CAN_PLAY:
+ TRACE("MCI_GETDEVCAPS_CAN_PLAY !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_CAN_RECORD:
+ TRACE("MCI_GETDEVCAPS_CAN_RECORD !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ case MCI_GETDEVCAPS_CAN_SAVE:
+ TRACE("MCI_GETDEVCAPS_CAN_SAVE !\n");
+ lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
+ ret = MCI_RESOURCE_RETURNED;
+ break;
+ default:
+ FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ } else {
+ WARN("No GetDevCaps-Item !\n");
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * MIDI_mciInfo [internal]
+ */
+static DWORD MIDI_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
+{
+ LPCWSTR str = 0;
+ WINE_MCIMIDI* wmm = MIDI_mciGetOpenDev(wDevID);
+ DWORD ret = 0;
+ static const WCHAR wszMidiSeq[] = {'W','i','n','e','\'','s',' ','M','I','D','I',' ','s','e','q','u','e','n','c','e','r',0};
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL || lpParms->lpstrReturn == NULL)
+ return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
+
+ switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
+ case MCI_INFO_PRODUCT: str = wszMidiSeq; break;
+ case MCI_INFO_FILE: str = wmm->lpstrElementName; break;
+ case MCI_INFO_COPYRIGHT: str = wmm->lpstrCopyright; break;
+ case MCI_INFO_NAME: str = wmm->lpstrName; break;
+ default:
+ WARN("Don't know this info command (%u)\n", dwFlags);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ if (str) {
+ if (lpParms->dwRetSize <= strlenW(str)) {
+ lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
+ ret = MCIERR_PARAM_OVERFLOW;
+ } else {
+ strcpyW(lpParms->lpstrReturn, str);
+ }
+ } else {
+ *lpParms->lpstrReturn = 0;
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * MIDI_mciSeek [internal]
+ */
+static DWORD MIDI_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
+{
+ DWORD ret = 0;
+ WINE_MCIMIDI* wmm = MIDI_mciGetOpenDev(wDevID);
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) {
+ ret = MCIERR_NULL_PARAMETER_BLOCK;
+ } else if (wmm == NULL) {
+ ret = MCIERR_INVALID_DEVICE_ID;
+ } else {
+ MIDI_mciStop(wDevID, MCI_WAIT, 0);
+
+ if (dwFlags & MCI_SEEK_TO_START) {
+ wmm->dwPositionMS = 0;
+ } else if (dwFlags & MCI_SEEK_TO_END) {
+ wmm->dwPositionMS = 0xFFFFFFFF; /* FIXME */
+ } else if (dwFlags & MCI_TO) {
+ wmm->dwPositionMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwTo);
+ } else {
+ WARN("dwFlag doesn't tell where to seek to...\n");
+ return MCIERR_MISSING_PARAMETER;
+ }
+
+ TRACE("Seeking to position=%u ms\n", wmm->dwPositionMS);
+
+ if (dwFlags & MCI_NOTIFY) {
+ TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ }
+ return ret;
+}
+
+/*======================================================================*
+ * MIDI entry points *
+ *======================================================================*/
+
+/**************************************************************************
+ * DriverProc (MCISEQ.@)
+ */
+LRESULT CALLBACK MCIMIDI_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
+ LPARAM dwParam1, LPARAM dwParam2)
+{
+ switch (wMsg) {
+ case DRV_LOAD: return 1;
+ case DRV_FREE: return 1;
+ case DRV_ENABLE: return 1;
+ case DRV_DISABLE: return 1;
+ case DRV_QUERYCONFIGURE: return 1;
+ case DRV_CONFIGURE: MessageBoxA(0, "Sample Midi Driver !", "OSS Driver", MB_OK); return 1;
+ case DRV_INSTALL: return DRVCNF_RESTART;
+ case DRV_REMOVE: return DRVCNF_RESTART;
+ case DRV_OPEN: return MIDI_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
+ case DRV_CLOSE: return MIDI_drvClose(dwDevID);
+ }
+
+ if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
+
+ switch (wMsg) {
+ case MCI_OPEN_DRIVER: return MIDI_mciOpen (dwDevID, dwParam1, (LPMCI_OPEN_PARMSW) dwParam2);
+ case MCI_CLOSE_DRIVER: return MIDI_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_PLAY: return MIDI_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
+ case MCI_RECORD: return MIDI_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
+ case MCI_STOP: return MIDI_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_SET: return MIDI_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
+ case MCI_PAUSE: return MIDI_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_RESUME: return MIDI_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_STATUS: return MIDI_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
+ case MCI_GETDEVCAPS: return MIDI_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
+ case MCI_INFO: return MIDI_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
+ case MCI_SEEK: return MIDI_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
+ /* commands that should be supported */
+ 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_STEP:
+ case MCI_SPIN:
+ case MCI_ESCAPE:
+ case MCI_COPY:
+ case MCI_CUT:
+ case MCI_DELETE:
+ case MCI_PASTE:
+ WARN("Unsupported command [%u]\n", wMsg);
+ break;
+ /* commands that should report an error */
+ case MCI_WINDOW:
+ TRACE("Unsupported command [%u]\n", wMsg);
+ break;
+ case MCI_OPEN:
+ case MCI_CLOSE:
+ FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n");
+ break;
+ default:
+ TRACE("Sending msg [%u] to default driver proc\n", wMsg);
+ return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+ }
+ return MCIERR_UNRECOGNIZED_COMMAND;
+}
diff --git a/reactos/dll/win32/mciseq/mciseq.rbuild b/reactos/dll/win32/mciseq/mciseq.rbuild
new file mode 100644
index 00000000000..661c7cf740a
--- /dev/null
+++ b/reactos/dll/win32/mciseq/mciseq.rbuild
@@ -0,0 +1,12 @@
+
+
+ .
+ include/reactos/wine
+
+ mcimidi.c
+ wine
+ winmm
+ user32
+ kernel32
+ ntdll
+
diff --git a/reactos/dll/win32/mciseq/mciseq.spec b/reactos/dll/win32/mciseq/mciseq.spec
new file mode 100644
index 00000000000..0f83118793b
--- /dev/null
+++ b/reactos/dll/win32/mciseq/mciseq.spec
@@ -0,0 +1 @@
+@ stdcall -private DriverProc(long long long long long) MCIMIDI_DriverProc
diff --git a/reactos/dll/win32/mciwave/mciwave.c b/reactos/dll/win32/mciwave/mciwave.c
new file mode 100644
index 00000000000..c533e857797
--- /dev/null
+++ b/reactos/dll/win32/mciwave/mciwave.c
@@ -0,0 +1,1669 @@
+/*
+ * Sample Wine Driver for MCI wave forms
+ *
+ * Copyright 1994 Martin Ayotte
+ * 1999,2000,2005 Eric Pouech
+ * 2000 Francois Jacques
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "mmddk.h"
+#include "wownt32.h"
+#include "digitalv.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+WINE_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_PARMSW openParms;
+ WAVEFORMATEX wfxRef;
+ 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 dwPosition; /* position in bytes in chunk */
+ HANDLE hEvent; /* for synchronization */
+ LONG dwEventCount; /* for synchronization */
+ 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_PTR dwParam1;
+ DWORD_PTR dwParam2;
+};
+
+/**************************************************************************
+ * 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);
+ HeapFree(GetProcessHeap(), 0, sca);
+ ExitThread(ret);
+ WARN("Should not happen ? what's wrong\n");
+ /* should not go after this point */
+ return ret;
+}
+
+/**************************************************************************
+ * MCI_SendCommandAsync [internal]
+ */
+static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD_PTR dwParam1,
+ DWORD_PTR dwParam2, UINT size)
+{
+ HANDLE handle;
+ struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
+
+ if (sca == 0)
+ return MCIERR_OUT_OF_MEMORY;
+
+ sca->wDevID = wDevID;
+ sca->wMsg = wMsg;
+ sca->dwParam1 = dwParam1;
+
+ if (size && dwParam2) {
+ sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
+ /* 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;
+ }
+
+ if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
+ WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
+ return MCI_SCAStarter(&sca);
+ }
+ SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
+ CloseHandle(handle);
+ return 0;
+}
+
+/*======================================================================*
+ * MCI WAVE implementation *
+ *======================================================================*/
+
+static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
+
+/**************************************************************************
+ * MCIWAVE_drvOpen [internal]
+ */
+static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
+{
+ WINE_MCIWAVE* wmw;
+
+ if (modp == NULL) return 0xFFFFFFFF;
+
+ wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
+
+ if (!wmw)
+ return 0;
+
+ wmw->wDevID = modp->wDeviceID;
+ mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
+ modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
+ modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
+
+ wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
+ wmw->wfxRef.nChannels = 1; /* MONO */
+ wmw->wfxRef.nSamplesPerSec = 11025;
+ wmw->wfxRef.nAvgBytesPerSec = 11025;
+ wmw->wfxRef.nBlockAlign = 1;
+ wmw->wfxRef.wBitsPerSample = 8;
+ wmw->wfxRef.cbSize = 0; /* don't care */
+
+ return modp->wDeviceID;
+}
+
+/**************************************************************************
+ * MCIWAVE_drvClose [internal]
+ */
+static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
+{
+ WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
+
+ if (wmw) {
+ HeapFree(GetProcessHeap(), 0, wmw);
+ mciSetDriverData(dwDevID, 0);
+ return 1;
+ }
+ return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
+}
+
+/**************************************************************************
+ * WAVE_mciGetOpenDev [internal]
+ */
+static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID 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 = MulDiv(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 ? wmw->lpWaveFormat->wBitsPerSample : 1);
+ break;
+ default:
+ WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
+ }
+ TRACE("val=%u=0x%08x [tf=%u] => ret=%u\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 %u!\n", wmw->dwMciTimeFormat);
+ }
+ TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
+ return ret;
+}
+
+/**************************************************************************
+ * WAVE_mciReadFmt [internal]
+ */
+static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const 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=%08X\n",
+ (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
+
+ wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
+ if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
+ r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
+ if (r < sizeof(WAVEFORMAT))
+ return MCIERR_INVALID_FILE;
+
+ TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
+ TRACE("nChannels=%d\n", wmw->lpWaveFormat->nChannels);
+ TRACE("nSamplesPerSec=%d\n", wmw->lpWaveFormat->nSamplesPerSec);
+ TRACE("nAvgBytesPerSec=%d\n", wmw->lpWaveFormat->nAvgBytesPerSec);
+ TRACE("nBlockAlign=%d\n", wmw->lpWaveFormat->nBlockAlign);
+ TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
+ 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=%08X\n",
+ (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
+ TRACE("nChannels=%d nSamplesPerSec=%d\n",
+ wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
+
+ return 0;
+}
+
+/**************************************************************************
+ * WAVE_mciDefaultFmt [internal]
+ */
+static DWORD WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
+{
+ wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, sizeof(*wmw->lpWaveFormat));
+ if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
+
+ wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
+ wmw->lpWaveFormat->nChannels = 1;
+ wmw->lpWaveFormat->nSamplesPerSec = 44000;
+ wmw->lpWaveFormat->nAvgBytesPerSec = 44000;
+ wmw->lpWaveFormat->nBlockAlign = 1;
+ wmw->lpWaveFormat->wBitsPerSample = 8;
+
+ return 0;
+}
+
+/**************************************************************************
+ * WAVE_mciCreateRIFFSkeleton [internal]
+ */
+static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
+{
+ MMCKINFO ckWaveFormat;
+ LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
+ LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
+
+ lpckRIFF->ckid = FOURCC_RIFF;
+ lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
+ lpckRIFF->cksize = 0;
+
+ if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
+ goto err;
+
+ ckWaveFormat.fccType = 0;
+ ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
+ ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
+
+ if (!wmw->lpWaveFormat)
+ {
+ wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
+ if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
+ *wmw->lpWaveFormat = wmw->wfxRef;
+ }
+
+ /* we can only record PCM files... there is no way in the MCI API to specify
+ * the necessary data to initialize the extra bytes of the WAVEFORMATEX
+ * structure
+ */
+ if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
+ goto err;
+
+ if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
+ goto err;
+
+ if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
+ goto err;
+
+ if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
+ goto err;
+
+ lpckWaveData->cksize = 0;
+ lpckWaveData->fccType = 0;
+ lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
+
+ /* create data chunk */
+ if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
+ goto err;
+
+ return 0;
+
+err:
+ HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
+ wmw->lpWaveFormat = NULL;
+ return MCIERR_INVALID_FILE;
+}
+
+static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
+{
+ WCHAR szTmpPath[MAX_PATH];
+ WCHAR szPrefix[4];
+ DWORD dwRet = MMSYSERR_NOERROR;
+
+ szPrefix[0] = 'M';
+ szPrefix[1] = 'C';
+ szPrefix[2] = 'I';
+ szPrefix[3] = '\0';
+
+ if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
+ WARN("can't retrieve temp path!\n");
+ return MCIERR_FILE_NOT_FOUND;
+ }
+
+ *pszTmpFileName = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ MAX_PATH * sizeof(WCHAR));
+ if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
+ WARN("can't retrieve temp file name!\n");
+ HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
+ return MCIERR_FILE_NOT_FOUND;
+ }
+
+ TRACE("%s!\n", debugstr_w(*pszTmpFileName));
+
+ if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
+
+ *hFile = mmioOpenW(*pszTmpFileName, NULL,
+ MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
+
+ if (*hFile == 0) {
+ WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
+ /* temporary file could not be created. clean filename. */
+ HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
+ dwRet = MCIERR_FILE_NOT_FOUND;
+ }
+ }
+ return dwRet;
+}
+
+static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename)
+{
+ LRESULT dwRet = MMSYSERR_NOERROR;
+ WCHAR* fn;
+
+ fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
+ if (!fn) return MCIERR_OUT_OF_MEMORY;
+ strcpyW(fn, filename);
+ HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
+ wmw->openParms.lpstrElementName = fn;
+
+ if (strlenW(filename) > 0) {
+ /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
+ TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
+
+ wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
+ MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
+
+ if (wmw->hFile == 0) {
+ WARN("can't find file=%s!\n", debugstr_w(filename));
+ 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=%08X\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);
+ }
+ }
+ }
+ }
+ return dwRet;
+}
+
+/**************************************************************************
+ * WAVE_mciOpen [internal]
+ */
+static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
+{
+ DWORD dwRet = 0;
+ WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
+
+ TRACE("(%04X, %08X, %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;
+ wmw->hFile = 0;
+ memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
+ /* will be set by WAVE_mciOpenFile */
+ wmw->openParms.lpstrElementName = NULL;
+
+ 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 {
+ dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
+ }
+ }
+
+ TRACE("hFile=%p\n", wmw->hFile);
+
+ if (dwRet == 0 && !wmw->lpWaveFormat)
+ dwRet = WAVE_mciDefaultFmt(wmw);
+
+ if (dwRet == 0) {
+ if (wmw->lpWaveFormat) {
+ switch (wmw->lpWaveFormat->wFormatTag) {
+ case WAVE_FORMAT_PCM:
+ if (wmw->lpWaveFormat->nAvgBytesPerSec !=
+ wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
+ WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
+ wmw->lpWaveFormat->nAvgBytesPerSec,
+ wmw->lpWaveFormat->nSamplesPerSec *
+ wmw->lpWaveFormat->nBlockAlign);
+ wmw->lpWaveFormat->nAvgBytesPerSec =
+ wmw->lpWaveFormat->nSamplesPerSec *
+ wmw->lpWaveFormat->nBlockAlign;
+ }
+ break;
+ }
+ }
+ wmw->dwPosition = 0;
+
+ wmw->dwStatus = MCI_MODE_STOP;
+ } else {
+ wmw->nUseCount--;
+ if (wmw->hFile != 0)
+ mmioClose(wmw->hFile, 0);
+ wmw->hFile = 0;
+ }
+ return dwRet;
+}
+
+/**************************************************************************
+ * WAVE_mciCue [internal]
+ */
+static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM 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;
+
+ /* FIXME */
+ /* 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(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ DWORD dwRet = 0;
+ WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+
+ TRACE("(%u, %08X, %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_32(LOWORD(lpParms->dwCallback)),
+ wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+
+ return dwRet;
+}
+
+/**************************************************************************
+ * WAVE_mciClose [internal]
+ */
+static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ DWORD dwRet = 0;
+ WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+
+ TRACE("(%u, %08X, %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;
+ }
+ }
+
+ HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
+ wmw->lpWaveFormat = NULL;
+ HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
+ wmw->openParms.lpstrElementName = NULL;
+
+ if ((dwFlags & MCI_NOTIFY) && lpParms) {
+ mciDriverNotify(HWND_32(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_PTR dwInstance,
+ LPARAM dwParam1, LPARAM 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);
+ }
+}
+
+/******************************************************************
+ * WAVE_mciPlayWaitDone
+ *
+ *
+ */
+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(MCIDEVICEID 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, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ wmw->fInput = FALSE;
+
+ if (wmw->hFile == 0) {
+ WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
+ 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_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
+ }
+
+ end = 0xFFFFFFFF;
+ if (lpParms && (dwFlags & MCI_FROM)) {
+ wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
+ }
+ if (lpParms && (dwFlags & MCI_TO)) {
+ end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
+ }
+
+ TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
+
+ if (end <= wmw->dwPosition)
+ return TRUE;
+
+
+#define WAVE_ALIGN_ON_BLOCK(wmw,v) \
+((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
+
+ wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
+ wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
+
+ if (dwRet == 0) {
+ if (wmw->lpWaveFormat) {
+ switch (wmw->lpWaveFormat->wFormatTag) {
+ case WAVE_FORMAT_PCM:
+ if (wmw->lpWaveFormat->nAvgBytesPerSec !=
+ wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
+ WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
+ wmw->lpWaveFormat->nAvgBytesPerSec,
+ wmw->lpWaveFormat->nSamplesPerSec *
+ wmw->lpWaveFormat->nBlockAlign);
+ wmw->lpWaveFormat->nAvgBytesPerSec =
+ wmw->lpWaveFormat->nSamplesPerSec *
+ wmw->lpWaveFormat->nBlockAlign;
+ }
+ break;
+ }
+ }
+ } else {
+ TRACE("can't retrieve wave format %d\n", dwRet);
+ goto cleanUp;
+ }
+
+
+ /* go back to beginning of chunk plus the requested position */
+ /* FIXME: I'm not sure this is correct, notably because some data linked to
+ * the decompression state machine will not be correctly initialized.
+ * 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((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
+ (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
+
+ if (dwRet != 0) {
+ TRACE("Can't open low level audio device %d\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 = CreateEventW(NULL, FALSE, FALSE, NULL);
+ wmw->dwEventCount = 1L; /* for first buffer */
+
+ TRACE("Playing (normalized) from byte=%u for %u 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=%d count=%d\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=%u dwBytesRecorded=%u\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=%u\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_32(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_PTR dwInstance,
+ LPARAM dwParam1, LPARAM dwParam2)
+{
+ WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
+ LPWAVEHDR lpWaveHdr;
+ 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;
+ if (count > 0)
+ wmw->dwPosition += count;
+ /* else error reporting ?? */
+ if (wmw->dwStatus == MCI_MODE_RECORD)
+ {
+ /* Only queue up another buffer if we are recording. We could receive this
+ message also when waveInReset() is called, since it notifies on all wave
+ buffers that are outstanding. Queueing up more sometimes causes waveInClose
+ to fail. */
+ waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
+ TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
+ }
+
+ SetEvent(wmw->hEvent);
+ break;
+ default:
+ ERR("Unknown uMsg=%d\n", uMsg);
+ }
+}
+
+/******************************************************************
+ * bWAVE_mciRecordWaitDone
+ *
+ */
+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(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
+{
+ DWORD end;
+ DWORD dwRet = MMSYSERR_NOERROR;
+ LONG bufsize;
+ LPWAVEHDR waveHdr = NULL;
+ WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+
+ TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+
+ /* FIXME : since there is no way to determine in which mode the device is
+ * open (recording/playback) automatically switch from a mode to another
+ */
+ wmw->fInput = TRUE;
+
+ if (wmw->dwStatus == MCI_MODE_PAUSE) {
+ /* 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_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
+ }
+
+ /* FIXME: we only re-create the RIFF structure from an existing file (if any)
+ * we don't modify the wave part of an existing file (ie. we always erase an
+ * existing content, we don't overwrite)
+ */
+ HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
+ dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
+ if (dwRet != 0) return dwRet;
+
+ /* new RIFF file */
+ dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
+ if (dwRet != 0) return dwRet; /* FIXME: we leak resources */
+
+ end = 0xFFFFFFFF;
+ if (lpParms && (dwFlags & MCI_FROM)) {
+ wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
+ }
+
+ if (lpParms && (dwFlags & MCI_TO)) {
+ end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
+ }
+
+ TRACE("Recording from byte=%u to byte=%u\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 the beginning of the chunk plus the requested position */
+ /* FIXME: I'm not sure this is correct, notably because some data linked to
+ * the decompression state machine will not be correctly initialized.
+ * 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((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
+ (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
+
+ if (dwRet != MMSYSERR_NOERROR) {
+ TRACE("Can't open low level audio device %d\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 = CreateEventW(NULL, FALSE, FALSE, NULL);
+ wmw->dwEventCount = 1L; /* for first buffer */
+
+ TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
+
+ dwRet = waveInStart(wmw->hWave);
+
+ while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
+ WAVE_mciRecordWaitDone(wmw);
+ }
+
+ /* needed so that the callback above won't add again the buffers returned by the reset */
+ wmw->dwStatus = MCI_MODE_STOP;
+
+ 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_32(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(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ DWORD dwRet;
+ WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+
+ TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (wmw->dwStatus == MCI_MODE_PLAY) {
+ wmw->dwStatus = MCI_MODE_PAUSE;
+ }
+
+ 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(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+ WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+ DWORD dwRet = 0;
+
+ TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (wmw->dwStatus == MCI_MODE_PAUSE) {
+ wmw->dwStatus = MCI_MODE_PLAY;
+ }
+
+ 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(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
+{
+ DWORD ret = 0;
+ WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+
+ TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) {
+ ret = MCIERR_NULL_PARAMETER_BLOCK;
+ } else if (wmw == NULL) {
+ ret = MCIERR_INVALID_DEVICE_ID;
+ } else {
+ WAVE_mciStop(wDevID, MCI_WAIT, 0);
+
+ if (dwFlags & MCI_SEEK_TO_START) {
+ wmw->dwPosition = 0;
+ } else if (dwFlags & MCI_SEEK_TO_END) {
+ wmw->dwPosition = wmw->ckWaveData.cksize;
+ } else if (dwFlags & MCI_TO) {
+ wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
+ } else {
+ WARN("dwFlag doesn't tell where to seek to...\n");
+ return MCIERR_MISSING_PARAMETER;
+ }
+
+ TRACE("Seeking to position=%u bytes\n", wmw->dwPosition);
+
+ if (dwFlags & MCI_NOTIFY) {
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * WAVE_mciSet [internal]
+ */
+static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
+{
+ WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+
+ TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (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 %u!\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;
+ }
+
+ switch (lpParms->dwAudio)
+ {
+ case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
+ case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
+ case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
+ default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
+ }
+ }
+ 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) {
+ wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
+ TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
+ }
+ if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
+ wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
+ TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
+ }
+ if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
+ wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
+ TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
+ }
+ if (dwFlags & MCI_WAVE_SET_CHANNELS) {
+ wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
+ TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
+ }
+ if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
+ wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
+ TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
+ }
+ if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
+ wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
+ TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * WAVE_mciSave [internal]
+ */
+static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
+{
+ WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+ DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
+ WPARAM wparam = MCI_NOTIFY_FAILURE;
+
+ TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (dwFlags & MCI_WAIT)
+ {
+ FIXME("MCI_WAIT not implemented\n");
+ }
+ WAVE_mciStop(wDevID, 0, NULL);
+
+ ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
+ ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
+
+ ret = mmioClose(wmw->hFile, 0);
+ wmw->hFile = 0;
+
+ /*
+ If the destination file already exists, it has to be overwritten. (Behaviour
+ verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
+ my applications. We are making use of mmioRename, which WILL NOT overwrite
+ the destination file (which is what Windows does, also verified in Win2K)
+ So, lets delete the destination file before calling mmioRename. If the
+ destination file DOESN'T exist, the delete will fail silently. Let's also be
+ careful not to lose our previous error code.
+ */
+ tmpRet = GetLastError();
+ DeleteFileW (lpParms->lpfilename);
+ SetLastError(tmpRet);
+
+ if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
+ ret = MMSYSERR_NOERROR;
+ }
+
+ if (dwFlags & MCI_NOTIFY) {
+ if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
+
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmw->openParms.wDeviceID, wparam);
+ }
+
+ if (ret == MMSYSERR_NOERROR)
+ ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
+
+ return ret;
+}
+
+/**************************************************************************
+ * WAVE_mciStatus [internal]
+ */
+static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
+{
+ WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+ DWORD ret = 0;
+
+ TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (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, MCI_FORMAT_RETURN_BASE + 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 %08X !\n", lpParms->dwItem);
+ return MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ }
+ if (dwFlags & MCI_NOTIFY) {
+ mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+ wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * WAVE_mciGetDevCaps [internal]
+ */
+static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
+ LPMCI_GETDEVCAPS_PARMS lpParms)
+{
+ WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+ DWORD ret = 0;
+
+ TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
+
+ if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
+ if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
+
+ if (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 (%08x) !\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(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
+{
+ DWORD ret = 0;
+ LPCWSTR str = 0;
+ WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+
+ TRACE("(%u, %08X, %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 {
+ static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
+ static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
+ static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
+
+ TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
+
+ switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
+ case MCI_INFO_PRODUCT: str = wszAudio; break;
+ case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
+ case MCI_WAVE_INPUT: str = wszWaveIn; break;
+ case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
+ default:
+ WARN("Don't know this info command (%u)\n", dwFlags);
+ ret = MCIERR_UNRECOGNIZED_COMMAND;
+ }
+ }
+ if (str) {
+ if (strlenW(str) + 1 > lpParms->dwRetSize) {
+ ret = MCIERR_PARAM_OVERFLOW;
+ } else {
+ lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
+ }
+ } else {
+ lpParms->lpstrReturn[0] = 0;
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ * DriverProc (MCIWAVE.@)
+ */
+LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
+ LPARAM dwParam1, LPARAM dwParam2)
+{
+ TRACE("(%08lX, %p, %08X, %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((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)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;
+ }
+
+ if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
+
+ switch (wMsg) {
+ case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
+ case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
+ case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
+ case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
+ case MCI_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_PARMSW) dwParam2);
+ case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
+ case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) 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 [%u]\n", wMsg);
+ break;
+ case MCI_WINDOW:
+ TRACE("Unsupported command [%u]\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 [%u]\n", wMsg);
+ return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+ }
+ return MCIERR_UNRECOGNIZED_COMMAND;
+}
diff --git a/reactos/dll/win32/mciwave/mciwave.rbuild b/reactos/dll/win32/mciwave/mciwave.rbuild
new file mode 100644
index 00000000000..cbbd3f4cc80
--- /dev/null
+++ b/reactos/dll/win32/mciwave/mciwave.rbuild
@@ -0,0 +1,12 @@
+
+
+ .
+ include/reactos/wine
+
+ mciwave.c
+ wine
+ winmm
+ user32
+ kernel32
+ ntdll
+
diff --git a/reactos/dll/win32/mciwave/mciwave.spec b/reactos/dll/win32/mciwave/mciwave.spec
new file mode 100644
index 00000000000..4b63f32db42
--- /dev/null
+++ b/reactos/dll/win32/mciwave/mciwave.spec
@@ -0,0 +1 @@
+@ stdcall -private DriverProc(long long long long long) MCIWAVE_DriverProc
diff --git a/reactos/dll/win32/win32.rbuild b/reactos/dll/win32/win32.rbuild
index 5bc9c676d91..57aa44a5a89 100644
--- a/reactos/dll/win32/win32.rbuild
+++ b/reactos/dll/win32/win32.rbuild
@@ -175,6 +175,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reactos/media/doc/README.WINE b/reactos/media/doc/README.WINE
index 3111864ec5e..128c43048a8 100644
--- a/reactos/media/doc/README.WINE
+++ b/reactos/media/doc/README.WINE
@@ -63,6 +63,11 @@ reactos/dll/win32/localspl # Autosync
reactos/dll/win32/localui # Autosync
reactos/dll/win32/lz32 # Autosync
reactos/dll/win32/mapi32 # Autosync
+reactos/dll/win32/mciavi32 # Autosync
+reactos/dll/win32/mcicda # Autosync
+reactos/dll/win32/mciqtz32 # Autosync
+reactos/dll/win32/mciseq # Autosync
+reactos/dll/win32/mciwave # Autosync
reactos/dll/win32/mlang # Autosync
reactos/dll/win32/mpr # Autosync
reactos/dll/win32/msacm32 # Out of sync