[WINMM_WINETEST] Sync with Wine Staging 1.7.37. CORE-9246

svn path=/trunk/; revision=66839
This commit is contained in:
Amine Khaldi 2015-03-20 18:22:14 +00:00
parent 0e83d43b36
commit c5f5d970f2
11 changed files with 3307 additions and 489 deletions

View file

@ -1,7 +1,10 @@
list(APPEND SOURCE
capture.c
joystick.c
mci.c
mcicda.c
midi.c
mixer.c
mmio.c
timer.c
@ -11,7 +14,7 @@ list(APPEND SOURCE
add_executable(winmm_winetest ${SOURCE})
target_link_libraries(winmm_winetest dxguid)
set_module_type(winmm_winetest win32cui)
add_importlibs(winmm_winetest winmm user32 msvcrt kernel32)
add_importlibs(winmm_winetest winmm user32 advapi32 msvcrt kernel32)
add_cd_file(TARGET winmm_winetest DESTINATION reactos/bin FOR all)
if(NOT MSVC)

View file

@ -27,6 +27,7 @@
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "mmddk.h"
#include "mmsystem.h"
#define NOBITMAP
#include "mmreg.h"
@ -42,9 +43,9 @@ static const char * wave_in_error(MMRESULT error)
static char long_msg[1100];
MMRESULT rc;
rc = waveInGetErrorText(error, msg, sizeof(msg));
rc = waveInGetErrorTextA(error, msg, sizeof(msg));
if (rc != MMSYSERR_NOERROR)
sprintf(long_msg, "waveInGetErrorText(%x) failed with error %x", error, rc);
sprintf(long_msg, "waveInGetErrorTextA(%x) failed with error %x", error, rc);
else
sprintf(long_msg, "%s(%s)", mmsys_error(error), msg);
return long_msg;
@ -54,14 +55,9 @@ static void check_position(int device, HWAVEIN win, DWORD bytes,
LPWAVEFORMATEX pwfx )
{
MMTIME mmtime;
DWORD samples;
double duration;
MMRESULT rc;
DWORD returned;
samples=bytes/(pwfx->wBitsPerSample/8*pwfx->nChannels);
duration=((double)samples)/pwfx->nSamplesPerSec;
mmtime.wType = TIME_BYTES;
rc=waveInGetPosition(win, &mmtime, sizeof(mmtime));
ok(rc==MMSYSERR_NOERROR,
@ -131,22 +127,19 @@ static void check_position(int device, HWAVEIN win, DWORD bytes,
dev_name(device));
}
static void wave_in_test_deviceIn(int device, LPWAVEFORMATEX pwfx, DWORD format, DWORD flags, LPWAVEINCAPS pcaps)
static void wave_in_test_deviceIn(int device, WAVEFORMATEX *pwfx, DWORD format, DWORD flags,
WAVEINCAPSA *pcaps)
{
HWAVEIN win;
HANDLE hevent;
HANDLE hevent = CreateEventW(NULL, FALSE, FALSE, NULL);
WAVEHDR frag;
MMRESULT rc;
DWORD res;
MMTIME mmt;
WORD nChannels = pwfx->nChannels;
WORD wBitsPerSample = pwfx->wBitsPerSample;
DWORD nSamplesPerSec = pwfx->nSamplesPerSec;
hevent=CreateEvent(NULL,FALSE,FALSE,NULL);
ok(hevent!=NULL,"CreateEvent(): error=%d\n",GetLastError());
if (hevent==NULL)
return;
win=NULL;
rc=waveInOpen(&win,device,pwfx,(DWORD_PTR)hevent,0,CALLBACK_EVENT|flags);
/* Note: Win9x doesn't know WAVE_FORMAT_DIRECT */
@ -159,13 +152,13 @@ static void wave_in_test_deviceIn(int device, LPWAVEFORMATEX pwfx, DWORD format,
(!(flags & WAVE_FORMAT_DIRECT) || (flags & WAVE_MAPPED)) &&
!(pcaps->dwFormats & format)) ||
(rc==MMSYSERR_INVALFLAG && (flags & WAVE_FORMAT_DIRECT)),
"waveInOpen(%s): format=%dx%2dx%d flags=%lx(%s) rc=%s\n",
"waveInOpen(%s): format=%dx%2dx%d flags=%x(%s) rc=%s\n",
dev_name(device),pwfx->nSamplesPerSec,pwfx->wBitsPerSample,
pwfx->nChannels,CALLBACK_EVENT|flags,
wave_open_flags(CALLBACK_EVENT|flags),wave_in_error(rc));
if ((rc==WAVERR_BADFORMAT || rc==MMSYSERR_NOTSUPPORTED) &&
(flags & WAVE_FORMAT_DIRECT) && (pcaps->dwFormats & format))
trace(" Reason: The device lists this format as supported in it's "
trace(" Reason: The device lists this format as supported in its "
"capabilities but opening it failed.\n");
if ((rc==WAVERR_BADFORMAT || rc==MMSYSERR_NOTSUPPORTED) &&
!(pcaps->dwFormats & format))
@ -229,6 +222,13 @@ static void wave_in_test_deviceIn(int device, LPWAVEFORMATEX pwfx, DWORD format,
"frag.dwBytesRecorded=%d, should=%d\n",
frag.dwBytesRecorded,pwfx->nAvgBytesPerSec);
mmt.wType = TIME_BYTES;
rc=waveInGetPosition(win, &mmt, sizeof(mmt));
ok(rc==MMSYSERR_NOERROR,"waveInGetPosition(%s): rc=%s\n",
dev_name(device),wave_in_error(rc));
ok(mmt.wType == TIME_BYTES, "doesn't support TIME_BYTES: %u\n", mmt.wType);
ok(mmt.u.cb == frag.dwBytesRecorded, "Got wrong position: %u\n", mmt.u.cb);
/* stop playing on error */
if (res!=WAIT_OBJECT_0) {
rc=waveInStop(win);
@ -261,7 +261,7 @@ static void wave_in_test_deviceIn(int device, LPWAVEFORMATEX pwfx, DWORD format,
rc==MMSYSERR_ALLOCATED ||
((rc==WAVERR_BADFORMAT || rc==MMSYSERR_NOTSUPPORTED) &&
!(pcaps->dwFormats & format)),
"waveOutOpen(%s) format=%dx%2dx%d flags=%lx(%s) rc=%s\n",
"waveOutOpen(%s) format=%dx%2dx%d flags=%x(%s) rc=%s\n",
dev_name(device),pwfx->nSamplesPerSec,pwfx->wBitsPerSample,
pwfx->nChannels,CALLBACK_EVENT|flags,
wave_open_flags(CALLBACK_EVENT),wave_out_error(rc));
@ -299,7 +299,7 @@ static void wave_in_test_device(UINT_PTR device)
{
WAVEINCAPSA capsA;
WAVEINCAPSW capsW;
WAVEFORMATEX format,oformat;
WAVEFORMATEX format;
WAVEFORMATEXTENSIBLE wfex;
HWAVEIN win;
MMRESULT rc;
@ -444,29 +444,6 @@ static void wave_in_test_device(UINT_PTR device)
VirtualFree(twoPages, 2 * dwPageSize, MEM_RELEASE);
}
/* Testing invalid format: 2 MHz sample rate */
format.wFormatTag=WAVE_FORMAT_PCM;
format.nChannels=2;
format.wBitsPerSample=16;
format.nSamplesPerSec=2000000;
format.nBlockAlign=format.nChannels*format.wBitsPerSample/8;
format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign;
format.cbSize=0;
oformat=format;
rc=waveInOpen(&win,device,&format,0,0,CALLBACK_NULL|WAVE_FORMAT_DIRECT);
ok(rc==WAVERR_BADFORMAT || rc==MMSYSERR_INVALFLAG ||
rc==MMSYSERR_INVALPARAM,
"waveInOpen(%s): opening the device with 2 MHz sample rate should fail: "
" rc=%s\n",dev_name(device),wave_in_error(rc));
if (rc==MMSYSERR_NOERROR) {
trace(" got %dx%2dx%d for %dx%2dx%d\n",
format.nSamplesPerSec, format.wBitsPerSample,
format.nChannels,
oformat.nSamplesPerSec, oformat.wBitsPerSample,
oformat.nChannels);
waveInClose(win);
}
/* test non PCM formats */
format.wFormatTag=WAVE_FORMAT_MULAW;
format.nChannels=1;
@ -477,7 +454,8 @@ static void wave_in_test_device(UINT_PTR device)
format.cbSize=0;
rc=waveInOpen(&win,device,&format,0,0,CALLBACK_NULL|WAVE_FORMAT_DIRECT);
ok(rc==MMSYSERR_NOERROR || rc==WAVERR_BADFORMAT ||
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM,
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM ||
rc==MMSYSERR_ALLOCATED,
"waveInOpen(%s): returned: %s\n",dev_name(device),wave_in_error(rc));
if (rc==MMSYSERR_NOERROR) {
waveInClose(win);
@ -495,7 +473,8 @@ static void wave_in_test_device(UINT_PTR device)
format.cbSize=0;
rc=waveInOpen(&win,device,&format,0,0,CALLBACK_NULL|WAVE_FORMAT_DIRECT);
ok(rc==MMSYSERR_NOERROR || rc==WAVERR_BADFORMAT ||
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM,
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM ||
rc==MMSYSERR_ALLOCATED,
"waveInOpen(%s): returned: %s\n",dev_name(device),wave_in_error(rc));
if (rc==MMSYSERR_NOERROR) {
waveInClose(win);
@ -519,7 +498,8 @@ static void wave_in_test_device(UINT_PTR device)
rc=waveInOpen(&win,device,&wfex.Format,0,0,
CALLBACK_NULL|WAVE_FORMAT_DIRECT);
ok(rc==MMSYSERR_NOERROR || rc==WAVERR_BADFORMAT ||
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM,
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM ||
rc==MMSYSERR_ALLOCATED,
"waveInOpen(%s): returned: %s\n",dev_name(device),wave_in_error(rc));
if (rc==MMSYSERR_NOERROR) {
waveInClose(win);
@ -543,7 +523,8 @@ static void wave_in_test_device(UINT_PTR device)
rc=waveInOpen(&win,device,&wfex.Format,0,0,
CALLBACK_NULL|WAVE_FORMAT_DIRECT);
ok(rc==MMSYSERR_NOERROR || rc==WAVERR_BADFORMAT ||
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM,
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM ||
rc==MMSYSERR_ALLOCATED,
"waveInOpen(%s): returned: %s\n",dev_name(device),wave_in_error(rc));
if (rc==MMSYSERR_NOERROR) {
waveInClose(win);
@ -567,7 +548,8 @@ static void wave_in_test_device(UINT_PTR device)
rc=waveInOpen(&win,device,&wfex.Format,0,0,
CALLBACK_NULL|WAVE_FORMAT_DIRECT);
ok(rc==MMSYSERR_NOERROR || rc==WAVERR_BADFORMAT ||
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM,
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM ||
rc==MMSYSERR_ALLOCATED,
"waveInOpen(%s): returned: %s\n",dev_name(device),wave_in_error(rc));
if (rc==MMSYSERR_NOERROR) {
waveInClose(win);
@ -594,7 +576,8 @@ static void wave_in_test_device(UINT_PTR device)
rc=waveInOpen(&win,device,&wfex.Format,0,0,
CALLBACK_NULL|WAVE_FORMAT_DIRECT);
ok(rc==MMSYSERR_NOERROR || rc==WAVERR_BADFORMAT ||
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM,
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM ||
rc==MMSYSERR_ALLOCATED,
"waveInOpen(%s): returned: %s\n",dev_name(device),wave_in_error(rc));
if (rc==MMSYSERR_NOERROR) {
waveInClose(win);
@ -619,7 +602,8 @@ static void wave_in_test_device(UINT_PTR device)
rc=waveInOpen(&win,device,&wfex.Format,0,0,
CALLBACK_NULL|WAVE_FORMAT_DIRECT);
ok(rc==MMSYSERR_NOERROR || rc==WAVERR_BADFORMAT ||
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM,
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM ||
rc==MMSYSERR_ALLOCATED,
"waveInOpen(%s): returned: %s\n",dev_name(device),wave_in_error(rc));
if (rc==MMSYSERR_NOERROR) {
waveInClose(win);
@ -643,7 +627,8 @@ static void wave_in_test_device(UINT_PTR device)
rc=waveInOpen(&win,device,&wfex.Format,0,0,
CALLBACK_NULL|WAVE_FORMAT_DIRECT);
ok(rc==MMSYSERR_NOERROR || rc==WAVERR_BADFORMAT ||
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM,
rc==MMSYSERR_INVALFLAG || rc==MMSYSERR_INVALPARAM ||
rc==MMSYSERR_ALLOCATED,
"waveInOpen(%s): returned: %s\n",dev_name(device),wave_in_error(rc));
if (rc==MMSYSERR_NOERROR) {
waveInClose(win);
@ -660,25 +645,30 @@ static void wave_in_tests(void)
WAVEFORMATEX format;
HWAVEIN win;
MMRESULT rc;
DWORD preferred, status;
UINT ndev,d;
ndev=waveInGetNumDevs();
trace("found %d WaveIn devices\n",ndev);
rc = waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
(DWORD_PTR)&preferred, (DWORD_PTR)&status);
ok((ndev == 0 && (rc == MMSYSERR_NODRIVER || rc == MMSYSERR_BADDEVICEID)) ||
rc == MMSYSERR_NOTSUPPORTED ||
rc == MMSYSERR_NOERROR, "waveInMessage(DRVM_MAPPER_PREFERRED_GET) failed: %u\n", rc);
if(rc != MMSYSERR_NOTSUPPORTED)
ok((ndev == 0 && (preferred == -1 || broken(preferred != -1))) ||
preferred < ndev, "Got invalid preferred device: 0x%x\n", preferred);
rc=waveInGetDevCapsA(ndev+1,&capsA,sizeof(capsA));
ok(rc==MMSYSERR_BADDEVICEID,
"waveInGetDevCapsA(%s): MMSYSERR_BADDEVICEID expected, got %s\n",
dev_name(ndev+1),wave_in_error(rc));
rc=waveInGetDevCapsA(WAVE_MAPPER,&capsA,sizeof(capsA));
if (ndev>0)
ok(rc==MMSYSERR_NOERROR || rc==MMSYSERR_NODRIVER,
"waveInGetDevCapsA(%s): MMSYSERR_NOERROR or MMSYSERR_NODRIVER "
"expected, got %s\n",dev_name(WAVE_MAPPER),wave_in_error(rc));
else
ok(rc==MMSYSERR_BADDEVICEID || rc==MMSYSERR_NODRIVER,
"waveInGetDevCapsA(%s): MMSYSERR_BADDEVICEID or MMSYSERR_NODRIVER "
"expected, got %s\n",dev_name(WAVE_MAPPER),wave_in_error(rc));
ok(rc==MMSYSERR_NOERROR || rc==MMSYSERR_NODRIVER || (!ndev && (rc==MMSYSERR_BADDEVICEID)),
"waveInGetDevCapsA(%s): got %s\n",dev_name(WAVE_MAPPER),wave_in_error(rc));
rc=waveInGetDevCapsW(ndev+1,&capsW,sizeof(capsW));
ok(rc==MMSYSERR_BADDEVICEID || rc==MMSYSERR_NOTSUPPORTED,
@ -686,18 +676,9 @@ static void wave_in_tests(void)
"expected, got %s\n",dev_name(ndev+1),wave_in_error(rc));
rc=waveInGetDevCapsW(WAVE_MAPPER,&capsW,sizeof(capsW));
if (ndev>0)
ok(rc==MMSYSERR_NOERROR || rc==MMSYSERR_NODRIVER ||
rc==MMSYSERR_NOTSUPPORTED,
"waveInGetDevCapsW(%s): MMSYSERR_NOERROR or MMSYSERR_NODRIVER or "
"MMSYSERR_NOTSUPPORTED expected, got %s\n",
dev_name(ndev+1),wave_in_error(rc));
else
ok(rc==MMSYSERR_BADDEVICEID || rc==MMSYSERR_NODRIVER ||
rc==MMSYSERR_NOTSUPPORTED,
"waveInGetDevCapsW(%s): MMSYSERR_BADDEVICEID or MMSYSERR_NODRIVER or"
"MMSYSERR_NOTSUPPORTED expected, got %s\n",
dev_name(ndev+1),wave_in_error(rc));
ok(rc==MMSYSERR_NOERROR || rc==MMSYSERR_NODRIVER ||
rc==MMSYSERR_NOTSUPPORTED || (!ndev && (rc==MMSYSERR_BADDEVICEID)),
"waveInGetDevCapsW(%s): got %s\n", dev_name(ndev+1),wave_in_error(rc));
format.wFormatTag=WAVE_FORMAT_PCM;
format.nChannels=2;

View file

@ -0,0 +1,205 @@
/*
* Unit tests for joystick APIs
*
* Copyright 2014 Bruno Jesus
*
* 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 <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "mmsystem.h"
#include "wine/test.h"
static HWND window;
static LRESULT CALLBACK proc_window(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
return DefWindowProcA(hwnd, msg, wparam, lparam);
}
static void create_window(void)
{
const char name[] = "Joystick Test";
WNDCLASSA wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = proc_window;
wc.hInstance = 0;
wc.lpszClassName = name;
RegisterClassA(&wc);
window = CreateWindowExA(0, name, name, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, NULL, NULL);
ok(window != NULL, "Expected CreateWindowEx to work, error %d\n", GetLastError());
}
static void destroy_window(void)
{
DestroyWindow(window);
window = NULL;
}
static void test_api(void)
{
MMRESULT ret;
JOYCAPSA jc;
JOYCAPSW jcw;
JOYINFO info;
union _infoex
{
JOYINFOEX ex;
char buffer[sizeof(JOYINFOEX) * 2];
} infoex;
UINT i, par, devices, joyid, win98 = 0, win8 = 0;
UINT period[] = {0, 1, 9, 10, 100, 1000, 1001, 10000, 65535, 65536, 0xFFFFFFFF};
UINT threshold_error = 0x600, period_win8_error = 0x7CE;
UINT flags[] = { JOY_RETURNALL, JOY_RETURNBUTTONS, JOY_RETURNCENTERED, JOY_RETURNPOV,
JOY_RETURNPOVCTS, JOY_RETURNR, JOY_RETURNRAWDATA, JOY_RETURNU,
JOY_RETURNV, JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ };
devices = joyGetNumDevs();
joyid = -1;
/* joyGetNumDevs does NOT return the number of joysticks connected, only slots in the OS */
for (i = 0; i < devices; i++)
{
memset(&jc, 0, sizeof(jc));
ret = joyGetDevCapsA(JOYSTICKID1 + i, &jc, sizeof(jc));
if (ret == JOYERR_NOERROR)
{
joyid = JOYSTICKID1 + i;
trace("Joystick[%d] - name: '%s', axes: %d, buttons: %d, period range: %d - %d\n",
JOYSTICKID1 + i, jc.szPname, jc.wNumAxes, jc.wNumButtons, jc.wPeriodMin, jc.wPeriodMax);
ret = joyGetDevCapsW(JOYSTICKID1 + i, &jcw, sizeof(jcw));
if (ret != MMSYSERR_NOTSUPPORTED) /* Win 98 */
{
ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
ok(jc.wNumAxes == jcw.wNumAxes, "Expected %d == %d\n", jc.wNumAxes, jcw.wNumAxes);
ok(jc.wNumButtons == jcw.wNumButtons, "Expected %d == %d\n", jc.wNumButtons, jcw.wNumButtons);
}
else win98++;
break;
}
else
{
ok(ret == JOYERR_PARMS, "Expected %d, got %d\n", JOYERR_PARMS, ret);
ret = joyGetDevCapsW(JOYSTICKID1 + i, &jcw, sizeof(jcw));
ok(ret == JOYERR_PARMS || (ret == MMSYSERR_NOTSUPPORTED) /* Win 98 */,
"Expected %d, got %d\n", JOYERR_PARMS, ret);
}
}
/* Test invalid joystick - If no joystick is present the driver is not initialized,
* so a NODRIVER error is returned, if at least one joystick is present the error is
* about invalid parameters. */
ret = joyGetDevCapsA(joyid + devices, &jc, sizeof(jc));
ok(ret == MMSYSERR_NODRIVER || ret == JOYERR_PARMS,
"Expected %d or %d, got %d\n", MMSYSERR_NODRIVER, JOYERR_PARMS, ret);
if (joyid == -1)
{
skip("This test requires a real joystick.\n");
return;
}
/* Capture tests */
ret = joySetCapture(NULL, joyid, 100, FALSE);
ok(ret == JOYERR_PARMS || broken(win98 && ret == MMSYSERR_INVALPARAM) /* Win 98 */,
"Expected %d, got %d\n", JOYERR_PARMS, ret);
ret = joySetCapture(window, joyid, 100, FALSE);
ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
ret = joySetCapture(window, joyid, 100, FALSE); /* double capture */
if (ret == JOYERR_NOCANDO)
{
todo_wine
ok(broken(1), "Expected double capture using joySetCapture to work\n");
if (!win98 && broken(1)) win8++; /* Windows 98 or 8 cannot cope with that */
}
else ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
ret = joyReleaseCapture(joyid);
ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
ret = joyReleaseCapture(joyid);
ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret); /* double release */
/* Try some unusual period values for joySetCapture and unusual threshold values for joySetThreshold.
* Windows XP allows almost all test values, Windows 8 will return error on most test values, Windows
* 98 allows anything but cuts the values to their maximum supported values internally. */
for (i = 0; i < sizeof(period) / sizeof(period[0]); i++)
{
ret = joySetCapture(window, joyid, period[i], FALSE);
if (win8 && ((1 << i) & period_win8_error))
ok(ret == JOYERR_NOCANDO, "Test [%d]: Expected %d, got %d\n", i, JOYERR_NOCANDO, ret);
else
ok(ret == JOYERR_NOERROR, "Test [%d]: Expected %d, got %d\n", i, JOYERR_NOERROR, ret);
ret = joyReleaseCapture(joyid);
ok(ret == JOYERR_NOERROR, "Test [%d]: Expected %d, got %d\n", i, JOYERR_NOERROR, ret);
/* Reuse the periods to test the threshold */
ret = joySetThreshold(joyid, period[i]);
if (!win98 && (1 << i) & threshold_error)
ok(ret == MMSYSERR_INVALPARAM, "Test [%d]: Expected %d, got %d\n", i, MMSYSERR_INVALPARAM, ret);
else
ok(ret == JOYERR_NOERROR, "Test [%d]: Expected %d, got %d\n", i, JOYERR_NOERROR, ret);
par = 0xdead;
ret = joyGetThreshold(joyid, &par);
ok(ret == JOYERR_NOERROR, "Test [%d]: Expected %d, got %d\n", i, JOYERR_NOERROR, ret);
if (!win98 || (win98 && i < 8))
{
if ((1 << i) & threshold_error)
ok(par == period[8], "Test [%d]: Expected %d, got %d\n", i, period[8], par);
else
ok(par == period[i], "Test [%d]: Expected %d, got %d\n", i, period[i], par);
}
}
/* Position tests */
ret = joyGetPos(joyid, NULL);
ok(ret == MMSYSERR_INVALPARAM, "Expected %d, got %d\n", MMSYSERR_INVALPARAM, ret);
ret = joyGetPos(joyid, &info);
ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
ret = joyGetPosEx(joyid, NULL);
ok(ret == MMSYSERR_INVALPARAM || broken(win8 && ret == JOYERR_PARMS) /* Win 8 */,
"Expected %d, got %d\n", MMSYSERR_INVALPARAM, ret);
memset(&infoex, 0, sizeof(infoex));
ret = joyGetPosEx(joyid, &infoex.ex);
ok(ret == JOYERR_PARMS || broken(win98 && ret == MMSYSERR_INVALPARAM),
"Expected %d, got %d\n", JOYERR_PARMS, ret);
infoex.ex.dwSize = sizeof(infoex.ex);
ret = joyGetPosEx(joyid, &infoex.ex);
ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
infoex.ex.dwSize = sizeof(infoex.ex) - 1;
ret = joyGetPosEx(joyid, &infoex.ex);
ok(ret == JOYERR_PARMS || broken(win98 && ret == MMSYSERR_INVALPARAM),
"Expected %d, got %d\n", JOYERR_PARMS, ret);
infoex.ex.dwSize = sizeof(infoex);
ret = joyGetPosEx(joyid, &infoex.ex);
ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
infoex.ex.dwSize = sizeof(infoex.ex);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++)
{
infoex.ex.dwFlags = flags[i];
ret = joyGetPosEx(joyid, &infoex.ex);
ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
}
}
START_TEST(joystick)
{
create_window();
test_api();
destroy_window();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,612 @@
/*
* Test MCI CD-ROM access
*
* Copyright 2010 Jörg Höhle
*
* 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 <stdio.h>
#include "windows.h"
#include "mmsystem.h"
#include "wine/test.h"
typedef union {
MCI_STATUS_PARMS status;
MCI_GETDEVCAPS_PARMS caps;
MCI_OPEN_PARMSA open;
MCI_PLAY_PARMS play;
MCI_SEEK_PARMS seek;
MCI_SAVE_PARMSA save;
MCI_GENERIC_PARMS gen;
} MCI_PARMS_UNION;
extern const char* dbg_mcierr(MCIERROR err); /* from mci.c */
static BOOL spurious_message(LPMSG msg)
{
/* WM_DEVICECHANGE 0x0219 appears randomly */
if(msg->message != MM_MCINOTIFY) {
trace("skipping spurious message %04x\n",msg->message);
return TRUE;
}
return FALSE;
}
/* A single ok() in each code path allows us to prefix this with todo_wine */
#define test_notification(hwnd, command, type) test_notification_dbg(hwnd, command, type, __LINE__)
static void test_notification_dbg(HWND hwnd, const char* command, WPARAM type, int line)
{ /* Use type 0 as meaning no message */
MSG msg;
BOOL seen;
do { seen = PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE); }
while(seen && spurious_message(&msg));
if(type && !seen) {
/* We observe transient delayed notification, mostly on native.
* Notification is not always present right when mciSend returns. */
trace_(__FILE__,line)("Waiting for delayed notification from %s\n", command);
MsgWaitForMultipleObjects(0, NULL, FALSE, 3000, QS_POSTMESSAGE);
seen = PeekMessageA(&msg, hwnd, MM_MCINOTIFY, MM_MCINOTIFY, PM_REMOVE);
}
if(!seen)
ok_(__FILE__,line)(type==0, "Expect message %04lx from %s\n", type, command);
else if(msg.hwnd != hwnd)
ok_(__FILE__,line)(msg.hwnd == hwnd, "Didn't get the handle to our test window\n");
else if(msg.message != MM_MCINOTIFY)
ok_(__FILE__,line)(msg.message == MM_MCINOTIFY, "got %04x instead of MM_MCINOTIFY from command %s\n", msg.message, command);
else ok_(__FILE__,line)(msg.wParam == type, "got %04lx instead of MCI_NOTIFY_xyz %04lx from command %s\n", msg.wParam, type, command);
}
#define CDFRAMES_PERSEC 75
static DWORD MSF_Add(DWORD d1, DWORD d2)
{
WORD c, m, s, f;
f = MCI_MSF_FRAME(d1) + MCI_MSF_FRAME(d2);
c = f / CDFRAMES_PERSEC;
f = f % CDFRAMES_PERSEC;
s = MCI_MSF_SECOND(d1) + MCI_MSF_SECOND(d2) + c;
c = s / 60;
s = s % 60;
m = MCI_MSF_MINUTE(d1) + MCI_MSF_MINUTE(d2) + c; /* may be > 60 */
return MCI_MAKE_MSF(m,s,f);
}
static MCIERROR ok_open = 0; /* MCIERR_CANNOT_LOAD_DRIVER */
/* TODO show that shareable flag is not what Wine implements. */
static void test_play(HWND hwnd)
{
MCIDEVICEID wDeviceID;
MCI_PARMS_UNION parm;
MCIERROR err, ok_hw;
DWORD numtracks, track, duration;
DWORD factor = winetest_interactive ? 3 : 1;
char buf[1024];
memset(buf, 0, sizeof(buf));
parm.gen.dwCallback = (DWORD_PTR)hwnd; /* once to rule them all */
err = mciSendStringA("open cdaudio alias c notify shareable", buf, sizeof(buf), hwnd);
ok(!err || err == MCIERR_CANNOT_LOAD_DRIVER || err == MCIERR_MUST_USE_SHAREABLE,
"mci open cdaudio notify returned %s\n", dbg_mcierr(err));
ok_open = err;
test_notification(hwnd, "open alias notify", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
/* Native returns MUST_USE_SHAREABLE when there's trouble with the hardware
* (e.g. unreadable disk) or when Media Player already has the device open,
* yet adding that flag does not help get past this error. */
if(err) {
skip("Cannot open any cdaudio device, %s.\n", dbg_mcierr(err));
return;
}
wDeviceID = atoi(buf);
ok(!strcmp(buf,"1"), "mci open deviceId: %s, expected 1\n", buf);
/* Win9X-ME may start the MCI and media player upon insertion of a CD. */
err = mciSendStringA("sysinfo all name 1 open", buf, sizeof(buf), NULL);
ok(!err,"sysinfo all name 1 returned %s\n", dbg_mcierr(err));
if(!err && wDeviceID != 1) trace("Device '%s' is open.\n", buf);
err = mciSendStringA("capability c has video notify", buf, sizeof(buf), hwnd);
ok(!err, "capability video: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "false"), "capability video is %s\n", buf);
test_notification(hwnd, "capability notify", MCI_NOTIFY_SUCCESSFUL);
err = mciSendStringA("capability c can play", buf, sizeof(buf), hwnd);
ok(!err, "capability video: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "true"), "capability play is %s\n", buf);
err = mciSendStringA("capability c", buf, sizeof(buf), NULL);
ok(err == MCIERR_MISSING_PARAMETER, "capability nokeyword: %s\n", dbg_mcierr(err));
parm.caps.dwItem = 0x4001;
parm.caps.dwReturn = 0xFEEDABAD;
err = mciSendCommandA(wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&parm);
ok(err == MCIERR_UNSUPPORTED_FUNCTION, "GETDEVCAPS %x: %s\n", parm.caps.dwItem, dbg_mcierr(err));
parm.caps.dwItem = MCI_GETDEVCAPS_DEVICE_TYPE;
err = mciSendCommandA(wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&parm);
ok(!err, "GETDEVCAPS device type: %s\n", dbg_mcierr(err));
if(!err) ok( parm.caps.dwReturn == MCI_DEVTYPE_CD_AUDIO, "getdevcaps device type: %u\n", parm.caps.dwReturn);
err = mciSendCommandA(wDeviceID, MCI_RECORD, 0, (DWORD_PTR)&parm);
ok(err == MCIERR_UNSUPPORTED_FUNCTION, "MCI_RECORD: %s\n", dbg_mcierr(err));
/* Wine's MCI_MapMsgAtoW crashes on MCI_SAVE without parm->lpfilename */
parm.save.lpfilename = "foo";
err = mciSendCommandA(wDeviceID, MCI_SAVE, 0, (DWORD_PTR)&parm);
ok(err == MCIERR_UNSUPPORTED_FUNCTION, "MCI_SAVE: %s\n", dbg_mcierr(err));
/* commands from the core set are UNSUPPORTED, others UNRECOGNIZED */
err = mciSendCommandA(wDeviceID, MCI_STEP, 0, (DWORD_PTR)&parm);
ok(err == MCIERR_UNRECOGNIZED_COMMAND, "MCI_STEP: %s\n", dbg_mcierr(err));
parm.status.dwItem = MCI_STATUS_TIME_FORMAT;
parm.status.dwReturn = 0xFEEDABAD;
err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
ok(!err, "STATUS time format: %s\n", dbg_mcierr(err));
if(!err) ok(parm.status.dwReturn == MCI_FORMAT_MSF, "status time default format: %ld\n", parm.status.dwReturn);
/* "CD-Audio" */
err = mciSendStringA("info c product wait notify", buf, sizeof(buf), hwnd);
ok(!err, "info product: %s\n", dbg_mcierr(err));
test_notification(hwnd, "info notify", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
parm.status.dwItem = MCI_STATUS_MEDIA_PRESENT;
parm.status.dwReturn = 0xFEEDABAD;
err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
ok(err || parm.status.dwReturn == TRUE || parm.status.dwReturn == FALSE,
"STATUS media present: %s\n", dbg_mcierr(err));
if (parm.status.dwReturn != TRUE) {
skip("No CD-ROM in drive.\n");
return;
}
parm.status.dwItem = MCI_STATUS_MODE;
err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
ok(!err, "STATUS mode: %s\n", dbg_mcierr(err));
switch(parm.status.dwReturn) {
case MCI_MODE_NOT_READY:
skip("CD-ROM mode not ready (DVD in drive?)\n");
return;
case MCI_MODE_OPEN: /* should not happen with MEDIA_PRESENT */
skip("CD-ROM drive is open\n");
/* set door closed may not work. */
return;
default: /* play/record/seek/pause */
ok(parm.status.dwReturn==MCI_MODE_STOP, "STATUS mode is %lx\n", parm.status.dwReturn);
/* fall through */
case MCI_MODE_STOP: /* normal */
break;
}
/* Initial mode is "stopped" with a CD in drive */
err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
ok(!err, "status mode: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "stopped"), "status mode is initially %s\n", buf);
err = mciSendStringA("status c ready", buf, sizeof(buf), hwnd);
ok(!err, "status ready: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "true"), "status ready with media is %s\n", buf);
err = mciSendStringA("info c product identity", buf, sizeof(buf), hwnd);
ok(!err, "info 2flags: %s\n", dbg_mcierr(err)); /* not MCIERR_FLAGS_NOT_COMPATIBLE */
/* Precedence rule p>u>i verified experimentally, not tested here. */
err = mciSendStringA("info c identity", buf, sizeof(buf), hwnd);
ok(!err || err == MCIERR_HARDWARE, "info identity: %s\n", dbg_mcierr(err));
/* a blank disk causes MCIERR_HARDWARE and other commands to fail likewise. */
ok_hw = err;
err = mciSendStringA("info c upc", buf, sizeof(buf), hwnd);
ok(err == ok_hw || err == MCIERR_NO_IDENTITY, "info upc: %s\n", dbg_mcierr(err));
parm.status.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
parm.status.dwReturn = 0;
err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
ok(err == ok_hw, "STATUS number of tracks: %s\n", dbg_mcierr(err));
numtracks = parm.status.dwReturn;
/* cf. MAXIMUM_NUMBER_TRACKS */
ok(0 < numtracks && numtracks <= 99, "number of tracks=%ld\n", parm.status.dwReturn);
err = mciSendStringA("status c length", buf, sizeof(buf), hwnd);
ok(err == ok_hw, "status length: %s\n", dbg_mcierr(err));
if(!err) trace("CD length %s\n", buf);
if(err) { /* MCIERR_HARDWARE when given a blank disk */
skip("status length %s (blank disk?)\n", dbg_mcierr(err));
return;
}
/* Linux leaves the drive at some random position,
* native initialises to the start position below. */
err = mciSendStringA("status c position", buf, sizeof(buf), hwnd);
ok(!err, "status position: %s\n", dbg_mcierr(err));
if(!err) todo_wine ok(!strcmp(buf, "00:02:00") || !strcmp(buf, "00:02:33") || !strcmp(buf, "00:03:00"),
"status position initially %s\n", buf);
/* 2 seconds is the initial position even with data tracks. */
err = mciSendStringA("status c position start notify", buf, sizeof(buf), hwnd);
ok(err == ok_hw, "status position start: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "00:02:00") || !strcmp(buf, "00:02:33") || !strcmp(buf, "00:03:00"),
"status position start %s\n", buf);
test_notification(hwnd, "status notify", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
err = mciSendStringA("status c position start track 1 notify", buf, sizeof(buf), hwnd);
ok(err == MCIERR_FLAGS_NOT_COMPATIBLE, "status position start: %s\n", dbg_mcierr(err));
test_notification(hwnd, "status 2flags", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
err = mciSendStringA("play c from 00:02:00 to 00:01:00 notify", buf, sizeof(buf), hwnd);
ok(err == MCIERR_OUTOFRANGE, "play 2s to 1s: %s\n", dbg_mcierr(err));
test_notification(hwnd, "play 2s to 1s", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
err = mciSendStringA("resume c", buf, sizeof(buf), hwnd);
ok(err == MCIERR_HARDWARE || /* Win9x */ err == MCIERR_UNSUPPORTED_FUNCTION,
"resume without play: %s\n", dbg_mcierr(err)); /* not NONAPPLICABLE_FUNCTION */
/* vmware with a .iso (data-only) yields no error on NT/w2k */
err = mciSendStringA("seek c wait", buf, sizeof(buf), hwnd);
ok(err == MCIERR_MISSING_PARAMETER, "seek noflag: %s\n", dbg_mcierr(err));
err = mciSendStringA("seek c to start to end", buf, sizeof(buf), hwnd);
ok(err == MCIERR_FLAGS_NOT_COMPATIBLE || broken(!err), "seek to start+end: %s\n", dbg_mcierr(err));
/* Win9x only errors out with Seek to start to <position> */
/* set Wine to a defined position before play */
err = mciSendStringA("seek c to start notify", buf, sizeof(buf), hwnd);
ok(!err, "seek to start: %s\n", dbg_mcierr(err));
test_notification(hwnd, "seek to start", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
/* Win9X Status position / current track then sometimes report the end position / track! */
err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
ok(!err, "status mode: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "stopped"), "status mode after seek is %s\n", buf);
/* MCICDA ignores MCI_SET_VIDEO */
err = mciSendStringA("set c video on", buf, sizeof(buf), hwnd);
ok(!err, "set video: %s\n", dbg_mcierr(err));
/* One xp machine ignored SET_AUDIO, one w2k and one w7 machine honoured it
* and simultaneously toggled the mute button in the mixer control panel.
* Or does it only depend on the HW, not the OS?
* Some vmware machines return MCIERR_HARDWARE. */
err = mciSendStringA("set c audio all on", buf, sizeof(buf), hwnd);
ok(!err || err == MCIERR_HARDWARE, "set audio: %s\n", dbg_mcierr(err));
err = mciSendStringA("set c time format ms", buf, sizeof(buf), hwnd);
ok(!err, "set time format ms: %s\n", dbg_mcierr(err));
memset(buf, 0, sizeof(buf));
err = mciSendStringA("status c position start", buf, sizeof(buf), hwnd);
ok(!err, "status position start (ms): %s\n", dbg_mcierr(err));
duration = atoi(buf);
if(!err) ok(duration > 2000, "status position initially %sms\n", buf);
/* 00:02:00 corresponds to 2001 ms, 02:33 -> 2441 etc. */
err = mciSendStringA("status c position start track 1", buf, sizeof(buf), hwnd);
ok(err == MCIERR_FLAGS_NOT_COMPATIBLE, "status position start+track: %s\n", dbg_mcierr(err));
err = mciSendStringA("status c notify wait", buf, sizeof(buf), hwnd);
ok(err == MCIERR_MISSING_PARAMETER, "status noflag: %s\n", dbg_mcierr(err));
err = mciSendStringA("status c length track 1", buf, sizeof(buf), hwnd);
ok(!err, "status length (ms): %s\n", dbg_mcierr(err));
if(!err) {
trace("track #1 length %sms\n", buf);
duration = atoi(buf);
} else duration = 2001; /* for the position test below */
if (0) { /* causes some native systems to return Seek and Play with MCIERR_HARDWARE */
/* depending on capability can eject only? */
err = mciSendStringA("set c door closed notify", buf, sizeof(buf), hwnd);
ok(!err, "set door closed: %s\n", dbg_mcierr(err));
test_notification(hwnd, "door closed", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
}
/* Changing the disk while the MCI device is open causes the Status
* command to report stale data. Native obviously caches the TOC. */
/* status type track is localised, strcmp("audio|other") may fail. */
parm.status.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
parm.status.dwTrack = 1;
parm.status.dwReturn = 0xFEEDABAD;
err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD_PTR)&parm);
ok(!err, "STATUS type track 1: %s\n", dbg_mcierr(err));
ok(parm.status.dwReturn==MCI_CDA_TRACK_OTHER || parm.status.dwReturn==MCI_CDA_TRACK_AUDIO,
"unknown track type %lx\n", parm.status.dwReturn);
if (parm.status.dwReturn == MCI_CDA_TRACK_OTHER) {
/* Find an audio track */
parm.status.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
parm.status.dwTrack = numtracks;
parm.status.dwReturn = 0xFEEDABAD;
err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD_PTR)&parm);
ok(!err, "STATUS type track %u: %s\n", numtracks, dbg_mcierr(err));
ok(parm.status.dwReturn == MCI_CDA_TRACK_OTHER || parm.status.dwReturn == MCI_CDA_TRACK_AUDIO,
"unknown track type %lx\n", parm.status.dwReturn);
track = (!err && parm.status.dwReturn == MCI_CDA_TRACK_AUDIO) ? numtracks : 0;
/* Seek to start (above) skips over data tracks
* In case of a data only CD, it seeks to the end of disk, however
* another Status position a few seconds later yields MCIERR_HARDWARE. */
parm.status.dwItem = MCI_STATUS_POSITION;
parm.status.dwReturn = 2000;
err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
ok(!err || broken(err == MCIERR_HARDWARE), "STATUS position: %s\n", dbg_mcierr(err));
if(!err && track) ok(parm.status.dwReturn > duration,
"Seek did not skip data tracks, position %lums\n", parm.status.dwReturn);
/* dwReturn > start + length(#1) may fail because of small position report fluctuation.
* On some native systems, status position fluctuates around the target position;
* Successive calls return varying positions! */
err = mciSendStringA("set c time format msf", buf, sizeof(buf), hwnd);
ok(!err, "set time format msf: %s\n", dbg_mcierr(err));
parm.status.dwItem = MCI_STATUS_LENGTH;
parm.status.dwTrack = 1;
parm.status.dwReturn = 0xFEEDABAD;
err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD_PTR)&parm);
ok(!err, "STATUS length track %u: %s\n", parm.status.dwTrack, dbg_mcierr(err));
duration = parm.status.dwReturn;
trace("track #1 length: %02um:%02us:%02uframes\n",
MCI_MSF_MINUTE(duration), MCI_MSF_SECOND(duration), MCI_MSF_FRAME(duration));
ok(duration>>24==0, "CD length high bits %08X\n", duration);
/* TODO only with mixed CDs? */
/* play track 1 to length silently works with data tracks */
parm.play.dwFrom = MCI_MAKE_MSF(0,2,0);
parm.play.dwTo = duration; /* omitting 2 seconds from end */
err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD_PTR)&parm);
ok(!err, "PLAY data to %08X: %s\n", duration, dbg_mcierr(err));
Sleep(1500*factor); /* Time to spin up, hopefully less than track length */
err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
ok(!err, "status mode: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "stopped"), "status mode on data is %s\n", buf);
} else if (parm.status.dwReturn == MCI_CDA_TRACK_AUDIO) {
skip("Got no mixed data+audio CD.\n");
track = 1;
} else track = 0;
if (!track) {
skip("Found no audio track.\n");
return;
}
err = mciSendStringA("set c time format msf", buf, sizeof(buf), hwnd);
ok(!err, "set time format msf: %s\n", dbg_mcierr(err));
parm.status.dwItem = MCI_STATUS_LENGTH;
parm.status.dwTrack = numtracks;
parm.status.dwReturn = 0xFEEDABAD;
err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD_PTR)&parm);
ok(!err, "STATUS length track %u: %s\n", parm.status.dwTrack, dbg_mcierr(err));
duration = parm.status.dwReturn;
trace("last track length: %02um:%02us:%02uframes\n",
MCI_MSF_MINUTE(duration), MCI_MSF_SECOND(duration), MCI_MSF_FRAME(duration));
ok(duration>>24==0, "CD length high bits %08X\n", duration);
parm.status.dwItem = MCI_STATUS_POSITION;
/* dwTrack is still set */
err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD_PTR)&parm);
ok(!err, "STATUS position start track %u: %s\n", parm.status.dwTrack, dbg_mcierr(err));
trace("last track position: %02um:%02us:%02uframes\n",
MCI_MSF_MINUTE(parm.status.dwReturn), MCI_MSF_SECOND(parm.status.dwReturn), MCI_MSF_FRAME(parm.status.dwReturn));
/* Seek to position + length always works, esp.
* for the last track it's NOT the position of the lead-out. */
parm.seek.dwTo = MSF_Add(parm.status.dwReturn, duration);
err = mciSendCommandA(wDeviceID, MCI_SEEK, MCI_TO, (DWORD_PTR)&parm);
ok(!err, "SEEK to %08X position last + length: %s\n", parm.seek.dwTo, dbg_mcierr(err));
parm.seek.dwTo = MSF_Add(parm.seek.dwTo, MCI_MAKE_MSF(0,0,1));
err = mciSendCommandA(wDeviceID, MCI_SEEK, MCI_TO, (DWORD_PTR)&parm);
ok(err == MCIERR_OUTOFRANGE, "SEEK past %08X position last + length: %s\n", parm.seek.dwTo, dbg_mcierr(err));
err = mciSendStringA("set c time format tmsf", buf, sizeof(buf), hwnd);
ok(!err, "set time format tmsf: %s\n", dbg_mcierr(err));
parm.play.dwFrom = track;
err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_NOTIFY, (DWORD_PTR)&parm);
ok(!err, "PLAY from %u notify: %s\n", track, dbg_mcierr(err));
if(err) {
skip("Cannot manage to play track %u.\n", track);
return;
}
Sleep(1800*factor); /* Time to spin up, hopefully less than track length */
err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
ok(!err, "status mode: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "playing"), "status mode during play is %s\n", buf);
err = mciSendStringA("pause c", buf, sizeof(buf), hwnd);
ok(!err, "pause: %s\n", dbg_mcierr(err));
test_notification(hwnd, "pause should abort notification", MCI_NOTIFY_ABORTED);
/* Native returns stopped when paused,
* yet the Stop command is different as it would disallow Resume. */
err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
ok(!err, "status mode: %s\n", dbg_mcierr(err));
if(!err) todo_wine ok(!strcmp(buf, "stopped"), "status mode while paused is %s\n", buf);
err = mciSendCommandA(wDeviceID, MCI_RESUME, 0, 0);
ok(!err || /* Win9x */ err == MCIERR_UNSUPPORTED_FUNCTION,
"RESUME without parms: %s\n", dbg_mcierr(err));
Sleep(1300*factor);
/* Native continues to play without interruption */
err = mciSendCommandA(wDeviceID, MCI_PLAY, 0, 0);
todo_wine ok(!err, "PLAY without parms: %s\n", dbg_mcierr(err));
parm.play.dwFrom = MCI_MAKE_TMSF(numtracks,0,1,0);
parm.play.dwTo = 1;
err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD_PTR)&parm);
ok(err == MCIERR_OUTOFRANGE, "PLAY: %s\n", dbg_mcierr(err));
err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
ok(!err, "status mode: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "playing"), "status mode after play is %s\n", buf);
err = mciSendCommandA(wDeviceID, MCI_STOP, MCI_NOTIFY, (DWORD_PTR)&parm);
ok(!err, "STOP notify: %s\n", dbg_mcierr(err));
test_notification(hwnd, "STOP notify", MCI_NOTIFY_SUCCESSFUL);
test_notification(hwnd, "STOP #1", 0);
parm.play.dwFrom = track;
err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_NOTIFY, (DWORD_PTR)&parm);
ok(!err, "PLAY from %u notify: %s\n", track, dbg_mcierr(err));
Sleep(1600*factor);
parm.seek.dwTo = 1; /* not <track>, to test position below */
err = mciSendCommandA(wDeviceID, MCI_SEEK, MCI_TO, (DWORD_PTR)&parm);
ok(!err, "SEEK to %u notify: %s\n", track, dbg_mcierr(err));
/* Note that native's Status position / current track may move the head
* and reflect the new position only seconds after issuing the command. */
/* Seek stops */
err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
ok(!err, "status mode: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "stopped"), "status mode after play is %s\n", buf);
test_notification(hwnd, "Seek aborts Play", MCI_NOTIFY_ABORTED);
test_notification(hwnd, "Seek", 0);
parm.play.dwFrom = track;
parm.play.dwTo = MCI_MAKE_TMSF(track,0,0,21); /* 21 frames, subsecond */
err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO | MCI_NOTIFY, (DWORD_PTR)&parm);
ok(!err, "PLAY from %u notify: %s\n", track, dbg_mcierr(err));
Sleep(2200*factor);
err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
ok(!err, "status mode: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "stopped") || broken(!strcmp(buf, "playing")), "status mode after play is %s\n", buf);
if(!err && !strcmp(buf, "playing")) trace("status playing after sleep\n");
/* Playing to end asynchronously sends no notification! */
test_notification(hwnd, "PLAY to end", 0);
err = mciSendStringA("status c mode notify", buf, sizeof(buf), hwnd);
ok(!err, "status mode: %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf, "stopped") || broken(!strcmp(buf, "playing")), "status mode after play is %s\n", buf);
if(!err && !strcmp(buf, "playing")) trace("status still playing\n");
/* Some systems report playing even after Sleep(3900ms) yet the successful
* notification tests (not ABORTED) indicates they are finished. */
test_notification(hwnd, "dangling from PLAY", MCI_NOTIFY_SUPERSEDED);
test_notification(hwnd, "status mode", MCI_NOTIFY_SUCCESSFUL);
err = mciSendStringA("stop c", buf, sizeof(buf), hwnd);
ok(!err, "stop: %s\n", dbg_mcierr(err));
test_notification(hwnd, "PLAY to end", 0);
/* length as MSF despite set time format TMSF */
parm.status.dwItem = MCI_STATUS_LENGTH;
parm.status.dwTrack = numtracks;
parm.status.dwReturn = 0xFEEDABAD;
err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD_PTR)&parm);
ok(!err, "STATUS length track %u: %s\n", parm.status.dwTrack, dbg_mcierr(err));
ok(duration == parm.status.dwReturn, "length MSF<>TMSF %08lX\n", parm.status.dwReturn);
/* Play from position start to start+length always works. */
/* TODO? also play it using MSF */
parm.play.dwFrom = numtracks;
parm.play.dwTo = (duration << 8) | numtracks; /* as TMSF */
err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO | MCI_NOTIFY, (DWORD_PTR)&parm);
ok(!err, "PLAY (TMSF) from %08X to %08X: %s\n", parm.play.dwFrom, parm.play.dwTo, dbg_mcierr(err));
Sleep(1400*factor);
err = mciSendStringA("status c current track", buf, sizeof(buf), hwnd);
ok(!err, "status track: %s\n", dbg_mcierr(err));
if(!err) todo_wine ok(numtracks == atoi(buf), "status current track gave %s, expected %u\n", buf, numtracks);
/* fails in Wine because SEEK is independent on IOCTL_CDROM_RAW_READ */
err = mciSendCommandA(wDeviceID, MCI_STOP, 0, 0);
ok(!err, "STOP: %s\n", dbg_mcierr(err));
test_notification(hwnd, "STOP aborts", MCI_NOTIFY_ABORTED);
test_notification(hwnd, "STOP final", 0);
}
static void test_openclose(HWND hwnd)
{
MCIDEVICEID wDeviceID;
MCI_PARMS_UNION parm;
MCIERROR err;
char drive[] = {'a',':','\\','X','\0'};
if (ok_open == MCIERR_CANNOT_LOAD_DRIVER) {
/* todo_wine Every open below should yield this same error. */
skip("CD-ROM device likely not installed or disabled.\n");
return;
}
/* Bug in native since NT: After OPEN "c" without MCI_OPEN_ALIAS fails with
* MCIERR_DEVICE_OPEN, any subsequent OPEN fails with EXTENSION_NOT_FOUND! */
parm.open.lpstrAlias = "x"; /* with alias, OPEN "c" behaves normally */
parm.open.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO;
parm.open.lpstrElementName = drive;
for ( ; strlen(drive); drive[strlen(drive)-1] = 0)
for (drive[0] = 'a'; drive[0] <= 'z'; drive[0]++) {
err = mciSendCommandA(0, MCI_OPEN, MCI_OPEN_ELEMENT | MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID |
MCI_OPEN_SHAREABLE | MCI_OPEN_ALIAS, (DWORD_PTR)&parm);
ok(!err || err == MCIERR_INVALID_FILE, "OPEN %s type: %s\n", drive, dbg_mcierr(err));
/* open X:\ fails in Win9x/NT. Only open X: works everywhere. */
if(!err) {
wDeviceID = parm.open.wDeviceID;
trace("ok with %s\n", drive);
err = mciSendCommandA(wDeviceID, MCI_CLOSE, 0, 0);
ok(!err,"mciCommand close returned %s\n", dbg_mcierr(err));
}
}
drive[0] = '\\';
err = mciSendCommandA(0, MCI_OPEN, MCI_OPEN_ELEMENT | MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID |
MCI_OPEN_SHAREABLE, (DWORD_PTR)&parm);
ok(err == MCIERR_INVALID_FILE, "OPEN %s type: %s\n", drive, dbg_mcierr(err));
if(!err) mciSendCommandA(parm.open.wDeviceID, MCI_CLOSE, 0, 0);
if (0) {
parm.open.lpstrElementName = (LPCSTR)0xDEADBEEF;
err = mciSendCommandA(0, MCI_OPEN, MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID |
MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE, (DWORD_PTR)&parm);
todo_wine ok(err == MCIERR_FLAGS_NOT_COMPATIBLE, "OPEN elt_ID: %s\n", dbg_mcierr(err));
if(!err) mciSendCommandA(parm.open.wDeviceID, MCI_CLOSE, 0, 0);
}
}
START_TEST(mcicda)
{
MCIERROR err;
HWND hwnd;
hwnd = CreateWindowExA(0, "static", "mcicda test", WS_POPUP, 0,0,100,100,
0, 0, 0, NULL);
test_notification(hwnd, "-prior to tests-", 0);
test_play(hwnd);
test_openclose(hwnd);
err = mciSendCommandA(MCI_ALL_DEVICE_ID, MCI_STOP, 0, 0);
todo_wine ok(!err || broken(err == MCIERR_HARDWARE /* blank CD or testbot without CD-ROM */),
"STOP all returned %s\n", dbg_mcierr(err));
err = mciSendStringA("close all", NULL, 0, hwnd);
ok(!err, "final close all returned %s\n", dbg_mcierr(err));
test_notification(hwnd, "-tests complete-", 0);
DestroyWindow(hwnd);
}

View file

@ -0,0 +1,848 @@
/*
* Test winmm midi
*
* Copyright 2010 Jörg Höhle
*
* 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
#include <stdio.h>
#include <stddef.h>
#include "windows.h"
#include "mmsystem.h"
#include "wine/test.h"
extern const char* mmsys_error(MMRESULT error); /* from wave.c */
/* Test in order of increasing probability to hang.
* On many UNIX systems, the Timidity softsynth provides
* MIDI sequencer services and it is not particularly robust.
*/
#define MYCBINST 0x4CAFE5A8 /* not used with window or thread callbacks */
#define WHATEVER 0xFEEDF00D
static BOOL spurious_message(LPMSG msg)
{
/* WM_DEVICECHANGE 0x0219 appears randomly */
if(msg->message == WM_DEVICECHANGE) {
trace("skipping spurious message %04x\n", msg->message);
return TRUE;
}
return FALSE;
}
static UINT cbmsg = 0;
static DWORD_PTR cbval1 = WHATEVER;
static DWORD_PTR cbval2 = 0;
static DWORD_PTR cbinst = MYCBINST;
static void CALLBACK callback_func(HWAVEOUT hwo, UINT uMsg,
DWORD_PTR dwInstance,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
if (winetest_debug>1)
trace("Callback! msg=%x %lx %lx\n", uMsg, dwParam1, dwParam2);
cbmsg = uMsg;
cbval1 = dwParam1; /* mhdr or 0 */
cbval2 = dwParam2; /* always 0 */
cbinst = dwInstance; /* MYCBINST, see midiOut/StreamOpen */
}
#define test_notification(hwnd, command, m1, p2) test_notification_dbg(hwnd, command, m1, p2, __LINE__)
static void test_notification_dbg(HWND hwnd, const char* command, UINT m1, DWORD_PTR p2, int line)
{ /* Use message type 0 as meaning no message */
MSG msg;
if (hwnd) {
/* msg.wParam is hmidiout, msg.lParam is the mhdr (or 0) */
BOOL seen;
do { seen = PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE); }
while(seen && spurious_message(&msg));
if (m1 && !seen) {
/* We observe transient delayed notification, mostly on native.
* Perhaps the OS preempts the player thread after setting MHDR_DONE
* or clearing MHDR_INQUEUE, before calling DriverCallback. */
DWORD rc;
trace_(__FILE__,line)("Waiting for delayed message %x from %s\n", m1, command);
SetLastError(0xDEADBEEF);
rc = MsgWaitForMultipleObjects(0, NULL, FALSE, 3000, QS_POSTMESSAGE);
ok_(__FILE__,line)(rc==WAIT_OBJECT_0, "Wait failed: %04x %d\n", rc, GetLastError());
seen = PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE);
}
if (seen) {
trace_(__FILE__,line)("Message %x, wParam=%lx, lParam=%lx from %s\n",
msg.message, msg.wParam, msg.lParam, command);
ok_(__FILE__,line)(msg.hwnd==hwnd, "Didn't get the handle to our test window\n");
ok_(__FILE__,line)(msg.message==m1 && msg.lParam==p2, "bad message %x/%lx from %s, expect %x/%lx\n", msg.message, msg.lParam, command, m1, p2);
}
else ok_(__FILE__,line)(m1==0, "Expect message %x from %s\n", m1, command);
}
else {
/* FIXME: MOM_POSITIONCB and MOM_DONE are so close that a queue is needed. */
if (cbmsg) {
ok_(__FILE__,line)(cbmsg==m1 && cbval1==p2 && cbval2==0, "bad callback %x/%lx/%lx from %s, expect %x/%lx\n", cbmsg, cbval1, cbval2, command, m1, p2);
cbmsg = 0; /* Mark as read */
cbval1 = cbval2 = WHATEVER;
ok_(__FILE__,line)(cbinst==MYCBINST, "callback dwInstance changed to %lx\n", cbinst);
}
else ok_(__FILE__,line)(m1==0, "Expect callback %x from %s\n", m1, command);
}
}
static void test_midiIn_device(UINT udev, HWND hwnd)
{
HMIDIIN hm;
MMRESULT rc;
MIDIINCAPSA capsA;
MIDIHDR mhdr;
rc = midiInGetDevCapsA(udev, &capsA, sizeof(capsA));
ok((MIDIMAPPER==udev) ? (rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*nt,w2k*/)) : rc==0,
"midiInGetDevCaps(dev=%d) rc=%s\n", udev, mmsys_error(rc));
if (!rc) {
/* MIDI IN capsA.dwSupport may contain garbage, absent in old MS-Windows */
trace("* %s: manufacturer=%d, product=%d, support=%X\n", capsA.szPname, capsA.wMid, capsA.wPid, capsA.dwSupport);
}
if (hwnd)
rc = midiInOpen(&hm, udev, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
else
rc = midiInOpen(&hm, udev, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
ok((MIDIMAPPER!=udev) ? rc==0 : (rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*nt,w2k*/)),
"midiInOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
if (rc) return;
test_notification(hwnd, "midiInOpen", MIM_OPEN, 0);
memset(&mhdr, 0, sizeof(mhdr));
mhdr.dwFlags = MHDR_DONE;
mhdr.dwUser = 0x56FA552C;
mhdr.dwBufferLength = 70000; /* > 64KB! */
mhdr.dwBytesRecorded = 5;
mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength);
ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength);
if (mhdr.lpData) {
rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1);
ok(rc==MMSYSERR_INVALPARAM, "midiInPrepare tiny rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
mhdr.dwFlags |= MHDR_INQUEUE;
rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiInPrepare old size rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_INQUEUE|MHDR_DONE)/*w9x*/ ||
mhdr.dwFlags == MHDR_PREPARED, "dwFlags=%x\n", mhdr.dwFlags);
trace("MIDIHDR flags=%x when unsent\n", mhdr.dwFlags);
mhdr.dwFlags |= MHDR_INQUEUE|MHDR_DONE;
rc = midiInPrepareHeader(hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiInPrepare rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_INQUEUE|MHDR_DONE), "dwFlags=%x\n", mhdr.dwFlags);
mhdr.dwFlags &= ~MHDR_INQUEUE;
rc = midiInUnprepareHeader(hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiInUnprepare rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
mhdr.dwFlags &= ~MHDR_DONE;
rc = midiInUnprepareHeader(hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiInUnprepare rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == 0, "dwFlags=%x\n", mhdr.dwFlags);
rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiInPrepare rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == MHDR_PREPARED, "dwFlags=%x\n", mhdr.dwFlags);
mhdr.dwFlags |= MHDR_DONE;
rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiInPrepare rc=%s\n", mmsys_error(rc));
ok(mhdr.dwBytesRecorded == 5, "BytesRec=%u\n", mhdr.dwBytesRecorded);
mhdr.dwFlags |= MHDR_DONE;
rc = midiInAddBuffer(hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiAddBuffer rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags);
/* w95 does not set dwBytesRecorded=0 within midiInAddBuffer. Wine does. */
if (mhdr.dwBytesRecorded != 0)
trace("dwBytesRecorded %u\n", mhdr.dwBytesRecorded);
rc = midiInAddBuffer(hm, &mhdr, sizeof(mhdr));
ok(rc==MIDIERR_STILLPLAYING, "midiAddBuffer rc=%s\n", mmsys_error(rc));
rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiInPrepare rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags);
}
rc = midiInReset(hm); /* Return any pending buffer */
ok(!rc, "midiInReset rc=%s\n", mmsys_error(rc));
test_notification(hwnd, "midiInReset", MIM_LONGDATA, (DWORD_PTR)&mhdr);
ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE), "dwFlags=%x\n", mhdr.dwFlags);
rc = midiInUnprepareHeader(hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiInUnprepare rc=%s\n", mmsys_error(rc));
ok(mhdr.dwBytesRecorded == 0, "Did some MIDI HW send %u bytes?\n", mhdr.dwBytesRecorded);
rc = midiInClose(hm);
ok(!rc, "midiInClose rc=%s\n", mmsys_error(rc));
ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
HeapFree(GetProcessHeap(), 0, mhdr.lpData);
test_notification(hwnd, "midiInClose", MIM_CLOSE, 0);
test_notification(hwnd, "midiIn over", 0, WHATEVER);
}
static void test_midi_infns(HWND hwnd)
{
HMIDIIN hm;
MMRESULT rc;
UINT udev, ndevs = midiInGetNumDevs();
rc = midiInOpen(&hm, ndevs, 0, 0, CALLBACK_NULL);
ok(rc==MMSYSERR_BADDEVICEID, "midiInOpen udev>max rc=%s\n", mmsys_error(rc));
if (!rc) {
rc = midiInClose(hm);
ok(!rc, "midiInClose rc=%s\n", mmsys_error(rc));
}
if (!ndevs) {
trace("Found no MIDI IN device\n"); /* no skip for this common situation */
rc = midiInOpen(&hm, MIDIMAPPER, 0, 0, CALLBACK_NULL);
ok(rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*nt,w2k*/), "midiInOpen MAPPER with no MIDI rc=%s\n", mmsys_error(rc));
if (!rc) {
rc = midiInClose(hm);
ok(!rc, "midiInClose rc=%s\n", mmsys_error(rc));
}
return;
}
trace("Found %d MIDI IN devices\n", ndevs);
for (udev=0; udev < ndevs; udev++) {
trace("** Testing device %d\n", udev);
test_midiIn_device(udev, hwnd);
Sleep(50);
}
trace("** Testing MIDI mapper\n");
test_midiIn_device(MIDIMAPPER, hwnd);
}
static void test_midi_mci(HWND hwnd)
{
MCIERROR err;
char buf[1024];
memset(buf, 0, sizeof(buf));
err = mciSendStringA("sysinfo sequencer quantity", buf, sizeof(buf), hwnd);
ok(!err, "mci sysinfo sequencer quantity returned %d\n", err);
if (!err) trace("Found %s MCI sequencer devices\n", buf);
if (!strcmp(buf, "0")) return;
err = mciSendStringA("capability sequencer can record", buf, sizeof(buf), hwnd);
ok(!err, "mci sysinfo sequencer quantity returned %d\n", err);
if(!err) ok(!strcmp(buf, "false"), "capability can record is %s\n", buf);
}
static void test_midiOut_device(UINT udev, HWND hwnd)
{
HMIDIOUT hm;
MMRESULT rc;
MIDIOUTCAPSA capsA;
DWORD ovolume;
UINT udevid;
MIDIHDR mhdr;
rc = midiOutGetDevCapsA(udev, &capsA, sizeof(capsA));
ok(!rc, "midiOutGetDevCaps(dev=%d) rc=%s\n", udev, mmsys_error(rc));
if (!rc) {
trace("* %s: manufacturer=%d, product=%d, tech=%d, support=%X: %d voices, %d notes\n",
capsA.szPname, capsA.wMid, capsA.wPid, capsA.wTechnology, capsA.dwSupport, capsA.wVoices, capsA.wNotes);
ok(!((MIDIMAPPER==udev) ^ (MOD_MAPPER==capsA.wTechnology)), "technology %d on device %d\n", capsA.wTechnology, udev);
if (MOD_MIDIPORT == capsA.wTechnology) {
ok(capsA.wVoices == 0 && capsA.wNotes == 0, "external device with notes or voices\n");
ok(capsA.wChannelMask == 0xFFFF, "external device channel mask %x\n", capsA.wChannelMask);
ok(!(capsA.dwSupport & (MIDICAPS_VOLUME|MIDICAPS_LRVOLUME|MIDICAPS_CACHE)), "external device support=%X\n", capsA.dwSupport);
}
}
if (hwnd)
rc = midiOutOpen(&hm, udev, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
else
rc = midiOutOpen(&hm, udev, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
if (rc == MMSYSERR_NOTSUPPORTED || rc == MMSYSERR_NODRIVER)
{
skip( "MIDI out not supported\n" );
return;
}
ok(!rc, "midiOutOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
if (rc) return;
test_notification(hwnd, "midiOutOpen", MOM_OPEN, 0);
rc = midiOutGetVolume(hm, &ovolume);
ok((capsA.dwSupport & MIDICAPS_VOLUME) ? rc==MMSYSERR_NOERROR : rc==MMSYSERR_NOTSUPPORTED, "midiOutGetVolume rc=%s\n", mmsys_error(rc));
/* The native mapper responds with FFFFFFFF initially,
* real devices with the volume GUI SW-synth settings. */
if (!rc) trace("Current volume %x on device %d\n", ovolume, udev);
/* The W95 ESFM Synthesis device reports NOTENABLED although
* GetVolume by handle works and music plays. */
rc = midiOutGetVolume(UlongToHandle(udev), &ovolume);
ok((capsA.dwSupport & MIDICAPS_VOLUME) ? rc==MMSYSERR_NOERROR || broken(rc==MMSYSERR_NOTENABLED) : rc==MMSYSERR_NOTSUPPORTED, "midiOutGetVolume(dev=%d) rc=%s\n", udev, mmsys_error(rc));
rc = midiOutGetVolume(hm, NULL);
ok(rc==MMSYSERR_INVALPARAM, "midiOutGetVolume NULL rc=%s\n", mmsys_error(rc));
/* Tests with midiOutSetvolume show that the midi mapper forwards
* the value to the real device, but Get initially always reports
* FFFFFFFF. Therefore, a Get+SetVolume pair with the mapper is
* not adequate to restore the value prior to tests.
*/
if (winetest_interactive && (capsA.dwSupport & MIDICAPS_VOLUME)) {
DWORD volume2 = (ovolume < 0x80000000) ? 0xC000C000 : 0x40004000;
rc = midiOutSetVolume(hm, volume2);
ok(!rc, "midiOutSetVolume rc=%s\n", mmsys_error(rc));
if (!rc) {
DWORD volume3;
rc = midiOutGetVolume(hm, &volume3);
ok(!rc, "midiOutGetVolume new rc=%s\n", mmsys_error(rc));
if (!rc) trace("New volume %x on device %d\n", volume3, udev);
todo_wine ok(volume2==volume3, "volume Set %x = Get %x\n", volume2, volume3);
rc = midiOutSetVolume(hm, ovolume);
ok(!rc, "midiOutSetVolume restore rc=%s\n", mmsys_error(rc));
}
}
rc = midiOutGetDevCapsA((UINT_PTR)hm, &capsA, sizeof(capsA));
ok(!rc, "midiOutGetDevCaps(dev=%d) by handle rc=%s\n", udev, mmsys_error(rc));
rc = midiInGetDevCapsA((UINT_PTR)hm, (LPMIDIINCAPSA)&capsA, sizeof(DWORD));
ok(rc==MMSYSERR_BADDEVICEID, "midiInGetDevCaps(dev=%d) by out handle rc=%s\n", udev, mmsys_error(rc));
{ DWORD e = 0x006F4893; /* velocity, note (#69 would be 440Hz) channel */
trace("ShortMsg type %x\n", LOBYTE(LOWORD(e)));
rc = midiOutShortMsg(hm, e);
ok(!rc, "midiOutShortMsg rc=%s\n", mmsys_error(rc));
if (!rc) Sleep(400); /* Hear note */
}
memset(&mhdr, 0, sizeof(mhdr));
mhdr.dwFlags = MHDR_DONE;
mhdr.dwUser = 0x56FA552C;
mhdr.dwOffset = 0xDEADBEEF;
mhdr.dwBufferLength = 70000; /* > 64KB! */
mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength);
ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength);
if (mhdr.lpData) {
rc = midiOutLongMsg(hm, &mhdr, sizeof(mhdr));
ok(rc==MIDIERR_UNPREPARED, "midiOutLongMsg unprepared rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER);
rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1);
ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
/* Since at least w2k, midiOutPrepare clears the DONE and INQUEUE flags. w95 didn't. */
/* mhdr.dwFlags |= MHDR_INQUEUE; would cause w95 to return STILLPLAYING from Unprepare */
rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiOutPrepare old size rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE)/*w9x*/ ||
mhdr.dwFlags == MHDR_PREPARED, "dwFlags=%x\n", mhdr.dwFlags);
trace("MIDIHDR flags=%x when unsent\n", mhdr.dwFlags);
/* No flag is cleared when already prepared. */
mhdr.dwFlags |= MHDR_DONE|MHDR_INQUEUE;
rc = midiOutPrepareHeader(hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags);
mhdr.dwFlags |= MHDR_INQUEUE;
rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
ok(rc==MIDIERR_STILLPLAYING, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags);
mhdr.dwFlags &= ~MHDR_INQUEUE;
rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
mhdr.dwFlags |= MHDR_INQUEUE;
rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags == (MHDR_INQUEUE|MHDR_DONE), "dwFlags=%x\n", mhdr.dwFlags);
HeapFree(GetProcessHeap(), 0, mhdr.lpData);
}
ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
ok(mhdr.dwOffset==0xDEADBEEF, "MIDIHDR.dwOffset changed to %x\n", mhdr.dwOffset);
rc = midiOutGetID(hm, &udevid);
ok(!rc, "midiOutGetID rc=%s\n", mmsys_error(rc));
if(!rc) ok(udevid==udev, "midiOutGetID gives %d, expect %d\n", udevid, udev);
rc = midiOutReset(hm); /* Quiet everything */
ok(!rc, "midiOutReset rc=%s\n", mmsys_error(rc));
rc = midiOutClose(hm);
ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
test_notification(hwnd, "midiOutClose", MOM_CLOSE, 0);
rc = midiOutOpen(&hm, udev, 0, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
/* w95 broken(rc==MMSYSERR_INVALPARAM) see WINMM_CheckCallback */
ok(!rc, "midiOutOpen(dev=%d) 0 CALLBACK_WINDOW rc=%s\n", udev, mmsys_error(rc));
/* PostMessage(hwnd=0) redirects to PostThreadMessage(GetCurrentThreadId())
* which PeekMessage((HWND)-1) queries. */
test_notification((HWND)-1, "midiOutOpen WINDOW->THREAD", 0, WHATEVER);
test_notification(hwnd, "midiOutOpen WINDOW", 0, WHATEVER);
if (!rc) {
rc = midiOutClose(hm);
ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
test_notification((HWND)-1, "midiOutClose WINDOW->THREAD", 0, WHATEVER);
test_notification(hwnd, "midiOutClose", 0, WHATEVER);
}
test_notification(hwnd, "midiOut over", 0, WHATEVER);
}
static void test_position(HMIDISTRM hm, UINT typein, UINT typeout)
{
MMRESULT rc;
MMTIME mmtime;
mmtime.wType = typein;
rc = midiStreamPosition(hm, &mmtime, sizeof(MMTIME));
/* Ugly, but a single ok() herein enables using the todo_wine prefix */
ok(!rc && (mmtime.wType == typeout), "midiStreamPosition type %x converted to %x rc=%s\n", typein, mmtime.wType, mmsys_error(rc));
if (!rc) switch(mmtime.wType) {
case TIME_MS:
trace("Stream position %ums\n", mmtime.u.ms);
break;
case TIME_TICKS:
trace("Stream position %u ticks\n", mmtime.u.ticks);
break;
case TIME_MIDI:
trace("Stream position song pointer %u\n", mmtime.u.midi.songptrpos);
break;
}
}
typedef struct midishortevent_tag { /* ideal size for MEVT_F_SHORT event type */
DWORD dwDeltaTime;
DWORD dwStreamID;
DWORD dwEvent;
} MIDISHORTEVENT;
/* Native crashes on a second run with the const qualifier set on this data! */
static BYTE strmEvents[] = { /* A set of variable-sized MIDIEVENT structs */
0, 0, 0, 0, 0, 0, 0, 0, /* dwDeltaTime and dwStreamID */
0, 0, 0, MEVT_NOP | 0x40, /* with MEVT_F_CALLBACK */
0, 0, 0, 0, 0, 0, 0, 0,
0xE0, 0x93, 0x04, MEVT_TEMPO, /* 0493E0 == 300000 */
0, 0, 0, 0, 0, 0, 0, 0,
0x93, 0x48, 0x6F, MEVT_SHORTMSG,
};
static MIDISHORTEVENT strmNops[] = { /* Test callback + dwOffset */
{ 0, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
{ 0, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
};
static MMRESULT playStream(HMIDISTRM hm, LPMIDIHDR lpMidiHdr)
{
MMRESULT rc = midiStreamOut(hm, lpMidiHdr, sizeof(MIDIHDR));
/* virtual machines may return MIDIERR_STILLPLAYING from the next request
* even after MHDR_DONE is set. It's still too early, so add MHDR_INQUEUE. */
if (!rc) while (!(lpMidiHdr->dwFlags & MHDR_DONE) || (lpMidiHdr->dwFlags & MHDR_INQUEUE)) { Sleep(100); }
return rc;
}
static void test_midiStream(UINT udev, HWND hwnd)
{
HMIDISTRM hm;
MMRESULT rc, rc2;
MIDIHDR mhdr;
union {
MIDIPROPTEMPO tempo;
MIDIPROPTIMEDIV tdiv;
} midiprop;
if (hwnd)
rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
else
rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
if (rc == MMSYSERR_NOTSUPPORTED || rc == MMSYSERR_NODRIVER)
{
skip( "MIDI stream not supported\n" );
return;
}
ok(!rc, "midiStreamOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
if (rc) return;
test_notification(hwnd, "midiStreamOpen", MOM_OPEN, 0);
midiprop.tempo.cbStruct = sizeof(midiprop.tempo);
rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TEMPO);
ok(!rc, "midiStreamProperty TEMPO rc=%s\n", mmsys_error(rc));
ok(midiprop.tempo.dwTempo==500000, "default stream tempo %u microsec per quarter note\n", midiprop.tempo.dwTempo);
midiprop.tdiv.cbStruct = sizeof(midiprop.tdiv);
rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TIMEDIV);
ok(!rc, "midiStreamProperty TIMEDIV rc=%s\n", mmsys_error(rc));
todo_wine ok(24==LOWORD(midiprop.tdiv.dwTimeDiv), "default stream time division %u\n", midiprop.tdiv.dwTimeDiv);
memset(&mhdr, 0, sizeof(mhdr));
mhdr.dwUser = 0x56FA552C;
mhdr.dwOffset = 1234567890;
mhdr.dwBufferLength = sizeof(strmEvents);
mhdr.dwBytesRecorded = mhdr.dwBufferLength;
mhdr.lpData = (LPSTR)&strmEvents[0];
if (mhdr.lpData) {
rc = midiOutLongMsg((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
ok(rc==MIDIERR_UNPREPARED, "midiOutLongMsg unprepared rc=%s\n", mmsys_error(rc));
test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER);
rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1);
ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc));
rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiOutPrepare old size rc=%s\n", mmsys_error(rc));
ok(mhdr.dwFlags & MHDR_PREPARED, "MHDR.dwFlags when prepared %x\n", mhdr.dwFlags);
/* The device is still in paused mode and should queue the message. */
rc = midiStreamOut(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiStreamOut old size rc=%s\n", mmsys_error(rc));
rc2 = rc;
trace("MIDIHDR flags=%x when submitted\n", mhdr.dwFlags);
/* w9X/me does not set MHDR_ISSTRM when StreamOut exits,
* but it will be set on all systems after the job is finished. */
Sleep(90);
/* Wine <1.1.39 started playing immediately */
test_notification(hwnd, "midiStream still paused", 0, WHATEVER);
/* MSDN asks to use midiStreamRestart prior to midiStreamOut()
* because the starting state is 'pause', but some apps seem to
* work with the inverse order: queue everything, then play.
*/
rc = midiStreamRestart(hm);
ok(!rc, "midiStreamRestart rc=%s\n", mmsys_error(rc));
if (!rc2) while(mhdr.dwFlags & MHDR_INQUEUE) {
trace("async MIDI still queued\n");
Sleep(100);
} /* Checking INQUEUE is not the recommended way to wait for the end of a job, but we're testing. */
/* MHDR_ISSTRM is not necessarily set when midiStreamOut returns
* rather than when the queue is eventually processed. */
ok(mhdr.dwFlags & MHDR_ISSTRM, "MHDR.dwFlags %x no ISSTRM when out of queue\n", mhdr.dwFlags);
if (!rc2) while(!(mhdr.dwFlags & MHDR_DONE)) {
/* Never to be seen except perhaps on multicore */
trace("async MIDI still not done\n");
Sleep(100);
}
ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags %x not DONE when out of queue\n", mhdr.dwFlags);
test_notification(hwnd, "midiStream callback", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr);
/* Native fills dwOffset regardless of the cbMidiHdr size argument to midiStreamOut */
ok(1234567890!=mhdr.dwOffset, "play left MIDIHDR.dwOffset at %u\n", mhdr.dwOffset);
rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiOutUnprepare #2 rc=%s\n", mmsys_error(rc));
trace("MIDIHDR stream flags=%x when finished\n", mhdr.dwFlags);
ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags when done %x\n", mhdr.dwFlags);
test_position(hm, TIME_MS, TIME_MS);
test_position(hm, TIME_TICKS, TIME_TICKS);
todo_wine test_position(hm, TIME_MIDI, TIME_MIDI);
test_position(hm, TIME_SMPTE, TIME_MS);
test_position(hm, TIME_SAMPLES, TIME_MS);
test_position(hm, TIME_BYTES, TIME_MS);
Sleep(400); /* Hear note */
midiprop.tempo.cbStruct = sizeof(midiprop.tempo);
rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TEMPO);
ok(!rc, "midiStreamProperty TEMPO rc=%s\n", mmsys_error(rc));
ok(0x0493E0==midiprop.tempo.dwTempo, "stream set tempo %u\n", midiprop.tdiv.dwTimeDiv);
rc = midiStreamRestart(hm);
ok(!rc, "midiStreamRestart #2 rc=%s\n", mmsys_error(rc));
mhdr.dwFlags |= MHDR_ISSTRM;
/* Preset flags (e.g. MHDR_ISSTRM) do not disturb. */
rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiOutPrepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc));
rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiOutUnprepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc));
rc = midiStreamRestart(hm);
ok(!rc, "midiStreamRestart #3 rc=%s\n", mmsys_error(rc));
}
ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
ok(0==((MIDISHORTEVENT*)&strmEvents)[0].dwStreamID, "dwStreamID set to %x\n", ((LPMIDIEVENT)&strmEvents[0])->dwStreamID);
/* dwBytesRecorded controls how much is played, not dwBufferLength
* allowing to immediately forward packets from midiIn to midiOut */
mhdr.dwOffset = 1234123123;
mhdr.dwBufferLength = sizeof(strmNops);
trace("buffer: %u\n", mhdr.dwBufferLength);
mhdr.dwBytesRecorded = 0;
mhdr.lpData = (LPSTR)&strmNops[0];
strmNops[0].dwEvent |= MEVT_F_CALLBACK;
strmNops[1].dwEvent |= MEVT_F_CALLBACK;
rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc));
rc = playStream(hm, &mhdr);
ok(!rc, "midiStreamOut 0 bytes recorded rc=%s\n", mmsys_error(rc));
test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr);
test_notification(hwnd, "0 bytes recorded", 0, WHATEVER);
/* FIXME: check dwOffset within callback
* instead of the unspecified value afterwards */
ok(1234123123==mhdr.dwOffset || broken(0==mhdr.dwOffset), "play 0 set MIDIHDR.dwOffset to %u\n", mhdr.dwOffset);
/* w2k and later only set dwOffset when processing MEVT_T_CALLBACK,
* while w9X/me/nt always sets it. Have Wine behave like w2k because the
* dwOffset slot does not exist in the small size MIDIHDR. */
mhdr.dwOffset = 1234123123;
mhdr.dwBytesRecorded = 1*sizeof(MIDISHORTEVENT);
rc = playStream(hm, &mhdr);
ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
test_notification(hwnd, "1 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
test_notification(hwnd, "1 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
test_notification(hwnd, "1 of 2 events", 0, WHATEVER);
ok(0==mhdr.dwOffset, "MIDIHDR.dwOffset 1/2 changed to %u\n", mhdr.dwOffset);
mhdr.dwOffset = 1234123123;
mhdr.dwBytesRecorded = 2*sizeof(MIDISHORTEVENT);
rc = playStream(hm, &mhdr);
ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
test_notification(hwnd, "2 of 2 events", 0, WHATEVER);
ok(sizeof(MIDISHORTEVENT)==mhdr.dwOffset, "MIDIHDR.dwOffset 2/2 changed to %u\n", mhdr.dwOffset);
ok(mhdr.dwBytesRecorded == 2*sizeof(MIDISHORTEVENT), "dwBytesRecorded changed to %u\n", mhdr.dwBytesRecorded);
strmNops[0].dwEvent &= ~MEVT_F_CALLBACK;
strmNops[1].dwEvent &= ~MEVT_F_CALLBACK;
mhdr.dwOffset = 1234123123;
rc = playStream(hm, &mhdr);
ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
test_notification(hwnd, "0 CB in 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
test_notification(hwnd, "0 CB in 2 events", 0, WHATEVER);
/* w9X/me/nt set dwOffset to the position played last */
ok(1234123123==mhdr.dwOffset || broken(sizeof(MIDISHORTEVENT)==mhdr.dwOffset), "MIDIHDR.dwOffset nocb changed to %u\n", mhdr.dwOffset);
mhdr.dwBytesRecorded = mhdr.dwBufferLength-1;
rc = playStream(hm, &mhdr);
ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBytesRecorded modulo MIDIEVENT rc=%s\n", mmsys_error(rc));
if (!rc) {
test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
}
mhdr.dwBytesRecorded = mhdr.dwBufferLength+1;
rc = playStream(hm, &mhdr);
ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBufferLength<dwBytesRecorded rc=%s\n", mmsys_error(rc));
test_notification(hwnd, "past MIDIHDR tests", 0, WHATEVER);
rc = midiStreamStop(hm);
ok(!rc, "midiStreamStop rc=%s\n", mmsys_error(rc));
ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
ok(0==strmNops[0].dwStreamID, "dwStreamID[0] set to %x\n", strmNops[0].dwStreamID);
ok(0==strmNops[1].dwStreamID, "dwStreamID[1] set to %x\n", strmNops[1].dwStreamID);
mhdr.dwBufferLength = 70000; /* > 64KB! */
mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength);
ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength);
if (mhdr.lpData) {
mhdr.dwFlags = 0;
/* PrepareHeader detects the too large buffer is for a stream. */
rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
todo_wine ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare stream too large rc=%s\n", mmsys_error(rc));
rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
HeapFree(GetProcessHeap(), 0, mhdr.lpData);
}
rc = midiStreamClose(hm);
ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
test_notification(hwnd, "midiStreamClose", MOM_CLOSE, 0);
test_notification(hwnd, "midiStream over", 0, WHATEVER);
rc = midiStreamOpen(&hm, &udev, 1, 0, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
ok(!rc /*w2k*/|| rc==MMSYSERR_INVALPARAM/*w98*/, "midiStreamOpen NULL function rc=%s\n", mmsys_error(rc));
if (!rc) {
trace("Device %d accepts NULL CALLBACK_FUNCTION\n", udev);
rc = midiStreamClose(hm);
ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
}
rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)0xDEADBEEF, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
ok(rc==MMSYSERR_INVALPARAM, "midiStreamOpen bad window rc=%s\n", mmsys_error(rc));
if (!rc) {
rc = midiStreamClose(hm);
ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
}
}
static BOOL scan_subkeys(HKEY parent, const LPCSTR *sub_keys)
{
char name[64];
DWORD index = 0;
DWORD name_len = sizeof(name);
BOOL found_vmware = FALSE;
if (sub_keys[0] == NULL)
{
/* We're at the deepest level, check "Identifier" value now */
char *test;
if (RegQueryValueExA(parent, "Identifier", NULL, NULL, (LPBYTE) name, &name_len) != ERROR_SUCCESS)
return FALSE;
for (test = name; test < name + lstrlenA(name) - 6 && ! found_vmware; test++)
{
char c = test[6];
test[6] = '\0';
found_vmware = (lstrcmpiA(test, "VMware") == 0);
test[6] = c;
}
return found_vmware;
}
while (RegEnumKeyExA(parent, index, name, &name_len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS &&
! found_vmware) {
char c = name[lstrlenA(sub_keys[0])];
name[lstrlenA(sub_keys[0])] = '\0';
if (lstrcmpiA(name, sub_keys[0]) == 0) {
HKEY sub_key;
name[lstrlenA(sub_keys[0])] = c;
if (RegOpenKeyExA(parent, name, 0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &sub_key) == ERROR_SUCCESS) {
found_vmware = scan_subkeys(sub_key, sub_keys + 1);
RegCloseKey(sub_key);
}
}
name_len = sizeof(name);
index++;
}
return found_vmware;
}
/*
* Usual method to detect whether running inside a VMware virtual machine involves direct port I/O requiring
* some assembly and an exception handler. Can't do that in Wine tests. Alternative method of querying WMI
* is not available on NT4. So instead we look at the device map and check the Identifier value in the
* registry keys HKLM\HARDWARE\DEVICEMAP\SCSI\Scsi Port x\Scsi Bus x\Target Id x\Logical Unit Id x (where
* x is some number). If the Identifier value contains the string "VMware" we assume running in a VMware VM.
*/
static BOOL on_vmware(void)
{
static const LPCSTR sub_keys[] = { "Scsi Port ", "Scsi Bus ", "Target Id ", "Logical Unit Id ", NULL };
HKEY scsi;
BOOL found_vmware = FALSE;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\Scsi", 0, KEY_ENUMERATE_SUB_KEYS, &scsi) != ERROR_SUCCESS)
return FALSE;
found_vmware = scan_subkeys(scsi, sub_keys);
RegCloseKey(scsi);
return found_vmware;
}
static void test_midi_outfns(HWND hwnd)
{
HMIDIOUT hm;
MMRESULT rc;
UINT udev, ndevs = midiOutGetNumDevs();
rc = midiOutOpen(&hm, ndevs, 0, 0, CALLBACK_NULL);
ok(rc==MMSYSERR_BADDEVICEID, "midiOutOpen udev>max rc=%s\n", mmsys_error(rc));
if (!rc) {
rc = midiOutClose(hm);
ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
}
if (!ndevs) {
MIDIOUTCAPSA capsA;
skip("Found no MIDI out device\n");
rc = midiOutGetDevCapsA(MIDIMAPPER, &capsA, sizeof(capsA));
/* GetDevCaps and Open must return compatible results */
ok(rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*nt,w2k*/), "midiOutGetDevCaps MAPPER with no MIDI rc=%s\n", mmsys_error(rc));
rc = midiOutOpen(&hm, MIDIMAPPER, 0, 0, CALLBACK_NULL);
if (rc==MIDIERR_INVALIDSETUP) todo_wine /* Wine without snd-seq */
ok(rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*w2k*/), "midiOutOpen MAPPER with no MIDI rc=%s\n", mmsys_error(rc));
else
ok(rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*w2k sound disabled*/),
"midiOutOpen MAPPER with no MIDI rc=%s\n", mmsys_error(rc));
if (!rc) {
rc = midiOutClose(hm);
ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
}
return;
}
trace("Found %d MIDI OUT devices\n", ndevs);
test_midi_mci(hwnd);
for (udev=0; udev < ndevs; udev++) {
MIDIOUTCAPSA capsA;
rc = midiOutGetDevCapsA(udev, &capsA, sizeof(capsA));
if (rc || strcmp(capsA.szPname, "Creative Sound Blaster MPU-401") != 0 || ! on_vmware()) {
trace("** Testing device %d\n", udev);
test_midiOut_device(udev, hwnd);
Sleep(800); /* Let the synth rest */
test_midiStream(udev, hwnd);
Sleep(800);
}
else
win_skip("Skipping this device on VMware, driver problem\n");
}
trace("** Testing MIDI mapper\n");
test_midiOut_device(MIDIMAPPER, hwnd);
Sleep(800);
test_midiStream(MIDIMAPPER, hwnd);
}
START_TEST(midi)
{
HWND hwnd = 0;
if (1) /* select 1 for CALLBACK_WINDOW or 0 for CALLBACK_FUNCTION */
hwnd = CreateWindowExA(0, "static", "winmm midi test", WS_POPUP, 0,0,100,100,
0, 0, 0, NULL);
test_midi_infns(hwnd);
test_midi_outfns(hwnd);
if (hwnd) DestroyWindow(hwnd);
}

View file

@ -184,7 +184,7 @@ static void test_mixerClose(HMIXER mix)
mmsys_error(rc));
}
static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
static void mixer_test_controlA(HMIXEROBJ mix, MIXERCONTROLA *control)
{
MMRESULT rc;
@ -197,11 +197,18 @@ static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
details.dwControlID = control->dwControlID;
details.cChannels = 1;
U(details).cMultipleItems = 0;
details.paDetails = &value;
details.cbDetails = sizeof(value);
/* test NULL paDetails */
details.paDetails = NULL;
rc = mixerGetControlDetailsA(mix, &details, MIXER_GETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_INVALPARAM,
"mixerGetDevCapsA: MMSYSERR_INVALPARAM expected, got %s\n",
mmsys_error(rc));
/* read the current control value */
rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
details.paDetails = &value;
rc = mixerGetControlDetailsA(mix, &details, MIXER_GETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -224,7 +231,7 @@ static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
new_details.cbDetails = sizeof(new_value);
/* change the control value by one step */
rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
rc = mixerSetControlDetails(mix, &new_details, MIXER_SETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -240,7 +247,7 @@ static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
ret_details.cbDetails = sizeof(ret_value);
/* read back the new control value */
rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
rc = mixerGetControlDetailsA(mix, &ret_details, MIXER_GETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -259,7 +266,7 @@ static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
details.cbDetails = sizeof(value);
/* restore original value */
rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
rc = mixerSetControlDetails(mix, &details, MIXER_SETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -280,7 +287,7 @@ static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
details.paDetails = &value;
details.cbDetails = sizeof(value);
rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
rc = mixerGetControlDetailsA(mix, &details, MIXER_GETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -303,7 +310,7 @@ static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
new_details.cbDetails = sizeof(new_value);
/* change the control value by one step */
rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
rc = mixerSetControlDetails(mix, &new_details, MIXER_SETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -319,7 +326,7 @@ static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
ret_details.cbDetails = sizeof(ret_value);
/* read back the new control value */
rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
rc = mixerGetControlDetailsA(mix, &ret_details, MIXER_GETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -338,7 +345,7 @@ static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
details.cbDetails = sizeof(value);
/* restore original value */
rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
rc = mixerSetControlDetails(mix, &details, MIXER_SETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -354,7 +361,7 @@ static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
static void mixer_test_deviceA(int device)
{
MIXERCAPSA capsA;
HMIXER mix;
HMIXEROBJ mix;
MMRESULT rc;
DWORD d,s,ns,nc;
@ -384,16 +391,23 @@ static void mixer_test_deviceA(int device)
capsA.vDriverVersion & 0xff,capsA.wMid,capsA.wPid);
}
rc=mixerOpen(&mix, device, 0, 0, 0);
rc = mixerOpen((HMIXER*)&mix, device, 0, 0, 0);
ok(rc==MMSYSERR_NOERROR,
"mixerOpen: MMSYSERR_NOERROR expected, got %s\n",mmsys_error(rc));
if (rc==MMSYSERR_NOERROR) {
MIXERCAPSA capsA2;
rc=mixerGetDevCapsA((UINT_PTR)mix,&capsA2,sizeof(capsA2));
ok(rc==MMSYSERR_NOERROR,
"mixerGetDevCapsA: MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
ok(!strcmp(capsA2.szPname, capsA.szPname), "Got wrong device caps\n");
for (d=0;d<capsA.cDestinations;d++) {
MIXERLINEA mixerlineA;
mixerlineA.cbStruct = 0;
mixerlineA.dwDestination=d;
rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
MIXER_GETLINEINFOF_DESTINATION);
rc = mixerGetLineInfoA(mix, &mixerlineA, MIXER_GETLINEINFOF_DESTINATION);
ok(rc==MMSYSERR_INVALPARAM,
"mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
"MMSYSERR_INVALPARAM expected, got %s\n",
@ -401,8 +415,7 @@ static void mixer_test_deviceA(int device)
mixerlineA.cbStruct = sizeof(mixerlineA);
mixerlineA.dwDestination=capsA.cDestinations;
rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
MIXER_GETLINEINFOF_DESTINATION);
rc = mixerGetLineInfoA(mix, &mixerlineA, MIXER_GETLINEINFOF_DESTINATION);
ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE,
"mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
"MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n",
@ -410,8 +423,7 @@ static void mixer_test_deviceA(int device)
mixerlineA.cbStruct = sizeof(mixerlineA);
mixerlineA.dwDestination=d;
rc=mixerGetLineInfoA((HMIXEROBJ)mix,0,
MIXER_GETLINEINFOF_DESTINATION);
rc = mixerGetLineInfoA(mix, 0, MIXER_GETLINEINFOF_DESTINATION);
ok(rc==MMSYSERR_INVALPARAM,
"mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
"MMSYSERR_INVALPARAM expected, got %s\n",
@ -419,15 +431,14 @@ static void mixer_test_deviceA(int device)
mixerlineA.cbStruct = sizeof(mixerlineA);
mixerlineA.dwDestination=d;
rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,-1);
rc = mixerGetLineInfoA(mix, &mixerlineA, -1);
ok(rc==MMSYSERR_INVALFLAG,
"mixerGetLineInfoA(-1): MMSYSERR_INVALFLAG expected, got %s\n",
mmsys_error(rc));
mixerlineA.cbStruct = sizeof(mixerlineA);
mixerlineA.dwDestination=d;
rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
MIXER_GETLINEINFOF_DESTINATION);
rc = mixerGetLineInfoA(mix, &mixerlineA, MIXER_GETLINEINFOF_DESTINATION);
ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
"mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
"MMSYSERR_NOERROR expected, got %s\n",
@ -461,8 +472,7 @@ static void mixer_test_deviceA(int device)
mixerlineA.cbStruct = sizeof(mixerlineA);
mixerlineA.dwDestination=d;
mixerlineA.dwSource=s;
rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
MIXER_GETLINEINFOF_SOURCE);
rc = mixerGetLineInfoA(mix, &mixerlineA, MIXER_GETLINEINFOF_SOURCE);
ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
"mixerGetLineInfoA(MIXER_GETLINEINFOF_SOURCE): "
"MMSYSERR_NOERROR expected, got %s\n",
@ -499,14 +509,13 @@ static void mixer_test_deviceA(int device)
if (array) {
memset(&controls, 0, sizeof(controls));
rc=mixerGetLineControlsA((HMIXEROBJ)mix,0,
MIXER_GETLINECONTROLSF_ALL);
rc = mixerGetLineControlsA(mix, 0, MIXER_GETLINECONTROLSF_ALL);
ok(rc==MMSYSERR_INVALPARAM,
"mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): "
"MMSYSERR_INVALPARAM expected, got %s\n",
mmsys_error(rc));
rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls,-1);
rc = mixerGetLineControlsA(mix, &controls, -1);
ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM,
"mixerGetLineControlsA(-1): "
"MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n",
@ -521,8 +530,7 @@ static void mixer_test_deviceA(int device)
/* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID
* and MIXER_GETLINECONTROLSF_ONEBYTYPE
*/
rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls,
MIXER_GETLINECONTROLSF_ALL);
rc = mixerGetLineControlsA(mix, &controls, MIXER_GETLINECONTROLSF_ALL);
ok(rc==MMSYSERR_NOERROR,
"mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): "
"MMSYSERR_NOERROR expected, got %s\n",
@ -556,11 +564,11 @@ static void mixer_test_deviceA(int device)
}
}
}
test_mixerClose(mix);
test_mixerClose((HMIXER)mix);
}
}
static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
static void mixer_test_controlW(HMIXEROBJ mix, MIXERCONTROLW *control)
{
MMRESULT rc;
@ -577,7 +585,7 @@ static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
details.cbDetails = sizeof(value);
/* read the current control value */
rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
rc = mixerGetControlDetailsW(mix, &details, MIXER_GETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -600,7 +608,7 @@ static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
new_details.cbDetails = sizeof(new_value);
/* change the control value by one step */
rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
rc = mixerSetControlDetails(mix, &new_details, MIXER_SETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -616,7 +624,7 @@ static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
ret_details.cbDetails = sizeof(ret_value);
/* read back the new control value */
rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
rc = mixerGetControlDetailsW(mix, &ret_details, MIXER_GETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -635,7 +643,7 @@ static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
details.cbDetails = sizeof(value);
/* restore original value */
rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
rc = mixerSetControlDetails(mix, &details, MIXER_SETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -656,7 +664,7 @@ static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
details.paDetails = &value;
details.cbDetails = sizeof(value);
rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
rc = mixerGetControlDetailsW(mix, &details, MIXER_GETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -679,7 +687,7 @@ static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
new_details.cbDetails = sizeof(new_value);
/* change the control value by one step */
rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
rc = mixerSetControlDetails(mix, &new_details, MIXER_SETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -695,7 +703,7 @@ static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
ret_details.cbDetails = sizeof(ret_value);
/* read back the new control value */
rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
rc = mixerGetControlDetailsW(mix, &ret_details, MIXER_GETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -714,7 +722,7 @@ static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
details.cbDetails = sizeof(value);
/* restore original value */
rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
rc = mixerSetControlDetails(mix, &details, MIXER_SETCONTROLDETAILSF_VALUE);
ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
"MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
@ -730,7 +738,7 @@ static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
static void mixer_test_deviceW(int device)
{
MIXERCAPSW capsW;
HMIXER mix;
HMIXEROBJ mix;
MMRESULT rc;
DWORD d,s,ns,nc;
char szShortName[MIXER_SHORT_NAME_CHARS];
@ -767,16 +775,23 @@ static void mixer_test_deviceW(int device)
}
rc=mixerOpen(&mix, device, 0, 0, 0);
rc = mixerOpen((HMIXER*)&mix, device, 0, 0, 0);
ok(rc==MMSYSERR_NOERROR,
"mixerOpen: MMSYSERR_NOERROR expected, got %s\n",mmsys_error(rc));
if (rc==MMSYSERR_NOERROR) {
MIXERCAPSW capsW2;
rc=mixerGetDevCapsW((UINT_PTR)mix,&capsW2,sizeof(capsW2));
ok(rc==MMSYSERR_NOERROR,
"mixerGetDevCapsW: MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
ok(!lstrcmpW(capsW2.szPname, capsW.szPname), "Got wrong device caps\n");
for (d=0;d<capsW.cDestinations;d++) {
MIXERLINEW mixerlineW;
mixerlineW.cbStruct = 0;
mixerlineW.dwDestination=d;
rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
MIXER_GETLINEINFOF_DESTINATION);
rc = mixerGetLineInfoW(mix, &mixerlineW, MIXER_GETLINEINFOF_DESTINATION);
ok(rc==MMSYSERR_INVALPARAM,
"mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
"MMSYSERR_INVALPARAM expected, got %s\n",
@ -784,8 +799,7 @@ static void mixer_test_deviceW(int device)
mixerlineW.cbStruct = sizeof(mixerlineW);
mixerlineW.dwDestination=capsW.cDestinations;
rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
MIXER_GETLINEINFOF_DESTINATION);
rc = mixerGetLineInfoW(mix, &mixerlineW, MIXER_GETLINEINFOF_DESTINATION);
ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE,
"mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
"MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n",
@ -793,8 +807,7 @@ static void mixer_test_deviceW(int device)
mixerlineW.cbStruct = sizeof(mixerlineW);
mixerlineW.dwDestination=d;
rc=mixerGetLineInfoW((HMIXEROBJ)mix,0,
MIXER_GETLINEINFOF_DESTINATION);
rc = mixerGetLineInfoW(mix, 0, MIXER_GETLINEINFOF_DESTINATION);
ok(rc==MMSYSERR_INVALPARAM,
"mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
"MMSYSERR_INVALPARAM expected, got %s\n",
@ -802,15 +815,14 @@ static void mixer_test_deviceW(int device)
mixerlineW.cbStruct = sizeof(mixerlineW);
mixerlineW.dwDestination=d;
rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,-1);
rc = mixerGetLineInfoW(mix, &mixerlineW, -1);
ok(rc==MMSYSERR_INVALFLAG,
"mixerGetLineInfoW(-1): MMSYSERR_INVALFLAG expected, got %s\n",
mmsys_error(rc));
mixerlineW.cbStruct = sizeof(mixerlineW);
mixerlineW.dwDestination=d;
rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
MIXER_GETLINEINFOF_DESTINATION);
rc = mixerGetLineInfoW(mix, &mixerlineW, MIXER_GETLINEINFOF_DESTINATION);
ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
"mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
"MMSYSERR_NOERROR expected, got %s\n",
@ -851,8 +863,7 @@ static void mixer_test_deviceW(int device)
mixerlineW.cbStruct = sizeof(mixerlineW);
mixerlineW.dwDestination=d;
mixerlineW.dwSource=s;
rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
MIXER_GETLINEINFOF_SOURCE);
rc = mixerGetLineInfoW(mix, &mixerlineW, MIXER_GETLINEINFOF_SOURCE);
ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
"mixerGetLineInfoW(MIXER_GETLINEINFOF_SOURCE): "
"MMSYSERR_NOERROR expected, got %s\n",
@ -895,16 +906,14 @@ static void mixer_test_deviceW(int device)
array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
mixerlineW.cControls*sizeof(MIXERCONTROLW));
if (array) {
rc=mixerGetLineControlsW((HMIXEROBJ)mix,0,
MIXER_GETLINECONTROLSF_ALL);
rc = mixerGetLineControlsW(mix, 0, MIXER_GETLINECONTROLSF_ALL);
ok(rc==MMSYSERR_INVALPARAM,
"mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
"MMSYSERR_INVALPARAM expected, got %s\n",
mmsys_error(rc));
rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
-1);
rc = mixerGetLineControlsW(mix, &controls, -1);
ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM,
"mixerGetLineControlsA(-1): "
"mixerGetLineControlsW(-1): "
"MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n",
mmsys_error(rc));
@ -917,8 +926,7 @@ static void mixer_test_deviceW(int device)
/* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID
* and MIXER_GETLINECONTROLSF_ONEBYTYPE
*/
rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
MIXER_GETLINECONTROLSF_ALL);
rc = mixerGetLineControlsW(mix, &controls, MIXER_GETLINECONTROLSF_ALL);
ok(rc==MMSYSERR_NOERROR,
"mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
"MMSYSERR_NOERROR expected, got %s\n",
@ -955,7 +963,7 @@ static void mixer_test_deviceW(int device)
}
}
}
test_mixerClose(mix);
test_mixerClose((HMIXER)mix);
}
}
@ -1004,6 +1012,7 @@ static void mixer_testsW(void)
static void test_mixerOpen(void)
{
HMIXER mix;
HANDLE event;
MMRESULT rc;
UINT ndev, d;
@ -1031,7 +1040,7 @@ static void test_mixerOpen(void)
rc = mixerOpen(&mix, d, 0xdeadbeef, 0, CALLBACK_WINDOW);
ok(rc == MMSYSERR_INVALPARAM ||
rc == MMSYSERR_NOERROR, /* 98 */
broken(rc == MMSYSERR_NOERROR /* 98 */),
"mixerOpen: MMSYSERR_INVALPARAM expected, got %s\n",
mmsys_error(rc));
if (rc == MMSYSERR_NOERROR)
@ -1042,7 +1051,34 @@ static void test_mixerOpen(void)
ok(rc == MMSYSERR_NOERROR,
"mixerOpen: MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
if (rc == MMSYSERR_NOERROR)
test_mixerClose(mix);
rc = mixerOpen(&mix, d, 0, 0, CALLBACK_THREAD);
ok(rc == MMSYSERR_NOERROR /* since w2k */ ||
rc == MMSYSERR_NOTSUPPORTED, /* 98 */
"mixerOpen: MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
if (rc == MMSYSERR_NOERROR)
test_mixerClose(mix);
rc = mixerOpen(&mix, d, 0, 0, CALLBACK_EVENT);
ok(rc == MMSYSERR_NOERROR /* since w2k */ ||
rc == MMSYSERR_NOTSUPPORTED, /* 98 */
"mixerOpen: MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
if (rc == MMSYSERR_NOERROR)
test_mixerClose(mix);
event = CreateEventW(NULL, FALSE, FALSE, NULL);
/* NOTSUPPORTED is not broken, but it enables the todo_wine marker. */
rc = mixerOpen(&mix, d, (DWORD_PTR)event, 0, CALLBACK_EVENT);
todo_wine
ok(rc == MMSYSERR_NOERROR /* since w2k */ ||
broken(rc == MMSYSERR_NOTSUPPORTED), /* 98 */
"mixerOpen: MMSYSERR_NOERROR expected, got %s\n",
mmsys_error(rc));
if (rc == MMSYSERR_NOERROR)
test_mixerClose(mix);
@ -1054,6 +1090,10 @@ static void test_mixerOpen(void)
if (rc == MMSYSERR_NOERROR)
test_mixerClose(mix);
rc = WaitForSingleObject(event, 0);
ok(rc == WAIT_TIMEOUT, "WaitEvent %d\n", rc);
CloseHandle(event);
}
}

View file

@ -18,7 +18,6 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <assert.h>
#include <stdarg.h>
#include "windef.h"
@ -45,42 +44,65 @@ static DWORD RIFF_buf[] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static void expect_buf_offset_dbg(HMMIO hmmio, LONG off, int line)
{
MMIOINFO mmio;
LONG ret;
memset(&mmio, 0, sizeof(mmio));
ret = mmioGetInfo(hmmio, &mmio, 0);
ok_(__FILE__, line)(ret == MMSYSERR_NOERROR, "mmioGetInfo error %u\n", ret);
ok_(__FILE__, line)(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok_(__FILE__, line)(ret == off, "expected %d, got %d\n", off, ret);
}
#define expect_buf_offset(a1, a2) expect_buf_offset_dbg(a1, a2, __LINE__)
static void test_mmioDescend(char *fname)
{
MMRESULT ret;
HMMIO hmmio;
MMIOINFO mmio;
MMCKINFO ckRiff, ckList, ck;
MMCKINFO ckRiff, ckList, ck, ckList2;
memset(&mmio, 0, sizeof(mmio));
mmio.fccIOProc = fname ? FOURCC_DOS : FOURCC_MEM;
mmio.cchBuffer = sizeof(RIFF_buf);
mmio.pchBuffer = (char *)RIFF_buf;
hmmio = mmioOpen(fname, &mmio, MMIO_READ);
hmmio = mmioOpenA(fname, &mmio, MMIO_READ);
if (fname && !hmmio)
{
skip("%s file is missing, skipping the test\n", fname);
trace("No optional %s file. Skipping the test\n", fname);
return;
}
ok(hmmio != 0, "mmioOpen error %u\n", mmio.wErrorRet);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
expect_buf_offset(hmmio, 0);
/* first normal RIFF AVI parsing */
ret = mmioDescend(hmmio, &ckRiff, NULL, 0);
ok(ret == MMSYSERR_NOERROR, "mmioDescend error %u\n", ret);
ok(ckRiff.ckid == FOURCC_RIFF, "wrong ckid: %04x\n", ckRiff.ckid);
ok(ckRiff.fccType == formtypeAVI, "wrong fccType: %04x\n", ckRiff.fccType);
ok(ckRiff.dwDataOffset == 8, "expected 8 got %u\n", ckRiff.dwDataOffset);
trace("ckid %4.4s cksize %04x fccType %4.4s off %04x flags %04x\n",
(LPCSTR)&ckRiff.ckid, ckRiff.cksize, (LPCSTR)&ckRiff.fccType,
ckRiff.dwDataOffset, ckRiff.dwFlags);
expect_buf_offset(hmmio, 12);
ret = mmioDescend(hmmio, &ckList, &ckRiff, 0);
ok(ret == MMSYSERR_NOERROR, "mmioDescend error %u\n", ret);
ok(ckList.ckid == FOURCC_LIST, "wrong ckid: %04x\n", ckList.ckid);
ok(ckList.fccType == listtypeAVIHEADER, "wrong fccType: %04x\n", ckList.fccType);
ok(ckList.dwDataOffset == 20, "expected 20 got %u\n", ckList.dwDataOffset);
trace("ckid %4.4s cksize %04x fccType %4.4s off %04x flags %04x\n",
(LPCSTR)&ckList.ckid, ckList.cksize, (LPCSTR)&ckList.fccType,
ckList.dwDataOffset, ckList.dwFlags);
expect_buf_offset(hmmio, 24);
ret = mmioDescend(hmmio, &ck, &ckList, 0);
ok(ret == MMSYSERR_NOERROR, "mmioDescend error %u\n", ret);
ok(ck.ckid == ckidAVIMAINHDR, "wrong ckid: %04x\n", ck.ckid);
@ -89,18 +111,23 @@ static void test_mmioDescend(char *fname)
(LPCSTR)&ck.ckid, ck.cksize, (LPCSTR)&ck.fccType,
ck.dwDataOffset, ck.dwFlags);
expect_buf_offset(hmmio, 32);
/* Skip chunk data */
mmioSeek(hmmio, ck.cksize, SEEK_CUR);
ret = mmioSeek(hmmio, ck.cksize, SEEK_CUR);
ok(ret == 0x58, "expected 0x58, got %#x\n", ret);
ret = mmioDescend(hmmio, &ckList, &ckList, 0);
ret = mmioDescend(hmmio, &ckList2, &ckList, 0);
ok(ret == MMSYSERR_NOERROR, "mmioDescend error %u\n", ret);
ok(ckList.ckid == FOURCC_LIST, "wrong ckid: %04x\n", ckList.ckid);
ok(ckList.fccType == listtypeSTREAMHEADER, "wrong fccType: %04x\n", ckList.fccType);
ok(ckList2.ckid == FOURCC_LIST, "wrong ckid: %04x\n", ckList2.ckid);
ok(ckList2.fccType == listtypeSTREAMHEADER, "wrong fccType: %04x\n", ckList2.fccType);
trace("ckid %4.4s cksize %04x fccType %4.4s off %04x flags %04x\n",
(LPCSTR)&ckList.ckid, ckList.cksize, (LPCSTR)&ckList.fccType,
ckList.dwDataOffset, ckList.dwFlags);
(LPCSTR)&ckList2.ckid, ckList2.cksize, (LPCSTR)&ckList2.fccType,
ckList2.dwDataOffset, ckList2.dwFlags);
ret = mmioDescend(hmmio, &ck, &ckList, 0);
expect_buf_offset(hmmio, 100);
ret = mmioDescend(hmmio, &ck, &ckList2, 0);
ok(ret == MMSYSERR_NOERROR, "mmioDescend error %u\n", ret);
ok(ck.ckid == ckidSTREAMHEADER, "wrong ckid: %04x\n", ck.ckid);
ok(ck.fccType == 0, "wrong fccType: %04x\n", ck.fccType);
@ -108,6 +135,8 @@ static void test_mmioDescend(char *fname)
(LPCSTR)&ck.ckid, ck.cksize, (LPCSTR)&ck.fccType,
ck.dwDataOffset, ck.dwFlags);
expect_buf_offset(hmmio, 108);
/* test various mmioDescend flags */
mmioSeek(hmmio, 0, SEEK_SET);
@ -199,7 +228,7 @@ static void test_mmioDescend(char *fname)
static void test_mmioOpen(char *fname)
{
char buf[256];
char buf[MMIO_DEFAULTBUFFER];
MMRESULT ret;
HMMIO hmmio;
MMIOINFO mmio;
@ -208,13 +237,13 @@ static void test_mmioOpen(char *fname)
mmio.fccIOProc = fname ? FOURCC_DOS : FOURCC_MEM;
mmio.cchBuffer = sizeof(buf);
mmio.pchBuffer = buf;
hmmio = mmioOpen(fname, &mmio, MMIO_READ);
hmmio = mmioOpenA(fname, &mmio, MMIO_READ);
if (fname && !hmmio)
{
skip("%s file is missing, skipping the test\n", fname);
trace("No optional %s file. Skipping the test\n", fname);
return;
}
ok(hmmio != 0, "mmioOpen error %u\n", mmio.wErrorRet);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
memset(&mmio, 0, sizeof(mmio));
ret = mmioGetInfo(hmmio, &mmio, 0);
@ -224,6 +253,17 @@ static void test_mmioOpen(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == sizeof(buf), "got %u\n", mmio.cchBuffer);
ok(mmio.pchBuffer == buf, "expected %p, got %p\n", buf, mmio.pchBuffer);
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
if (mmio.fccIOProc == FOURCC_DOS)
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
else
ok(mmio.pchEndRead == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
mmioClose(hmmio, 0);
@ -231,8 +271,8 @@ static void test_mmioOpen(char *fname)
mmio.fccIOProc = fname ? FOURCC_DOS : FOURCC_MEM;
mmio.cchBuffer = 0;
mmio.pchBuffer = buf;
hmmio = mmioOpen(fname, &mmio, MMIO_READ);
ok(hmmio != 0, "mmioOpen error %u\n", mmio.wErrorRet);
hmmio = mmioOpenA(fname, &mmio, MMIO_READ);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
memset(&mmio, 0, sizeof(mmio));
ret = mmioGetInfo(hmmio, &mmio, 0);
@ -242,6 +282,14 @@ static void test_mmioOpen(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == 0, "expected 0, got %u\n", mmio.cchBuffer);
ok(mmio.pchBuffer == buf, "expected %p, got %p\n", buf, mmio.pchBuffer);
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
mmioClose(hmmio, 0);
@ -249,8 +297,8 @@ static void test_mmioOpen(char *fname)
mmio.fccIOProc = fname ? FOURCC_DOS : FOURCC_MEM;
mmio.cchBuffer = 0;
mmio.pchBuffer = NULL;
hmmio = mmioOpen(fname, &mmio, MMIO_READ);
ok(hmmio != 0, "mmioOpen error %u\n", mmio.wErrorRet);
hmmio = mmioOpenA(fname, &mmio, MMIO_READ);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
memset(&mmio, 0, sizeof(mmio));
ret = mmioGetInfo(hmmio, &mmio, 0);
@ -260,6 +308,14 @@ static void test_mmioOpen(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == 0, "expected 0, got %u\n", mmio.cchBuffer);
ok(mmio.pchBuffer == NULL, "expected NULL\n");
ok(mmio.pchNext == NULL, "expected NULL\n");
ok(mmio.pchEndRead == NULL, "expected NULL\n");
ok(mmio.pchEndWrite == NULL, "expected NULL\n");
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
mmioClose(hmmio, 0);
@ -267,8 +323,8 @@ static void test_mmioOpen(char *fname)
mmio.fccIOProc = fname ? FOURCC_DOS : FOURCC_MEM;
mmio.cchBuffer = 256;
mmio.pchBuffer = NULL;
hmmio = mmioOpen(fname, &mmio, MMIO_READ);
ok(hmmio != 0, "mmioOpen error %u\n", mmio.wErrorRet);
hmmio = mmioOpenA(fname, &mmio, MMIO_READ);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
memset(&mmio, 0, sizeof(mmio));
ret = mmioGetInfo(hmmio, &mmio, 0);
@ -278,6 +334,17 @@ static void test_mmioOpen(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == 256, "expected 256, got %u\n", mmio.cchBuffer);
ok(mmio.pchBuffer != NULL, "expected not NULL\n");
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
if (mmio.fccIOProc == FOURCC_DOS)
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
else
ok(mmio.pchEndRead == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
mmioClose(hmmio, 0);
@ -285,8 +352,8 @@ static void test_mmioOpen(char *fname)
mmio.fccIOProc = fname ? FOURCC_DOS : FOURCC_MEM;
mmio.cchBuffer = sizeof(buf);
mmio.pchBuffer = buf;
hmmio = mmioOpen(fname, &mmio, MMIO_READ | MMIO_ALLOCBUF);
ok(hmmio != 0, "mmioOpen error %u\n", mmio.wErrorRet);
hmmio = mmioOpenA(fname, &mmio, MMIO_READ | MMIO_ALLOCBUF);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
memset(&mmio, 0, sizeof(mmio));
ret = mmioGetInfo(hmmio, &mmio, 0);
@ -296,6 +363,17 @@ static void test_mmioOpen(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == sizeof(buf), "got %u\n", mmio.cchBuffer);
ok(mmio.pchBuffer == buf, "expected %p, got %p\n", buf, mmio.pchBuffer);
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
if (mmio.fccIOProc == FOURCC_DOS)
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
else
ok(mmio.pchEndRead == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
mmioClose(hmmio, 0);
@ -303,8 +381,8 @@ static void test_mmioOpen(char *fname)
mmio.fccIOProc = fname ? FOURCC_DOS : FOURCC_MEM;
mmio.cchBuffer = 0;
mmio.pchBuffer = NULL;
hmmio = mmioOpen(fname, &mmio, MMIO_READ | MMIO_ALLOCBUF);
ok(hmmio != 0, "mmioOpen error %u\n", mmio.wErrorRet);
hmmio = mmioOpenA(fname, &mmio, MMIO_READ | MMIO_ALLOCBUF);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
memset(&mmio, 0, sizeof(mmio));
ret = mmioGetInfo(hmmio, &mmio, 0);
@ -314,6 +392,17 @@ static void test_mmioOpen(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == MMIO_DEFAULTBUFFER, "expected MMIO_DEFAULTBUFFER, got %u\n", mmio.cchBuffer);
ok(mmio.pchBuffer != NULL, "expected not NULL\n");
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
if (mmio.fccIOProc == FOURCC_DOS)
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
else
ok(mmio.pchEndRead == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
mmioClose(hmmio, 0);
@ -321,8 +410,8 @@ static void test_mmioOpen(char *fname)
mmio.fccIOProc = fname ? FOURCC_DOS : FOURCC_MEM;
mmio.cchBuffer = 256;
mmio.pchBuffer = NULL;
hmmio = mmioOpen(fname, &mmio, MMIO_READ | MMIO_ALLOCBUF);
ok(hmmio != 0, "mmioOpen error %u\n", mmio.wErrorRet);
hmmio = mmioOpenA(fname, &mmio, MMIO_READ | MMIO_ALLOCBUF);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
memset(&mmio, 0, sizeof(mmio));
ret = mmioGetInfo(hmmio, &mmio, 0);
@ -332,6 +421,17 @@ static void test_mmioOpen(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == 256, "expected 256, got %u\n", mmio.cchBuffer);
ok(mmio.pchBuffer != NULL, "expected not NULL\n");
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
if (mmio.fccIOProc == FOURCC_DOS)
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
else
ok(mmio.pchEndRead == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
mmioClose(hmmio, 0);
@ -339,14 +439,14 @@ static void test_mmioOpen(char *fname)
mmio.fccIOProc = fname ? FOURCC_DOS : FOURCC_MEM;
mmio.cchBuffer = 0;
mmio.pchBuffer = buf;
hmmio = mmioOpen(fname, &mmio, MMIO_READ | MMIO_ALLOCBUF);
hmmio = mmioOpenA(fname, &mmio, MMIO_READ | MMIO_ALLOCBUF);
if (!hmmio && mmio.wErrorRet == ERROR_BAD_FORMAT)
{
/* Seen on Win9x, WinMe but also XP-SP1 */
skip("Some Windows versions don't like a 0 size and a given buffer\n");
return;
}
ok(hmmio != 0, "mmioOpen error %u\n", mmio.wErrorRet);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
memset(&mmio, 0, sizeof(mmio));
ret = mmioGetInfo(hmmio, &mmio, 0);
@ -356,6 +456,17 @@ static void test_mmioOpen(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == MMIO_DEFAULTBUFFER, "expected MMIO_DEFAULTBUFFER, got %u\n", mmio.cchBuffer);
ok(mmio.pchBuffer == buf, "expected %p, got %p\n", buf, mmio.pchBuffer);
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
if (mmio.fccIOProc == FOURCC_DOS)
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
else
ok(mmio.pchEndRead == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
mmioClose(hmmio, 0);
}
@ -371,13 +482,13 @@ static void test_mmioSetBuffer(char *fname)
mmio.fccIOProc = fname ? FOURCC_DOS : FOURCC_MEM;
mmio.cchBuffer = sizeof(buf);
mmio.pchBuffer = buf;
hmmio = mmioOpen(fname, &mmio, MMIO_READ);
hmmio = mmioOpenA(fname, &mmio, MMIO_READ);
if (fname && !hmmio)
{
skip("%s file is missing, skipping the test\n", fname);
trace("No optional %s file. Skipping the test\n", fname);
return;
}
ok(hmmio != 0, "mmioOpen error %u\n", mmio.wErrorRet);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
memset(&mmio, 0, sizeof(mmio));
ret = mmioGetInfo(hmmio, &mmio, 0);
@ -387,6 +498,17 @@ static void test_mmioSetBuffer(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == sizeof(buf), "got %u\n", mmio.cchBuffer);
ok(mmio.pchBuffer == buf, "expected %p, got %p\n", buf, mmio.pchBuffer);
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
if (mmio.fccIOProc == FOURCC_DOS)
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
else
ok(mmio.pchEndRead == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
ret = mmioSetBuffer(hmmio, NULL, 0, 0);
ok(ret == MMSYSERR_NOERROR, "mmioSetBuffer error %u\n", ret);
@ -399,6 +521,14 @@ static void test_mmioSetBuffer(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == 0, "got not 0\n");
ok(mmio.pchBuffer == NULL, "got not NULL buf\n");
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
ret = mmioSetBuffer(hmmio, NULL, 0, MMIO_ALLOCBUF);
ok(ret == MMSYSERR_NOERROR, "mmioSetBuffer error %u\n", ret);
@ -411,6 +541,14 @@ static void test_mmioSetBuffer(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == 0, "got not 0\n");
ok(mmio.pchBuffer == NULL, "got not NULL buf\n");
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
ret = mmioSetBuffer(hmmio, buf, 0, MMIO_ALLOCBUF);
ok(ret == MMSYSERR_NOERROR, "mmioSetBuffer error %u\n", ret);
@ -423,6 +561,14 @@ static void test_mmioSetBuffer(char *fname)
ok(mmio.fccIOProc == (fname ? FOURCC_DOS : FOURCC_MEM), "got %4.4s\n", (LPCSTR)&mmio.fccIOProc);
ok(mmio.cchBuffer == 0, "got not 0\n");
ok(mmio.pchBuffer == buf, "expected %p, got %p\n", buf, mmio.pchBuffer);
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
ret = mmioSetBuffer(hmmio, NULL, 256, MMIO_WRITE|MMIO_ALLOCBUF);
ok(ret == MMSYSERR_NOERROR, "mmioSetBuffer error %u\n", ret);
@ -436,12 +582,336 @@ static void test_mmioSetBuffer(char *fname)
ok(mmio.cchBuffer == 256, "got %u\n", mmio.cchBuffer);
ok(mmio.pchBuffer != NULL, "expected not NULL\n");
ok(mmio.pchBuffer != buf, "expected != buf\n");
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", buf, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected 0, got %d\n", mmio.lDiskOffset);
ret = mmioSeek(hmmio, 0, SEEK_CUR);
ok(ret == 0, "expected 0, got %d\n", ret);
mmioClose(hmmio, 0);
}
#define FOURCC_XYZ mmioFOURCC('X', 'Y', 'Z', ' ')
static LRESULT CALLBACK mmio_test_IOProc(LPSTR lpMMIOInfo, UINT uMessage, LPARAM lParam1, LPARAM lParam2)
{
LPMMIOINFO lpInfo = (LPMMIOINFO) lpMMIOInfo;
switch (uMessage)
{
case MMIOM_OPEN:
if (lpInfo->fccIOProc == FOURCC_DOS)
lpInfo->fccIOProc = mmioFOURCC('F', 'A', 'I', 'L');
return MMSYSERR_NOERROR;
case MMIOM_CLOSE:
return MMSYSERR_NOERROR;
case MMIOM_SEEK:
lpInfo->adwInfo[1]++;
lpInfo->lDiskOffset = 0xdeadbeef;
return 0;
default:
return 0;
}
}
static void test_mmioOpen_fourcc(void)
{
char fname[] = "file+name.xyz+one.two";
LPMMIOPROC lpProc;
HMMIO hmmio;
MMIOINFO mmio;
lpProc = mmioInstallIOProcA(FOURCC_DOS, mmio_test_IOProc, MMIO_INSTALLPROC);
ok(lpProc == mmio_test_IOProc, "mmioInstallIOProcA error\n");
lpProc = mmioInstallIOProcA(FOURCC_XYZ, mmio_test_IOProc, MMIO_INSTALLPROC);
ok(lpProc == mmio_test_IOProc, "mmioInstallIOProcA error\n");
memset(&mmio, 0, sizeof(mmio));
hmmio = mmioOpenA(fname, &mmio, MMIO_READ);
mmioGetInfo(hmmio, &mmio, 0);
ok(hmmio && mmio.fccIOProc == FOURCC_XYZ, "mmioOpenA error %u, got %4.4s\n",
mmio.wErrorRet, (LPCSTR)&mmio.fccIOProc);
ok(mmio.adwInfo[1] == 0, "mmioOpenA sent MMIOM_SEEK, got %d\n",
mmio.adwInfo[1]);
ok(mmio.lDiskOffset == 0, "mmioOpenA updated lDiskOffset, got %d\n",
mmio.lDiskOffset);
mmioClose(hmmio, 0);
mmioInstallIOProcA(FOURCC_XYZ, NULL, MMIO_REMOVEPROC);
memset(&mmio, 0, sizeof(mmio));
hmmio = mmioOpenA(fname, &mmio, MMIO_READ);
mmioGetInfo(hmmio, &mmio, 0);
ok(!hmmio && mmio.wErrorRet == MMIOERR_FILENOTFOUND, "mmioOpenA error %u, got %4.4s\n",
mmio.wErrorRet, (LPCSTR)&mmio.fccIOProc);
mmioClose(hmmio, 0);
mmioInstallIOProcA(FOURCC_DOS, NULL, MMIO_REMOVEPROC);
}
static BOOL create_test_file(char *temp_file)
{
char temp_path[MAX_PATH];
DWORD ret, written;
HANDLE h;
ret = GetTempPathA(sizeof(temp_path), temp_path);
ok(ret, "Failed to get a temp path, err %d\n", GetLastError());
if (!ret)
return FALSE;
ret = GetTempFileNameA(temp_path, "mmio", 0, temp_file);
ok(ret, "Failed to get a temp name, err %d\n", GetLastError());
if (!ret)
return FALSE;
h = CreateFileA(temp_file, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
ok(h != INVALID_HANDLE_VALUE, "Failed to create a file, err %d\n", GetLastError());
if (h == INVALID_HANDLE_VALUE) return FALSE;
ret = WriteFile(h, RIFF_buf, sizeof(RIFF_buf), &written, NULL);
ok(ret, "Failed to write a file, err %d\n", GetLastError());
CloseHandle(h);
if (!ret) DeleteFileA(temp_file);
return ret;
}
static void test_mmioSeek(void)
{
HMMIO hmmio;
MMIOINFO mmio;
LONG end, pos;
const LONG size = sizeof(RIFF_buf), offset = 16;
char test_file[MAX_PATH];
MMRESULT res;
HFILE hfile;
OFSTRUCT ofs;
/* test memory file */
memset(&mmio, 0, sizeof(mmio));
mmio.fccIOProc = FOURCC_MEM;
mmio.pchBuffer = (char*)&RIFF_buf;
mmio.cchBuffer = sizeof(RIFF_buf);
hmmio = mmioOpenA(NULL, &mmio, MMIO_READ);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
if (hmmio != NULL) {
/* seek to the end */
end = mmioSeek(hmmio, 0, SEEK_END);
ok(end == size, "expected %d, got %d\n", size, end);
/* test MMIOINFO values */
res = mmioGetInfo(hmmio, &mmio, 0);
ok(res == MMSYSERR_NOERROR, "expected 0, got %d\n", res);
ok(mmio.pchNext == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchNext);
ok(mmio.pchEndRead == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == 0, "expected %d, got %d\n", 0, mmio.lBufOffset);
ok(mmio.lDiskOffset == 0, "expected %d, got %d\n", 0, mmio.lDiskOffset);
/* seek backward from the end */
pos = mmioSeek(hmmio, offset, SEEK_END);
ok(pos == size-offset, "expected %d, got %d\n", size-offset, pos);
mmioClose(hmmio, 0);
}
if (!create_test_file(test_file)) return;
/* test standard file without buffering */
hmmio = NULL;
memset(&mmio, 0, sizeof(mmio));
mmio.fccIOProc = FOURCC_DOS;
mmio.pchBuffer = 0;
mmio.cchBuffer = 0;
hmmio = mmioOpenA(test_file, &mmio, MMIO_READ);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
if (hmmio != NULL) {
/* seek to the end */
end = mmioSeek(hmmio, 0, SEEK_END);
ok(end == size, "expected %d, got %d\n", size, end);
/* test MMIOINFO values */
res = mmioGetInfo(hmmio, &mmio, 0);
ok(res == MMSYSERR_NOERROR, "expected 0, got %d\n", res);
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == size, "expected %d, got %d\n", size, mmio.lBufOffset);
ok(mmio.lDiskOffset == size, "expected %d, got %d\n", size, mmio.lDiskOffset);
/* seek backward from the end */
pos = mmioSeek(hmmio, offset, SEEK_END);
ok(pos == size-offset, "expected %d, got %d\n", size-offset, pos);
mmioClose(hmmio, 0);
}
/* test standard file with buffering */
hmmio = NULL;
memset(&mmio, 0, sizeof(mmio));
mmio.fccIOProc = FOURCC_DOS;
mmio.pchBuffer = 0;
mmio.cchBuffer = 0;
hmmio = mmioOpenA(test_file, &mmio, MMIO_READ | MMIO_ALLOCBUF);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
if (hmmio != NULL) {
/* seek to the end */
end = mmioSeek(hmmio, 0, SEEK_END);
ok(end == size, "expected %d, got %d\n", size, end);
/* test MMIOINFO values */
res = mmioGetInfo(hmmio, &mmio, 0);
ok(res == MMSYSERR_NOERROR, "expected 0, got %d\n", res);
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
ok(mmio.pchEndWrite == mmio.pchBuffer + mmio.cchBuffer, "expected %p + %d, got %p\n", mmio.pchBuffer, mmio.cchBuffer, mmio.pchEndWrite);
ok(mmio.lBufOffset == end, "expected %d, got %d\n", end, mmio.lBufOffset);
ok(mmio.lDiskOffset == size, "expected %d, got %d\n", size, mmio.lDiskOffset);
/* seek backward from the end */
pos = mmioSeek(hmmio, offset, SEEK_END);
ok(pos == size-offset, "expected %d, got %d\n", size-offset, pos);
mmioClose(hmmio, 0);
}
/* test seek position inheritance from standard file handle */
hfile = OpenFile(test_file, &ofs, OF_READ);
ok(hfile != HFILE_ERROR, "Failed to open the file, err %d\n", GetLastError());
if (hfile != HFILE_ERROR) {
pos = _llseek(hfile, offset, SEEK_SET);
ok(pos != HFILE_ERROR, "Failed to seek, err %d\n", GetLastError());
memset(&mmio, 0, sizeof(mmio));
mmio.fccIOProc = FOURCC_DOS;
mmio.adwInfo[0] = (DWORD)hfile;
hmmio = mmioOpenA(NULL, &mmio, MMIO_READ | MMIO_DENYWRITE | MMIO_ALLOCBUF);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
if (hmmio != NULL) {
pos = mmioSeek(hmmio, 0, SEEK_CUR);
ok(pos == offset, "expected %d, got %d\n", offset, pos);
mmioClose(hmmio, 0);
}
}
DeleteFileA(test_file);
}
static void test_mmio_end_of_file(void)
{
char test_file[MAX_PATH], buffer[128], data[16];
MMIOINFO mmio;
HMMIO hmmio;
LONG ret;
MMRESULT res;
if (!create_test_file(test_file)) return;
memset(&mmio, 0, sizeof(mmio));
mmio.fccIOProc = FOURCC_DOS;
mmio.pchBuffer = buffer;
mmio.cchBuffer = sizeof(buffer);
hmmio = mmioOpenA(test_file, &mmio, MMIO_READ);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
if (hmmio == NULL) {
DeleteFileA(test_file);
return;
}
ret = mmioSeek(hmmio, 0, SEEK_END);
ok(sizeof(RIFF_buf) == ret, "got %d\n", ret);
ret = mmioRead(hmmio, data, sizeof(data));
ok(ret == 0, "expected %d, got %d\n", 0, ret);
res = mmioGetInfo(hmmio, &mmio, 0);
ok(res == MMSYSERR_NOERROR, "expected 0, got %d\n", res);
res = mmioAdvance(hmmio, &mmio, MMIO_READ);
ok(res == MMSYSERR_NOERROR, "expected 0, got %d\n", res);
ok(mmio.pchNext == mmio.pchEndRead, "expected %p, got %p\n", mmio.pchEndRead, mmio.pchNext);
mmioClose(hmmio, 0);
DeleteFileA(test_file);
}
static void test_mmio_buffer_pointer(void)
{
char test_file[MAX_PATH];
char buffer[5], data[16];
MMIOINFO mmio;
HMMIO hmmio;
LONG size, pos;
MMRESULT res;
if (!create_test_file(test_file)) return;
memset(&mmio, 0, sizeof(mmio));
mmio.fccIOProc = FOURCC_DOS;
mmio.pchBuffer = buffer;
mmio.cchBuffer = sizeof(buffer);
hmmio = mmioOpenA(test_file, &mmio, MMIO_READ);
ok(hmmio != 0, "mmioOpenA error %u\n", mmio.wErrorRet);
if (hmmio == NULL) {
DeleteFileA(test_file);
return;
}
/* the buffer is empty */
size = mmioRead(hmmio, data, 0);
ok(size == 0, "expected 0, got %d\n", size);
res = mmioGetInfo(hmmio, &mmio, 0);
ok(res == MMSYSERR_NOERROR, "expected 0, got %d\n", res);
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
/* fill the buffer */
size = mmioAdvance(hmmio, &mmio, MMIO_READ);
ok(mmio.pchEndRead-mmio.pchBuffer == sizeof(buffer), "got %d\n", (int)(mmio.pchEndRead-mmio.pchBuffer));
/* seeking to the same buffer chunk, the buffer is kept */
size = sizeof(buffer)/2;
pos = mmioSeek(hmmio, size, SEEK_SET);
ok(pos == size, "failed to seek, expected %d, got %d\n", size, pos);
res = mmioGetInfo(hmmio, &mmio, 0);
ok(res == MMSYSERR_NOERROR, "expected 0, got %d\n", res);
ok(mmio.lBufOffset == 0, "expected 0, got %d\n", mmio.lBufOffset);
ok(mmio.pchNext-mmio.pchBuffer == size, "expected %d, got %d\n", size, (int)(mmio.pchNext-mmio.pchBuffer));
ok(mmio.pchEndRead-mmio.pchBuffer == sizeof(buffer), "got %d\n", (int)(mmio.pchEndRead-mmio.pchBuffer));
/* seeking to another buffer chunk, the buffer is empty */
size = sizeof(buffer) * 3 + sizeof(buffer) / 2;
pos = mmioSeek(hmmio, size, SEEK_SET);
ok(pos == size, "failed to seek, got %d\n", pos);
res = mmioGetInfo(hmmio, &mmio, 0);
ok(res == MMSYSERR_NOERROR, "expected 0, got %d\n", res);
ok(mmio.lBufOffset == size, "expected %d, got %d\n", size, mmio.lBufOffset);
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
/* reading a lot (as sizeof(data) > mmio.cchBuffer), the buffer is empty */
size = mmioRead(hmmio, data, sizeof(data));
ok(size == sizeof(data), "failed to read, got %d\n", size);
res = mmioGetInfo(hmmio, &mmio, 0);
ok(res == MMSYSERR_NOERROR, "expected 0, got %d\n", res);
ok(mmio.lBufOffset == pos+size, "expected %d, got %d\n", pos+size, mmio.lBufOffset);
ok(mmio.pchNext == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchNext);
ok(mmio.pchEndRead == mmio.pchBuffer, "expected %p, got %p\n", mmio.pchBuffer, mmio.pchEndRead);
mmioClose(hmmio, 0);
DeleteFileA(test_file);
}
START_TEST(mmio)
{
/* Make it possible to run the tests against a specific AVI file in
* addition to the builtin test data. This is mostly meant as a
* debugging aid and is not part of the standard tests.
*/
char fname[] = "msrle.avi";
test_mmioDescend(NULL);
@ -450,4 +920,8 @@ START_TEST(mmio)
test_mmioOpen(fname);
test_mmioSetBuffer(NULL);
test_mmioSetBuffer(fname);
test_mmioOpen_fourcc();
test_mmioSeek();
test_mmio_end_of_file();
test_mmio_buffer_pointer();
}

View file

@ -1,13 +1,13 @@
/* Automatically generated file; DO NOT EDIT!! */
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define STANDALONE
#include "wine/test.h"
#include <wine/test.h>
extern void func_capture(void);
extern void func_joystick(void);
extern void func_mci(void);
extern void func_mcicda(void);
extern void func_midi(void);
extern void func_mixer(void);
extern void func_mmio(void);
extern void func_timer(void);
@ -16,7 +16,10 @@ extern void func_wave(void);
const struct test winetest_testlist[] =
{
{ "capture", func_capture },
{ "joystick", func_joystick },
{ "mci", func_mci },
{ "mcicda", func_mcicda },
{ "midi", func_midi },
{ "mixer", func_mixer },
{ "mmio", func_mmio },
{ "timer", func_timer },

View file

@ -190,6 +190,7 @@ static void test_priority(void)
"thread priority is %s, should be THREAD_PRIORITY_TIME_CRITICAL\n",
get_priority(priority));
}
timeKillEvent(id);
}
START_TEST(timer)

View file

@ -30,12 +30,20 @@
#include "winnls.h"
#include "mmsystem.h"
#define NOBITMAP
#include "mmddk.h"
#include "mmreg.h"
#include "ks.h"
#include "ksmedia.h"
//#include "ks.h"
//#include "ksguid.h"
//#include "ksmedia.h"
#include "winmm_test.h"
/* FIXME */
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
static DWORD g_tid;
static void test_multiple_waveopens(void)
{
HWAVEOUT handle1, handle2;
@ -262,10 +270,9 @@ const char* wave_out_error(MMRESULT error)
static char long_msg[1100];
MMRESULT rc;
rc = waveOutGetErrorText(error, msg, sizeof(msg));
rc = waveOutGetErrorTextA(error, msg, sizeof(msg));
if (rc != MMSYSERR_NOERROR)
sprintf(long_msg, "waveOutGetErrorText(%x) failed with error %x",
error, rc);
sprintf(long_msg, "waveOutGetErrorTextA(%x) failed with error %x", error, rc);
else
sprintf(long_msg, "%s(%s)", mmsys_error(error), msg);
return long_msg;
@ -274,7 +281,7 @@ const char* wave_out_error(MMRESULT error)
const char * wave_open_flags(DWORD flags)
{
static char msg[1024];
int first = TRUE;
BOOL first = TRUE;
msg[0] = 0;
if ((flags & CALLBACK_TYPEMASK) == CALLBACK_EVENT) {
strcat(msg, "CALLBACK_EVENT");
@ -318,7 +325,6 @@ const char * wave_open_flags(DWORD flags)
if ((flags & WAVE_MAPPED) == WAVE_MAPPED) {
if (!first) strcat(msg, "|");
strcat(msg, "WAVE_MAPPED");
first = FALSE;
}
return msg;
}
@ -327,7 +333,7 @@ static const char * wave_header_flags(DWORD flags)
{
#define WHDR_MASK (WHDR_BEGINLOOP|WHDR_DONE|WHDR_ENDLOOP|WHDR_INQUEUE|WHDR_PREPARED)
static char msg[1024];
int first = TRUE;
BOOL first = TRUE;
msg[0] = 0;
if (flags & WHDR_BEGINLOOP) {
strcat(msg, "WHDR_BEGINLOOP");
@ -468,10 +474,10 @@ DWORD time_to_bytes(LPMMTIME mmtime, LPWAVEFORMATEX pwfx)
else if (mmtime->wType == TIME_MS)
return mmtime->u.ms * pwfx->nAvgBytesPerSec / 1000;
else if (mmtime->wType == TIME_SMPTE)
return ((mmtime->u.smpte.hour * 60.0 * 60.0) +
(mmtime->u.smpte.min * 60.0) +
(mmtime->u.smpte.sec) +
(mmtime->u.smpte.frame / 30.0)) * pwfx->nAvgBytesPerSec;
return ((mmtime->u.smpte.hour * 60 * 60) +
(mmtime->u.smpte.min * 60) +
(mmtime->u.smpte.sec)) * pwfx->nAvgBytesPerSec +
mmtime->u.smpte.frame * pwfx->nAvgBytesPerSec / 30;
trace("FIXME: time_to_bytes() type not supported\n");
return -1;
@ -481,16 +487,16 @@ static void check_position(int device, HWAVEOUT wout, DWORD bytes,
LPWAVEFORMATEX pwfx )
{
MMTIME mmtime;
DWORD samples;
double duration;
MMRESULT rc;
DWORD returned;
samples=bytes/(pwfx->wBitsPerSample/8*pwfx->nChannels);
duration=((double)samples)/pwfx->nSamplesPerSec;
mmtime.wType = TIME_BYTES;
rc=waveOutGetPosition(wout, &mmtime, sizeof(mmtime) - 1);
ok(rc==MMSYSERR_ERROR,
"waveOutGetPosition(%s): rc=%s\n",dev_name(device),wave_out_error(rc));
mmtime.wType = TIME_BYTES;
rc=waveOutGetPosition(wout, &mmtime, sizeof(mmtime));
rc=waveOutGetPosition(wout, &mmtime, sizeof(mmtime) + 1);
ok(rc==MMSYSERR_NOERROR,
"waveOutGetPosition(%s): rc=%s\n",dev_name(device),wave_out_error(rc));
if (mmtime.wType != TIME_BYTES && winetest_debug > 1)
@ -564,6 +570,8 @@ static void CALLBACK callback_func(HWAVEOUT hwo, UINT uMsg,
DWORD_PTR dwInstance,
DWORD dwParam1, DWORD dwParam2)
{
if(uMsg == WOM_OPEN || uMsg == WOM_CLOSE)
ok(GetCurrentThreadId() == g_tid, "Got different thread ID\n");
SetEvent((HANDLE)dwInstance);
}
@ -574,12 +582,12 @@ static DWORD WINAPI callback_thread(LPVOID lpParameter)
PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ); /* make sure the thread has a message queue */
SetEvent(lpParameter);
while (GetMessage(&msg, 0, 0, 0)) {
while (GetMessageA(&msg, 0, 0, 0)) {
UINT message = msg.message;
/* for some reason XP sends a WM_USER message before WOM_OPEN */
ok (message == WOM_OPEN || message == WOM_DONE ||
message == WOM_CLOSE || message == WM_USER || message == WM_APP,
"GetMessage returned unexpected message: %u\n", message);
"GetMessageA returned unexpected message: %u\n", message);
if (message == WOM_OPEN || message == WOM_DONE || message == WOM_CLOSE)
SetEvent(lpParameter);
else if (message == WM_APP) {
@ -591,21 +599,19 @@ static DWORD WINAPI callback_thread(LPVOID lpParameter)
return 0;
}
static void wave_out_test_deviceOut(int device, double duration,
int headers, int loops,
LPWAVEFORMATEX pwfx, DWORD format,
DWORD flags, LPWAVEOUTCAPS pcaps,
BOOL interactive, BOOL sine, BOOL pause)
static void wave_out_test_deviceOut(int device, double duration, int headers, int loops,
WAVEFORMATEX *pwfx, DWORD format, DWORD flags, WAVEOUTCAPSA *pcaps, BOOL interactive,
BOOL sine, BOOL pause)
{
HWAVEOUT wout;
HANDLE hevent;
HANDLE hevent = CreateEventW(NULL, FALSE, FALSE, NULL);
WAVEHDR *frags = 0;
MMRESULT rc;
DWORD volume;
WORD nChannels = pwfx->nChannels;
WORD wBitsPerSample = pwfx->wBitsPerSample;
DWORD nSamplesPerSec = pwfx->nSamplesPerSec;
BOOL has_volume = pcaps->dwSupport & WAVECAPS_VOLUME ? TRUE : FALSE;
BOOL has_volume = (pcaps->dwSupport & WAVECAPS_VOLUME) != 0;
double paused = 0.0;
DWORD_PTR callback = 0;
DWORD_PTR callback_instance = 0;
@ -616,11 +622,6 @@ static void wave_out_test_deviceOut(int device, double duration,
DWORD frag_length;
int i, j;
hevent=CreateEvent(NULL,FALSE,FALSE,NULL);
ok(hevent!=NULL,"CreateEvent(): error=%d\n",GetLastError());
if (hevent==NULL)
return;
if ((flags & CALLBACK_TYPEMASK) == CALLBACK_EVENT) {
callback = (DWORD_PTR)hevent;
callback_instance = 0;
@ -653,6 +654,7 @@ static void wave_out_test_deviceOut(int device, double duration,
return;
}
wout=NULL;
g_tid = GetCurrentThreadId();
rc=waveOutOpen(&wout,device,pwfx,callback,callback_instance,flags);
/* Note: Win9x doesn't know WAVE_FORMAT_DIRECT */
/* It is acceptable to fail on formats that are not specified to work */
@ -665,13 +667,13 @@ static void wave_out_test_deviceOut(int device, double duration,
(!(flags & WAVE_FORMAT_DIRECT) || (flags & WAVE_MAPPED)) &&
!(pcaps->dwFormats & format)) ||
(rc==MMSYSERR_INVALFLAG && (flags & WAVE_FORMAT_DIRECT)),
"waveOutOpen(%s): format=%dx%2dx%d flags=%lx(%s) rc=%s\n",
"waveOutOpen(%s): format=%dx%2dx%d flags=%x(%s) rc=%s\n",
dev_name(device),pwfx->nSamplesPerSec,pwfx->wBitsPerSample,
pwfx->nChannels,CALLBACK_EVENT|flags,
wave_open_flags(CALLBACK_EVENT|flags),wave_out_error(rc));
if ((rc==WAVERR_BADFORMAT || rc==MMSYSERR_NOTSUPPORTED) &&
(flags & WAVE_FORMAT_DIRECT) && (pcaps->dwFormats & format))
trace(" Reason: The device lists this format as supported in it's "
trace(" Reason: The device lists this format as supported in its "
"capabilities but opening it failed.\n");
if ((rc==WAVERR_BADFORMAT || rc==MMSYSERR_NOTSUPPORTED) &&
!(pcaps->dwFormats & format))
@ -683,7 +685,8 @@ static void wave_out_test_deviceOut(int device, double duration,
if (rc!=MMSYSERR_NOERROR)
goto EXIT;
WaitForSingleObject(hevent,10000);
rc=WaitForSingleObject(hevent,9000);
ok(rc==WAIT_OBJECT_0, "missing WOM_OPEN notification\n");
ok(pwfx->nChannels==nChannels &&
pwfx->wBitsPerSample==wBitsPerSample &&
@ -726,9 +729,8 @@ static void wave_out_test_deviceOut(int device, double duration,
}
if (interactive && rc==MMSYSERR_NOERROR) {
DWORD start;
trace("Playing %g second %s at %5dx%2dx%d %2d header%s %d loop%s %d bytes %s %s\n",duration,
sine ? "440Hz tone" : "silence",pwfx->nSamplesPerSec,
sine ? "440 Hz tone" : "silence", pwfx->nSamplesPerSec,
pwfx->wBitsPerSample,pwfx->nChannels, headers, headers > 1 ? "s": " ",
loops, loops == 1 ? " " : "s", length * (loops + 1),
get_format_str(pwfx->wFormatTag),
@ -747,8 +749,6 @@ static void wave_out_test_deviceOut(int device, double duration,
ok(has_volume ? rc==MMSYSERR_NOERROR : rc==MMSYSERR_NOTSUPPORTED,
"waveOutSetVolume(%s): rc=%s\n",dev_name(device),wave_out_error(rc));
start=GetTickCount();
rc=waveOutWrite(wout, &frags[0], sizeof(frags[0]));
ok(rc==MMSYSERR_NOERROR,"waveOutWrite(%s): rc=%s\n",
dev_name(device),wave_out_error(rc));
@ -789,7 +789,8 @@ static void wave_out_test_deviceOut(int device, double duration,
ok(rc==MMSYSERR_NOERROR,"waveOutWrite(%s, header[%d]): rc=%s\n",
dev_name(device),(i+1)%headers,wave_out_error(rc));
}
WaitForSingleObject(hevent,10000);
rc=WaitForSingleObject(hevent,8000);
ok(rc==WAIT_OBJECT_0, "missing WOM_DONE notification\n");
}
}
@ -809,15 +810,65 @@ static void wave_out_test_deviceOut(int device, double duration,
"waveOutUnprepareHeader(%s): rc=%s\n",dev_name(device),
wave_out_error(rc));
}
HeapFree(GetProcessHeap(), 0, buffer);
ok(frags[0].dwFlags==(interactive ? WHDR_DONE : 0), "dwFlags(%d)=%x\n",device,frags[0].dwFlags);
frags[0].dwFlags |= WHDR_DONE;
rc=waveOutUnprepareHeader(wout, &frags[0], sizeof(frags[0]));
ok(rc==MMSYSERR_NOERROR, "waveOutUnprepareHeader(%d): rc=%s\n",device,wave_out_error(rc));
ok(frags[0].dwFlags==WHDR_DONE, "dwFlags(%d)=%x\n",device,frags[0].dwFlags);
frags[0].dwFlags |= WHDR_INQUEUE;
rc=waveOutPrepareHeader(wout, &frags[0], sizeof(frags[0]));
ok(rc==MMSYSERR_NOERROR, "waveOutPrepareHeader(%d): rc=%s\n",device,wave_out_error(rc));
ok(frags[0].dwFlags==WHDR_PREPARED, "dwFlags(%d)=%x\n",device,frags[0].dwFlags);
frags[0].dwFlags |= WHDR_INQUEUE;
rc=waveOutPrepareHeader(wout, &frags[0], sizeof(frags[0]));
ok(rc==MMSYSERR_NOERROR, "waveOutPrepareHeader(%d): rc=%s\n",device,wave_out_error(rc));
ok(frags[0].dwFlags==(WHDR_PREPARED|WHDR_INQUEUE), "dwFlags(%d)=%x\n",device,frags[0].dwFlags);
frags[0].dwFlags &= ~(WHDR_INQUEUE|WHDR_DONE);
rc=waveOutUnprepareHeader(wout, &frags[0], sizeof(frags[0]));
ok(rc==MMSYSERR_NOERROR, "waveOutUnprepareHeader(%d): rc=%s\n",device,wave_out_error(rc));
ok(frags[0].dwFlags==0, "dwFlags(%d)=%x\n",device,frags[0].dwFlags);
rc=waveOutClose(wout);
ok(rc==MMSYSERR_NOERROR,"waveOutClose(%s): rc=%s\n",dev_name(device),
wave_out_error(rc));
WaitForSingleObject(hevent,10000);
if (rc==WAVERR_STILLPLAYING) {
/* waveOutReset ought to return all buffers s.t. waveOutClose succeeds */
rc=waveOutReset(wout);
ok(rc==MMSYSERR_NOERROR,"waveOutReset(%s): rc=%s\n",dev_name(device),
wave_out_error(rc));
for (i = 0; i < headers; i++) {
rc=waveOutUnprepareHeader(wout, &frags[i], sizeof(frags[0]));
ok(rc==MMSYSERR_NOERROR,
"waveOutUnprepareHeader(%s): rc=%s\n",dev_name(device),
wave_out_error(rc));
}
rc=waveOutClose(wout);
ok(rc==MMSYSERR_NOERROR,"waveOutClose(%s): rc=%s\n",dev_name(device),
wave_out_error(rc));
}
rc=WaitForSingleObject(hevent,1500);
ok(rc==WAIT_OBJECT_0, "missing WOM_CLOSE notification\n");
wout = (HWAVEOUT)0xdeadf00d;
rc=waveOutOpen(&wout,device,pwfx,callback,callback_instance,flags|WAVE_FORMAT_QUERY);
ok(rc==MMSYSERR_NOERROR, "WAVE_FORMAT_QUERY(%s): rc=%s\n",dev_name(device),
wave_out_error(rc));
ok(wout==(HWAVEOUT)0xdeadf00d, "WAVE_FORMAT_QUERY handle %p\n", wout);
rc=WaitForSingleObject(hevent,20);
ok(rc==WAIT_TIMEOUT, "Notification from %s rc=%x\n",
wave_open_flags(flags|WAVE_FORMAT_QUERY),rc);
HeapFree(GetProcessHeap(), 0, buffer);
EXIT:
if ((flags & CALLBACK_TYPEMASK) == CALLBACK_THREAD) {
PostThreadMessage(thread_id, WM_APP, 0, 0);
PostThreadMessageW(thread_id, WM_APP, 0, 0);
WaitForSingleObject(hevent,10000);
}
CloseHandle(hevent);
@ -828,7 +879,7 @@ static void wave_out_test_device(UINT_PTR device)
{
WAVEOUTCAPSA capsA;
WAVEOUTCAPSW capsW;
WAVEFORMATEX format, oformat;
WAVEFORMATEX format;
WAVEFORMATEXTENSIBLE wfex;
IMAADPCMWAVEFORMAT wfa;
HWAVEOUT wout;
@ -894,11 +945,16 @@ static void wave_out_test_device(UINT_PTR device)
"waveOutGetDevCapsW(%s): unexpected return value %s\n",
dev_name(device),wave_out_error(rc));
rc=waveOutMessage((HWAVEOUT)device, DRV_QUERYMAPPABLE, 0, 0);
ok(rc==MMSYSERR_NOERROR || rc==MMSYSERR_NOTSUPPORTED,
"DRV_QUERYMAPPABLE(%s): unexpected return value %s\n",
dev_name(device),wave_out_error(rc));
nameA=NULL;
rc=waveOutMessage((HWAVEOUT)device, DRV_QUERYDEVICEINTERFACESIZE,
(DWORD_PTR)&size, 0);
ok(rc==MMSYSERR_NOERROR || rc==MMSYSERR_INVALPARAM ||
rc==MMSYSERR_NOTSUPPORTED,
ok(rc==MMSYSERR_NOERROR || broken(rc==MMSYSERR_INVALPARAM ||
rc==MMSYSERR_NOTSUPPORTED),
"waveOutMessage(%s): failed to get interface size, rc=%s\n",
dev_name(device),wave_out_error(rc));
if (rc==MMSYSERR_NOERROR) {
@ -967,20 +1023,20 @@ static void wave_out_test_device(UINT_PTR device)
format.nBlockAlign=format.nChannels*format.wBitsPerSample/8;
format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign;
format.cbSize=0;
wave_out_test_deviceOut(device,1.0,1,0,&format,WAVE_FORMAT_2M08,
wave_out_test_deviceOut(device,0.6,1,0,&format,WAVE_FORMAT_2M08,
CALLBACK_EVENT,&capsA,TRUE,FALSE,FALSE);
wave_out_test_deviceOut(device,1.0,1,0,&format,WAVE_FORMAT_2M08,
wave_out_test_deviceOut(device,0.6,1,0,&format,WAVE_FORMAT_2M08,
CALLBACK_EVENT,&capsA,TRUE,FALSE,TRUE);
wave_out_test_deviceOut(device,1.0,1,0,&format,WAVE_FORMAT_2M08,
wave_out_test_deviceOut(device,0.6,1,0,&format,WAVE_FORMAT_2M08,
CALLBACK_FUNCTION,&capsA,TRUE,FALSE,FALSE);
wave_out_test_deviceOut(device,1.0,1,0,&format,WAVE_FORMAT_2M08,
wave_out_test_deviceOut(device,0.6,1,0,&format,WAVE_FORMAT_2M08,
CALLBACK_FUNCTION,&capsA,TRUE,FALSE,TRUE);
wave_out_test_deviceOut(device,1.0,1,0,&format,WAVE_FORMAT_2M08,
wave_out_test_deviceOut(device,0.6,1,0,&format,WAVE_FORMAT_2M08,
CALLBACK_THREAD,&capsA,TRUE,FALSE,FALSE);
wave_out_test_deviceOut(device,1.0,1,0,&format,WAVE_FORMAT_2M08,
wave_out_test_deviceOut(device,0.6,1,0,&format,WAVE_FORMAT_2M08,
CALLBACK_THREAD,&capsA,TRUE,FALSE,TRUE);
wave_out_test_deviceOut(device,1.0,10,0,&format,WAVE_FORMAT_2M08,
wave_out_test_deviceOut(device,0.8,10,0,&format,WAVE_FORMAT_2M08,
CALLBACK_EVENT,&capsA,TRUE,FALSE,FALSE);
wave_out_test_deviceOut(device,1.0,5,1,&format,WAVE_FORMAT_2M08,
CALLBACK_EVENT,&capsA,TRUE,FALSE,FALSE);
@ -1111,52 +1167,6 @@ static void wave_out_test_device(UINT_PTR device)
VirtualFree(twoPages, 0, MEM_RELEASE);
}
/* Testing invalid format: 11 bits per sample */
format.wFormatTag=WAVE_FORMAT_PCM;
format.nChannels=2;
format.wBitsPerSample=11;
format.nSamplesPerSec=22050;
format.nBlockAlign=format.nChannels*format.wBitsPerSample/8;
format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign;
format.cbSize=0;
oformat=format;
rc=waveOutOpen(&wout,device,&format,0,0,CALLBACK_NULL|WAVE_FORMAT_DIRECT);
ok(rc==WAVERR_BADFORMAT || rc==MMSYSERR_INVALFLAG ||
rc==MMSYSERR_INVALPARAM,
"waveOutOpen(%s): opening the device in 11 bits mode should fail: "
"rc=%s\n",dev_name(device),wave_out_error(rc));
if (rc==MMSYSERR_NOERROR) {
trace(" got %dx%2dx%d for %dx%2dx%d\n",
format.nSamplesPerSec, format.wBitsPerSample,
format.nChannels,
oformat.nSamplesPerSec, oformat.wBitsPerSample,
oformat.nChannels);
waveOutClose(wout);
}
/* Testing invalid format: 2 MHz sample rate */
format.wFormatTag=WAVE_FORMAT_PCM;
format.nChannels=2;
format.wBitsPerSample=16;
format.nSamplesPerSec=2000000;
format.nBlockAlign=format.nChannels*format.wBitsPerSample/8;
format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign;
format.cbSize=0;
oformat=format;
rc=waveOutOpen(&wout,device,&format,0,0,CALLBACK_NULL|WAVE_FORMAT_DIRECT);
ok(rc==WAVERR_BADFORMAT || rc==MMSYSERR_INVALFLAG ||
rc==MMSYSERR_INVALPARAM,
"waveOutOpen(%s): opening the device at 2 MHz sample rate should fail: "
"rc=%s\n",dev_name(device),wave_out_error(rc));
if (rc==MMSYSERR_NOERROR) {
trace(" got %dx%2dx%d for %dx%2dx%d\n",
format.nSamplesPerSec, format.wBitsPerSample,
format.nChannels,
oformat.nSamplesPerSec, oformat.wBitsPerSample,
oformat.nChannels);
waveOutClose(wout);
}
/* try some non PCM formats */
format.wFormatTag=WAVE_FORMAT_MULAW;
format.nChannels=1;
@ -1177,10 +1187,23 @@ static void wave_out_test_device(UINT_PTR device)
&capsA,winetest_interactive,TRUE,FALSE);
wave_out_test_deviceOut(device,1.0,5,1,&format,0,CALLBACK_EVENT,
&capsA,winetest_interactive,TRUE,FALSE);
} else
} else {
MMRESULT query_rc;
trace("waveOutOpen(%s): WAVE_FORMAT_MULAW not supported\n",
dev_name(device));
query_rc = waveOutOpen(NULL, device, &format, 0, 0, CALLBACK_NULL | WAVE_FORMAT_QUERY);
ok(query_rc==MMSYSERR_NOERROR || query_rc==WAVERR_BADFORMAT || query_rc==MMSYSERR_INVALPARAM,
"waveOutOpen(%s): returned %s\n",dev_name(device),wave_out_error(rc));
rc = waveOutOpen(&wout, device, &format, 0, 0, CALLBACK_NULL);
ok(rc == query_rc,
"waveOutOpen(%s): returned different from query: %s\n",dev_name(device),wave_out_error(rc));
if(rc == MMSYSERR_NOERROR)
waveOutClose(wout);
}
wfa.wfx.wFormatTag=WAVE_FORMAT_IMA_ADPCM;
wfa.wfx.nChannels=1;
wfa.wfx.nSamplesPerSec=11025;
@ -1402,11 +1425,22 @@ static void wave_out_tests(void)
WAVEFORMATEX format;
HWAVEOUT wout;
MMRESULT rc;
DWORD preferred, status;
UINT ndev,d;
ndev=waveOutGetNumDevs();
trace("found %d WaveOut devices\n",ndev);
rc = waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
(DWORD_PTR)&preferred, (DWORD_PTR)&status);
ok((ndev == 0 && (rc == MMSYSERR_NODRIVER || rc == MMSYSERR_BADDEVICEID)) ||
rc == MMSYSERR_NOTSUPPORTED ||
rc == MMSYSERR_NOERROR, "waveOutMessage(DRVM_MAPPER_PREFERRED_GET) failed: %u\n", rc);
if(rc != MMSYSERR_NOTSUPPORTED)
ok((ndev == 0 && (preferred == -1 || broken(preferred != -1))) ||
preferred < ndev, "Got invalid preferred device: 0x%x\n", preferred);
rc=waveOutGetDevCapsA(ndev+1,&capsA,sizeof(capsA));
ok(rc==MMSYSERR_BADDEVICEID,
"waveOutGetDevCapsA(%s): MMSYSERR_BADDEVICEID expected, got %s\n",
@ -1451,15 +1485,128 @@ static void wave_out_tests(void)
"waveOutOpen(%s): MMSYSERR_BADDEVICEID expected, got %s\n",
dev_name(ndev+1),mmsys_error(rc));
for (d=0;d<ndev;d++)
wave_out_test_device(d);
if(winetest_interactive)
for (d=0;d<ndev;d++)
wave_out_test_device(d);
if (ndev>0)
wave_out_test_device(WAVE_MAPPER);
}
static void test_sndPlaySound(void)
{
BOOL br;
static const WCHAR not_existW[] = {'C',':','\\','n','o','t','_','e','x','i','s','t','.','w','a','v',0};
static const WCHAR SystemAsteriskW[] = {'S','y','s','t','e','m','A','s','t','e','r','i','s','k',0};
br = sndPlaySoundA((LPCSTR)SND_ALIAS_SYSTEMASTERISK, SND_ALIAS_ID|SND_SYNC);
ok(br == TRUE || br == FALSE, "sndPlaySound gave strange return: %u\n", br);
br = sndPlaySoundW((LPCWSTR)SND_ALIAS_SYSTEMASTERISK, SND_ALIAS_ID|SND_SYNC);
ok(br == TRUE || br == FALSE, "sndPlaySound gave strange return: %u\n", br);
br = sndPlaySoundA((LPCSTR)sndAlias('X','Y'), SND_ALIAS_ID|SND_SYNC);
ok(br == TRUE || br == FALSE, "sndPlaySound gave strange return: %u\n", br);
br = sndPlaySoundW((LPCWSTR)sndAlias('X','Y'), SND_ALIAS_ID|SND_SYNC);
ok(br == TRUE || br == FALSE, "sndPlaySound gave strange return: %u\n", br);
br = sndPlaySoundA("SystemAsterisk", SND_ALIAS|SND_SYNC);
ok(br == TRUE || br == FALSE, "sndPlaySound gave strange return: %u\n", br);
br = sndPlaySoundW(SystemAsteriskW, SND_ALIAS|SND_SYNC);
ok(br == TRUE || br == FALSE, "sndPlaySound gave strange return: %u\n", br);
br = sndPlaySoundA("C:\not_exist.wav", SND_FILENAME|SND_SYNC);
ok(br == TRUE || br == FALSE, "sndPlaySound gave strange return: %u\n", br);
br = sndPlaySoundW(not_existW, SND_FILENAME|SND_SYNC);
ok(br == TRUE || br == FALSE, "sndPlaySound gave strange return: %u\n", br);
}
static void test_fragmentsize(void)
{
MMRESULT rc;
WAVEHDR hdr[2];
HWAVEOUT wout;
WAVEFORMATEX fmt;
MMTIME mmtime;
DWORD wait;
HANDLE hevent;
if(waveOutGetNumDevs() == 0)
return;
fmt.wFormatTag = WAVE_FORMAT_PCM;
fmt.nChannels = 2;
fmt.nSamplesPerSec = 44100;
fmt.wBitsPerSample = 16;
fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
fmt.nAvgBytesPerSec = fmt.nBlockAlign * fmt.nSamplesPerSec;
fmt.cbSize = sizeof(WAVEFORMATEX);
hevent = CreateEventW(NULL, FALSE, FALSE, NULL);
g_tid = GetCurrentThreadId();
rc = waveOutOpen(&wout, WAVE_MAPPER, &fmt, (DWORD_PTR)callback_func,
(DWORD_PTR)hevent, CALLBACK_FUNCTION);
ok(rc == MMSYSERR_NOERROR || rc == WAVERR_BADFORMAT ||
rc == MMSYSERR_INVALFLAG || rc == MMSYSERR_INVALPARAM,
"waveOutOpen(%s) failed: %s\n", dev_name(WAVE_MAPPER), wave_out_error(rc));
if(rc != MMSYSERR_NOERROR){
CloseHandle(hevent);
return;
}
wait = WaitForSingleObject(hevent, 1000);
ok(wait == WAIT_OBJECT_0, "wave open callback missed\n");
memset(hdr, 0, sizeof(hdr));
hdr[0].dwBufferLength = (fmt.nSamplesPerSec * fmt.nBlockAlign / 4) + 1;
hdr[1].dwBufferLength = hdr[0].dwBufferLength - 2;
hdr[1].lpData = hdr[0].lpData =
HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, hdr[0].dwBufferLength);
rc = waveOutPrepareHeader(wout, &hdr[0], sizeof(hdr[0]));
ok(rc == MMSYSERR_NOERROR, "waveOutPrepareHeader failed: %s\n", wave_out_error(rc));
rc = waveOutPrepareHeader(wout, &hdr[1], sizeof(hdr[1]));
ok(rc == MMSYSERR_NOERROR, "waveOutPrepareHeader failed: %s\n", wave_out_error(rc));
trace("writing %u bytes then %u bytes\n", hdr[0].dwBufferLength, hdr[1].dwBufferLength);
rc = waveOutWrite(wout, &hdr[0], sizeof(hdr[0]));
ok(rc == MMSYSERR_NOERROR, "waveOutWrite failed: %s\n", wave_out_error(rc));
rc = waveOutWrite(wout, &hdr[1], sizeof(hdr[1]));
ok(rc == MMSYSERR_NOERROR, "waveOutWrite failed: %s\n", wave_out_error(rc));
wait = WaitForSingleObject(hevent, 1000);
ok(wait == WAIT_OBJECT_0, "header 1 callback missed\n");
wait = WaitForSingleObject(hevent, 1000);
ok(wait == WAIT_OBJECT_0, "header 2 callback missed\n");
memset(&mmtime, 0, sizeof(mmtime));
mmtime.wType = TIME_BYTES;
rc = waveOutGetPosition(wout, &mmtime, sizeof(mmtime));
ok(rc == MMSYSERR_NOERROR, "waveOutGetPosition failed: %s\n", wave_out_error(rc));
/* windows behavior is inconsistent */
ok(mmtime.u.cb == 88200 ||
mmtime.u.cb == 88196, "after position: %u\n", mmtime.u.cb);
rc = waveOutClose(wout);
ok(rc == MMSYSERR_NOERROR, "waveOutClose failed: %s\n", wave_out_error(rc));
CloseHandle(hevent);
}
START_TEST(wave)
{
test_multiple_waveopens();
wave_out_tests();
test_sndPlaySound();
test_fragmentsize();
}