list(APPEND SOURCE
capture.c
+ joystick.c
mci.c
+ mcicda.c
+ midi.c
mixer.c
mmio.c
timer.c
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)
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
+#include "mmddk.h"
#include "mmsystem.h"
#define NOBITMAP
#include "mmreg.h"
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;
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,
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 */
(!(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))
"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);
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));
{
WAVEINCAPSA capsA;
WAVEINCAPSW capsW;
- WAVEFORMATEX format,oformat;
+ WAVEFORMATEX format;
WAVEFORMATEXTENSIBLE wfex;
HWAVEIN win;
MMRESULT rc;
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;
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);
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);
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);
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);
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);
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);
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);
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);
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,
"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;
--- /dev/null
+/*
+ * 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();
+}
static MCIERROR ok_saved = MCIERR_FILE_NOT_FOUND;
typedef union {
+ MCI_INFO_PARMSA info;
MCI_STATUS_PARMS status;
MCI_WAVE_SET_PARMS set;
- MCI_WAVE_OPEN_PARMS open;
+ MCI_WAVE_OPEN_PARMSA open;
+ MCI_GETDEVCAPS_PARMS caps;
+ MCI_SYSINFO_PARMSA sys;
MCI_SEEK_PARMS seek;
+ MCI_GENERIC_PARMS gen;
} MCI_PARMS_UNION;
-static const char* dbg_mcierr(MCIERROR err)
+const char* dbg_mcierr(MCIERROR err)
{
switch (err) {
case 0: return "0=NOERROR";
return FALSE;
}
-static void test_notification(HWND hwnd, const char* command, WPARAM type)
+/* 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==0)
- ok(!seen, "Expect no message from command %s\n", command);
- else
- ok(seen, "PeekMessage should succeed for command %s\n", command);
- if(seen) {
- ok(msg.hwnd == hwnd, "Didn't get the handle to our test window\n");
- ok(msg.message == MM_MCINOTIFY, "got %04x instead of MM_MCINOTIFY from command %s\n", msg.message, command);
- ok(msg.wParam == type, "got %04lx instead of MCI_NOTIFY_xyz %04lx from command %s\n", msg.wParam, type, command);
+ 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);
}
-static void test_notification1(HWND hwnd, const char* command, WPARAM type)
-{ /* This version works with todo_wine prefix. */
- MSG msg;
- BOOL seen;
- do { seen = PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE); }
- while(seen && spurious_message(&msg));
- if(type==0)
- ok(!seen, "Expect no message from command %s\n", command);
- else if(seen)
- ok(msg.message == MM_MCINOTIFY && msg.wParam == type,"got %04lx instead of MCI_NOTIFY_xyz %04lx from command %s\n", msg.wParam, type, command);
- else ok(seen, "PeekMessage should succeed for command %s\n", command);
+
+static int strcmp_wa(LPCWSTR strw, const char *stra)
+{
+ CHAR buf[512];
+ WideCharToMultiByte(CP_ACP, 0, strw, -1, buf, sizeof(buf), 0, 0);
+ return lstrcmpA(buf, stra);
+}
+
+static void test_mciParser(HWND hwnd)
+{
+ MCIERROR err;
+ MCIDEVICEID wDeviceID;
+ MCI_PARMS_UNION parm;
+ char buf[1024];
+ memset(buf, 0, sizeof(buf));
+ test_notification(hwnd, "-prior to parser test-", 0);
+
+ /* Get a handle on an MCI device, works even without sound. */
+ parm.open.lpstrDeviceType = "waveaudio";
+ parm.open.lpstrElementName = ""; /* "new" at the command level */
+ parm.open.lpstrAlias = "x"; /* to enable mciSendStringA */
+ parm.open.dwCallback = (DWORD_PTR)hwnd;
+ err = mciSendCommandA(0, MCI_OPEN,
+ MCI_OPEN_ELEMENT | MCI_OPEN_TYPE | MCI_OPEN_ALIAS | MCI_NOTIFY, (DWORD_PTR)&parm);
+ ok(!err,"mciCommand open new type waveaudio alias x notify: %s\n", dbg_mcierr(err));
+ wDeviceID = parm.open.wDeviceID;
+ ok(!strcmp(parm.open.lpstrDeviceType,"waveaudio"), "open modified device type\n");
+
+ test_notification(hwnd, "MCI_OPEN", MCI_NOTIFY_SUCCESSFUL);
+ test_notification(hwnd, "MCI_OPEN no #2", 0);
+
+ err = mciSendStringA("open avivideo alias a", buf, sizeof(buf), hwnd);
+ ok(!err,"open another: %s\n", dbg_mcierr(err));
+
+ buf[0]='z';
+ err = mciSendStringA("", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_MISSING_COMMAND_STRING,"empty string: %s\n", dbg_mcierr(err));
+ ok(!buf[0], "error buffer %s\n", buf);
+
+ buf[0]='d';
+ err = mciSendStringA("open", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_MISSING_DEVICE_NAME,"open void: %s\n", dbg_mcierr(err));
+ ok(!buf[0], "open error buffer %s\n", buf);
+
+ err = mciSendStringA("open notify", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_INVALID_DEVICE_NAME,"open notify: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("open new", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_NEW_REQUIRES_ALIAS,"open new: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("open new type waveaudio alias r shareable shareable", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_DUPLICATE_FLAGS,"open new: %s\n", dbg_mcierr(err));
+ if(!err) mciSendStringA("close r", NULL, 0, NULL);
+
+ err = mciSendStringA("status x position wait wait", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_DUPLICATE_FLAGS,"status wait wait: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("status x length length", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_FLAGS_NOT_COMPATIBLE,"status 2xlength: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("status x length position", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_FLAGS_NOT_COMPATIBLE,"status length+position: %s\n", dbg_mcierr(err));
+
+ buf[0]='I';
+ err = mciSendStringA("set x time format milliseconds time format ms", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_FLAGS_NOT_COMPATIBLE,"status length+position: %s\n", dbg_mcierr(err));
+ ok(!buf[0], "set error buffer %s\n", buf);
+
+ /* device's response, not a parser test */
+ err = mciSendStringA("status x", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_MISSING_PARAMETER,"status waveaudio nokeyword: %s\n", dbg_mcierr(err));
+
+ buf[0]='G';
+ err = mciSendStringA("status a", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_UNSUPPORTED_FUNCTION,"status avivideo nokeyword: %s\n", dbg_mcierr(err));
+ ok(!buf[0], "status error buffer %s\n", buf);
+
+ err = mciSendStringA("status x track", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_BAD_INTEGER,"status waveaudio no track: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("status x track 3", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_MISSING_PARAMETER,"status waveaudio track 3: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("status x 2 track 3", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_OUTOFRANGE,"status 2(position) track 3: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("status x 0x4", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_BAD_CONSTANT, "status 0x4: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("status x 4", buf, sizeof(buf), hwnd);
+ ok(!err,"status 4(mode): %s\n", dbg_mcierr(err));
+ if(!err)ok(!strcmp(buf,"stopped"), "status 4(mode), got: %s\n", buf);
+
+ err = mciSendStringA("status x 4 notify", buf, sizeof(buf), hwnd);
+ todo_wine ok(!err,"status 4(mode) notify: %s\n", dbg_mcierr(err));
+ if(!err)ok(!strcmp(buf,"stopped"), "status 4(mode), got: %s\n", buf);
+ test_notification(hwnd, "status 4 notify", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
+
+ err = mciSendStringA("set x milliseconds", buf, sizeof(buf), hwnd);
+ todo_wine ok(err==MCIERR_UNRECOGNIZED_KEYWORD,"set milliseconds: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("set x milliseconds ms", buf, sizeof(buf), hwnd);
+ todo_wine ok(err==MCIERR_UNRECOGNIZED_KEYWORD,"set milliseconds ms: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("capability x can save", buf, sizeof(buf), hwnd);
+ todo_wine ok(!err,"capability can (space) save: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("status x nsa", buf, sizeof(buf), hwnd);
+ todo_wine ok(err==MCIERR_BAD_CONSTANT,"status nsa: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("seek x to 0:0:0:0:0", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_BAD_INTEGER,"seek to 0:0:0:0:0 returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("seek x to 0:0:0:0:", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_BAD_INTEGER,"seek to 0:0:0:0: returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("seek x to :0:0:0:0", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_BAD_INTEGER,"seek to :0:0:0:0 returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("seek x to 256:0:0:0", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_BAD_INTEGER,"seek to 256:0:0:0 returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("seek x to 0:256", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_BAD_INTEGER,"seek to 0:256 returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("status all time format", buf, sizeof(buf), hwnd);
+ ok(err==MCIERR_CANNOT_USE_ALL,"status all: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("cue all", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_UNRECOGNIZED_COMMAND,"cue all: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("open all", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_CANNOT_USE_ALL,"open all: %s\n", dbg_mcierr(err));
+
+ /* avivideo is not a known MCI_DEVTYPE resource name */
+ err = mciSendStringA("sysinfo avivideo quantity", buf, sizeof(buf), hwnd);
+ ok(err==MCIERR_DEVICE_TYPE_REQUIRED,"sysinfo sequencer quantity: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("sysinfo digitalvideo quantity", buf, sizeof(buf), hwnd);
+ ok(!err,"sysinfo digitalvideo quantity: %s\n", dbg_mcierr(err));
+ if(!err) ok(!strcmp(buf,"0"), "sysinfo digitalvideo quantity returned %s\n", buf);
+
+ /* quantity 0 yet open 1 (via type "avivideo"), fun */
+ err = mciSendStringA("sysinfo digitalvideo quantity open", buf, sizeof(buf), hwnd);
+ ok(!err,"sysinfo digitalvideo quantity open: %s\n", dbg_mcierr(err));
+ if(!err) ok(!strcmp(buf,"1"), "sysinfo digitalvideo quantity open returned %s\n", buf);
+
+ err = mciSendStringA("put a window at 0 0", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_BAD_INTEGER,"put incomplete rect: %s\n", dbg_mcierr(err));
+
+ /*w9X-w2k report code from device last opened, newer versions compare them all
+ * and return the one error code or MCIERR_MULTIPLE if they differ. */
+ err = mciSendStringA("pause all", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_MULTIPLE || broken(err==MCIERR_NONAPPLICABLE_FUNCTION),"pause all: %s\n", dbg_mcierr(err));
+ ok(!buf[0], "pause error buffer %s\n", buf);
+
+ /* MCI_STATUS' dwReturn is a DWORD_PTR, others' a plain DWORD. */
+ parm.status.dwItem = MCI_STATUS_TIME_FORMAT;
+ parm.status.dwReturn = 0xFEEDABAD;
+ err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
+ ok(!err,"mciCommand status time format: %s\n", dbg_mcierr(err));
+ if(!err) ok(MCI_FORMAT_MILLISECONDS==parm.status.dwReturn,"status time format: %ld\n",parm.status.dwReturn);
+
+ parm.status.dwItem = MCI_STATUS_MODE;
+ parm.status.dwReturn = 0xFEEDABAD;
+ err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
+ ok(!err,"mciCommand status mode: %s\n", dbg_mcierr(err));
+ if(!err) ok(MCI_MODE_STOP==parm.status.dwReturn,"STATUS mode: %ld\n",parm.status.dwReturn);
+
+ err = mciSendStringA("status x mode", buf, sizeof(buf), hwnd);
+ ok(!err,"status mode: %s\n", dbg_mcierr(err));
+ if(!err) ok(!strcmp(buf, "stopped"), "status mode is %s\n", buf);
+
+ parm.caps.dwItem = MCI_GETDEVCAPS_USES_FILES;
+ parm.caps.dwReturn = 0xFEEDABAD;
+ err = mciSendCommandA(wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&parm);
+ ok(!err,"mciCommand getdevcaps files: %s\n", dbg_mcierr(err));
+ if(!err) ok(1==parm.caps.dwReturn,"getdevcaps files: %d\n",parm.caps.dwReturn);
+
+ parm.caps.dwItem = MCI_GETDEVCAPS_HAS_VIDEO;
+ parm.caps.dwReturn = 0xFEEDABAD;
+ err = mciSendCommandA(wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&parm);
+ ok(!err,"mciCommand getdevcaps video: %s\n", dbg_mcierr(err));
+ if(!err) ok(0==parm.caps.dwReturn,"getdevcaps video: %d\n",parm.caps.dwReturn);
+
+ parm.caps.dwItem = MCI_GETDEVCAPS_DEVICE_TYPE;
+ parm.caps.dwReturn = 0xFEEDABAD;
+ err = mciSendCommandA(wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&parm);
+ ok(!err,"mciCommand getdevcaps video: %s\n", dbg_mcierr(err));
+ if(!err) ok(MCI_DEVTYPE_WAVEFORM_AUDIO==parm.caps.dwReturn,"getdevcaps device type: %d\n",parm.caps.dwReturn);
+
+ err = mciSendStringA("capability x uses files", buf, sizeof(buf), hwnd);
+ ok(!err,"capability files: %s\n", dbg_mcierr(err));
+ if(!err) ok(!strcmp(buf, "true"), "capability files is %s\n", buf);
+
+ err = mciSendStringA("capability x has video", 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);
+
+ err = mciSendStringA("capability x device type", buf, sizeof(buf), hwnd);
+ ok(!err,"capability device type: %s\n", dbg_mcierr(err));
+ if(!err) ok(!strcmp(buf, "waveaudio"), "capability device type is %s\n", buf);
+
+ err = mciSendCommandA(wDeviceID, MCI_CLOSE, 0, 0);
+ ok(!err,"mciCommand close returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("close a", buf, sizeof(buf), hwnd);
+ ok(!err,"close avi: %s\n", dbg_mcierr(err));
+
+ test_notification(hwnd, "-end of 1st set-", 0);
}
static void test_openCloseWAVE(HWND hwnd)
{
MCIERROR err;
- MCI_GENERIC_PARMS parm;
+ MCI_PARMS_UNION parm;
const char command_open[] = "open new type waveaudio alias mysound notify";
const char command_close_my[] = "close mysound notify";
const char command_close_all[] = "close all notify";
const char command_sysinfo[] = "sysinfo waveaudio quantity open";
char buf[1024];
+ DWORD intbuf[3] = { 0xDEADF00D, 99, 0xABADCAFE };
memset(buf, 0, sizeof(buf));
test_notification(hwnd, "-prior to any command-", 0);
- err = mciSendString("sysinfo all quantity", buf, sizeof(buf), hwnd);
- todo_wine ok(!err,"mci %s returned %s\n", command_open, dbg_mcierr(err));
+ /* Avoid Sysinfo quantity with notify because Win9x and newer differ. */
+ err = mciSendStringA("sysinfo all quantity", buf, sizeof(buf), hwnd);
+ ok(!err,"mci sysinfo all quantity returned %s\n", dbg_mcierr(err));
if(!err) trace("[MCI] with %s drivers\n", buf);
- err = mciSendString("open new type waveaudio alias r shareable", buf, sizeof(buf), NULL);
+ parm.sys.lpstrReturn = (LPSTR)&intbuf[1];
+ parm.sys.dwRetSize = sizeof(DWORD);
+ parm.sys.wDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; /* ignored */
+ err = mciSendCommandA(MCI_ALL_DEVICE_ID, MCI_SYSINFO, MCI_SYSINFO_QUANTITY | MCI_WAIT,
+ (DWORD_PTR)&parm);
+ ok(!err, "mciCommand sysinfo all quantity returned %s\n", dbg_mcierr(err));
+ if(!err) ok(atoi(buf)==intbuf[1],"sysinfo all quantity string and command differ\n");
+
+ parm.sys.dwRetSize = sizeof(DWORD)-1;
+ err = mciSendCommandA(MCI_ALL_DEVICE_ID, MCI_SYSINFO, MCI_SYSINFO_QUANTITY, (DWORD_PTR)&parm);
+ ok(err == MCIERR_PARAM_OVERFLOW || broken(!err/* Win9x */),
+ "mciCommand sysinfo with too small buffer returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("open new type waveaudio alias r shareable", buf, sizeof(buf), NULL);
ok(err==MCIERR_UNSUPPORTED_FUNCTION,"mci open new shareable returned %s\n", dbg_mcierr(err));
if(!err) {
- err = mciSendString("close r", NULL, 0, NULL);
+ err = mciSendStringA("close r", NULL, 0, NULL);
ok(!err,"mci close shareable returned %s\n", dbg_mcierr(err));
}
- err = mciSendString(command_open, buf, sizeof(buf), hwnd);
+ err = mciGetDeviceIDA("waveaudio");
+ ok(!err, "mciGetDeviceIDA waveaudio returned %u, expected 0\n", err);
+
+ err = mciSendStringA(command_open, buf, sizeof(buf), hwnd);
ok(!err,"mci %s returned %s\n", command_open, dbg_mcierr(err));
ok(!strcmp(buf,"1"), "mci open deviceId: %s, expected 1\n", buf);
/* Wine<=1.1.33 used to ignore anything past alias XY */
test_notification(hwnd,"open new alias notify",MCI_NOTIFY_SUCCESSFUL);
- err = mciSendString("status mysound time format", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status mysound time format", buf, sizeof(buf), hwnd);
ok(!err,"mci status time format returned %s\n", dbg_mcierr(err));
if(!err) {
if (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) == LANG_ENGLISH)
else trace("locale-dependent time format: %s (ms)\n", buf);
}
- err = mciSendString(command_close_my, NULL, 0, hwnd);
+ memset(buf, 0, sizeof(buf));
+ parm.sys.dwNumber = 1;
+ parm.sys.wDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; /* ignored */
+ parm.sys.lpstrReturn = buf;
+ parm.sys.dwRetSize = sizeof(buf);
+ parm.sys.dwCallback = (DWORD_PTR)hwnd;
+ err = mciSendCommandA(MCI_ALL_DEVICE_ID, MCI_SYSINFO,
+ MCI_SYSINFO_NAME | MCI_SYSINFO_OPEN | MCI_NOTIFY, (DWORD_PTR)&parm);
+ ok(!err,"mciCommand MCI_SYSINFO all name 1 open notify: %s\n", dbg_mcierr(err));
+ if(!err) ok(!strcmp(buf,"mysound"), "sysinfo name returned %s\n", buf);
+ test_notification(hwnd, "SYSINFO name notify\n", MCI_NOTIFY_SUCCESSFUL);
+
+ memset(buf, 0, sizeof(buf));
+ parm.sys.dwNumber = 1;
+ parm.sys.wDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; /* ignored */
+ parm.sys.lpstrReturn = buf;
+ parm.sys.dwRetSize = 8; /* mysound\0 */
+ err = mciSendCommandA(MCI_ALL_DEVICE_ID, MCI_SYSINFO, MCI_SYSINFO_NAME | MCI_SYSINFO_OPEN,
+ (DWORD_PTR)&parm);
+ ok(!err,"mciCommand MCI_SYSINFO all name 1 open buffer[8]: %s\n", dbg_mcierr(err));
+ if(!err) ok(!strcmp(buf,"mysound"), "sysinfo name returned %s\n", buf);
+
+ memset(buf, 0, sizeof(buf));
+ /* dwRetSize counts characters, not bytes, despite what MSDN says. */
+ parm.sys.dwNumber = 1;
+ parm.sys.wDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; /* ignored */
+ parm.sys.lpstrReturn = buf;
+ parm.sys.dwRetSize = 8; /* mysound\0 */
+ /* MCI_..._PARMSA and PARMSW share the same layout, use one for both tests. */
+ err = mciSendCommandW(MCI_ALL_DEVICE_ID, MCI_SYSINFO, MCI_SYSINFO_NAME | MCI_SYSINFO_OPEN, (DWORD_PTR)&parm);
+ ok(!err || broken(err==MMSYSERR_NOTSUPPORTED/* Win9x */), "mciCommandW MCI_SYSINFO all name 1 open buffer[8]: %s\n", dbg_mcierr(err));
+ if(!err) ok(!strcmp_wa((LPWSTR)buf,"mysound"), "sysinfo name 1 open contents\n");
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = 'Y';
+ parm.sys.dwNumber = 1;
+ parm.sys.wDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; /* ignored */
+ parm.sys.lpstrReturn = buf;
+ parm.sys.dwRetSize = 7; /* too short for mysound\0 */
+ err = mciSendCommandW(MCI_ALL_DEVICE_ID, MCI_SYSINFO, MCI_SYSINFO_NAME | MCI_SYSINFO_OPEN, (DWORD_PTR)&parm);
+ ok(err==MCIERR_PARAM_OVERFLOW || broken(err==MMSYSERR_NOTSUPPORTED/* Win9x */), "mciCommandW MCI_SYSINFO all name 1 open too small: %s\n", dbg_mcierr(err));
+ ok(!strcmp(buf,"Y"), "output buffer %s\n", buf);
+
+ /* Win9x overwrites the tiny buffer and returns success, newer versions signal overflow. */
+ memset(buf, 0, sizeof(buf));
+ buf[0] = 'Y';
+ parm.sys.dwNumber = 1;
+ parm.sys.wDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; /* ignored */
+ parm.sys.lpstrReturn = buf;
+ parm.sys.dwRetSize = 2; /* too short for mysound\0 */
+ err = mciSendCommandA(MCI_ALL_DEVICE_ID, MCI_SYSINFO, MCI_SYSINFO_NAME | MCI_SYSINFO_OPEN,
+ (DWORD_PTR)&parm);
+ ok(err==MCIERR_PARAM_OVERFLOW || broken(!err /* Win9x */),"mciCommand MCI_SYSINFO all name 1 open too small: %s\n", dbg_mcierr(err));
+ ok(!strcmp(buf, err ? "Y" : "mysound"), "sysinfo short name returned %s\n", buf);
+
+ err = mciSendStringA("sysinfo mysound quantity open", buf, sizeof(buf), hwnd);
+ ok(err==MCIERR_DEVICE_TYPE_REQUIRED,"sysinfo alias quantity: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("sysinfo nosuchalias quantity open", buf, sizeof(buf), hwnd);
+ ok(err==MCIERR_DEVICE_TYPE_REQUIRED,"sysinfo unknown quantity open: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("sysinfo all installname", buf, sizeof(buf), hwnd);
+ ok(err==MCIERR_CANNOT_USE_ALL,"sysinfo all installname: %s\n", dbg_mcierr(err));
+
+ buf[0] = 'M'; buf[1] = 0;
+ parm.sys.lpstrReturn = buf;
+ parm.sys.dwRetSize = sizeof(buf);
+ err = mciSendCommandA(MCI_ALL_DEVICE_ID, MCI_SYSINFO, MCI_SYSINFO_INSTALLNAME, (DWORD_PTR)&parm);
+ ok(err==MCIERR_CANNOT_USE_ALL,"mciCommand MCI_SYSINFO all installname: %s\n", dbg_mcierr(err));
+ ok(!strcmp(buf,"M"), "output buffer %s\n", buf);
+
+ err = mciSendStringA("sysinfo nodev installname", buf, sizeof(buf), hwnd);
+ ok(err==MCIERR_INVALID_DEVICE_NAME,"sysinfo nodev installname: %s\n", dbg_mcierr(err));
+ ok(!buf[0], "sysinfo error buffer %s\n", buf);
+
+ buf[0] = 'K';
+ parm.sys.lpstrReturn = buf;
+ parm.sys.dwRetSize = sizeof(buf);
+ err = mciSendCommandW(24000, MCI_SYSINFO, MCI_SYSINFO_INSTALLNAME, (DWORD_PTR)&parm);
+ ok(err==MCIERR_INVALID_DEVICE_NAME || broken(err==MMSYSERR_NOTSUPPORTED/* Win9x */), "mciCommand MCI_SYSINFO nodev installname: %s\n", dbg_mcierr(err));
+ ok(!strcmp(buf,"K"), "output buffer %s\n", buf);
+
+ buf[0] = 0; buf[1] = 'A'; buf[2] = 'j'; buf[3] = 0;
+ parm.info.lpstrReturn = buf;
+ parm.info.dwRetSize = 2;
+ err = mciSendCommandA(1, MCI_INFO, MCI_INFO_PRODUCT, (DWORD_PTR)&parm);
+ ok(!err, "mciCommand MCI_INFO product: %s\n", dbg_mcierr(err));
+ ok(buf[0] /* && !buf[1] */ && (buf[2] == 'j' || broken(!buf[2])), "info product output buffer %s\n", buf);
+ /* Producing non-ASCII multi-byte output, native forgets to zero-terminate a too small buffer
+ * with SendStringA, while SendStringW works correctly (jap. and chin. locale): ignore buf[1] */
+ /* Bug in 64 bit Vista/w2k8/w7: mciSendStringW is used! (not in xp nor w2k3) */
+
+ buf[0] = 'K'; buf[1] = 0;
+ parm.info.dwRetSize = sizeof(buf);
+ err = mciSendCommandW(1, MCI_INFO, 0x07000000, (DWORD_PTR)&parm);
+ ok(err==MCIERR_UNRECOGNIZED_KEYWORD || broken(err==MMSYSERR_NOTSUPPORTED/* Win9x */), "mciCommand MCI_INFO other: %s\n", dbg_mcierr(err));
+ ok(!strcmp(buf,"K"), "info output buffer %s\n", buf);
+
+ err = mciGetDeviceIDA("all");
+ ok(err == MCI_ALL_DEVICE_ID, "mciGetDeviceIDA all returned %u, expected MCI_ALL_DEVICE_ID\n", err);
+
+ err = mciSendStringA(command_close_my, NULL, 0, hwnd);
ok(!err,"mci %s returned %s\n", command_close_my, dbg_mcierr(err));
test_notification(hwnd, command_close_my, MCI_NOTIFY_SUCCESSFUL);
Sleep(5);
test_notification(hwnd, command_close_my, 0);
- err = mciSendString("open no-such-file-exists.wav alias y", buf, sizeof(buf), NULL);
+ err = mciSendStringA("open no-such-file-exists.wav alias y buffer 6", buf, sizeof(buf), NULL);
ok(err==MCIERR_FILE_NOT_FOUND,"open no-such-file.wav returned %s\n", dbg_mcierr(err));
if(!err) {
- err = mciSendString("close y", NULL, 0, NULL);
+ err = mciSendStringA("close y", NULL, 0, NULL);
ok(!err,"close y returned %s\n", dbg_mcierr(err));
}
- err = mciSendString("open no-such-dir\\file.wav alias y type waveaudio", buf, sizeof(buf), NULL);
+ err = mciSendStringA("open no-such-dir\\file.wav alias y type waveaudio", buf, sizeof(buf), NULL);
ok(err==MCIERR_FILE_NOT_FOUND || broken(err==MCIERR_INVALID_FILE /* Win9X */),"open no-such-dir/file.wav returned %s\n", dbg_mcierr(err));
if(!err) {
- err = mciSendString("close y", NULL, 0, NULL);
+ err = mciSendStringA("close y", NULL, 0, NULL);
ok(!err,"close y returned %s\n", dbg_mcierr(err));
}
- err = mciSendString(command_close_all, NULL, 0, NULL);
- todo_wine ok(!err,"mci %s (without buffer) returned %s\n", command_close_all, dbg_mcierr(err));
+ err = mciSendStringA("open ! alias no", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_INVALID_DEVICE_NAME,"open !(void): %s\n", dbg_mcierr(err));
- memset(buf, 0, sizeof(buf));
- err = mciSendString(command_close_all, buf, sizeof(buf), hwnd);
- todo_wine ok(!err,"mci %s (with output buffer) returned %s\n", command_close_all, dbg_mcierr(err));
- ok(buf[0] == 0, "mci %s changed output buffer: %s\n", command_close_all, buf);
+ err = mciSendStringA("open !no-such-file-exists.wav alias no", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_FILE_NOT_FOUND || /* Win9X */err==MCIERR_INVALID_DEVICE_NAME,"open !name: %s\n", dbg_mcierr(err));
+
+ /* FILE_NOT_FOUND stems from mciwave,
+ * the complete name including ! is passed through since NT */
+ err = mciSendStringA("open nosuchdevice!tempfile.wav alias no", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_FILE_NOT_FOUND || /* Win9X */err==MCIERR_INVALID_DEVICE_NAME,"open nosuchdevice!name: %s\n", dbg_mcierr(err));
+ /* FIXME? use broken(INVALID_DEVICE_NAME) and have Wine not mimic Win9X? */
+
+ err = mciSendStringA("close waveaudio", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_INVALID_DEVICE_NAME,"close waveaudio: %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA(command_close_all, NULL, 0, NULL);
+ ok(!err,"mci %s (without buffer) returned %s\n", command_close_all, dbg_mcierr(err));
+
+ err = mciSendStringA(command_close_all, buf, sizeof(buf), hwnd);
+ ok(!err,"mci %s (with output buffer) returned %s\n", command_close_all, dbg_mcierr(err));
+ ok(buf[0] == 0, "mci %s output buffer: %s\n", command_close_all, buf);
/* No notification left, everything closed already */
test_notification(hwnd, command_close_all, 0);
/* TODO test close all sends one notification per open device */
- memset(buf, 0, sizeof(buf));
- err = mciSendString(command_sysinfo, buf, sizeof(buf), NULL);
+ err = mciSendStringA(command_sysinfo, buf, sizeof(buf), NULL);
ok(!err,"mci %s returned %s\n", command_sysinfo, dbg_mcierr(err));
- todo_wine ok(buf[0] == '0' && buf[1] == 0, "mci %s, expected output buffer '0', got: '%s'\n", command_sysinfo, buf);
+ ok(buf[0] == '0' && buf[1] == 0, "mci %s, expected output buffer '0', got: '%s'\n", command_sysinfo, buf);
- err = mciSendString("open new type waveaudio", buf, sizeof(buf), NULL);
+ err = mciSendStringA("open new type waveaudio", buf, sizeof(buf), NULL);
ok(err==MCIERR_NEW_REQUIRES_ALIAS,"mci open new without alias returned %s\n", dbg_mcierr(err));
- err = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_WAIT, 0); /* from MSDN */
- ok(!err,"mciSendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_NOTIFY, 0) returned %s\n", dbg_mcierr(err));
+ parm.open.lpstrDeviceType = (LPSTR)MCI_DEVTYPE_WAVEFORM_AUDIO;
+ err = mciSendCommandA(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD_PTR)&parm);
+ ok(!err,"mciCommand OPEN_TYPE_ID waveaudio: %s\n", dbg_mcierr(err));
+
+ if(!err) {
+ MCIDEVICEID wDeviceID = parm.open.wDeviceID;
+ parm.caps.dwItem = MCI_GETDEVCAPS_DEVICE_TYPE;
+ err = mciSendCommandA(wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&parm);
+ ok(!err,"mciCommand MCI_GETDEVCAPS device type: %s\n", dbg_mcierr(err));
+ ok(MCI_DEVTYPE_WAVEFORM_AUDIO==parm.caps.dwReturn,"mciCommand GETDEVCAPS says %u, expected %u\n", parm.caps.dwReturn, MCI_DEVTYPE_WAVEFORM_AUDIO);
+ }
+
+ ok(0xDEADF00D==intbuf[0] && 0xABADCAFE==intbuf[2],"DWORD buffer corruption\n");
+
+ err = mciGetDeviceIDA("waveaudio");
+ ok(err == 1, "mciGetDeviceIDA waveaudio returned %u, expected 1\n", err);
+
+ err = mciSendStringA("open no-such-file.wav alias waveaudio", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_DUPLICATE_ALIAS, "mci open alias waveaudio returned %s\n", dbg_mcierr(err));
+ /* If it were not already in use, open avivideo alias waveaudio would succeed,
+ * making for funny test cases. */
+
+ err = mciSendCommandA(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_WAIT, 0); /* from MSDN */
+ ok(!err, "mciCommand close returned %s\n", dbg_mcierr(err));
- err = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_NOTIFY, 0);
- ok(!err,"mciSendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_NOTIFY, 0) returned %s\n", dbg_mcierr(err));
+ err = mciSendCommandA(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_NOTIFY, 0);
+ ok(!err, "mciCommand close returned %s\n", dbg_mcierr(err));
- parm.dwCallback = (DWORD_PTR)hwnd;
- err = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_NOTIFY, (DWORD_PTR)&parm);
- ok(!err,"mciSendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_NOTIFY, hwnd) returned %s\n", dbg_mcierr(err));
+ parm.gen.dwCallback = (DWORD_PTR)hwnd;
+ err = mciSendCommandA(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_NOTIFY, (DWORD_PTR)&parm);
+ ok(!err, "mciCommand close returned %s\n", dbg_mcierr(err));
test_notification(hwnd, command_close_all, 0); /* None left */
}
WORD nch = 1;
WORD nbits = 16;
DWORD nsamp = 16000, expect;
+ UINT ndevs = waveInGetNumDevs();
MCIERROR err, ok_pcm;
MCIDEVICEID wDeviceID;
MCI_PARMS_UNION parm;
parm.open.lpstrDeviceType = "waveaudio";
parm.open.lpstrElementName = ""; /* "new" at the command level */
- parm.open.lpstrAlias = "x"; /* to enable mciSendString */
+ parm.open.lpstrAlias = "x"; /* to enable mciSendStringA */
parm.open.dwCallback = (DWORD_PTR)hwnd;
- err = mciSendCommand(0, MCI_OPEN,
- MCI_OPEN_ELEMENT | MCI_OPEN_TYPE | MCI_OPEN_ALIAS | MCI_NOTIFY,
- (DWORD_PTR)&parm);
+ err = mciSendCommandA(0, MCI_OPEN,
+ MCI_OPEN_ELEMENT | MCI_OPEN_TYPE | MCI_OPEN_ALIAS | MCI_NOTIFY, (DWORD_PTR)&parm);
ok(!err,"mciCommand open new type waveaudio alias x notify: %s\n", dbg_mcierr(err));
wDeviceID = parm.open.wDeviceID;
- /* In Wine, both MCI_Open and the individual drivers send notifications. */
+ err = mciGetDeviceIDA("x");
+ ok(err == wDeviceID, "mciGetDeviceIDA x returned %u, expected %u\n", err, wDeviceID);
+
+ /* Only the alias is looked up. */
+ err = mciGetDeviceIDA("waveaudio");
+ ok(!err, "mciGetDeviceIDA waveaudio returned %u, expected 0\n", err);
+
test_notification(hwnd, "open new", MCI_NOTIFY_SUCCESSFUL);
- todo_wine test_notification1(hwnd, "open new no #2", 0);
+ test_notification(hwnd, "open new no #2", 0);
/* Do not query time format as string because result depends on locale! */
parm.status.dwItem = MCI_STATUS_TIME_FORMAT;
- err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
+ err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
ok(!err,"mciCommand status time format: %s\n", dbg_mcierr(err));
ok(parm.status.dwReturn==MCI_FORMAT_MILLISECONDS,"status time format: %ld\n",parm.status.dwReturn);
/* Info file fails until named in Open or Save. */
- err = mciSendString("info x file", buf, sizeof(buf), NULL);
+ err = mciSendStringA("info x file", buf, sizeof(buf), NULL);
todo_wine ok(err==MCIERR_NONAPPLICABLE_FUNCTION,"mci info new file returned %s\n", dbg_mcierr(err));
+ ok(!buf[0], "info error buffer %s\n", buf);
+
+ err = mciSendStringA("status x length", buf, sizeof(buf), NULL);
+ todo_wine ok(!err,"status x length initial: %s\n", dbg_mcierr(err));
+ if(!err) ok(!strcmp(buf,"0"), "mci status length expected 0, got: %s\n", buf);
/* Check the default recording: 8-bits per sample, mono, 11kHz */
- err = mciSendString("status x samplespersec", buf, sizeof(buf), NULL);
+ err = mciSendStringA("status x samplespersec", buf, sizeof(buf), NULL);
ok(!err,"mci status samplespersec returned %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf,"11025"), "mci status samplespersec expected 11025, got: %s\n", buf);
/* MCI seems to solely support PCM, no need for ACM conversion. */
- err = mciSendString("set x format tag 2", NULL, 0, NULL);
+ err = mciSendStringA("set x format tag 2", NULL, 0, NULL);
ok(err==MCIERR_OUTOFRANGE,"mci set format tag 2 returned %s\n", dbg_mcierr(err));
/* MCI appears to scan the available devices for support of this format,
* returning MCIERR_OUTOFRANGE on machines with no sound.
+ * However some w2k8/w7 machines return no error when there's no wave
+ * input device (perhaps querying waveOutGetNumDevs instead of waveIn?),
+ * still the record command below fails with MCIERR_WAVE_INPUTSUNSUITABLE.
* Don't skip here, record will fail below. */
- err = mciSendString("set x format tag pcm", NULL, 0, NULL);
+ err = mciSendStringA("set x format tag pcm", NULL, 0, NULL);
ok(!err || err==MCIERR_OUTOFRANGE,"mci set format tag pcm returned %s\n", dbg_mcierr(err));
ok_pcm = err;
- err = mciSendString("set x samplespersec 41000 alignment 4 channels 2", NULL, 0, NULL);
- ok(err==ok_pcm,"mci set samples+align+channels returned %s\n", dbg_mcierr(err));
-
+ /* MSDN warns against not setting all wave format parameters.
+ * Indeed, it produces strange results, incl.
+ * inconsistent PCMWAVEFORMAT headers in the saved file.
+ */
+ err = mciSendStringA("set x bytespersec 22050 alignment 2 samplespersec 11025 channels 1 bitspersample 16", NULL, 0, NULL);
+ ok(err==ok_pcm,"mci set 5 wave parameters returned %s\n", dbg_mcierr(err));
/* Investigate: on w2k, set samplespersec 22050 sets nChannels to 2!
- * err = mciSendString("set x samplespersec 22050", NULL, 0, NULL);
+ * err = mciSendStringA("set x samplespersec 22050", NULL, 0, NULL);
* ok(!err,"mci set samplespersec returned %s\n", dbg_mcierr(err));
*/
+ /* Checks are generally performed immediately. */
+ err = mciSendStringA("set x bitspersample 4", NULL, 0, NULL);
+ todo_wine ok(err==MCIERR_OUTOFRANGE,"mci set bitspersample 4 returned %s\n", dbg_mcierr(err));
+
parm.set.wFormatTag = WAVE_FORMAT_PCM;
parm.set.nSamplesPerSec = nsamp;
parm.set.wBitsPerSample = nbits;
parm.set.nChannels = nch;
parm.set.nBlockAlign = parm.set.nChannels * parm.set.wBitsPerSample /8;
parm.set.nAvgBytesPerSec= parm.set.nSamplesPerSec * parm.set.nBlockAlign;
- err = mciSendCommand(wDeviceID, MCI_SET,
- MCI_WAVE_SET_SAMPLESPERSEC | MCI_WAVE_SET_CHANNELS |
- MCI_WAVE_SET_BITSPERSAMPLE | MCI_WAVE_SET_BLOCKALIGN |
- MCI_WAVE_SET_AVGBYTESPERSEC| MCI_WAVE_SET_FORMATTAG, (DWORD_PTR)&parm);
+ err = mciSendCommandA(wDeviceID, MCI_SET,
+ MCI_WAVE_SET_SAMPLESPERSEC | MCI_WAVE_SET_CHANNELS | MCI_WAVE_SET_BITSPERSAMPLE |
+ MCI_WAVE_SET_BLOCKALIGN | MCI_WAVE_SET_AVGBYTESPERSEC| MCI_WAVE_SET_FORMATTAG,
+ (DWORD_PTR)&parm);
ok(err==ok_pcm,"mciCommand set wave format: %s\n", dbg_mcierr(err));
+ parm.caps.dwItem = MCI_WAVE_GETDEVCAPS_INPUTS;
+ parm.caps.dwCallback = (DWORD_PTR)hwnd;
+ err = mciSendCommandA(wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM | MCI_NOTIFY,
+ (DWORD_PTR)&parm);
+ ok(!err,"mciCommand MCI_GETDEVCAPS inputs: %s\n", dbg_mcierr(err));
+ ok(parm.caps.dwReturn==ndevs,"mciCommand GETDEVCAPS claims %u inputs, expected %u\n", parm.caps.dwReturn, ndevs);
+ ok(!ok_pcm || !parm.caps.dwReturn,"No input device accepts PCM!?\n");
+ test_notification(hwnd, "GETDEVCAPS inputs", MCI_NOTIFY_SUCCESSFUL);
+
/* A few ME machines pass all tests except set format tag pcm! */
- err = mciSendString("record x to 2000 wait", NULL, 0, hwnd);
+ err = mciSendStringA("record x to 2000 wait", NULL, 0, hwnd);
ok(err || !ok_pcm,"can record yet set wave format pcm returned %s\n", dbg_mcierr(ok_pcm));
- ok(!err || err==(ok_pcm==MCIERR_OUTOFRANGE ? MCIERR_WAVE_INPUTSUNSUITABLE : 0),"mci record to 2000 returned %s\n", dbg_mcierr(err));
+ if(!ndevs) todo_wine /* with sound disabled */
+ ok(ndevs>0 ? !err : err==MCIERR_WAVE_INPUTSUNSUITABLE,"mci record to 2000 returned %s\n", dbg_mcierr(err));
+ else
+ ok(ndevs>0 ? !err : err==MCIERR_WAVE_INPUTSUNSUITABLE,"mci record to 2000 returned %s\n", dbg_mcierr(err));
if(err) {
if (err==MCIERR_WAVE_INPUTSUNSUITABLE)
skip("Please install audio driver. Everything is skipped.\n");
else skip("Cannot record cause %s. Everything is skipped.\n", dbg_mcierr(err));
- err = mciSendString("close x", NULL, 0, NULL);
+ err = mciSendStringA("close x", NULL, 0, NULL);
ok(!err,"mci close returned %s\n", dbg_mcierr(err));
test_notification(hwnd,"record skipped",0);
return;
}
/* Query some wave format parameters depending on the time format. */
- err = mciSendString("status x position", buf, sizeof(buf), NULL);
+ err = mciSendStringA("status x position", buf, sizeof(buf), NULL);
ok(!err,"mci status position returned %s\n", dbg_mcierr(err));
if(!err) todo_wine ok(!strcmp(buf,"2000"), "mci status position gave %s, expected 2000, some tests will fail\n", buf);
- err = mciSendString("set x time format 8", NULL, 0, NULL); /* bytes */
+ err = mciSendStringA("set x time format 8", NULL, 0, NULL); /* bytes */
ok(!err,"mci returned %s\n", dbg_mcierr(err));
parm.status.dwItem = MCI_STATUS_POSITION;
- err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
+ err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
ok(!err,"mciCommand status position: %s\n", dbg_mcierr(err));
expect = 2 * nsamp * nch * nbits/8;
if(!err) todo_wine ok(parm.status.dwReturn==expect,"recorded %lu bytes, expected %u\n",parm.status.dwReturn,expect);
parm.set.dwTimeFormat = MCI_FORMAT_SAMPLES;
- err = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&parm);
+ err = mciSendCommandA(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&parm);
ok(!err,"mciCommand set time format samples: %s\n", dbg_mcierr(err));
parm.status.dwItem = MCI_STATUS_POSITION;
- err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
+ err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
ok(!err,"mciCommand status position: %s\n", dbg_mcierr(err));
expect = 2 * nsamp;
if(!err) todo_wine ok(parm.status.dwReturn==expect,"recorded %lu samples, expected %u\n",parm.status.dwReturn,expect);
- err = mciSendString("set x time format milliseconds", NULL, 0, NULL);
+ err = mciSendStringA("set x time format milliseconds", NULL, 0, NULL);
ok(!err,"mci set time format milliseconds returned %s\n", dbg_mcierr(err));
- err = mciSendString("save x tempfile1.wav", NULL, 0, NULL);
+ err = mciSendStringA("save x tempfile1.wav", NULL, 0, NULL);
ok(!err,"mci save returned %s\n", dbg_mcierr(err));
- err = mciSendString("save x tempfile.wav", NULL, 0, NULL);
+ err = mciSendStringA("save x tempfile.wav", NULL, 0, NULL);
ok(!err,"mci save returned %s\n", dbg_mcierr(err));
if(!err) ok_saved = 0;
/* Save must not rename the original file. */
- if (!DeleteFile("tempfile1.wav"))
- todo_wine ok(FALSE, "Save must not rename the original file; DeleteFile returned %d\n", GetLastError());
+ if (!DeleteFileA("tempfile1.wav"))
+ todo_wine ok(FALSE, "Save must not rename the original file; DeleteFileA returned %d\n",
+ GetLastError());
- err = mciSendString("set x channels 2", NULL, 0, NULL);
+ err = mciSendStringA("set x channels 2", NULL, 0, NULL);
ok(err==MCIERR_NONAPPLICABLE_FUNCTION,"mci set channels after saving returned %s\n", dbg_mcierr(err));
parm.seek.dwTo = 600;
- err = mciSendCommand(wDeviceID, MCI_SEEK, MCI_TO | MCI_WAIT, (DWORD_PTR)&parm);
+ err = mciSendCommandA(wDeviceID, MCI_SEEK, MCI_TO | MCI_WAIT, (DWORD_PTR)&parm);
ok(!err,"mciCommand seek to 600: %s\n", dbg_mcierr(err));
/* Truncate to current position */
- err = mciSendString("delete x", NULL, 0, NULL);
+ err = mciSendStringA("delete x", NULL, 0, NULL);
todo_wine ok(!err,"mci delete returned %s\n", dbg_mcierr(err));
- buf[0]='\0';
- err = mciSendString("status x length", buf, sizeof(buf), NULL);
+ err = mciSendStringA("status x length", buf, sizeof(buf), NULL);
ok(!err,"mci status length returned %s\n", dbg_mcierr(err));
todo_wine ok(!strcmp(buf,"600"), "mci status length after delete gave %s, expected 600\n", buf);
- err = mciSendString("close x", NULL, 0, NULL);
+ err = mciSendStringA("close x", NULL, 0, NULL);
ok(!err,"mci close returned %s\n", dbg_mcierr(err));
test_notification(hwnd,"record complete",0);
}
char buf[1024];
memset(buf, 0, sizeof(buf));
- err = mciSendString("open waveaudio!tempfile.wav alias mysound", NULL, 0, NULL);
+ err = mciSendStringA("open waveaudio!tempfile.wav alias mysound", NULL, 0, NULL);
ok(err==ok_saved,"mci open waveaudio!tempfile.wav returned %s\n", dbg_mcierr(err));
if(err) {
skip("Cannot open waveaudio!tempfile.wav for playing (%s), skipping\n", dbg_mcierr(err));
return;
}
- err = mciSendString("status mysound length", buf, sizeof(buf), NULL);
+ err = mciGetDeviceIDA("mysound");
+ ok(err == 1, "mciGetDeviceIDA mysound returned %u, expected 1\n", err);
+
+ err = mciGetDeviceIDA("tempfile.wav");
+ ok(!err, "mciGetDeviceIDA tempfile.wav returned %u, expected 0\n", err);
+
+ err = mciGetDeviceIDA("waveaudio");
+ ok(!err, "mciGetDeviceIDA waveaudio returned %u, expected 0\n", err);
+
+ err = mciSendStringA("status mysound length", buf, sizeof(buf), NULL);
ok(!err,"mci status length returned %s\n", dbg_mcierr(err));
todo_wine ok(!strcmp(buf,"2000"), "mci status length gave %s, expected 2000, some tests will fail.\n", buf);
- err = mciSendString("cue output", NULL, 0, NULL);
- todo_wine ok(err==MCIERR_UNRECOGNIZED_COMMAND,"mci incorrect cue output returned %s\n", dbg_mcierr(err));
+ err = mciSendStringA("cue output", NULL, 0, NULL);
+ ok(err==MCIERR_UNRECOGNIZED_COMMAND,"mci incorrect cue output returned %s\n", dbg_mcierr(err));
/* Test MCI to the bones -- Some todo_wine from Cue and
* from Play from 0 to 0 are not worth fixing. */
- err = mciSendString("cue mysound output notify", NULL, 0, hwnd);
+ err = mciSendStringA("cue mysound output notify", NULL, 0, hwnd);
ok(!err,"mci cue output after open file returned %s\n", dbg_mcierr(err));
/* Notification is delayed as a play thread is started. */
- todo_wine test_notification1(hwnd, "cue immediate", 0);
+ todo_wine test_notification(hwnd, "cue immediate", 0);
/* Cue pretends to put the MCI into paused state. */
- err = mciSendString("status mysound mode", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status mysound mode", buf, sizeof(buf), hwnd);
ok(!err,"mci status mode returned %s\n", dbg_mcierr(err));
todo_wine ok(!strcmp(buf,"paused"), "mci status mode: %s, expected (pseudo)paused\n", buf);
/* Strange pause where Pause is rejected, unlike Play; Pause; Pause tested below */
- err = mciSendString("pause mysound", NULL, 0, hwnd);
+ err = mciSendStringA("pause mysound", NULL, 0, hwnd);
ok(err==MCIERR_NONAPPLICABLE_FUNCTION,"mci pause after cue returned %s\n", dbg_mcierr(err));
/* MCI appears to start the play thread in this border case.
* Guessed that from (flaky) status mode and late notification arrival. */
- err = mciSendString("play mysound from 0 to 0 notify", NULL, 0, hwnd);
+ err = mciSendStringA("play mysound from 0 to 0 notify", NULL, 0, hwnd);
ok(!err,"mci play from 0 to 0 returned %s\n", dbg_mcierr(err));
- todo_wine test_notification1(hwnd, "cue aborted by play", MCI_NOTIFY_ABORTED);
+ todo_wine test_notification(hwnd, "cue aborted by play", MCI_NOTIFY_ABORTED);
/* play's own notification follows below */
- err = mciSendString("play mysound from 250 to 0", NULL, 0, NULL);
+ err = mciSendStringA("play mysound from 250 to 0", NULL, 0, NULL);
ok(err==MCIERR_OUTOFRANGE,"mci play from 250 to 0 returned %s\n", dbg_mcierr(err));
Sleep(50); /* Give play from 0 to 0 time to finish. */
- todo_wine test_notification1(hwnd, "play from 0 to 0", MCI_NOTIFY_SUCCESSFUL);
+ todo_wine test_notification(hwnd, "play from 0 to 0", MCI_NOTIFY_SUCCESSFUL);
- err = mciSendString("status mysound mode", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status mysound mode", buf, sizeof(buf), hwnd);
ok(!err,"mci status mode returned %s\n", dbg_mcierr(err));
ok(!strcmp(buf,"stopped"), "mci status mode: %s after play from 0 to 0\n", buf);
- err = mciSendString("play MYSOUND from 250 to 0 notify", NULL, 0, hwnd);
+ err = mciSendStringA("play MYSOUND from 250 to 0 notify", NULL, 0, hwnd);
ok(err==MCIERR_OUTOFRANGE,"mci play from 250 to 0 notify returned %s\n", dbg_mcierr(err));
/* No notification (checked below) sent if error */
/* A second play caused Wine<1.1.33 to hang */
- err = mciSendString("play mysound from 500 to 1500 wait", NULL, 0, NULL);
- ok(!err,"mci play from 500 to 1500 returned %s\n", dbg_mcierr(err));
+ err = mciSendStringA("play mysound from 500 to 220:5:0 wait", NULL, 0, NULL);
+ ok(!err,"mci play from 500 to 220:5:0 (=1500) returned %s\n", dbg_mcierr(err));
- memset(buf, 0, sizeof(buf));
- err = mciSendString("status mysound position", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status mysound position", buf, sizeof(buf), hwnd);
ok(!err,"mci status position returned %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf,"1500"), "mci status position: %s\n", buf);
/* mci will not play position < current */
- err = mciSendString("play mysound to 1000", NULL, 0, NULL);
+ err = mciSendStringA("play mysound to 1000", NULL, 0, NULL);
ok(err==MCIERR_OUTOFRANGE,"mci play to 1000 returned %s\n", dbg_mcierr(err));
/* mci will not play to > end */
- err = mciSendString("play mysound TO 3000 notify", NULL, 0, hwnd);
+ err = mciSendStringA("play mysound TO 3000 notify", NULL, 0, hwnd);
ok(err==MCIERR_OUTOFRANGE,"mci play to 3000 notify returned %s\n", dbg_mcierr(err));
- err = mciSendString("play mysound to 2000", NULL, 0, NULL);
+ err = mciSendStringA("play mysound to 2000", NULL, 0, NULL);
ok(!err,"mci play to 2000 returned %s\n", dbg_mcierr(err));
/* Rejected while playing */
- err = mciSendString("cue mysound output", NULL, 0, NULL);
+ err = mciSendStringA("cue mysound output", NULL, 0, NULL);
ok(err==MCIERR_NONAPPLICABLE_FUNCTION,"mci cue output while playing returned %s\n", dbg_mcierr(err));
- err = mciSendString("play mysound to 3000", NULL, 0, NULL);
+ err = mciSendStringA("play mysound to 3000", NULL, 0, NULL);
ok(err==MCIERR_OUTOFRANGE,"mci play to 3000 returned %s\n", dbg_mcierr(err));
- err = mciSendString("stop mysound Wait", NULL, 0, NULL);
+ err = mciSendStringA("stop mysound Wait", NULL, 0, NULL);
ok(!err,"mci stop wait returned %s\n", dbg_mcierr(err));
test_notification(hwnd, "play/cue/pause/stop", 0);
- err = mciSendString("Seek Mysound to 250 wait Notify", NULL, 0, hwnd);
+ err = mciSendStringA("Seek Mysound to 250 wait Notify", NULL, 0, hwnd);
ok(!err,"mci seek to 250 wait notify returned %s\n", dbg_mcierr(err));
test_notification(hwnd,"seek wait notify",MCI_NOTIFY_SUCCESSFUL);
- memset(buf, 0, sizeof(buf));
- err = mciSendString("status mysound position notify", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("seek mysound to 0xfa", NULL, 0, NULL);
+ ok(err==MCIERR_BAD_INTEGER,"mci seek to 0xfa returned %s\n", dbg_mcierr(err));
+
+ /* MCI_INTEGER always accepts colon notation */
+ err = mciSendStringA("seek mysound to :1", NULL, 0, NULL);
+ ok(!err,"mci seek to :1 (=256) returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("seek mysound to 250::", NULL, 0, NULL);
+ ok(!err,"mci seek to 250:: returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("seek mysound to 250:0", NULL, 0, NULL);
+ ok(!err,"mci seek to 250:0 returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("status mysound position notify", buf, sizeof(buf), hwnd);
ok(!err,"mci status position notify returned %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf,"250"), "mci status position: %s\n", buf);
/* Immediate commands like status also send notifications. */
test_notification(hwnd,"status position",MCI_NOTIFY_SUCCESSFUL);
- memset(buf, 0, sizeof(buf));
- err = mciSendString("status mysound mode", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status mysound mode", buf, sizeof(buf), hwnd);
ok(!err,"mci status mode returned %s\n", dbg_mcierr(err));
ok(!strcmp(buf,"stopped"), "mci status mode: %s\n", buf);
/* Another play from == to testcase */
- err = mciSendString("play mysound to 250 wait notify", NULL, 0, hwnd);
+ err = mciSendStringA("play mysound to 250 wait notify", NULL, 0, hwnd);
ok(!err,"mci play (from 250) to 250 returned %s\n", dbg_mcierr(err));
- todo_wine test_notification1(hwnd,"play to 250 wait notify",MCI_NOTIFY_SUCCESSFUL);
+ todo_wine test_notification(hwnd,"play to 250 wait notify",MCI_NOTIFY_SUCCESSFUL);
- err = mciSendString("cue mysound output", NULL, 0, NULL);
+ err = mciSendStringA("cue mysound output", NULL, 0, NULL);
ok(!err,"mci cue output after play returned %s\n", dbg_mcierr(err));
- err = mciSendString("close mysound", NULL, 0, NULL);
+ err = mciSendStringA("close mysound", NULL, 0, NULL);
ok(!err,"mci close returned %s\n", dbg_mcierr(err));
test_notification(hwnd,"after close",0);
}
char buf[1024];
memset(buf, 0, sizeof(buf));
- err = mciSendString("open tempfile.wav alias mysound notify", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("open tempfile.wav alias mysound notify type waveaudio", buf, sizeof(buf), hwnd);
ok(err==ok_saved,"mci open tempfile.wav returned %s\n", dbg_mcierr(err));
if(err) {
skip("Cannot open tempfile.wav for playing (%s), skipping\n", dbg_mcierr(err));
ok(wDeviceID,"mci open DeviceID: %d\n", wDeviceID);
test_notification(hwnd,"open alias notify",MCI_NOTIFY_SUCCESSFUL);
- err = mciSendString("status mysound mode", buf, sizeof(buf), hwnd);
+ err = mciGetDeviceIDA("mysound");
+ ok(err == wDeviceID, "mciGetDeviceIDA alias returned %u, expected %u\n", err, wDeviceID);
+
+ /* Only the alias is looked up. */
+ err = mciGetDeviceIDA("tempfile.wav");
+ ok(!err, "mciGetDeviceIDA tempfile.wav returned %u, expected 0\n", err);
+
+ err = mciGetDeviceIDA("waveaudio");
+ ok(!err, "mciGetDeviceIDA waveaudio returned %u, expected 0\n", err);
+
+ err = mciSendStringA("status mysound mode", buf, sizeof(buf), hwnd);
ok(!err,"mci status mode returned %s\n", dbg_mcierr(err));
ok(!strcmp(buf,"stopped"), "mci status mode: %s\n", buf);
- err = mciSendString("play mysound notify", NULL, 0, hwnd);
+ err = mciSendStringA("play mysound notify", NULL, 0, hwnd);
ok(!err,"mci play returned %s\n", dbg_mcierr(err));
- /* Give Wine's asynchronous thread time to start up. Furthermore,
- * it uses 3 buffers per second, so that the positions reported
- * will be 333ms, 667ms etc. at best. */
- Sleep(100); /* milliseconds */
+ Sleep(500); /* milliseconds */
/* Do not query time format as string because result depends on locale! */
parm.status.dwItem = MCI_STATUS_TIME_FORMAT;
- err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
+ err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
ok(!err,"mciCommand status time format: %s\n", dbg_mcierr(err));
if(!err) ok(parm.status.dwReturn==MCI_FORMAT_MILLISECONDS,"status time format: %ld\n",parm.status.dwReturn);
parm.set.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
- err = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&parm);
+ err = mciSendCommandA(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&parm);
ok(!err,"mciCommand set time format ms: %s\n", dbg_mcierr(err));
- buf[0]=0;
- err = mciSendString("status mysound position", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status mysound position", buf, sizeof(buf), hwnd);
ok(!err,"mci status position returned %s\n", dbg_mcierr(err));
- ok(strcmp(buf,"2000"), "mci status position: %s, expected 2000\n", buf);
- trace("position after Sleep: %sms\n",buf);
+ trace("position after Sleep: %sms\n", buf);
p2 = atoi(buf);
- /* Some machines reach 79ms only during the 100ms sleep. */
- ok(p2>=67,"not enough time elapsed %ums\n",p2);
+ /* Check that the 2s sound plays at a normal pace, giving a wide margin to
+ * account for timing granularity and small delays.
+ */
+ todo_wine ok(350 <= p2 && p2 <= 600, "%ums is not in the expected 350-600ms range\n", p2);
+ /* Wine's asynchronous thread needs some time to start up. Furthermore, it
+ * uses 3 buffers per second, so that the positions reported will be 333ms,
+ * 667ms etc. at best, which is why it fails the above test. So add a
+ * second test specifically to prevent Wine from getting even worse.
+ * FIXME: To be removed when Wine is fixed and passes the above test.
+ */
+ ok(350 <= p2 && p2 <= 1000, "%ums is not even in the expected 350-1000ms range\n", p2);
test_notification(hwnd,"play (nowait)",0);
- err = mciSendString("pause mysound wait", NULL, 0, hwnd);
+ err = mciSendStringA("pause mysound wait", NULL, 0, hwnd);
ok(!err,"mci pause wait returned %s\n", dbg_mcierr(err));
- buf[0]=0;
- err = mciSendString("status mysound mode notify", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status mysound mode notify", buf, sizeof(buf), hwnd);
ok(!err,"mci status mode returned %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf,"paused"), "mci status mode: %s\n", buf);
test_notification(hwnd,"play",MCI_NOTIFY_SUPERSEDED);
test_notification(hwnd,"status",MCI_NOTIFY_SUCCESSFUL);
- buf[0]=0;
- err = mciSendString("status mysound position", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status mysound position", buf, sizeof(buf), hwnd);
ok(!err,"mci status position returned %s\n", dbg_mcierr(err));
trace("position while paused: %sms\n",buf);
p1 = atoi(buf);
ok(p1>=p2, "position not increasing: %u > %u\n", p2, p1);
- err = mciSendString("stop mysound wait", NULL, 0, NULL);
+ err = mciSendStringA("stop mysound wait", NULL, 0, NULL);
ok(!err,"mci stop returned %s\n", dbg_mcierr(err));
- buf[0]=0;
- err = mciSendString("info mysound file notify", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("info mysound file notify", buf, sizeof(buf), hwnd);
ok(!err,"mci info file returned %s\n", dbg_mcierr(err));
if(!err) { /* fully qualified name */
int len = strlen(buf);
}
test_notification(hwnd,"info file",MCI_NOTIFY_SUCCESSFUL);
- buf[0]=0;
- err = mciSendString("status mysound mode", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status mysound mode", buf, sizeof(buf), hwnd);
ok(!err,"mci status mode returned %s\n", dbg_mcierr(err));
ok(!strcmp(buf,"stopped"), "mci status mode: %s\n", buf);
- buf[0]=0;
- err = mciSendString("status mysound position", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status mysound position", buf, sizeof(buf), hwnd);
ok(!err,"mci status position returned %s\n", dbg_mcierr(err));
trace("position once stopped: %sms\n",buf);
p2 = atoi(buf);
ok(p2>=p1 && p2<=p1+16,"position changed from %ums to %ums\n",p1,p2);
/* No Resume once stopped (waveaudio, sequencer and cdaudio differ). */
- err = mciSendString("resume mysound wait", NULL, 0, NULL);
+ err = mciSendStringA("resume mysound wait", NULL, 0, NULL);
ok(err==MCIERR_NONAPPLICABLE_FUNCTION,"mci resume wait returned %s\n", dbg_mcierr(err));
- err = mciSendString("play mysound wait", NULL, 0, NULL);
+ err = mciSendStringA("play mysound wait", NULL, 0, NULL);
ok(!err,"mci play wait returned %s\n", dbg_mcierr(err));
- buf[0]=0;
- err = mciSendString("status mysound position", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status mysound position", buf, sizeof(buf), hwnd);
ok(!err,"mci status position returned %s\n", dbg_mcierr(err));
todo_wine ok(!strcmp(buf,"2000"), "mci status position: %s\n", buf);
- err = mciSendString("seek mysound to start wait", NULL, 0, NULL);
+ err = mciSendStringA("seek mysound to start wait", NULL, 0, NULL);
ok(!err,"mci seek to start wait returned %s\n", dbg_mcierr(err));
- err = mciSendString("play mysound to 1000 notify", NULL, 0, hwnd);
+ err = mciSendStringA("play mysound to 1000 notify", NULL, 0, hwnd);
ok(!err,"mci play returned %s\n", dbg_mcierr(err));
/* Sleep(200); not needed with Wine any more. */
- err = mciSendString("pause mysound notify", NULL, 0, NULL); /* notify no callback */
+ err = mciSendStringA("pause mysound notify", NULL, 0, NULL); /* notify no callback */
ok(!err,"mci pause notify returned %s\n", dbg_mcierr(err));
/* Supersede even though pause cannot notify given no callback */
test_notification(hwnd,"pause aborted play #1 notification",MCI_NOTIFY_SUPERSEDED);
test_notification(hwnd,"impossible pause notification",0);
- err = mciSendString("cue mysound output notify", NULL, 0, hwnd);
+ err = mciSendStringA("cue mysound output notify", NULL, 0, hwnd);
ok(err==MCIERR_NONAPPLICABLE_FUNCTION,"mci cue output while paused returned %s\n", dbg_mcierr(err));
test_notification(hwnd,"cue output notify #2",0);
- err = mciSendString("resume mysound notify", NULL, 0, hwnd);
+ err = mciSendStringA("resume mysound notify", NULL, 0, hwnd);
ok(!err,"mci resume notify returned %s\n", dbg_mcierr(err));
test_notification(hwnd, "resume notify", MCI_NOTIFY_SUCCESSFUL);
/* Seek or even Stop used to hang Wine<1.1.32 on MacOS. */
- err = mciSendString("seek mysound to 0 wait", NULL, 0, NULL);
+ err = mciSendStringA("seek mysound to 0 wait", NULL, 0, NULL);
ok(!err,"mci seek to start returned %s\n", dbg_mcierr(err));
/* Seek stops. */
- err = mciSendString("status mysound mode", buf, sizeof(buf), NULL);
+ err = mciSendStringA("status mysound mode", buf, sizeof(buf), NULL);
ok(!err,"mci status mode returned %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf,"stopped"), "mci status mode: %s\n", buf);
- err = mciSendString("seek mysound wait", NULL, 0, NULL);
+ err = mciSendStringA("seek mysound wait", NULL, 0, NULL);
ok(err==MCIERR_MISSING_PARAMETER,"mci seek to nowhere returned %s\n", dbg_mcierr(err));
/* cdaudio does not detect to start to end as error */
- err = mciSendString("seek mysound to start to 0", NULL, 0, NULL);
+ err = mciSendStringA("seek mysound to start to 0", NULL, 0, NULL);
ok(err==MCIERR_FLAGS_NOT_COMPATIBLE,"mci seek to start to 0 returned %s\n", dbg_mcierr(err));
- err = mciSendString("PLAY mysound to 1000 notify", NULL, 0, hwnd);
+ err = mciSendStringA("PLAY mysound to 1000 notify", NULL, 0, hwnd);
ok(!err,"mci play to 1000 notify returned %s\n", dbg_mcierr(err));
/* Sleep(200); not needed with Wine any more. */
/* Give it 400ms and resume will appear to complete below. */
- err = mciSendString("pause mysound wait", NULL, 0, NULL);
+ err = mciSendStringA("pause mysound wait", NULL, 0, NULL);
ok(!err,"mci pause wait returned %s\n", dbg_mcierr(err));
/* Unlike sequencer and cdaudio, waveaudio's pause does not abort. */
test_notification(hwnd,"pause aborted play #2 notification",0);
- err = mciSendString("resume mysound wait", NULL, 0, NULL);
+ err = mciSendStringA("resume mysound wait", NULL, 0, NULL);
ok(!err,"mci resume wait returned %s\n", dbg_mcierr(err));
/* Resume is a short asynchronous call, something else is playing. */
- err = mciSendString("status mysound mode", buf, sizeof(buf), NULL);
+ err = mciSendStringA("status mysound mode", buf, sizeof(buf), NULL);
ok(!err,"mci status mode returned %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf,"playing"), "mci status mode: %s\n", buf);
/* Note extra space before alias */
- err = mciSendString("pause mysound wait", NULL, 0, NULL);
+ err = mciSendStringA("pause mysound wait", NULL, 0, NULL);
todo_wine ok(!err,"mci pause (space) wait returned %s\n", dbg_mcierr(err));
- err = mciSendString("pause mysound wait", NULL, 0, NULL);
+ err = mciSendStringA("pause mysound wait", NULL, 0, NULL);
ok(!err,"mci pause wait returned %s\n", dbg_mcierr(err));
/* Better ask position only when paused, is it updated while playing? */
- buf[0]='\0';
- err = mciSendString("status mysound position", buf, sizeof(buf), NULL);
+ err = mciSendStringA("status mysound position", buf, sizeof(buf), NULL);
ok(!err,"mci status position returned %s\n", dbg_mcierr(err));
/* TODO compare position < 900 */
ok(strcmp(buf,"1000"), "mci resume waited\n");
trace("position after resume: %sms\n",buf);
test_notification(hwnd,"play (aborted by pause/resume/pause)",0);
- err = mciSendString("close mysound wait", NULL, 0, NULL);
+ err = mciSendStringA("close mysound wait", NULL, 0, NULL);
ok(!err,"mci close wait returned %s\n", dbg_mcierr(err));
test_notification(hwnd,"play (aborted by close)",MCI_NOTIFY_ABORTED);
}
/* This test used(?) to cause intermittent crashes when Wine exits, after
* fixme:winmm:MMDRV_Exit Closing while ll-driver open
*/
- MCIERROR err, ok_snd;
+ UINT ndevs = waveOutGetNumDevs();
+ MCIERROR err, ok_snd = ndevs ? 0 : MCIERR_HARDWARE;
+ MCI_PARMS_UNION parm;
char buf[512], path[300], command[330];
+ DWORD intbuf[3] = { 0xDEADF00D, 99, 0xABADCAFE };
memset(buf, 0, sizeof(buf)); memset(path, 0, sizeof(path));
/* Do not crash on NULL buffer pointer */
- err = mciSendString("sysinfo waveaudio quantity open", NULL, 0, NULL);
+ err = mciSendStringA("sysinfo waveaudio quantity open", NULL, 0, NULL);
ok(err==MCIERR_PARAM_OVERFLOW,"mci sysinfo without buffer returned %s\n", dbg_mcierr(err));
- err = mciSendString("sysinfo waveaudio quantity open", buf, sizeof(buf), NULL);
+ err = mciSendStringA("sysinfo waveaudio quantity open", buf, sizeof(buf), NULL);
ok(!err,"mci sysinfo waveaudio quantity open returned %s\n", dbg_mcierr(err));
- if(!err) todo_wine ok(!strcmp(buf,"0"), "sysinfo quantity open expected 0, got: %s, some more tests will fail.\n", buf);
+ if(!err) ok(!strcmp(buf,"0"), "sysinfo quantity open expected 0, got: %s, some more tests will fail.\n", buf);
- ok_snd = waveOutGetNumDevs() ? 0 : MCIERR_HARDWARE;
- err = mciSendString("sound NoSuchSoundDefined wait", NULL, 0, NULL);
- todo_wine ok(err==ok_snd,"mci sound NoSuchSoundDefined returned %s\n", dbg_mcierr(err));
+ /* Who knows why some MS machines pass all tests but return MCIERR_HARDWARE here? */
+ /* Wine returns MCIERR_HARDWARE when no default sound is found in win.ini or the registry. */
+ err = mciSendStringA("sound NoSuchSoundDefined wait", NULL, 0, NULL);
+ ok(err==ok_snd || err==MCIERR_HARDWARE, "mci sound NoSuchSoundDefined returned %s\n", dbg_mcierr(err));
- err = mciSendString("sound SystemExclamation notify wait", NULL, 0, hwnd);
- todo_wine ok(err==ok_snd,"mci sound SystemExclamation returned %s\n", dbg_mcierr(err));
+ err = mciSendStringA("sound SystemExclamation notify wait", NULL, 0, hwnd);
+ ok(err==ok_snd || err==MCIERR_HARDWARE, "mci sound SystemExclamation returned %s\n", dbg_mcierr(err));
test_notification(hwnd, "sound notify", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
- buf[0]=0;
- err = mciSendString("sysinfo waveaudio name 1 open", buf, sizeof(buf), NULL);
- todo_wine ok(err==MCIERR_OUTOFRANGE,"sysinfo waveaudio name 1 returned %s\n", dbg_mcierr(err));
+ Sleep(16); /* time to auto-close makes sysinfo below return expected error */
+ err = mciSendStringA("sysinfo waveaudio notify name 1 open", buf, sizeof(buf), hwnd);
+ ok(err==MCIERR_OUTOFRANGE,"sysinfo waveaudio name 1 returned %s\n", dbg_mcierr(err));
if(!err) trace("sysinfo dangling open alias: %s\n", buf);
+ test_notification(hwnd, "sysinfo name outofrange\n", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
- err = mciSendString("play no-such-file-exists.wav notify", buf, sizeof(buf), NULL);
- if(err==MCIERR_FILE_NOT_FOUND) { /* a Wine detector */
- /* Unsupported auto-open leaves the file open, preventing clean-up */
- skip("Skipping auto-open tests in Wine\n");
- return;
- }
+ err = mciSendStringA("play no-such-file-exists.wav notify", buf, sizeof(buf), NULL);
+ todo_wine ok(err==MCIERR_NOTIFY_ON_AUTO_OPEN,"mci auto-open notify returned %s\n", dbg_mcierr(err));
+ /* FILE_NOT_FOUND in Wine because auto-open fails before testing the notify flag */
- err = mciSendString("play tempfile.wav notify", buf, sizeof(buf), hwnd);
- todo_wine ok(err==MCIERR_NOTIFY_ON_AUTO_OPEN,"mci auto-open play notify returned %s\n", dbg_mcierr(err));
+ test_notification(hwnd, "-prior to auto-open-", 0);
+
+ err = mciSendStringA("play tempfile.wav notify", buf, sizeof(buf), hwnd);
+ if(ok_saved==MCIERR_FILE_NOT_FOUND) todo_wine /* same as above */
+ ok(err==MCIERR_NOTIFY_ON_AUTO_OPEN,"mci auto-open play notify returned %s\n", dbg_mcierr(err));
+ else
+ ok(err==MCIERR_NOTIFY_ON_AUTO_OPEN,"mci auto-open play notify returned %s\n", dbg_mcierr(err));
if(err) /* FIXME: don't open twice yet, it confuses Wine. */
- err = mciSendString("play tempfile.wav", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("play tempfile.wav", buf, sizeof(buf), hwnd);
ok(err==ok_saved,"mci auto-open play returned %s\n", dbg_mcierr(err));
if(err==MCIERR_FILE_NOT_FOUND) {
return;
}
- buf[0]=0;
- err = mciSendString("sysinfo waveaudio quantity open", buf, sizeof(buf), NULL);
+ err = mciSendStringA("sysinfo waveaudio quantity open", buf, sizeof(buf), NULL);
ok(!err,"mci sysinfo waveaudio quantity after auto-open returned %s\n", dbg_mcierr(err));
- if(!err) todo_wine ok(!strcmp(buf,"1"), "sysinfo quantity open expected 1, got: %s\n", buf);
+ if(!err) ok(!strcmp(buf,"1"), "sysinfo quantity open expected 1, got: %s\n", buf);
- buf[0]=0;
- err = mciSendString("sysinfo waveaudio name 1 open", buf, sizeof(buf), NULL);
- todo_wine ok(!err,"mci sysinfo waveaudio name after auto-open returned %s\n", dbg_mcierr(err));
+ parm.sys.lpstrReturn = (LPSTR)&intbuf[1];
+ parm.sys.dwRetSize = 2*sizeof(DWORD); /* only one DWORD is used */
+ parm.sys.wDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
+ err = mciSendCommandA(0, MCI_SYSINFO, MCI_SYSINFO_QUANTITY | MCI_SYSINFO_OPEN, (DWORD_PTR)&parm);
+ ok(!err, "mciCommand sysinfo waveaudio open notify returned %s\n", dbg_mcierr(err));
+ if(!err) ok(atoi(buf)==intbuf[1],"sysinfo waveaudio quantity open string and command differ\n");
+
+ err = mciSendStringA("sysinfo waveaudio name 1 open notify", buf, sizeof(buf), hwnd);
+ ok(!err,"mci sysinfo waveaudio name after auto-open returned %s\n", dbg_mcierr(err));
/* This is the alias, not necessarily a file name. */
if(!err) ok(!strcmp(buf,"tempfile.wav"), "sysinfo name 1 open: %s\n", buf);
+ test_notification(hwnd, "sysinfo name notify\n", MCI_NOTIFY_SUCCESSFUL);
+
+ err = mciGetDeviceIDA("tempfile.wav");
+ ok(err == 1, "mciGetDeviceIDA tempfile.wav returned %u, expected 1\n", err);
/* Save the full pathname to the file. */
- err = mciSendString("info tempfile.wav file", path, sizeof(path), NULL);
+ err = mciSendStringA("info tempfile.wav file", path, sizeof(path), NULL);
ok(!err,"mci info tempfile.wav file returned %s\n", dbg_mcierr(err));
if(err) strcpy(path,"tempfile.wav");
- err = mciSendString("status tempfile.wav mode", NULL, 0, hwnd);
+ err = mciSendStringA("status tempfile.wav mode", NULL, 0, hwnd);
ok(!err,"mci status tempfile.wav mode without buffer returned %s\n", dbg_mcierr(err));
sprintf(command,"status \"%s\" mode",path);
- err = mciSendString(command, buf, sizeof(buf), hwnd);
+ err = mciSendStringA(command, buf, sizeof(buf), hwnd);
ok(!err,"mci status \"%s\" mode returned %s\n", path, dbg_mcierr(err));
- buf[0]=0;
- err = mciSendString("status tempfile.wav mode", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status tempfile.wav mode", buf, sizeof(buf), hwnd);
ok(!err,"mci status tempfile.wav mode returned %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf,"playing"), "mci auto-open status mode, got: %s\n", buf);
- err = mciSendString("open tempfile.wav", buf, sizeof(buf), NULL);
- todo_wine ok(err==MCIERR_DEVICE_OPEN, "mci open from auto-open returned %s\n", dbg_mcierr(err));
+ err = mciSendStringA("open tempfile.wav", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_DEVICE_OPEN, "mci open from auto-open returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("open foo.wav alias tempfile.wav", buf, sizeof(buf), NULL);
+ ok(err==MCIERR_DUPLICATE_ALIAS, "mci open re-using alias returned %s\n", dbg_mcierr(err));
/* w2k/xp and Wine differ. While the device is busy playing, it is
* regularly open and accessible via the filename: subsequent
* MCIERR_NOTIFY_ON_AUTO_OPEN and thus don't abort the original
* command.
*/
- err = mciSendString("status tempfile.wav mode notify", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status tempfile.wav mode notify", buf, sizeof(buf), hwnd);
todo_wine ok(err==MCIERR_NOTIFY_ON_AUTO_OPEN, "mci status auto-open notify returned %s\n", dbg_mcierr(err));
if(!err) {
trace("Wine style MCI auto-close upon notification\n");
/* "playing" because auto-close comes after the status call. */
- todo_wine ok(!strcmp(buf,"playing"), "mci auto-open status mode notify, got: %s\n", buf);
+ ok(!strcmp(buf,"playing"), "mci auto-open status mode notify, got: %s\n", buf);
/* fixme:winmm:MMDRV_Exit Closing while ll-driver open
* is explained by failure to auto-close a device. */
test_notification(hwnd,"status notify",MCI_NOTIFY_SUCCESSFUL);
/* MCI received NOTIFY_SUPERSEDED and auto-closed the device. */
+
+ /* Until this is implemented, force closing the device */
+ err = mciSendStringA("close tempfile.wav", NULL, 0, hwnd);
+ ok(!err,"mci auto-still-open stop returned %s\n", dbg_mcierr(err));
Sleep(16);
test_notification(hwnd,"auto-open",0);
} else if(err==MCIERR_NOTIFY_ON_AUTO_OPEN) { /* MS style */
trace("MS style MCI auto-open forbids notification\n");
- err = mciSendString("pause tempfile.wav", NULL, 0, hwnd);
+ err = mciSendStringA("pause tempfile.wav", NULL, 0, hwnd);
ok(!err,"mci auto-still-open pause returned %s\n", dbg_mcierr(err));
- err = mciSendString("status tempfile.wav mode", buf, sizeof(buf), hwnd);
+ err = mciSendStringA("status tempfile.wav mode", buf, sizeof(buf), hwnd);
ok(!err,"mci status mode returned %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf,"paused"), "mci auto-open status mode, got: %s\n", buf);
/* Auto-close */
- err = mciSendString("stop tempfile.wav", NULL, 0, hwnd);
+ err = mciSendStringA("stop tempfile.wav wait", NULL, 0, hwnd);
ok(!err,"mci auto-still-open stop returned %s\n", dbg_mcierr(err));
Sleep(16); /* makes sysinfo quantity open below succeed */
}
- err = mciSendString("sysinfo waveaudio quantity open", buf, sizeof(buf), NULL);
+ err = mciSendStringA("sysinfo waveaudio quantity open", buf, sizeof(buf), NULL);
ok(!err,"mci sysinfo waveaudio quantity open after close returned %s\n", dbg_mcierr(err));
- if(!err) todo_wine ok(!strcmp(buf,"0"), "sysinfo quantity open expected 0 after auto-close, got: %s\n", buf);
+ if(!err) ok(!strcmp(buf,"0"), "sysinfo quantity open expected 0 after auto-close, got: %s\n", buf);
/* w95-WinME (not w2k/XP) switch to C:\ after auto-playing once. Prevent
* MCIERR_FILE_NOT_FOUND by using the full path name from the Info file command.
*/
sprintf(command,"status \"%s\" mode wait",path);
- err = mciSendString(command, buf, sizeof(buf), hwnd);
+ err = mciSendStringA(command, buf, sizeof(buf), hwnd);
ok(!err,"mci re-auto-open status mode returned %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf,"stopped"), "mci re-auto-open status mode, got: %s\n", buf);
- err = mciSendString("capability waveaudio device type", buf, sizeof(buf), hwnd);
+ /* This uses auto-open as well. */
+ err = mciSendStringA("capability waveaudio outputs", buf, sizeof(buf), NULL);
+ ok(!err,"mci capability waveaudio outputs returned %s\n", dbg_mcierr(err));
+ /* Wine with no sound selected in winecfg's audio tab fails this test. */
+ if(!err) ok(atoi(buf)==ndevs,"Expected %d audio outputs, got %s\n", ndevs, buf);
+
+ err = mciSendStringA("capability waveaudio device type", buf, sizeof(buf), hwnd);
ok(!err,"mci capability device type returned %s\n", dbg_mcierr(err));
if(!err) ok(!strcmp(buf,"waveaudio"), "mci capability device type response: %s\n", buf);
/* waveaudio forbids Pause without Play. */
sprintf(command,"pause \"%s\"",path);
- err = mciSendString(command, NULL, 0, hwnd);
+ err = mciSendStringA(command, NULL, 0, hwnd);
ok(err==MCIERR_NONAPPLICABLE_FUNCTION,"mci auto-open pause returned %s\n", dbg_mcierr(err));
+
+ ok(0xDEADF00D==intbuf[0] && 0xABADCAFE==intbuf[2],"DWORD buffer corruption\n");
+}
+
+static void test_playWaveTypeMpegvideo(void)
+{
+ MCIERROR err;
+ MCIDEVICEID wDeviceID;
+ MCI_PLAY_PARMS play_parm;
+ MCI_STATUS_PARMS status_parm;
+ char buf[1024];
+ memset(buf, 0, sizeof(buf));
+
+ err = mciSendStringA("open tempfile.wav type MPEGVideo alias mysound", NULL, 0, NULL);
+ ok(err==ok_saved,"mci open tempfile.wav type MPEGVideo returned %s\n", dbg_mcierr(err));
+ if(err) {
+ skip("Cannot open tempfile.wav type MPEGVideo for playing (%s), skipping\n", dbg_mcierr(err));
+ return;
+ }
+
+ wDeviceID = mciGetDeviceIDA("mysound");
+ ok(wDeviceID == 1, "mciGetDeviceIDA mysound returned %u, expected 1\n", wDeviceID);
+
+ err = mciSendCommandA(wDeviceID, MCI_PLAY, 0, (DWORD_PTR)&play_parm);
+ ok(!err,"mciCommand play returned %s\n", dbg_mcierr(err));
+
+ err = mciSendStringA("status mysound mode", buf, sizeof(buf), NULL);
+ ok(!err,"mci status mode returned %s\n", dbg_mcierr(err));
+ ok(!strcmp(buf,"playing"), "mci status mode: %s\n", buf);
+
+ status_parm.dwItem = MCI_STATUS_MODE;
+ err = mciSendCommandA(wDeviceID, MCI_STATUS,
+ MCI_STATUS_ITEM,
+ (DWORD_PTR)&status_parm);
+ ok(!err,"mciCommand status mode returned %s\n", dbg_mcierr(err));
+ ok(status_parm.dwReturn == MCI_MODE_PLAY,
+ "mciCommand status mode: %u\n", (DWORD)status_parm.dwReturn);
+
+ err = mciSendStringA("close mysound", NULL, 0, NULL);
+ ok(!err,"mci close returned %s\n", dbg_mcierr(err));
}
START_TEST(mci)
{
+ char curdir[MAX_PATH], tmpdir[MAX_PATH];
MCIERROR err;
HWND hwnd;
+
+ GetCurrentDirectoryA(MAX_PATH, curdir);
+ GetTempPathA(MAX_PATH, tmpdir);
+ SetCurrentDirectoryA(tmpdir);
+
hwnd = CreateWindowExA(0, "static", "winmm test", WS_POPUP, 0,0,100,100,
0, 0, 0, NULL);
+ test_mciParser(hwnd);
test_openCloseWAVE(hwnd);
test_recordWAVE(hwnd);
- test_playWAVE(hwnd);
- test_asyncWAVE(hwnd);
- test_AutoOpenWAVE(hwnd);
+ if(waveOutGetNumDevs()){
+ test_playWAVE(hwnd);
+ test_asyncWAVE(hwnd);
+ test_AutoOpenWAVE(hwnd);
+ test_playWaveTypeMpegvideo();
+ }else
+ skip("No output devices available, skipping all output tests\n");
/* Win9X hangs when exiting with something still open. */
- err = mciSendString("close all", NULL, 0, hwnd);
- todo_wine ok(!err,"final close all returned %s\n", dbg_mcierr(err));
- ok(DeleteFile("tempfile.wav")||ok_saved,"Delete tempfile.wav (cause auto-open?)\n");
+ err = mciSendStringA("close all", NULL, 0, hwnd);
+ ok(!err,"final close all returned %s\n", dbg_mcierr(err));
+ ok(DeleteFileA("tempfile.wav") || ok_saved, "Delete tempfile.wav (cause auto-open?)\n");
DestroyWindow(hwnd);
+
+ SetCurrentDirectoryA(curdir);
}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
mmsys_error(rc));
}
-static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
+static void mixer_test_controlA(HMIXEROBJ mix, MIXERCONTROLA *control)
{
MMRESULT rc;
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));
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));
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));
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));
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));
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));
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));
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));
static void mixer_test_deviceA(int device)
{
MIXERCAPSA capsA;
- HMIXER mix;
+ HMIXEROBJ mix;
MMRESULT rc;
DWORD d,s,ns,nc;
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",
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",
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",
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",
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",
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",
/* 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",
}
}
}
- 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;
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));
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));
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));
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));
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));
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));
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));
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));
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];
}
- 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",
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",
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",
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",
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",
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));
/* 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",
}
}
}
- test_mixerClose(mix);
+ test_mixerClose((HMIXER)mix);
}
}
static void test_mixerOpen(void)
{
HMIXER mix;
+ HANDLE event;
MMRESULT rc;
UINT ndev, d;
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)
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);
if (rc == MMSYSERR_NOERROR)
test_mixerClose(mix);
+
+ rc = WaitForSingleObject(event, 0);
+ ok(rc == WAIT_TIMEOUT, "WaitEvent %d\n", rc);
+ CloseHandle(event);
}
}
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
-#include <assert.h>
#include <stdarg.h>
#include "windef.h"
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);
(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);
(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);
static void test_mmioOpen(char *fname)
{
- char buf[256];
+ char buf[MMIO_DEFAULTBUFFER];
MMRESULT ret;
HMMIO hmmio;
MMIOINFO mmio;
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
}
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);
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);
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);
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);
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);
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);
test_mmioOpen(fname);
test_mmioSetBuffer(NULL);
test_mmioSetBuffer(fname);
+ test_mmioOpen_fourcc();
+ test_mmioSeek();
+ test_mmio_end_of_file();
+ test_mmio_buffer_pointer();
}
/* 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);
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 },
"thread priority is %s, should be THREAD_PRIORITY_TIME_CRITICAL\n",
get_priority(priority));
}
+ timeKillEvent(id);
}
START_TEST(timer)
#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;
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;
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");
if ((flags & WAVE_MAPPED) == WAVE_MAPPED) {
if (!first) strcat(msg, "|");
strcat(msg, "WAVE_MAPPED");
- first = FALSE;
}
return msg;
}
{
#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");
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;
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)
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);
}
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) {
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;
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;
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 */
(!(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))
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 &&
}
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),
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));
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");
}
}
"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);
{
WAVEOUTCAPSA capsA;
WAVEOUTCAPSW capsW;
- WAVEFORMATEX format, oformat;
+ WAVEFORMATEX format;
WAVEFORMATEXTENSIBLE wfex;
IMAADPCMWAVEFORMAT wfa;
HWAVEOUT wout;
"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) {
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);
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;
&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;
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",
"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();
}