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