midi.c 36.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * 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
 */

#include <stdio.h>
22
#include <stddef.h>
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
#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 */
}

64 65
#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)
66 67 68 69 70 71 72
{   /* 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));
73 74 75 76 77
        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;
78
          trace_(__FILE__,line)("Waiting for delayed message %x from %s\n", m1, command);
79 80
          SetLastError(0xDEADBEEF);
          rc = MsgWaitForMultipleObjects(0, NULL, FALSE, 3000, QS_POSTMESSAGE);
81
          ok_(__FILE__,line)(rc==WAIT_OBJECT_0, "Wait failed: %04x %d\n", rc, GetLastError());
82 83
          seen = PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE);
        }
84
        if (seen) {
85
            trace_(__FILE__,line)("Message %x, wParam=%lx, lParam=%lx from %s\n",
86
                  msg.message, msg.wParam, msg.lParam, command);
87 88
            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);
89
        }
90
        else ok_(__FILE__,line)(m1==0, "Expect message %x from %s\n", m1, command);
91 92 93 94
    }
    else {
        /* FIXME: MOM_POSITIONCB and MOM_DONE are so close that a queue is needed. */
        if (cbmsg) {
95
            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);
96 97
            cbmsg = 0; /* Mark as read */
            cbval1 = cbval2 = WHATEVER;
98
            ok_(__FILE__,line)(cbinst==MYCBINST, "callback dwInstance changed to %lx\n", cbinst);
99
        }
100
        else ok_(__FILE__,line)(m1==0, "Expect callback %x from %s\n", m1, command);
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
    }
}


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);

130
    memset(&mhdr, 0, sizeof(mhdr));
131
    mhdr.dwFlags = MHDR_DONE;
132 133
    mhdr.dwUser = 0x56FA552C;
    mhdr.dwBufferLength = 70000; /* > 64KB! */
134
    mhdr.dwBytesRecorded = 5;
135 136 137
    mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength);
    ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength);
    if (mhdr.lpData) {
138 139
        rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1);
        ok(rc==MMSYSERR_INVALPARAM, "midiInPrepare tiny rc=%s\n", mmsys_error(rc));
140 141 142
        ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);

        mhdr.dwFlags |= MHDR_INQUEUE;
143 144
        rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
        ok(!rc, "midiInPrepare old size rc=%s\n", mmsys_error(rc));
145 146 147 148 149
        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;
150
        rc = midiInPrepareHeader(hm, &mhdr, sizeof(mhdr));
151
        ok(!rc, "midiInPrepare rc=%s\n", mmsys_error(rc));
152 153 154
        ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_INQUEUE|MHDR_DONE), "dwFlags=%x\n", mhdr.dwFlags);

        mhdr.dwFlags &= ~MHDR_INQUEUE;
155 156
        rc = midiInUnprepareHeader(hm, &mhdr, sizeof(mhdr));
        ok(!rc, "midiInUnprepare rc=%s\n", mmsys_error(rc));
157
        ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
158

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
        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);
181

182 183 184 185 186 187 188
        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);
    }
189 190
    rc = midiInReset(hm); /* Return any pending buffer */
    ok(!rc, "midiInReset rc=%s\n", mmsys_error(rc));
191 192 193 194 195 196 197
    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);
198 199 200

    rc = midiInClose(hm);
    ok(!rc, "midiInClose rc=%s\n", mmsys_error(rc));
201 202 203

    ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
    HeapFree(GetProcessHeap(), 0, mhdr.lpData);
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
    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));

247
    err = mciSendStringA("sysinfo sequencer quantity", buf, sizeof(buf), hwnd);
248 249
    ok(!err, "mci sysinfo sequencer quantity returned %d\n", err);
    if (!err) trace("Found %s MCI sequencer devices\n", buf);
250 251 252

    if (!strcmp(buf, "0")) return;

253
    err = mciSendStringA("capability sequencer can record", buf, sizeof(buf), hwnd);
254 255
    ok(!err, "mci sysinfo sequencer quantity returned %d\n", err);
    if(!err) ok(!strcmp(buf, "false"), "capability can record is %s\n", buf);
256 257 258 259 260 261 262 263 264
}


static void test_midiOut_device(UINT udev, HWND hwnd)
{
    HMIDIOUT hm;
    MMRESULT rc;
    MIDIOUTCAPSA capsA;
    DWORD ovolume;
265
    UINT  udevid;
266 267 268 269 270
    MIDIHDR mhdr;

    rc = midiOutGetDevCapsA(udev, &capsA, sizeof(capsA));
    ok(!rc, "midiOutGetDevCaps(dev=%d) rc=%s\n", udev, mmsys_error(rc));
    if (!rc) {
271 272 273 274 275 276 277 278
        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);
        }
279 280 281 282 283 284
    }

    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);
285
    if (rc == MMSYSERR_NOTSUPPORTED || rc == MMSYSERR_NODRIVER)
286 287 288 289
    {
        skip( "MIDI out not supported\n" );
        return;
    }
290 291 292 293 294 295
    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);
296
    ok((capsA.dwSupport & MIDICAPS_VOLUME) ? rc==MMSYSERR_NOERROR : rc==MMSYSERR_NOTSUPPORTED, "midiOutGetVolume rc=%s\n", mmsys_error(rc));
297 298 299 300
    /* 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);

301 302
    /* The W95 ESFM Synthesis device reports NOTENABLED although
     * GetVolume by handle works and music plays. */
303
    rc = midiOutGetVolume(UlongToHandle(udev), &ovolume);
304 305
    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));

306 307 308
    rc = midiOutGetVolume(hm, NULL);
    ok(rc==MMSYSERR_INVALPARAM, "midiOutGetVolume NULL rc=%s\n", mmsys_error(rc));

309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
    /* 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));
        }
    }
329
    rc = midiOutGetDevCapsA((UINT_PTR)hm, &capsA, sizeof(capsA));
330
    ok(!rc, "midiOutGetDevCaps(dev=%d) by handle rc=%s\n", udev, mmsys_error(rc));
331
    rc = midiInGetDevCapsA((UINT_PTR)hm, (LPMIDIINCAPSA)&capsA, sizeof(DWORD));
332 333 334 335 336 337 338 339 340
    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 */
    }

341
    memset(&mhdr, 0, sizeof(mhdr));
342
    mhdr.dwFlags = MHDR_DONE;
343 344
    mhdr.dwUser   = 0x56FA552C;
    mhdr.dwOffset = 0xDEADBEEF;
345 346 347 348 349 350
    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));
351
        ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
352 353
        test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER);

354 355
        rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1);
        ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc));
356 357 358 359
        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 */
360 361
        rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
        ok(!rc, "midiOutPrepare old size rc=%s\n", mmsys_error(rc));
362 363 364 365 366 367
        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;
368
        rc = midiOutPrepareHeader(hm, &mhdr, sizeof(mhdr));
369
        ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc));
370 371 372 373 374 375 376 377
        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;
378 379
        rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
        ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
380 381 382 383 384 385
        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);
386 387 388 389

        HeapFree(GetProcessHeap(), 0, mhdr.lpData);
    }
    ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
390
    ok(mhdr.dwOffset==0xDEADBEEF, "MIDIHDR.dwOffset changed to %x\n", mhdr.dwOffset);
391

392 393 394 395
    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);

396 397 398 399 400 401
    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);
402 403

    rc = midiOutOpen(&hm, udev, 0, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
404
    /* w95 broken(rc==MMSYSERR_INVALPARAM) see WINMM_CheckCallback */
405 406 407 408 409 410 411 412 413 414 415
    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);
    }
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
    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;
    }
}

440 441 442 443 444 445
typedef struct midishortevent_tag { /* ideal size for MEVT_F_SHORT event type */
    DWORD dwDeltaTime;
    DWORD dwStreamID;
    DWORD dwEvent;
} MIDISHORTEVENT;

446
/* Native crashes on a second run with the const qualifier set on this data! */
447 448 449
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 */
450 451 452
    0, 0, 0, 0,  0, 0, 0, 0,
    0xE0, 0x93, 0x04, MEVT_TEMPO, /* 0493E0 == 300000 */
    0, 0, 0, 0,  0, 0, 0, 0,
453 454 455
    0x93, 0x48, 0x6F, MEVT_SHORTMSG,
};

456 457 458
static MIDISHORTEVENT strmNops[] = { /* Test callback + dwOffset */
  { 0, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
  { 0, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
459 460 461 462 463 464 465 466 467 468 469
};

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;
}

470 471 472 473 474
static void test_midiStream(UINT udev, HWND hwnd)
{
    HMIDISTRM hm;
    MMRESULT rc, rc2;
    MIDIHDR mhdr;
475 476 477 478
    union {
        MIDIPROPTEMPO tempo;
        MIDIPROPTIMEDIV tdiv;
    } midiprop;
479 480 481 482 483

    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);
484
    if (rc == MMSYSERR_NOTSUPPORTED || rc == MMSYSERR_NODRIVER)
485 486 487 488
    {
        skip( "MIDI stream not supported\n" );
        return;
    }
489 490 491 492 493
    ok(!rc, "midiStreamOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
    if (rc) return;

    test_notification(hwnd, "midiStreamOpen", MOM_OPEN, 0);

494 495 496
    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));
497
    ok(midiprop.tempo.dwTempo==500000, "default stream tempo %u microsec per quarter note\n", midiprop.tempo.dwTempo);
498 499 500 501 502 503

    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);

504
    memset(&mhdr, 0, sizeof(mhdr));
505 506
    mhdr.dwUser   = 0x56FA552C;
    mhdr.dwOffset = 1234567890;
507
    mhdr.dwBufferLength = sizeof(strmEvents);
508
    mhdr.dwBytesRecorded = mhdr.dwBufferLength;
509 510 511 512 513 514
    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);

515 516 517 518
        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));
519 520 521
        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. */
522 523
        rc = midiStreamOut(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
        ok(!rc, "midiStreamOut old size rc=%s\n", mmsys_error(rc));
524 525 526 527 528 529
        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);
530 531
        /* Wine <1.1.39 started playing immediately */
        test_notification(hwnd, "midiStream still paused", 0, WHATEVER);
532 533 534

    /* MSDN asks to use midiStreamRestart prior to midiStreamOut()
     * because the starting state is 'pause', but some apps seem to
535
     * work with the inverse order: queue everything, then play.
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
     */

        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);
554 555
        test_notification(hwnd, "midiStream callback", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
        test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr);
556

557 558 559 560
        /* 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));
561
        ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
562
        rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
563 564 565 566 567 568 569 570 571 572 573 574 575 576
        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 */

577 578 579 580 581
        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);

582 583 584
        rc = midiStreamRestart(hm);
        ok(!rc, "midiStreamRestart #2 rc=%s\n", mmsys_error(rc));

585
        mhdr.dwFlags |= MHDR_ISSTRM;
586
        /* Preset flags (e.g. MHDR_ISSTRM) do not disturb. */
587
        rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
588
        ok(!rc, "midiOutPrepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc));
589
        rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
590 591 592 593 594 595
        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);
596
    ok(0==((MIDISHORTEVENT*)&strmEvents)[0].dwStreamID, "dwStreamID set to %x\n", ((LPMIDIEVENT)&strmEvents[0])->dwStreamID);
597 598 599 600

    /* dwBytesRecorded controls how much is played, not dwBufferLength
     * allowing to immediately forward packets from midiIn to midiOut */
    mhdr.dwOffset = 1234123123;
601 602
    mhdr.dwBufferLength = sizeof(strmNops);
    trace("buffer: %u\n", mhdr.dwBufferLength);
603 604
    mhdr.dwBytesRecorded = 0;
    mhdr.lpData = (LPSTR)&strmNops[0];
605 606
    strmNops[0].dwEvent |= MEVT_F_CALLBACK;
    strmNops[1].dwEvent |= MEVT_F_CALLBACK;
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624

    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;
625
    mhdr.dwBytesRecorded = 1*sizeof(MIDISHORTEVENT);
626 627 628 629 630 631 632 633 634 635

    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;
636
    mhdr.dwBytesRecorded = 2*sizeof(MIDISHORTEVENT);
637 638 639 640 641 642 643 644

    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);
645 646
    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);
647

648 649
    strmNops[0].dwEvent &= ~MEVT_F_CALLBACK;
    strmNops[1].dwEvent &= ~MEVT_F_CALLBACK;
650 651 652 653 654 655 656
    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 */
657
    ok(1234123123==mhdr.dwOffset || broken(sizeof(MIDISHORTEVENT)==mhdr.dwOffset), "MIDIHDR.dwOffset nocb changed to %u\n", mhdr.dwOffset);
658 659 660

    mhdr.dwBytesRecorded = mhdr.dwBufferLength-1;
    rc = playStream(hm, &mhdr);
661
    ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBytesRecorded modulo MIDIEVENT rc=%s\n", mmsys_error(rc));
662 663 664 665 666 667 668 669
    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);
670 671 672

    rc = midiStreamStop(hm);
    ok(!rc, "midiStreamStop rc=%s\n", mmsys_error(rc));
673 674 675 676
    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));
677 678
    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);
679 680 681 682 683 684 685 686

    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));
687
        todo_wine ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare stream too large rc=%s\n", mmsys_error(rc));
688 689 690 691 692 693 694 695 696 697 698

        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);
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713

    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));
    }
714 715
}

716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
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;
}

782 783 784 785 786 787 788 789 790 791 792 793 794
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) {
795
        MIDIOUTCAPSA capsA;
796
        skip("Found no MIDI out device\n");
797 798 799 800 801

        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));

802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
        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++) {
819 820 821 822 823 824 825 826 827 828 829
        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");
830 831 832 833 834 835 836 837 838
    }
    trace("** Testing MIDI mapper\n");
    test_midiOut_device(MIDIMAPPER, hwnd);
    Sleep(800);
    test_midiStream(MIDIMAPPER, hwnd);
}

START_TEST(midi)
{
839
    HWND hwnd = 0;
840 841 842 843 844 845 846
    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);
}