1 /*
2  * Test winmm midi
3  *
4  * Copyright 2010 Jörg Höhle
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #define _WINE
22 
23 #include <stdio.h>
24 #include <stddef.h>
25 #include "windows.h"
26 #include "mmsystem.h"
27 #include "wine/test.h"
28 
29 extern const char* mmsys_error(MMRESULT error); /* from wave.c */
30 
31 /* Test in order of increasing probability to hang.
32  * On many UNIX systems, the Timidity softsynth provides
33  * MIDI sequencer services and it is not particularly robust.
34  */
35 
36 #define MYCBINST 0x4CAFE5A8 /* not used with window or thread callbacks */
37 #define WHATEVER 0xFEEDF00D
38 
39 static BOOL spurious_message(LPMSG msg)
40 {
41   /* WM_DEVICECHANGE 0x0219 appears randomly */
42   if(msg->message == WM_DEVICECHANGE) {
43     trace("skipping spurious message %04x\n", msg->message);
44     return TRUE;
45   }
46   return FALSE;
47 }
48 
49 static UINT      cbmsg  = 0;
50 static DWORD_PTR cbval1 = WHATEVER;
51 static DWORD_PTR cbval2 = 0;
52 static DWORD_PTR cbinst = MYCBINST;
53 
54 static void CALLBACK callback_func(HWAVEOUT hwo, UINT uMsg,
55                                    DWORD_PTR dwInstance,
56                                    DWORD_PTR dwParam1, DWORD_PTR dwParam2)
57 {
58     if (winetest_debug>1)
59         trace("Callback! msg=%x %lx %lx\n", uMsg, dwParam1, dwParam2);
60     cbmsg = uMsg;
61     cbval1 = dwParam1;   /* mhdr or 0 */
62     cbval2 = dwParam2;   /* always 0 */
63     cbinst = dwInstance; /* MYCBINST, see midiOut/StreamOpen */
64 }
65 
66 #define test_notification(hwnd, command, m1, p2) test_notification_dbg(hwnd, command, m1, p2, __LINE__)
67 static void test_notification_dbg(HWND hwnd, const char* command, UINT m1, DWORD_PTR p2, int line)
68 {   /* Use message type 0 as meaning no message */
69     MSG msg;
70     if (hwnd) {
71         /* msg.wParam is hmidiout, msg.lParam is the mhdr (or 0) */
72         BOOL seen;
73         do { seen = PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE); }
74         while(seen && spurious_message(&msg));
75         if (m1 && !seen) {
76           /* We observe transient delayed notification, mostly on native.
77            * Perhaps the OS preempts the player thread after setting MHDR_DONE
78            * or clearing MHDR_INQUEUE, before calling DriverCallback. */
79           DWORD rc;
80           trace_(__FILE__,line)("Waiting for delayed message %x from %s\n", m1, command);
81           SetLastError(0xDEADBEEF);
82           rc = MsgWaitForMultipleObjects(0, NULL, FALSE, 3000, QS_POSTMESSAGE);
83           ok_(__FILE__,line)(rc==WAIT_OBJECT_0, "Wait failed: %04x %d\n", rc, GetLastError());
84           seen = PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE);
85         }
86         if (seen) {
87             trace_(__FILE__,line)("Message %x, wParam=%lx, lParam=%lx from %s\n",
88                   msg.message, msg.wParam, msg.lParam, command);
89             ok_(__FILE__,line)(msg.hwnd==hwnd, "Didn't get the handle to our test window\n");
90             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);
91         }
92         else ok_(__FILE__,line)(m1==0, "Expect message %x from %s\n", m1, command);
93     }
94     else {
95         /* FIXME: MOM_POSITIONCB and MOM_DONE are so close that a queue is needed. */
96         if (cbmsg) {
97             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);
98             cbmsg = 0; /* Mark as read */
99             cbval1 = cbval2 = WHATEVER;
100             ok_(__FILE__,line)(cbinst==MYCBINST, "callback dwInstance changed to %lx\n", cbinst);
101         }
102         else ok_(__FILE__,line)(m1==0, "Expect callback %x from %s\n", m1, command);
103     }
104 }
105 
106 
107 static void test_midiIn_device(UINT udev, HWND hwnd)
108 {
109     HMIDIIN hm;
110     MMRESULT rc;
111     MIDIINCAPSA capsA;
112     MIDIHDR mhdr;
113 
114     rc = midiInGetDevCapsA(udev, &capsA, sizeof(capsA));
115     ok((MIDIMAPPER==udev) ? (rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*nt,w2k*/)) : rc==0,
116        "midiInGetDevCaps(dev=%d) rc=%s\n", udev, mmsys_error(rc));
117     if (!rc) {
118         /* MIDI IN capsA.dwSupport may contain garbage, absent in old MS-Windows */
119         trace("* %s: manufacturer=%d, product=%d, support=%X\n", capsA.szPname, capsA.wMid, capsA.wPid, capsA.dwSupport);
120     }
121 
122     if (hwnd)
123         rc = midiInOpen(&hm, udev, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
124     else
125         rc = midiInOpen(&hm, udev, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
126     ok((MIDIMAPPER!=udev) ? rc==0 : (rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*nt,w2k*/)),
127        "midiInOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
128     if (rc) return;
129 
130     test_notification(hwnd, "midiInOpen", MIM_OPEN, 0);
131 
132     memset(&mhdr, 0, sizeof(mhdr));
133     mhdr.dwFlags = MHDR_DONE;
134     mhdr.dwUser = 0x56FA552C;
135     mhdr.dwBufferLength = 70000; /* > 64KB! */
136     mhdr.dwBytesRecorded = 5;
137     mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength);
138     ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength);
139     if (mhdr.lpData) {
140         rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1);
141         ok(rc==MMSYSERR_INVALPARAM, "midiInPrepare tiny rc=%s\n", mmsys_error(rc));
142         ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
143 
144         mhdr.dwFlags |= MHDR_INQUEUE;
145         rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
146         ok(!rc, "midiInPrepare old size rc=%s\n", mmsys_error(rc));
147         ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_INQUEUE|MHDR_DONE)/*w9x*/ ||
148            mhdr.dwFlags == MHDR_PREPARED, "dwFlags=%x\n", mhdr.dwFlags);
149         trace("MIDIHDR flags=%x when unsent\n", mhdr.dwFlags);
150 
151         mhdr.dwFlags |= MHDR_INQUEUE|MHDR_DONE;
152         rc = midiInPrepareHeader(hm, &mhdr, sizeof(mhdr));
153         ok(!rc, "midiInPrepare rc=%s\n", mmsys_error(rc));
154         ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_INQUEUE|MHDR_DONE), "dwFlags=%x\n", mhdr.dwFlags);
155 
156         mhdr.dwFlags &= ~MHDR_INQUEUE;
157         rc = midiInUnprepareHeader(hm, &mhdr, sizeof(mhdr));
158         ok(!rc, "midiInUnprepare rc=%s\n", mmsys_error(rc));
159         ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
160 
161         mhdr.dwFlags &= ~MHDR_DONE;
162         rc = midiInUnprepareHeader(hm, &mhdr, sizeof(mhdr));
163         ok(!rc, "midiInUnprepare rc=%s\n", mmsys_error(rc));
164         ok(mhdr.dwFlags == 0, "dwFlags=%x\n", mhdr.dwFlags);
165 
166         rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
167         ok(!rc, "midiInPrepare rc=%s\n", mmsys_error(rc));
168         ok(mhdr.dwFlags == MHDR_PREPARED, "dwFlags=%x\n", mhdr.dwFlags);
169 
170         mhdr.dwFlags |= MHDR_DONE;
171         rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
172         ok(!rc, "midiInPrepare rc=%s\n", mmsys_error(rc));
173         ok(mhdr.dwBytesRecorded == 5, "BytesRec=%u\n", mhdr.dwBytesRecorded);
174 
175         mhdr.dwFlags |= MHDR_DONE;
176         rc = midiInAddBuffer(hm, &mhdr, sizeof(mhdr));
177         ok(!rc, "midiAddBuffer rc=%s\n", mmsys_error(rc));
178         ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags);
179 
180         /* w95 does not set dwBytesRecorded=0 within midiInAddBuffer.  Wine does. */
181         if (mhdr.dwBytesRecorded != 0)
182             trace("dwBytesRecorded %u\n", mhdr.dwBytesRecorded);
183 
184         rc = midiInAddBuffer(hm, &mhdr, sizeof(mhdr));
185         ok(rc==MIDIERR_STILLPLAYING, "midiAddBuffer rc=%s\n", mmsys_error(rc));
186 
187         rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
188         ok(!rc, "midiInPrepare rc=%s\n", mmsys_error(rc));
189         ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags);
190     }
191     rc = midiInReset(hm); /* Return any pending buffer */
192     ok(!rc, "midiInReset rc=%s\n", mmsys_error(rc));
193     test_notification(hwnd, "midiInReset", MIM_LONGDATA, (DWORD_PTR)&mhdr);
194 
195     ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE), "dwFlags=%x\n", mhdr.dwFlags);
196     rc = midiInUnprepareHeader(hm, &mhdr, sizeof(mhdr));
197     ok(!rc, "midiInUnprepare rc=%s\n", mmsys_error(rc));
198 
199     ok(mhdr.dwBytesRecorded == 0, "Did some MIDI HW send %u bytes?\n", mhdr.dwBytesRecorded);
200 
201     rc = midiInClose(hm);
202     ok(!rc, "midiInClose rc=%s\n", mmsys_error(rc));
203 
204     ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
205     HeapFree(GetProcessHeap(), 0, mhdr.lpData);
206     test_notification(hwnd, "midiInClose", MIM_CLOSE, 0);
207     test_notification(hwnd, "midiIn over", 0, WHATEVER);
208 }
209 
210 static void test_midi_infns(HWND hwnd)
211 {
212     HMIDIIN hm;
213     MMRESULT rc;
214     UINT udev, ndevs = midiInGetNumDevs();
215 
216     rc = midiInOpen(&hm, ndevs, 0, 0, CALLBACK_NULL);
217     ok(rc==MMSYSERR_BADDEVICEID, "midiInOpen udev>max rc=%s\n", mmsys_error(rc));
218     if (!rc) {
219         rc = midiInClose(hm);
220         ok(!rc, "midiInClose rc=%s\n", mmsys_error(rc));
221     }
222     if (!ndevs) {
223         trace("Found no MIDI IN device\n"); /* no skip for this common situation */
224         rc = midiInOpen(&hm, MIDIMAPPER, 0, 0, CALLBACK_NULL);
225         ok(rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*nt,w2k*/), "midiInOpen MAPPER with no MIDI rc=%s\n", mmsys_error(rc));
226         if (!rc) {
227             rc = midiInClose(hm);
228             ok(!rc, "midiInClose rc=%s\n", mmsys_error(rc));
229         }
230         return;
231     }
232     trace("Found %d MIDI IN devices\n", ndevs);
233     for (udev=0; udev < ndevs; udev++) {
234         trace("** Testing device %d\n", udev);
235         test_midiIn_device(udev, hwnd);
236         Sleep(50);
237     }
238     trace("** Testing MIDI mapper\n");
239     test_midiIn_device(MIDIMAPPER, hwnd);
240 }
241 
242 
243 static void test_midi_mci(HWND hwnd)
244 {
245     MCIERROR err;
246     char buf[1024];
247     memset(buf, 0, sizeof(buf));
248 
249     err = mciSendStringA("sysinfo sequencer quantity", buf, sizeof(buf), hwnd);
250     ok(!err, "mci sysinfo sequencer quantity returned %d\n", err);
251     if (!err) trace("Found %s MCI sequencer devices\n", buf);
252 
253     if (!strcmp(buf, "0")) return;
254 
255     err = mciSendStringA("capability sequencer can record", buf, sizeof(buf), hwnd);
256     ok(!err, "mci sysinfo sequencer quantity returned %d\n", err);
257     if(!err) ok(!strcmp(buf, "false"), "capability can record is %s\n", buf);
258 }
259 
260 
261 static void test_midiOut_device(UINT udev, HWND hwnd)
262 {
263     HMIDIOUT hm;
264     MMRESULT rc;
265     MIDIOUTCAPSA capsA;
266     DWORD ovolume;
267     UINT  udevid;
268     MIDIHDR mhdr;
269 
270     rc = midiOutGetDevCapsA(udev, &capsA, sizeof(capsA));
271     ok(!rc, "midiOutGetDevCaps(dev=%d) rc=%s\n", udev, mmsys_error(rc));
272     if (!rc) {
273         trace("* %s: manufacturer=%d, product=%d, tech=%d, support=%X: %d voices, %d notes\n",
274               capsA.szPname, capsA.wMid, capsA.wPid, capsA.wTechnology, capsA.dwSupport, capsA.wVoices, capsA.wNotes);
275         ok(!((MIDIMAPPER==udev) ^ (MOD_MAPPER==capsA.wTechnology)), "technology %d on device %d\n", capsA.wTechnology, udev);
276         if (MOD_MIDIPORT == capsA.wTechnology) {
277             ok(capsA.wVoices == 0 && capsA.wNotes == 0, "external device with notes or voices\n");
278             ok(capsA.wChannelMask == 0xFFFF, "external device channel mask %x\n", capsA.wChannelMask);
279             ok(!(capsA.dwSupport & (MIDICAPS_VOLUME|MIDICAPS_LRVOLUME|MIDICAPS_CACHE)), "external device support=%X\n", capsA.dwSupport);
280         }
281     }
282 
283     if (hwnd)
284         rc = midiOutOpen(&hm, udev, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
285     else
286         rc = midiOutOpen(&hm, udev, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
287     if (rc == MMSYSERR_NOTSUPPORTED || rc == MMSYSERR_NODRIVER)
288     {
289         skip( "MIDI out not supported\n" );
290         return;
291     }
292     ok(!rc, "midiOutOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
293     if (rc) return;
294 
295     test_notification(hwnd, "midiOutOpen", MOM_OPEN, 0);
296 
297     rc = midiOutGetVolume(hm, &ovolume);
298     ok((capsA.dwSupport & MIDICAPS_VOLUME) ? rc==MMSYSERR_NOERROR : rc==MMSYSERR_NOTSUPPORTED, "midiOutGetVolume rc=%s\n", mmsys_error(rc));
299     /* The native mapper responds with FFFFFFFF initially,
300      * real devices with the volume GUI SW-synth settings. */
301     if (!rc) trace("Current volume %x on device %d\n", ovolume, udev);
302 
303     /* The W95 ESFM Synthesis device reports NOTENABLED although
304      * GetVolume by handle works and music plays. */
305     rc = midiOutGetVolume(UlongToHandle(udev), &ovolume);
306     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));
307 
308     rc = midiOutGetVolume(hm, NULL);
309     ok(rc==MMSYSERR_INVALPARAM, "midiOutGetVolume NULL rc=%s\n", mmsys_error(rc));
310 
311     /* Tests with midiOutSetvolume show that the midi mapper forwards
312      * the value to the real device, but Get initially always reports
313      * FFFFFFFF.  Therefore, a Get+SetVolume pair with the mapper is
314      * not adequate to restore the value prior to tests.
315      */
316     if (winetest_interactive && (capsA.dwSupport & MIDICAPS_VOLUME)) {
317         DWORD volume2 = (ovolume < 0x80000000) ? 0xC000C000 : 0x40004000;
318         rc = midiOutSetVolume(hm, volume2);
319         ok(!rc, "midiOutSetVolume rc=%s\n", mmsys_error(rc));
320         if (!rc) {
321             DWORD volume3;
322             rc = midiOutGetVolume(hm, &volume3);
323             ok(!rc, "midiOutGetVolume new rc=%s\n", mmsys_error(rc));
324             if (!rc) trace("New volume %x on device %d\n", volume3, udev);
325             todo_wine ok(volume2==volume3, "volume Set %x = Get %x\n", volume2, volume3);
326 
327             rc = midiOutSetVolume(hm, ovolume);
328             ok(!rc, "midiOutSetVolume restore rc=%s\n", mmsys_error(rc));
329         }
330     }
331     rc = midiOutGetDevCapsA((UINT_PTR)hm, &capsA, sizeof(capsA));
332     ok(!rc, "midiOutGetDevCaps(dev=%d) by handle rc=%s\n", udev, mmsys_error(rc));
333     rc = midiInGetDevCapsA((UINT_PTR)hm, (LPMIDIINCAPSA)&capsA, sizeof(DWORD));
334     ok(rc==MMSYSERR_BADDEVICEID, "midiInGetDevCaps(dev=%d) by out handle rc=%s\n", udev, mmsys_error(rc));
335 
336     {   DWORD e = 0x006F4893; /* velocity, note (#69 would be 440Hz) channel */
337         trace("ShortMsg type %x\n", LOBYTE(LOWORD(e)));
338         rc = midiOutShortMsg(hm, e);
339         ok(!rc, "midiOutShortMsg rc=%s\n", mmsys_error(rc));
340         if (!rc) Sleep(400); /* Hear note */
341     }
342 
343     memset(&mhdr, 0, sizeof(mhdr));
344     mhdr.dwFlags = MHDR_DONE;
345     mhdr.dwUser   = 0x56FA552C;
346     mhdr.dwOffset = 0xDEADBEEF;
347     mhdr.dwBufferLength = 70000; /* > 64KB! */
348     mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength);
349     ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength);
350     if (mhdr.lpData) {
351         rc = midiOutLongMsg(hm, &mhdr, sizeof(mhdr));
352         ok(rc==MIDIERR_UNPREPARED, "midiOutLongMsg unprepared rc=%s\n", mmsys_error(rc));
353         ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
354         test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER);
355 
356         rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1);
357         ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc));
358         ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
359 
360         /* Since at least w2k, midiOutPrepare clears the DONE and INQUEUE flags.  w95 didn't. */
361         /* mhdr.dwFlags |= MHDR_INQUEUE; would cause w95 to return STILLPLAYING from Unprepare */
362         rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
363         ok(!rc, "midiOutPrepare old size rc=%s\n", mmsys_error(rc));
364         ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE)/*w9x*/ ||
365            mhdr.dwFlags == MHDR_PREPARED, "dwFlags=%x\n", mhdr.dwFlags);
366         trace("MIDIHDR flags=%x when unsent\n", mhdr.dwFlags);
367 
368         /* No flag is cleared when already prepared. */
369         mhdr.dwFlags |= MHDR_DONE|MHDR_INQUEUE;
370         rc = midiOutPrepareHeader(hm, &mhdr, sizeof(mhdr));
371         ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc));
372         ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags);
373 
374         mhdr.dwFlags |= MHDR_INQUEUE;
375         rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
376         ok(rc==MIDIERR_STILLPLAYING, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
377         ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags);
378 
379         mhdr.dwFlags &= ~MHDR_INQUEUE;
380         rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
381         ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
382         ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
383 
384         mhdr.dwFlags |= MHDR_INQUEUE;
385         rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
386         ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
387         ok(mhdr.dwFlags == (MHDR_INQUEUE|MHDR_DONE), "dwFlags=%x\n", mhdr.dwFlags);
388 
389         HeapFree(GetProcessHeap(), 0, mhdr.lpData);
390     }
391     ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
392     ok(mhdr.dwOffset==0xDEADBEEF, "MIDIHDR.dwOffset changed to %x\n", mhdr.dwOffset);
393 
394     rc = midiOutGetID(hm, &udevid);
395     ok(!rc, "midiOutGetID rc=%s\n", mmsys_error(rc));
396     if(!rc) ok(udevid==udev, "midiOutGetID gives %d, expect %d\n", udevid, udev);
397 
398     rc = midiOutReset(hm); /* Quiet everything */
399     ok(!rc, "midiOutReset rc=%s\n", mmsys_error(rc));
400 
401     rc = midiOutClose(hm);
402     ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
403     test_notification(hwnd, "midiOutClose", MOM_CLOSE, 0);
404 
405     rc = midiOutOpen(&hm, udev, 0, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
406     /* w95 broken(rc==MMSYSERR_INVALPARAM) see WINMM_CheckCallback */
407     ok(!rc, "midiOutOpen(dev=%d) 0 CALLBACK_WINDOW rc=%s\n", udev, mmsys_error(rc));
408     /* PostMessage(hwnd=0) redirects to PostThreadMessage(GetCurrentThreadId())
409      * which PeekMessage((HWND)-1) queries. */
410     test_notification((HWND)-1, "midiOutOpen WINDOW->THREAD", 0, WHATEVER);
411     test_notification(hwnd, "midiOutOpen WINDOW", 0, WHATEVER);
412     if (!rc) {
413         rc = midiOutClose(hm);
414         ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
415         test_notification((HWND)-1, "midiOutClose WINDOW->THREAD", 0, WHATEVER);
416         test_notification(hwnd, "midiOutClose", 0, WHATEVER);
417     }
418     test_notification(hwnd, "midiOut over", 0, WHATEVER);
419 }
420 
421 static void test_position(HMIDISTRM hm, UINT typein, UINT typeout)
422 {
423     MMRESULT rc;
424     MMTIME mmtime;
425     mmtime.wType = typein;
426     rc = midiStreamPosition(hm, &mmtime, sizeof(MMTIME));
427     /* Ugly, but a single ok() herein enables using the todo_wine prefix */
428     ok(!rc && (mmtime.wType == typeout), "midiStreamPosition type %x converted to %x rc=%s\n", typein, mmtime.wType, mmsys_error(rc));
429     if (!rc) switch(mmtime.wType) {
430     case TIME_MS:
431         trace("Stream position %ums\n", mmtime.u.ms);
432         break;
433     case TIME_TICKS:
434         trace("Stream position %u ticks\n", mmtime.u.ticks);
435         break;
436     case TIME_MIDI:
437         trace("Stream position song pointer %u\n", mmtime.u.midi.songptrpos);
438         break;
439     }
440 }
441 
442 typedef struct midishortevent_tag { /* ideal size for MEVT_F_SHORT event type */
443     DWORD dwDeltaTime;
444     DWORD dwStreamID;
445     DWORD dwEvent;
446 } MIDISHORTEVENT;
447 
448 /* Native crashes on a second run with the const qualifier set on this data! */
449 static BYTE strmEvents[] = { /* A set of variable-sized MIDIEVENT structs */
450     0, 0, 0, 0,  0, 0, 0, 0, /* dwDeltaTime and dwStreamID */
451     0, 0, 0, MEVT_NOP | 0x40, /* with MEVT_F_CALLBACK */
452     0, 0, 0, 0,  0, 0, 0, 0,
453     0xE0, 0x93, 0x04, MEVT_TEMPO, /* 0493E0 == 300000 */
454     0, 0, 0, 0,  0, 0, 0, 0,
455     0x93, 0x48, 0x6F, MEVT_SHORTMSG,
456 };
457 
458 static MIDISHORTEVENT strmNops[] = { /* Test callback + dwOffset */
459   { 0, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
460   { 0, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
461 };
462 
463 static MMRESULT playStream(HMIDISTRM hm, LPMIDIHDR lpMidiHdr)
464 {
465     MMRESULT rc = midiStreamOut(hm, lpMidiHdr, sizeof(MIDIHDR));
466     /* virtual machines may return MIDIERR_STILLPLAYING from the next request
467      * even after MHDR_DONE is set. It's still too early, so add MHDR_INQUEUE. */
468     if (!rc) while (!(lpMidiHdr->dwFlags & MHDR_DONE) || (lpMidiHdr->dwFlags & MHDR_INQUEUE)) { Sleep(100); }
469     return rc;
470 }
471 
472 static void test_midiStream(UINT udev, HWND hwnd)
473 {
474     HMIDISTRM hm;
475     MMRESULT rc, rc2;
476     MIDIHDR mhdr;
477     union {
478         MIDIPROPTEMPO tempo;
479         MIDIPROPTIMEDIV tdiv;
480     } midiprop;
481 
482     if (hwnd)
483         rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
484     else
485         rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
486     if (rc == MMSYSERR_NOTSUPPORTED || rc == MMSYSERR_NODRIVER)
487     {
488         skip( "MIDI stream not supported\n" );
489         return;
490     }
491     ok(!rc, "midiStreamOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
492     if (rc) return;
493 
494     test_notification(hwnd, "midiStreamOpen", MOM_OPEN, 0);
495 
496     midiprop.tempo.cbStruct = sizeof(midiprop.tempo);
497     rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TEMPO);
498     ok(!rc, "midiStreamProperty TEMPO rc=%s\n", mmsys_error(rc));
499     ok(midiprop.tempo.dwTempo==500000, "default stream tempo %u microsec per quarter note\n", midiprop.tempo.dwTempo);
500 
501     midiprop.tdiv.cbStruct = sizeof(midiprop.tdiv);
502     rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TIMEDIV);
503     ok(!rc, "midiStreamProperty TIMEDIV rc=%s\n", mmsys_error(rc));
504     todo_wine ok(24==LOWORD(midiprop.tdiv.dwTimeDiv), "default stream time division %u\n", midiprop.tdiv.dwTimeDiv);
505 
506     memset(&mhdr, 0, sizeof(mhdr));
507     mhdr.dwUser   = 0x56FA552C;
508     mhdr.dwOffset = 1234567890;
509     mhdr.dwBufferLength = sizeof(strmEvents);
510     mhdr.dwBytesRecorded = mhdr.dwBufferLength;
511     mhdr.lpData = (LPSTR)&strmEvents[0];
512     if (mhdr.lpData) {
513         rc = midiOutLongMsg((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
514         ok(rc==MIDIERR_UNPREPARED, "midiOutLongMsg unprepared rc=%s\n", mmsys_error(rc));
515         test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER);
516 
517         rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1);
518         ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc));
519         rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
520         ok(!rc, "midiOutPrepare size rc=%s\n", mmsys_error(rc));
521         ok(mhdr.dwFlags & MHDR_PREPARED, "MHDR.dwFlags when prepared %x\n", mhdr.dwFlags);
522 
523         /* The device is still in paused mode and should queue the message. */
524         rc = midiStreamOut(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
525         ok(!rc, "midiStreamOut old size rc=%s\n", mmsys_error(rc));
526         rc2 = rc;
527         trace("MIDIHDR flags=%x when submitted\n", mhdr.dwFlags);
528         /* w9X/me does not set MHDR_ISSTRM when StreamOut exits,
529          * but it will be set on all systems after the job is finished. */
530 
531         Sleep(90);
532         /* Wine <1.1.39 started playing immediately */
533         test_notification(hwnd, "midiStream still paused", 0, WHATEVER);
534 
535     /* MSDN asks to use midiStreamRestart prior to midiStreamOut()
536      * because the starting state is 'pause', but some apps seem to
537      * work with the inverse order: queue everything, then play.
538      */
539 
540         rc = midiStreamRestart(hm);
541         ok(!rc, "midiStreamRestart rc=%s\n", mmsys_error(rc));
542 
543         if (!rc2) while(mhdr.dwFlags & MHDR_INQUEUE) {
544             trace("async MIDI still queued\n");
545             Sleep(100);
546         } /* Checking INQUEUE is not the recommended way to wait for the end of a job, but we're testing. */
547         /* MHDR_ISSTRM is not necessarily set when midiStreamOut returns
548          * rather than when the queue is eventually processed. */
549         ok(mhdr.dwFlags & MHDR_ISSTRM, "MHDR.dwFlags %x no ISSTRM when out of queue\n", mhdr.dwFlags);
550         if (!rc2) while(!(mhdr.dwFlags & MHDR_DONE)) {
551             /* Never to be seen except perhaps on multicore */
552             trace("async MIDI still not done\n");
553             Sleep(100);
554         }
555         ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags %x not DONE when out of queue\n", mhdr.dwFlags);
556         test_notification(hwnd, "midiStream callback", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
557         test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr);
558 
559         /* Native fills dwOffset regardless of the cbMidiHdr size argument to midiStreamOut */
560         ok(1234567890!=mhdr.dwOffset, "play left MIDIHDR.dwOffset at %u\n", mhdr.dwOffset);
561 
562         rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
563         ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
564         rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
565         ok(!rc, "midiOutUnprepare #2 rc=%s\n", mmsys_error(rc));
566 
567         trace("MIDIHDR stream flags=%x when finished\n", mhdr.dwFlags);
568         ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags when done %x\n", mhdr.dwFlags);
569 
570         test_position(hm, TIME_MS,      TIME_MS);
571         test_position(hm, TIME_TICKS,   TIME_TICKS);
572         todo_wine test_position(hm, TIME_MIDI,    TIME_MIDI);
573         test_position(hm, TIME_SMPTE,   TIME_MS);
574         test_position(hm, TIME_SAMPLES, TIME_MS);
575         test_position(hm, TIME_BYTES,   TIME_MS);
576 
577         Sleep(400); /* Hear note */
578 
579         midiprop.tempo.cbStruct = sizeof(midiprop.tempo);
580         rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TEMPO);
581         ok(!rc, "midiStreamProperty TEMPO rc=%s\n", mmsys_error(rc));
582         ok(0x0493E0==midiprop.tempo.dwTempo, "stream set tempo %u\n", midiprop.tdiv.dwTimeDiv);
583 
584         rc = midiStreamRestart(hm);
585         ok(!rc, "midiStreamRestart #2 rc=%s\n", mmsys_error(rc));
586 
587         mhdr.dwFlags |= MHDR_ISSTRM;
588         /* Preset flags (e.g. MHDR_ISSTRM) do not disturb. */
589         rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
590         ok(!rc, "midiOutPrepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc));
591         rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
592         ok(!rc, "midiOutUnprepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc));
593 
594         rc = midiStreamRestart(hm);
595         ok(!rc, "midiStreamRestart #3 rc=%s\n", mmsys_error(rc));
596     }
597     ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
598     ok(0==((MIDISHORTEVENT*)&strmEvents)[0].dwStreamID, "dwStreamID set to %x\n", ((LPMIDIEVENT)&strmEvents[0])->dwStreamID);
599 
600     /* dwBytesRecorded controls how much is played, not dwBufferLength
601      * allowing to immediately forward packets from midiIn to midiOut */
602     mhdr.dwOffset = 1234123123;
603     mhdr.dwBufferLength = sizeof(strmNops);
604     trace("buffer: %u\n", mhdr.dwBufferLength);
605     mhdr.dwBytesRecorded = 0;
606     mhdr.lpData = (LPSTR)&strmNops[0];
607     strmNops[0].dwEvent |= MEVT_F_CALLBACK;
608     strmNops[1].dwEvent |= MEVT_F_CALLBACK;
609 
610     rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
611     ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc));
612 
613     rc = playStream(hm, &mhdr);
614     ok(!rc, "midiStreamOut 0 bytes recorded rc=%s\n", mmsys_error(rc));
615 
616     test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr);
617     test_notification(hwnd, "0 bytes recorded", 0, WHATEVER);
618 
619     /* FIXME: check dwOffset within callback
620      * instead of the unspecified value afterwards */
621     ok(1234123123==mhdr.dwOffset || broken(0==mhdr.dwOffset), "play 0 set MIDIHDR.dwOffset to %u\n", mhdr.dwOffset);
622     /* w2k and later only set dwOffset when processing MEVT_T_CALLBACK,
623      * while w9X/me/nt always sets it.  Have Wine behave like w2k because the
624      * dwOffset slot does not exist in the small size MIDIHDR. */
625 
626     mhdr.dwOffset = 1234123123;
627     mhdr.dwBytesRecorded = 1*sizeof(MIDISHORTEVENT);
628 
629     rc = playStream(hm, &mhdr);
630     ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
631 
632     test_notification(hwnd, "1 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
633     test_notification(hwnd, "1 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
634     test_notification(hwnd, "1 of 2 events", 0, WHATEVER);
635     ok(0==mhdr.dwOffset, "MIDIHDR.dwOffset 1/2 changed to %u\n", mhdr.dwOffset);
636 
637     mhdr.dwOffset = 1234123123;
638     mhdr.dwBytesRecorded = 2*sizeof(MIDISHORTEVENT);
639 
640     rc = playStream(hm, &mhdr);
641     ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
642 
643     test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
644     test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
645     test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
646     test_notification(hwnd, "2 of 2 events", 0, WHATEVER);
647     ok(sizeof(MIDISHORTEVENT)==mhdr.dwOffset, "MIDIHDR.dwOffset 2/2 changed to %u\n", mhdr.dwOffset);
648     ok(mhdr.dwBytesRecorded == 2*sizeof(MIDISHORTEVENT), "dwBytesRecorded changed to %u\n", mhdr.dwBytesRecorded);
649 
650     strmNops[0].dwEvent &= ~MEVT_F_CALLBACK;
651     strmNops[1].dwEvent &= ~MEVT_F_CALLBACK;
652     mhdr.dwOffset = 1234123123;
653     rc = playStream(hm, &mhdr);
654     ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
655 
656     test_notification(hwnd, "0 CB in 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
657     test_notification(hwnd, "0 CB in 2 events", 0, WHATEVER);
658     /* w9X/me/nt set dwOffset to the position played last */
659     ok(1234123123==mhdr.dwOffset || broken(sizeof(MIDISHORTEVENT)==mhdr.dwOffset), "MIDIHDR.dwOffset nocb changed to %u\n", mhdr.dwOffset);
660 
661     mhdr.dwBytesRecorded = mhdr.dwBufferLength-1;
662     rc = playStream(hm, &mhdr);
663     ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBytesRecorded modulo MIDIEVENT rc=%s\n", mmsys_error(rc));
664     if (!rc) {
665          test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
666     }
667 
668     mhdr.dwBytesRecorded = mhdr.dwBufferLength+1;
669     rc = playStream(hm, &mhdr);
670     ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBufferLength<dwBytesRecorded rc=%s\n", mmsys_error(rc));
671     test_notification(hwnd, "past MIDIHDR tests", 0, WHATEVER);
672 
673     rc = midiStreamStop(hm);
674     ok(!rc, "midiStreamStop rc=%s\n", mmsys_error(rc));
675     ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
676 
677     rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
678     ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
679     ok(0==strmNops[0].dwStreamID, "dwStreamID[0] set to %x\n", strmNops[0].dwStreamID);
680     ok(0==strmNops[1].dwStreamID, "dwStreamID[1] set to %x\n", strmNops[1].dwStreamID);
681 
682     mhdr.dwBufferLength = 70000; /* > 64KB! */
683     mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength);
684     ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength);
685     if (mhdr.lpData) {
686         mhdr.dwFlags = 0;
687         /* PrepareHeader detects the too large buffer is for a stream. */
688         rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
689         todo_wine ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare stream too large rc=%s\n", mmsys_error(rc));
690 
691         rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
692         ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
693 
694         HeapFree(GetProcessHeap(), 0, mhdr.lpData);
695     }
696 
697     rc = midiStreamClose(hm);
698     ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
699     test_notification(hwnd, "midiStreamClose", MOM_CLOSE, 0);
700     test_notification(hwnd, "midiStream over", 0, WHATEVER);
701 
702     rc = midiStreamOpen(&hm, &udev, 1, 0, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
703     ok(!rc /*w2k*/|| rc==MMSYSERR_INVALPARAM/*w98*/, "midiStreamOpen NULL function rc=%s\n", mmsys_error(rc));
704     if (!rc) {
705         trace("Device %d accepts NULL CALLBACK_FUNCTION\n", udev);
706         rc = midiStreamClose(hm);
707         ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
708     }
709 
710     rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)0xDEADBEEF, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
711     ok(rc==MMSYSERR_INVALPARAM, "midiStreamOpen bad window rc=%s\n", mmsys_error(rc));
712     if (!rc) {
713         rc = midiStreamClose(hm);
714         ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
715     }
716 }
717 
718 static BOOL scan_subkeys(HKEY parent, const LPCSTR *sub_keys)
719 {
720     char name[64];
721     DWORD index = 0;
722     DWORD name_len = sizeof(name);
723     BOOL found_vmware = FALSE;
724 
725     if (sub_keys[0] == NULL)
726     {
727        /* We're at the deepest level, check "Identifier" value now */
728        char *test;
729        if (RegQueryValueExA(parent, "Identifier", NULL, NULL, (LPBYTE) name, &name_len) != ERROR_SUCCESS)
730            return FALSE;
731        for (test = name; test < name + lstrlenA(name) - 6 && ! found_vmware; test++)
732        {
733            char c = test[6];
734            test[6] = '\0';
735            found_vmware = (lstrcmpiA(test, "VMware") == 0);
736            test[6] = c;
737        }
738        return found_vmware;
739     }
740 
741     while (RegEnumKeyExA(parent, index, name, &name_len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS &&
742            ! found_vmware) {
743         char c = name[lstrlenA(sub_keys[0])];
744         name[lstrlenA(sub_keys[0])] = '\0';
745         if (lstrcmpiA(name, sub_keys[0]) == 0) {
746             HKEY sub_key;
747             name[lstrlenA(sub_keys[0])] = c;
748             if (RegOpenKeyExA(parent, name, 0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &sub_key) == ERROR_SUCCESS) {
749                 found_vmware = scan_subkeys(sub_key, sub_keys + 1);
750                 RegCloseKey(sub_key);
751             }
752         }
753 
754         name_len = sizeof(name);
755         index++;
756     }
757 
758     return found_vmware;
759 }
760 
761 /*
762  * Usual method to detect whether running inside a VMware virtual machine involves direct port I/O requiring
763  * some assembly and an exception handler. Can't do that in Wine tests. Alternative method of querying WMI
764  * is not available on NT4. So instead we look at the device map and check the Identifier value in the
765  * registry keys HKLM\HARDWARE\DEVICEMAP\SCSI\Scsi Port x\Scsi Bus x\Target Id x\Logical Unit Id x (where
766  * x is some number). If the Identifier value contains the string "VMware" we assume running in a VMware VM.
767  */
768 static BOOL on_vmware(void)
769 {
770     static const LPCSTR sub_keys[] = { "Scsi Port ", "Scsi Bus ", "Target Id ", "Logical Unit Id ", NULL };
771     HKEY scsi;
772     BOOL found_vmware = FALSE;
773 
774     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\Scsi", 0, KEY_ENUMERATE_SUB_KEYS, &scsi) != ERROR_SUCCESS)
775         return FALSE;
776 
777     found_vmware = scan_subkeys(scsi, sub_keys);
778 
779     RegCloseKey(scsi);
780 
781     return found_vmware;
782 }
783 
784 static void test_midi_outfns(HWND hwnd)
785 {
786     HMIDIOUT hm;
787     MMRESULT rc;
788     UINT udev, ndevs = midiOutGetNumDevs();
789 
790     rc = midiOutOpen(&hm, ndevs, 0, 0, CALLBACK_NULL);
791     ok(rc==MMSYSERR_BADDEVICEID, "midiOutOpen udev>max rc=%s\n", mmsys_error(rc));
792     if (!rc) {
793         rc = midiOutClose(hm);
794         ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
795     }
796     if (!ndevs) {
797         MIDIOUTCAPSA capsA;
798         skip("Found no MIDI out device\n");
799 
800         rc = midiOutGetDevCapsA(MIDIMAPPER, &capsA, sizeof(capsA));
801         /* GetDevCaps and Open must return compatible results */
802         ok(rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*nt,w2k*/), "midiOutGetDevCaps MAPPER with no MIDI rc=%s\n", mmsys_error(rc));
803 
804         rc = midiOutOpen(&hm, MIDIMAPPER, 0, 0, CALLBACK_NULL);
805         todo_wine_if (rc == MIDIERR_INVALIDSETUP) /* Wine without snd-seq */
806             ok(rc == MMSYSERR_BADDEVICEID || broken(rc == MMSYSERR_NODRIVER /*w2k sound disabled*/),
807                "midiOutOpen MAPPER with no MIDI rc=%s\n", mmsys_error(rc));
808         if (!rc) {
809             rc = midiOutClose(hm);
810             ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
811         }
812         return;
813     }
814     trace("Found %d MIDI OUT devices\n", ndevs);
815 
816     test_midi_mci(hwnd);
817 
818     for (udev=0; udev < ndevs; udev++) {
819         MIDIOUTCAPSA capsA;
820         rc = midiOutGetDevCapsA(udev, &capsA, sizeof(capsA));
821         if (rc || strcmp(capsA.szPname, "Creative Sound Blaster MPU-401") != 0 || ! on_vmware()) {
822             trace("** Testing device %d\n", udev);
823             test_midiOut_device(udev, hwnd);
824             Sleep(800); /* Let the synth rest */
825             test_midiStream(udev, hwnd);
826             Sleep(800);
827         }
828         else
829             win_skip("Skipping this device on VMware, driver problem\n");
830     }
831     trace("** Testing MIDI mapper\n");
832     test_midiOut_device(MIDIMAPPER, hwnd);
833     Sleep(800);
834     test_midiStream(MIDIMAPPER, hwnd);
835 }
836 
837 START_TEST(midi)
838 {
839     HWND hwnd = 0;
840 
841     CoInitialize(NULL); /* Needed for Win 10 */
842 
843     if (1) /* select 1 for CALLBACK_WINDOW or 0 for CALLBACK_FUNCTION */
844     hwnd = CreateWindowExA(0, "static", "winmm midi test", WS_POPUP, 0,0,100,100,
845                            0, 0, 0, NULL);
846     test_midi_infns(hwnd);
847     test_midi_outfns(hwnd);
848     if (hwnd) DestroyWindow(hwnd);
849 
850     CoUninitialize();
851 }
852