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