mfplat: Read queue subscriber within the critical section.
[wine/zf.git] / dlls / winmm / tests / midi.c
bloba78f9b5ac71f8f304992908e317a9bcf316d413e
1 /*
2 * Test winmm midi
4 * Copyright 2010 Jörg Höhle
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.
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.
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
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stddef.h>
25 #include "windows.h"
26 #include "mmsystem.h"
27 #include "objbase.h"
28 #include "wine/test.h"
30 extern const char* mmsys_error(MMRESULT error); /* from wave.c */
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.
37 #define MYCBINST 0x4CAFE5A8 /* not used with window or thread callbacks */
38 #define WHATEVER 0xFEEDF00D
40 static BOOL spurious_message(LPMSG msg)
42 /* WM_DEVICECHANGE 0x0219 appears randomly */
43 if(msg->message == WM_DEVICECHANGE) {
44 trace("skipping spurious message %04x\n", msg->message);
45 return TRUE;
47 return FALSE;
50 static UINT cbmsg = 0;
51 static DWORD_PTR cbval1 = WHATEVER;
52 static DWORD_PTR cbval2 = 0;
53 static DWORD_PTR cbinst = MYCBINST;
55 static void CALLBACK callback_func(HWAVEOUT hwo, UINT uMsg,
56 DWORD_PTR dwInstance,
57 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
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 */
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);
83 rc = MsgWaitForMultipleObjects(0, NULL, FALSE, 3000, QS_POSTMESSAGE);
84 ok_(__FILE__,line)(rc==WAIT_OBJECT_0, "Wait failed: %04x %d\n", rc, GetLastError());
85 seen = PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE);
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);
93 else ok_(__FILE__,line)(m1==0, "Expect message %x from %s\n", m1, command);
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);
103 else ok_(__FILE__,line)(m1==0, "Expect callback %x from %s\n", m1, command);
108 static void test_midiIn_device(UINT udev, HWND hwnd)
110 HMIDIIN hm;
111 MMRESULT rc;
112 MIDIINCAPSA capsA;
113 MIDIHDR mhdr;
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);
123 if (hwnd)
124 rc = midiInOpen(&hm, udev, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
125 else
126 rc = midiInOpen(&hm, udev, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
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;
131 test_notification(hwnd, "midiInOpen", MIM_OPEN, 0);
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);
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);
152 mhdr.dwFlags |= MHDR_INQUEUE|MHDR_DONE;
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);
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);
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);
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);
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);
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);
181 /* w95 does not set dwBytesRecorded=0 within midiInAddBuffer. Wine does. */
182 if (mhdr.dwBytesRecorded != 0)
183 trace("dwBytesRecorded %u\n", mhdr.dwBytesRecorded);
185 rc = midiInAddBuffer(hm, &mhdr, sizeof(mhdr));
186 ok(rc==MIDIERR_STILLPLAYING, "midiAddBuffer rc=%s\n", mmsys_error(rc));
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);
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);
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));
200 ok(mhdr.dwBytesRecorded == 0, "Did some MIDI HW send %u bytes?\n", mhdr.dwBytesRecorded);
202 rc = midiInClose(hm);
203 ok(!rc, "midiInClose rc=%s\n", mmsys_error(rc));
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);
211 static void test_midi_infns(HWND hwnd)
213 HMIDIIN hm;
214 MMRESULT rc;
215 UINT udev, ndevs = midiInGetNumDevs();
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));
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));
231 return;
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);
239 trace("** Testing MIDI mapper\n");
240 test_midiIn_device(MIDIMAPPER, hwnd);
244 static void test_midi_mci(HWND hwnd)
246 MCIERROR err;
247 char buf[1024];
248 memset(buf, 0, sizeof(buf));
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);
254 if (!strcmp(buf, "0")) return;
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);
261 static BYTE SysEx_reset[] = {
262 0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7 /* GM System ON */
264 static BYTE SysEx_volume_off[] = {
265 0xF0, 0x7F, 0x7F, 0x04, 0x01, 0x00, 0x00, 0xF7
267 static BYTE SysEx_volume_full[] = {
268 0xF0, 0x7F, 0x7F, 0x04, 0x01, 0x00, 0x7F, 0xF7
270 static BOOL found_fluidsynth;
271 const static char fluidsynth_prefix[] = "Synth input port ";
273 static void test_midiOut_device(UINT udev, HWND hwnd)
275 HMIDIOUT hm;
276 MMRESULT rc;
277 MIDIOUTCAPSA capsA;
278 DWORD ovolume;
279 UINT udevid;
280 MIDIHDR mhdr;
282 rc = midiOutGetDevCapsA(udev, &capsA, sizeof(capsA));
283 ok(!rc, "midiOutGetDevCaps(dev=%d) rc=%s\n", udev, mmsys_error(rc));
284 if (!rc) {
285 trace("* %s: manufacturer=%d, product=%d, tech=%d, support=%X: %d voices, %d notes\n",
286 capsA.szPname, capsA.wMid, capsA.wPid, capsA.wTechnology, capsA.dwSupport, capsA.wVoices, capsA.wNotes);
287 ok(!((MIDIMAPPER==udev) ^ (MOD_MAPPER==capsA.wTechnology)), "technology %d on device %d\n", capsA.wTechnology, udev);
288 if (MOD_MIDIPORT == capsA.wTechnology) {
289 ok(capsA.wVoices == 0 && capsA.wNotes == 0, "external device with notes or voices\n");
290 ok(capsA.wChannelMask == 0xFFFF, "external device channel mask %x\n", capsA.wChannelMask);
291 ok(!(capsA.dwSupport & (MIDICAPS_VOLUME|MIDICAPS_LRVOLUME|MIDICAPS_CACHE)), "external device support=%X\n", capsA.dwSupport);
295 if (hwnd)
296 rc = midiOutOpen(&hm, udev, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
297 else
298 rc = midiOutOpen(&hm, udev, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
299 if (rc == MMSYSERR_NOTSUPPORTED || rc == MMSYSERR_NODRIVER)
301 skip( "MIDI out not supported\n" );
302 return;
304 ok(!rc, "midiOutOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
305 if (rc) return;
307 test_notification(hwnd, "midiOutOpen", MOM_OPEN, 0);
309 rc = midiOutGetVolume(hm, &ovolume);
310 ok((capsA.dwSupport & MIDICAPS_VOLUME) ? rc==MMSYSERR_NOERROR : rc==MMSYSERR_NOTSUPPORTED, "midiOutGetVolume rc=%s\n", mmsys_error(rc));
311 /* The native mapper responds with FFFFFFFF initially,
312 * real devices with the volume GUI SW-synth settings. */
313 if (!rc) trace("Current volume %x on device %d\n", ovolume, udev);
315 /* The W95 ESFM Synthesis device reports NOTENABLED although
316 * GetVolume by handle works and music plays. */
317 rc = midiOutGetVolume(UlongToHandle(udev), &ovolume);
318 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));
320 rc = midiOutGetVolume(hm, NULL);
321 ok(rc==MMSYSERR_INVALPARAM, "midiOutGetVolume NULL rc=%s\n", mmsys_error(rc));
323 /* Tests with midiOutSetvolume show that the midi mapper forwards
324 * the value to the real device, but Get initially always reports
325 * FFFFFFFF. Therefore, a Get+SetVolume pair with the mapper is
326 * not adequate to restore the value prior to tests.
328 if (winetest_interactive && (capsA.dwSupport & MIDICAPS_VOLUME)) {
329 DWORD volume2 = (ovolume < 0x80000000) ? 0xC000C000 : 0x40004000;
330 rc = midiOutSetVolume(hm, volume2);
331 ok(!rc, "midiOutSetVolume rc=%s\n", mmsys_error(rc));
332 if (!rc) {
333 DWORD volume3;
334 rc = midiOutGetVolume(hm, &volume3);
335 ok(!rc, "midiOutGetVolume new rc=%s\n", mmsys_error(rc));
336 if (!rc) trace("New volume %x on device %d\n", volume3, udev);
337 todo_wine ok(volume2==volume3, "volume Set %x = Get %x\n", volume2, volume3);
339 rc = midiOutSetVolume(hm, ovolume);
340 ok(!rc, "midiOutSetVolume restore rc=%s\n", mmsys_error(rc));
343 rc = midiOutGetDevCapsA((UINT_PTR)hm, &capsA, sizeof(capsA));
344 ok(!rc, "midiOutGetDevCaps(dev=%d) by handle rc=%s\n", udev, mmsys_error(rc));
345 rc = midiInGetDevCapsA((UINT_PTR)hm, (LPMIDIINCAPSA)&capsA, sizeof(DWORD));
346 ok(rc==MMSYSERR_BADDEVICEID, "midiInGetDevCaps(dev=%d) by out handle rc=%s\n", udev, mmsys_error(rc));
348 { DWORD e = 0x006F4893; /* velocity, note (#69 would be 440Hz) channel */
349 trace("ShortMsg type %x\n", LOBYTE(LOWORD(e)));
350 rc = midiOutShortMsg(hm, e);
351 ok(!rc, "midiOutShortMsg rc=%s\n", mmsys_error(rc));
352 if (!rc) Sleep(400); /* Hear note */
355 memset(&mhdr, 0, sizeof(mhdr));
356 mhdr.dwFlags = MHDR_DONE;
357 mhdr.dwUser = 0x56FA552C;
358 mhdr.dwOffset = 0xDEADBEEF;
359 mhdr.dwBufferLength = 70000; /* > 64KB! */
360 mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength);
361 ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength);
362 if (mhdr.lpData) {
363 rc = midiOutLongMsg(hm, &mhdr, sizeof(mhdr));
364 ok(rc==MIDIERR_UNPREPARED, "midiOutLongMsg unprepared rc=%s\n", mmsys_error(rc));
365 ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
366 test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER);
368 rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1);
369 ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc));
370 ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
372 /* Since at least w2k, midiOutPrepare clears the DONE and INQUEUE flags. w95 didn't. */
373 /* mhdr.dwFlags |= MHDR_INQUEUE; would cause w95 to return STILLPLAYING from Unprepare */
374 rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
375 ok(!rc, "midiOutPrepare old size rc=%s\n", mmsys_error(rc));
376 ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE)/*w9x*/ ||
377 mhdr.dwFlags == MHDR_PREPARED, "dwFlags=%x\n", mhdr.dwFlags);
378 trace("MIDIHDR flags=%x when unsent\n", mhdr.dwFlags);
380 /* No flag is cleared when already prepared. */
381 mhdr.dwFlags |= MHDR_DONE|MHDR_INQUEUE;
382 rc = midiOutPrepareHeader(hm, &mhdr, sizeof(mhdr));
383 ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc));
384 ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags);
386 mhdr.dwFlags |= MHDR_INQUEUE;
387 rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
388 ok(rc==MIDIERR_STILLPLAYING, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
389 ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags);
391 mhdr.dwFlags &= ~MHDR_INQUEUE;
392 rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
393 ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
394 ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags);
396 mhdr.dwFlags |= MHDR_INQUEUE;
397 rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
398 ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
399 ok(mhdr.dwFlags == (MHDR_INQUEUE|MHDR_DONE), "dwFlags=%x\n", mhdr.dwFlags);
401 HeapFree(GetProcessHeap(), 0, mhdr.lpData);
403 ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
404 ok(mhdr.dwOffset==0xDEADBEEF, "MIDIHDR.dwOffset changed to %x\n", mhdr.dwOffset);
406 rc = midiOutGetID(hm, &udevid);
407 ok(!rc, "midiOutGetID rc=%s\n", mmsys_error(rc));
408 if(!rc) ok(udevid==udev, "midiOutGetID gives %d, expect %d\n", udevid, udev);
410 rc = midiOutReset(hm); /* Quiet everything */
411 ok(!rc, "midiOutReset rc=%s\n", mmsys_error(rc));
413 rc = midiOutClose(hm);
414 ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
415 test_notification(hwnd, "midiOutClose", MOM_CLOSE, 0);
417 rc = midiOutOpen(&hm, udev, 0, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
418 /* w95 broken(rc==MMSYSERR_INVALPARAM) see WINMM_CheckCallback */
419 ok(!rc, "midiOutOpen(dev=%d) 0 CALLBACK_WINDOW rc=%s\n", udev, mmsys_error(rc));
420 /* PostMessage(hwnd=0) redirects to PostThreadMessage(GetCurrentThreadId())
421 * which PeekMessage((HWND)-1) queries. */
422 test_notification((HWND)-1, "midiOutOpen WINDOW->THREAD", 0, WHATEVER);
423 test_notification(hwnd, "midiOutOpen WINDOW", 0, WHATEVER);
424 if (!rc) {
425 rc = midiOutClose(hm);
426 ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
427 test_notification((HWND)-1, "midiOutClose WINDOW->THREAD", 0, WHATEVER);
428 test_notification(hwnd, "midiOutClose", 0, WHATEVER);
430 test_notification(hwnd, "midiOut over", 0, WHATEVER);
431 if (!strncmp(capsA.szPname, fluidsynth_prefix, strlen(fluidsynth_prefix)) ||
432 (udev == MIDIMAPPER && found_fluidsynth)) {
433 found_fluidsynth = TRUE;
434 skip("FluidSynth (at least 1.1.6) doesn't support desired System Exclusive message.\n");
435 return;
438 if (hwnd)
439 rc = midiOutOpen(&hm, udev, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
440 else
441 rc = midiOutOpen(&hm, udev, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
442 ok(!rc, "midiOutOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
443 test_notification(hwnd, "midiOutOpen", MOM_OPEN, 0);
445 memset(&mhdr, 0, sizeof(mhdr));
446 mhdr.lpData = (LPSTR)SysEx_reset;
447 mhdr.dwBufferLength = sizeof(SysEx_reset);
448 rc = midiOutPrepareHeader(hm, &mhdr, sizeof(mhdr));
449 ok(!rc, "midiOutPrepareHeader rc=%s\n", mmsys_error(rc));
450 rc = midiOutLongMsg(hm, &mhdr, sizeof(mhdr));
451 ok(!rc, "midiOutLongMsg rc=%s\n", mmsys_error(rc));
452 rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
453 ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
454 test_notification(hwnd, "midiOutLongMsg", MOM_DONE, (DWORD_PTR)&mhdr);
455 Sleep(60);
457 mhdr.lpData = (LPSTR)SysEx_volume_off;
458 mhdr.dwBufferLength = sizeof(SysEx_volume_off);
459 rc = midiOutPrepareHeader(hm, &mhdr, sizeof(mhdr));
460 ok(!rc, "midiOutPrepareHeader rc=%s\n", mmsys_error(rc));
461 rc = midiOutLongMsg(hm, &mhdr, sizeof(mhdr));
462 ok(!rc, "midiOutLongMsg rc=%s\n", mmsys_error(rc));
463 rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
464 ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
465 test_notification(hwnd, "midiOutLongMsg", MOM_DONE, (DWORD_PTR)&mhdr);
468 DWORD e = 0x006F4593; /* velocity 111, note #69, channel 4 */
469 trace("ShortMsg type %x (muted)\n", LOBYTE(LOWORD(e)));
470 rc = midiOutShortMsg(hm, e);
471 ok(!rc, "midiOutShortMsg rc=%s\n", mmsys_error(rc));
472 /* We can't hear this voice due to volume settings */
473 if (!rc) Sleep(200);
475 rc = midiOutShortMsg(hm, 0x00004593); /* velocity 0 */
476 ok(!rc, "midiOutShortMsg rc=%s\n", mmsys_error(rc));
479 mhdr.lpData = (LPSTR)SysEx_volume_full;
480 mhdr.dwBufferLength = sizeof(SysEx_volume_full);
481 rc = midiOutPrepareHeader(hm, &mhdr, sizeof(mhdr));
482 ok(!rc, "midiOutPrepareHeader rc=%s\n", mmsys_error(rc));
483 rc = midiOutLongMsg(hm, &mhdr, sizeof(mhdr));
484 ok(!rc, "midiOutLongMsg rc=%s\n", mmsys_error(rc));
485 rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr));
486 ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
487 test_notification(hwnd, "midiOutLongMsg", MOM_DONE, (DWORD_PTR)&mhdr);
489 rc = midiOutClose(hm);
490 ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
491 test_notification(hwnd, "midiOuClose", MOM_CLOSE, 0);
494 static void test_position(HMIDISTRM hm, UINT typein, UINT typeout)
496 MMRESULT rc;
497 MMTIME mmtime;
498 mmtime.wType = typein;
499 rc = midiStreamPosition(hm, &mmtime, sizeof(MMTIME));
500 /* Ugly, but a single ok() herein enables using the todo_wine prefix */
501 ok(!rc && (mmtime.wType == typeout), "midiStreamPosition type %x converted to %x rc=%s\n", typein, mmtime.wType, mmsys_error(rc));
502 if (!rc) switch(mmtime.wType) {
503 case TIME_MS:
504 trace("Stream position %ums\n", mmtime.u.ms);
505 break;
506 case TIME_TICKS:
507 trace("Stream position %u ticks\n", mmtime.u.ticks);
508 break;
509 case TIME_MIDI:
510 trace("Stream position song pointer %u\n", mmtime.u.midi.songptrpos);
511 break;
512 case TIME_SMPTE:
513 trace("Stream position %02u:%02u:%02u.%02u/%02u\n",
514 mmtime.u.smpte.hour, mmtime.u.smpte.min, mmtime.u.smpte.sec,
515 mmtime.u.smpte.frame, mmtime.u.smpte.fps);
516 break;
520 typedef struct midishortevent_tag { /* ideal size for MEVT_F_SHORT event type */
521 DWORD dwDeltaTime;
522 DWORD dwStreamID;
523 DWORD dwEvent;
524 } MIDISHORTEVENT;
526 /* Native crashes on a second run with the const qualifier set on this data! */
527 static BYTE strmEvents[] = { /* A set of variable-sized MIDIEVENT structs */
528 0, 0, 0, 0, 0, 0, 0, 0, /* dwDeltaTime and dwStreamID */
529 0, 0, 0, MEVT_NOP | 0x40, /* with MEVT_F_CALLBACK */
530 0, 0, 0, 0, 0, 0, 0, 0,
531 0xE0, 0x93, 0x04, MEVT_TEMPO, /* 0493E0 == 300000 */
532 0, 0, 0, 0, 0, 0, 0, 0,
533 0x93, 0x48, 0x6F, MEVT_SHORTMSG,
536 static MIDISHORTEVENT strmNops[] = { /* Test callback + dwOffset */
537 { 0, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
538 { 0, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
541 static MIDISHORTEVENT strmNopsWithDelta[] = {
542 { 0, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
543 { 12, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
546 struct time_stamp_records {
547 UINT count;
548 DWORD time_stamp[2];
549 HANDLE done;
552 static void CALLBACK time_stamp_callback(HMIDIOUT hmo, UINT msg, DWORD_PTR instance, DWORD_PTR p1, DWORD_PTR p2)
554 struct time_stamp_records *records = (struct time_stamp_records *)instance;
555 switch (msg) {
556 case MM_MOM_POSITIONCB:
557 if (records->count < ARRAY_SIZE(records->time_stamp))
558 records->time_stamp[records->count] = timeGetTime();
559 records->count++;
560 break;
561 case MM_MOM_DONE:
562 SetEvent(records->done);
563 break;
567 static DWORD get_position(HMIDISTRM hm, UINT type)
569 MMRESULT rc;
570 MMTIME mmtime;
571 mmtime.wType = type;
572 rc = midiStreamPosition(hm, &mmtime, sizeof(mmtime));
573 if (rc != MMSYSERR_NOERROR || mmtime.wType != type)
574 return MAXDWORD;
576 switch (mmtime.wType) {
577 case TIME_MS:
578 return mmtime.u.ms;
579 case TIME_TICKS:
580 return mmtime.u.ticks;
581 case TIME_MIDI:
582 return mmtime.u.midi.songptrpos;
583 default:
584 return MAXDWORD;
588 static MMRESULT playStream(HMIDISTRM hm, LPMIDIHDR lpMidiHdr)
590 MMRESULT rc = midiStreamOut(hm, lpMidiHdr, sizeof(MIDIHDR));
591 /* virtual machines may return MIDIERR_STILLPLAYING from the next request
592 * even after MHDR_DONE is set. It's still too early, so add MHDR_INQUEUE. */
593 if (!rc) while (!(lpMidiHdr->dwFlags & MHDR_DONE) || (lpMidiHdr->dwFlags & MHDR_INQUEUE)) { Sleep(100); }
594 return rc;
597 static void test_midiStream(UINT udev, HWND hwnd)
599 HMIDISTRM hm;
600 MMRESULT rc, rc2;
601 MIDIHDR mhdr;
602 union {
603 MIDIPROPTEMPO tempo;
604 MIDIPROPTIMEDIV tdiv;
605 } midiprop;
606 DWORD diff, expected, ret;
607 const DWORD MARGIN = 50;
608 struct time_stamp_records records;
609 MIDIOUTCAPSA capsA;
611 if (hwnd)
612 rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
613 else
614 rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
615 if (rc == MMSYSERR_NOTSUPPORTED || rc == MMSYSERR_NODRIVER)
617 skip( "MIDI stream not supported\n" );
618 return;
620 ok(!rc, "midiStreamOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
621 if (rc) return;
623 test_notification(hwnd, "midiStreamOpen", MOM_OPEN, 0);
625 midiprop.tempo.cbStruct = sizeof(midiprop.tempo);
626 rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TEMPO);
627 ok(!rc, "midiStreamProperty TEMPO rc=%s\n", mmsys_error(rc));
628 ok(midiprop.tempo.dwTempo==500000, "default stream tempo %u microsec per quarter note\n", midiprop.tempo.dwTempo);
630 midiprop.tdiv.cbStruct = sizeof(midiprop.tdiv);
631 rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TIMEDIV);
632 ok(!rc, "midiStreamProperty TIMEDIV rc=%s\n", mmsys_error(rc));
633 ok(24==LOWORD(midiprop.tdiv.dwTimeDiv), "default stream time division %u\n", midiprop.tdiv.dwTimeDiv);
635 memset(&mhdr, 0, sizeof(mhdr));
636 mhdr.dwUser = 0x56FA552C;
637 mhdr.dwOffset = 1234567890;
638 mhdr.dwBufferLength = sizeof(strmEvents);
639 mhdr.dwBytesRecorded = mhdr.dwBufferLength;
640 mhdr.lpData = (LPSTR)&strmEvents[0];
641 if (mhdr.lpData) {
642 rc = midiOutLongMsg((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
643 ok(rc==MIDIERR_UNPREPARED, "midiOutLongMsg unprepared rc=%s\n", mmsys_error(rc));
644 test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER);
646 rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1);
647 ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc));
648 rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
649 ok(!rc, "midiOutPrepare size rc=%s\n", mmsys_error(rc));
650 ok(mhdr.dwFlags & MHDR_PREPARED, "MHDR.dwFlags when prepared %x\n", mhdr.dwFlags);
652 /* The device is still in paused mode and should queue the message. */
653 rc = midiStreamOut(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
654 ok(!rc, "midiStreamOut old size rc=%s\n", mmsys_error(rc));
655 rc2 = rc;
656 trace("MIDIHDR flags=%x when submitted\n", mhdr.dwFlags);
657 /* w9X/me does not set MHDR_ISSTRM when StreamOut exits,
658 * but it will be set on all systems after the job is finished. */
660 Sleep(90);
661 /* Wine <1.1.39 started playing immediately */
662 test_notification(hwnd, "midiStream still paused", 0, WHATEVER);
664 /* MSDN asks to use midiStreamRestart prior to midiStreamOut()
665 * because the starting state is 'pause', but some apps seem to
666 * work with the inverse order: queue everything, then play.
669 rc = midiStreamRestart(hm);
670 ok(!rc, "midiStreamRestart rc=%s\n", mmsys_error(rc));
672 if (!rc2) while(mhdr.dwFlags & MHDR_INQUEUE) {
673 trace("async MIDI still queued\n");
674 Sleep(100);
675 } /* Checking INQUEUE is not the recommended way to wait for the end of a job, but we're testing. */
676 /* MHDR_ISSTRM is not necessarily set when midiStreamOut returns
677 * rather than when the queue is eventually processed. */
678 ok(mhdr.dwFlags & MHDR_ISSTRM, "MHDR.dwFlags %x no ISSTRM when out of queue\n", mhdr.dwFlags);
679 if (!rc2) while(!(mhdr.dwFlags & MHDR_DONE)) {
680 /* Never to be seen except perhaps on multicore */
681 trace("async MIDI still not done\n");
682 Sleep(100);
684 ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags %x not DONE when out of queue\n", mhdr.dwFlags);
685 test_notification(hwnd, "midiStream callback", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
686 test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr);
688 /* Native fills dwOffset regardless of the cbMidiHdr size argument to midiStreamOut */
689 ok(1234567890!=mhdr.dwOffset, "play left MIDIHDR.dwOffset at %u\n", mhdr.dwOffset);
691 rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
692 ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
693 rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
694 ok(!rc, "midiOutUnprepare #2 rc=%s\n", mmsys_error(rc));
696 trace("MIDIHDR stream flags=%x when finished\n", mhdr.dwFlags);
697 ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags when done %x\n", mhdr.dwFlags);
699 test_position(hm, TIME_MS, TIME_MS);
700 test_position(hm, TIME_TICKS, TIME_TICKS);
701 test_position(hm, TIME_MIDI, TIME_MIDI);
702 test_position(hm, TIME_SMPTE, TIME_MS);
703 test_position(hm, TIME_SAMPLES, TIME_MS);
704 test_position(hm, TIME_BYTES, TIME_MS);
706 Sleep(400); /* Hear note */
708 midiprop.tempo.cbStruct = sizeof(midiprop.tempo);
709 rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TEMPO);
710 ok(!rc, "midiStreamProperty TEMPO rc=%s\n", mmsys_error(rc));
711 ok(0x0493E0==midiprop.tempo.dwTempo, "stream set tempo %u\n", midiprop.tdiv.dwTimeDiv);
713 rc = midiStreamRestart(hm);
714 ok(!rc, "midiStreamRestart #2 rc=%s\n", mmsys_error(rc));
716 mhdr.dwFlags |= MHDR_ISSTRM;
717 /* Preset flags (e.g. MHDR_ISSTRM) do not disturb. */
718 rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
719 ok(!rc, "midiOutPrepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc));
720 rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
721 ok(!rc, "midiOutUnprepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc));
723 rc = midiStreamRestart(hm);
724 ok(!rc, "midiStreamRestart #3 rc=%s\n", mmsys_error(rc));
726 ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
727 ok(0==((MIDISHORTEVENT*)&strmEvents)[0].dwStreamID, "dwStreamID set to %x\n", ((LPMIDIEVENT)&strmEvents[0])->dwStreamID);
729 /* dwBytesRecorded controls how much is played, not dwBufferLength
730 * allowing to immediately forward packets from midiIn to midiOut */
731 mhdr.dwOffset = 1234123123;
732 mhdr.dwBufferLength = sizeof(strmNops);
733 trace("buffer: %u\n", mhdr.dwBufferLength);
734 mhdr.dwBytesRecorded = 0;
735 mhdr.lpData = (LPSTR)&strmNops[0];
736 strmNops[0].dwEvent |= MEVT_F_CALLBACK;
737 strmNops[1].dwEvent |= MEVT_F_CALLBACK;
739 rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
740 ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc));
742 rc = playStream(hm, &mhdr);
743 ok(!rc, "midiStreamOut 0 bytes recorded rc=%s\n", mmsys_error(rc));
745 test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr);
746 test_notification(hwnd, "0 bytes recorded", 0, WHATEVER);
748 /* FIXME: check dwOffset within callback
749 * instead of the unspecified value afterwards */
750 ok(1234123123==mhdr.dwOffset || broken(0==mhdr.dwOffset), "play 0 set MIDIHDR.dwOffset to %u\n", mhdr.dwOffset);
751 /* w2k and later only set dwOffset when processing MEVT_T_CALLBACK,
752 * while w9X/me/nt always sets it. Have Wine behave like w2k because the
753 * dwOffset slot does not exist in the small size MIDIHDR. */
755 mhdr.dwOffset = 1234123123;
756 mhdr.dwBytesRecorded = 1*sizeof(MIDISHORTEVENT);
758 rc = playStream(hm, &mhdr);
759 ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
761 test_notification(hwnd, "1 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
762 test_notification(hwnd, "1 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
763 test_notification(hwnd, "1 of 2 events", 0, WHATEVER);
764 ok(0==mhdr.dwOffset, "MIDIHDR.dwOffset 1/2 changed to %u\n", mhdr.dwOffset);
766 mhdr.dwOffset = 1234123123;
767 mhdr.dwBytesRecorded = 2*sizeof(MIDISHORTEVENT);
769 rc = playStream(hm, &mhdr);
770 ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
772 test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
773 test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
774 test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
775 test_notification(hwnd, "2 of 2 events", 0, WHATEVER);
776 ok(sizeof(MIDISHORTEVENT)==mhdr.dwOffset, "MIDIHDR.dwOffset 2/2 changed to %u\n", mhdr.dwOffset);
777 ok(mhdr.dwBytesRecorded == 2*sizeof(MIDISHORTEVENT), "dwBytesRecorded changed to %u\n", mhdr.dwBytesRecorded);
779 strmNops[0].dwEvent &= ~MEVT_F_CALLBACK;
780 strmNops[1].dwEvent &= ~MEVT_F_CALLBACK;
781 mhdr.dwOffset = 1234123123;
782 rc = playStream(hm, &mhdr);
783 ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
785 test_notification(hwnd, "0 CB in 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
786 test_notification(hwnd, "0 CB in 2 events", 0, WHATEVER);
787 /* w9X/me/nt set dwOffset to the position played last */
788 ok(1234123123==mhdr.dwOffset || broken(sizeof(MIDISHORTEVENT)==mhdr.dwOffset), "MIDIHDR.dwOffset nocb changed to %u\n", mhdr.dwOffset);
790 mhdr.dwBytesRecorded = mhdr.dwBufferLength-1;
791 rc = playStream(hm, &mhdr);
792 ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBytesRecorded modulo MIDIEVENT rc=%s\n", mmsys_error(rc));
793 if (!rc) {
794 test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
797 mhdr.dwBytesRecorded = mhdr.dwBufferLength+1;
798 rc = playStream(hm, &mhdr);
799 ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBufferLength<dwBytesRecorded rc=%s\n", mmsys_error(rc));
800 test_notification(hwnd, "past MIDIHDR tests", 0, WHATEVER);
802 rc = midiStreamStop(hm);
803 ok(!rc, "midiStreamStop rc=%s\n", mmsys_error(rc));
804 ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
806 rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
807 ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
808 ok(0==strmNops[0].dwStreamID, "dwStreamID[0] set to %x\n", strmNops[0].dwStreamID);
809 ok(0==strmNops[1].dwStreamID, "dwStreamID[1] set to %x\n", strmNops[1].dwStreamID);
811 mhdr.dwBufferLength = 70000; /* > 64KB! */
812 mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength);
813 ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength);
814 if (mhdr.lpData) {
815 mhdr.dwFlags = 0;
816 /* PrepareHeader detects the too large buffer is for a stream. */
817 rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
818 todo_wine ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare stream too large rc=%s\n", mmsys_error(rc));
820 rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
821 ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
823 HeapFree(GetProcessHeap(), 0, mhdr.lpData);
826 rc = midiStreamClose(hm);
827 ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
828 test_notification(hwnd, "midiStreamClose", MOM_CLOSE, 0);
829 test_notification(hwnd, "midiStream over", 0, WHATEVER);
831 rc = midiStreamOpen(&hm, &udev, 1, 0, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
832 ok(!rc /*w2k*/|| rc==MMSYSERR_INVALPARAM/*w98*/, "midiStreamOpen NULL function rc=%s\n", mmsys_error(rc));
833 if (!rc) {
834 trace("Device %d accepts NULL CALLBACK_FUNCTION\n", udev);
835 rc = midiStreamClose(hm);
836 ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
839 rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)0xDEADBEEF, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
840 ok(rc==MMSYSERR_INVALPARAM, "midiStreamOpen bad window rc=%s\n", mmsys_error(rc));
841 if (!rc) {
842 rc = midiStreamClose(hm);
843 ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
846 /* Test the player time clock and positions */
847 memset(&records, 0, sizeof(records));
848 records.done = CreateEventA(NULL, FALSE, FALSE, NULL);
849 ok(records.done != NULL, "CreateEvent failed (dev=%d)\n", udev);
851 rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)time_stamp_callback, (DWORD_PTR)&records, CALLBACK_FUNCTION);
852 ok(!rc, "midiStreamOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
854 expected = 0;
855 ret = get_position(hm, TIME_MS);
856 ok(ret == expected, "expected %u, got %u\n", expected, ret);
858 memset(&mhdr, 0, sizeof(mhdr));
859 mhdr.lpData = (LPSTR)strmNopsWithDelta;
860 mhdr.dwBytesRecorded = mhdr.dwBufferLength = sizeof(strmNopsWithDelta);
861 rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
862 ok(!rc, "midiOutPrepareHeader(dev=%d) rc=%s\n", udev, mmsys_error(rc));
864 rc = midiStreamOut(hm, &mhdr, sizeof(mhdr));
865 ok(!rc, "midiStreamOut(dev=%d) rc=%s\n", udev, mmsys_error(rc));
867 ret = get_position(hm, TIME_MS);
868 ok(ret == expected, "expected %u, got %u\n", expected, ret);
870 rc = midiStreamRestart(hm);
871 ok(!rc, "midiStreamRestart(dev=%d) rc=%s\n", udev, mmsys_error(rc));
872 Sleep(50);
874 rc = midiStreamPause(hm);
875 ok(!rc, "midiStreamPause(dev=%d) rc=%s\n", udev, mmsys_error(rc));
877 expected = 50;
878 ret = get_position(hm, TIME_MS);
879 ok(ret >= expected && ret < expected + MARGIN, "expected %ums or greater, got %ums\n", expected, ret);
880 expected = ret;
882 Sleep(100);
884 ret = get_position(hm, TIME_MS);
885 ok(ret == expected, "expected %ums, got %ums\n", expected, ret);
887 rc = midiStreamRestart(hm);
888 ok(!rc, "midiStreamRestart(dev=%d) rc=%s\n", udev, mmsys_error(rc));
890 Sleep(1);
891 ret = get_position(hm, TIME_MS);
892 ok(ret > expected && ret < expected + MARGIN, "expected greater than %ums, got %ums\n", expected, ret);
893 expected = ret;
895 ret = get_position(hm, TIME_TICKS);
896 ok(ret > strmNopsWithDelta[0].dwDeltaTime && ret < strmNopsWithDelta[1].dwDeltaTime,
897 "TIME_TICKS position is continuous, got %u\n", ret);
899 /* shouldn't set time division property while playing */
900 midiprop.tdiv.cbStruct = sizeof(midiprop.tdiv);
901 midiprop.tdiv.dwTimeDiv = 24;
902 rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_SET | MIDIPROP_TIMEDIV);
903 ok(rc == MMSYSERR_INVALPARAM, "midiStreamProperty(SET|TIMEDIV, dev=%d) rc=%s\n", udev, mmsys_error(rc));
905 ret = WaitForSingleObject(records.done, INFINITE);
906 ok(ret == WAIT_OBJECT_0, "WaitForSingleObject failed, got %d\n", ret);
908 rc = midiStreamPause(hm);
909 ok(!rc, "midiStreamPause(dev=%d) rc=%s\n", udev, mmsys_error(rc));
911 expected = 250; /* = 12 ticks in 120 BPM */
912 ret = get_position(hm, TIME_MS);
913 ok(ret >= expected - MARGIN && ret <= expected + MARGIN,
914 "expected greater than %ums, got %ums\n", expected, ret);
915 trace("after playing, got %ums\n", ret);
917 /* set tempo to 240 BPM */
918 midiprop.tempo.cbStruct = sizeof(midiprop.tempo);
919 midiprop.tempo.dwTempo = 250000;
920 rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_SET | MIDIPROP_TEMPO);
921 ok(!rc, "midiStreamProperty(SET|TEMPO, dev=%d) rc=%s\n", udev, mmsys_error(rc));
923 /* a tempo change doesn't affect elapsed ticks */
924 ret = get_position(hm, TIME_TICKS);
925 ok(ret >= strmNopsWithDelta[1].dwDeltaTime && ret < strmNopsWithDelta[1].dwDeltaTime + 3,
926 "expected %u ticks, got %u\n", strmNopsWithDelta[1].dwDeltaTime, ret);
928 midiprop.tdiv.cbStruct = sizeof(midiprop.tdiv);
929 rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET | MIDIPROP_TIMEDIV);
930 ok(!rc, "midiStreamProperty(GET|TIMEDIV, dev=%d) rc=%s\n", udev, mmsys_error(rc));
931 ok(midiprop.tdiv.dwTimeDiv == 24, "expected 24, got %u\n", midiprop.tdiv.dwTimeDiv);
933 /* TIME_MIDI value is a quarter of TIME_TICKS, rounded */
934 expected = (ret + midiprop.tdiv.dwTimeDiv/8) / (midiprop.tdiv.dwTimeDiv/4);
935 ret = get_position(hm, TIME_MIDI);
936 ok(ret == expected, "expected song pointer %u, got %u\n", expected, ret);
938 ok(records.count == 2, "expected 2 MM_MOM_DONE messages, got %d\n", records.count);
940 /* Time between midiStreamPause and midiStreamRestart isn't counted.
941 So, the second event happens at dwDeltaTime(250ms) + 100ms after the first event. */
942 expected = 250 + 100;
943 diff = records.time_stamp[1] - records.time_stamp[0];
944 ok(diff >= expected - MARGIN && diff <= expected + MARGIN,
945 "expected %u ~ %ums, got %ums (dev=%d)\n", expected - MARGIN, expected + MARGIN, diff, udev);
947 rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
948 ok(!rc, "midiOutUnprepareHeader(dev=%d) rc=%s\n", udev, mmsys_error(rc));
950 rc = midiStreamStop(hm);
951 ok(!rc, "midiStreamStop(dev=%d) rc=%s\n", udev, mmsys_error(rc));
953 ret = get_position(hm, TIME_MS);
954 ok(ret == 0, "expected 0ms, got %ums\n", ret);
956 midiprop.tdiv.cbStruct = sizeof(midiprop.tdiv);
957 midiprop.tdiv.dwTimeDiv = 0xe204; /* 30 fps, 4 ticks/frame */
958 rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_SET | MIDIPROP_TIMEDIV);
959 ok(!rc, "midiStreamProperty(SET|TIMEDIV, dev=%d) rc=%s\n", udev, mmsys_error(rc));
961 test_position(hm, TIME_MS, TIME_MS);
962 test_position(hm, TIME_TICKS, TIME_TICKS);
963 test_position(hm, TIME_MIDI, TIME_MS);
964 todo_wine test_position(hm, TIME_SMPTE, TIME_SMPTE);
965 test_position(hm, TIME_SAMPLES, TIME_MS);
966 test_position(hm, TIME_BYTES, TIME_MS);
968 rc = midiStreamClose(hm);
969 ok(!rc, "midiStreamClose(dev=%d) rc=%s\n", udev, mmsys_error(rc));
970 CloseHandle(records.done);
972 rc = midiOutGetDevCapsA((UINT_PTR)udev, &capsA, sizeof(capsA));
973 ok(!rc, "midiOutGetDevCaps(dev=%d) rc=%s\n", udev, mmsys_error(rc));
974 if (!strncmp(capsA.szPname, fluidsynth_prefix, strlen(fluidsynth_prefix)) ||
975 (udev == MIDIMAPPER && found_fluidsynth)) {
976 found_fluidsynth = TRUE;
977 skip("FluidSynth (at least 1.1.6) doesn't support desired System Exclusive message.\n");
978 return;
981 #define ROUNDUP4(n) (((n) + 3) & ~3)
982 hm = NULL;
983 if (hwnd)
984 rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
985 else
986 rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
987 ok(!rc, "midiOutOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
988 test_notification(hwnd, "midiStreamOpen", MOM_OPEN, 0);
990 midiprop.tdiv.cbStruct = sizeof(midiprop.tdiv);
991 midiprop.tdiv.dwTimeDiv = 480;
992 rc = midiStreamProperty(hm, (LPBYTE)&midiprop, MIDIPROP_TIMEDIV | MIDIPROP_SET);
993 ok(!rc, "midiStreamProperty(TimeDiv) rc=%s\n", mmsys_error(rc));
995 rc = midiStreamRestart(hm);
996 ok(!rc, "midiStreamRestart rc=%s\n", mmsys_error(rc));
998 memset(&mhdr, 0, sizeof(mhdr));
999 mhdr.dwBufferLength = sizeof(MIDISHORTEVENT) * 5 + ROUNDUP4(sizeof(SysEx_reset)) +
1000 ROUNDUP4(sizeof(SysEx_volume_off)) + ROUNDUP4(sizeof(SysEx_volume_full));
1001 mhdr.lpData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, mhdr.dwBufferLength);
1002 ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength);
1003 if (mhdr.lpData) {
1004 MIDIEVENT *e;
1005 char *p = mhdr.lpData;
1006 /* GM System ON */
1007 e = (MIDIEVENT *)p;
1008 e->dwEvent = MEVT_F_LONG | sizeof(SysEx_reset);
1009 memcpy(&e->dwParms[0], SysEx_reset, sizeof(SysEx_reset));
1010 p += sizeof(MIDISHORTEVENT) + ROUNDUP4(sizeof(SysEx_reset));
1011 /* Master volume: off */
1012 e = (MIDIEVENT *)p;
1013 e->dwDeltaTime = 96;
1014 e->dwEvent = MEVT_F_LONG | sizeof(SysEx_volume_off);
1015 memcpy(&e->dwParms[0], SysEx_volume_off, sizeof(SysEx_volume_off));
1016 p += sizeof(MIDISHORTEVENT) + ROUNDUP4(sizeof(SysEx_volume_off));
1017 /* Note On (We can't hear this voice due to volume settings) */
1018 e = (MIDIEVENT *)p;
1019 e->dwEvent = MEVT_F_SHORT | 0x6F4593; /* note #69 */
1020 p += sizeof(MIDISHORTEVENT);
1021 /* Note Off */
1022 e = (MIDIEVENT *)p;
1023 e->dwDeltaTime = 240;
1024 e->dwEvent = MEVT_F_SHORT | 0x004593; /* velocity 0 */
1025 p += sizeof(MIDISHORTEVENT);
1026 /* Master volume: full */
1027 e = (MIDIEVENT *)p;
1028 e->dwEvent = MEVT_F_LONG | sizeof(SysEx_volume_full);
1029 memcpy(&e->dwParms[0], SysEx_volume_full, sizeof(SysEx_volume_full));
1030 p += sizeof(MIDISHORTEVENT) + ROUNDUP4(sizeof(SysEx_volume_full));
1031 mhdr.dwBytesRecorded = (DWORD)(p - mhdr.lpData);
1032 #undef ROUNDUP4
1034 rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
1035 ok(!rc, "midiOutPrepareHeader rc=%s\n", mmsys_error(rc));
1037 rc = playStream(hm, &mhdr);
1038 ok(!rc, "midiStreamOut rc=%s\n", mmsys_error(rc));
1039 test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr);
1041 rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
1042 ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
1044 HeapFree(GetProcessHeap(), 0, mhdr.lpData);
1046 rc = midiStreamClose(hm);
1047 ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
1048 test_notification(hwnd, "midiStreamClose", MOM_CLOSE, 0);
1051 static BOOL scan_subkeys(HKEY parent, const LPCSTR *sub_keys)
1053 char name[64];
1054 DWORD index = 0;
1055 DWORD name_len = sizeof(name);
1056 BOOL found_vmware = FALSE;
1058 if (sub_keys[0] == NULL)
1060 /* We're at the deepest level, check "Identifier" value now */
1061 char *test;
1062 if (RegQueryValueExA(parent, "Identifier", NULL, NULL, (LPBYTE) name, &name_len) != ERROR_SUCCESS)
1063 return FALSE;
1064 for (test = name; test < name + lstrlenA(name) - 6 && ! found_vmware; test++)
1066 char c = test[6];
1067 test[6] = '\0';
1068 found_vmware = (lstrcmpiA(test, "VMware") == 0);
1069 test[6] = c;
1071 return found_vmware;
1074 while (RegEnumKeyExA(parent, index, name, &name_len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS &&
1075 ! found_vmware) {
1076 char c = name[lstrlenA(sub_keys[0])];
1077 name[lstrlenA(sub_keys[0])] = '\0';
1078 if (lstrcmpiA(name, sub_keys[0]) == 0) {
1079 HKEY sub_key;
1080 name[lstrlenA(sub_keys[0])] = c;
1081 if (RegOpenKeyExA(parent, name, 0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &sub_key) == ERROR_SUCCESS) {
1082 found_vmware = scan_subkeys(sub_key, sub_keys + 1);
1083 RegCloseKey(sub_key);
1087 name_len = sizeof(name);
1088 index++;
1091 return found_vmware;
1095 * Usual method to detect whether running inside a VMware virtual machine involves direct port I/O requiring
1096 * some assembly and an exception handler. Can't do that in Wine tests. Alternative method of querying WMI
1097 * is not available on NT4. So instead we look at the device map and check the Identifier value in the
1098 * registry keys HKLM\HARDWARE\DEVICEMAP\SCSI\Scsi Port x\Scsi Bus x\Target Id x\Logical Unit Id x (where
1099 * x is some number). If the Identifier value contains the string "VMware" we assume running in a VMware VM.
1101 static BOOL on_vmware(void)
1103 static const LPCSTR sub_keys[] = { "Scsi Port ", "Scsi Bus ", "Target Id ", "Logical Unit Id ", NULL };
1104 HKEY scsi;
1105 BOOL found_vmware = FALSE;
1107 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\Scsi", 0, KEY_ENUMERATE_SUB_KEYS, &scsi) != ERROR_SUCCESS)
1108 return FALSE;
1110 found_vmware = scan_subkeys(scsi, sub_keys);
1112 RegCloseKey(scsi);
1114 return found_vmware;
1117 static void test_midi_outfns(HWND hwnd)
1119 HMIDIOUT hm;
1120 MMRESULT rc;
1121 UINT udev, ndevs = midiOutGetNumDevs();
1123 rc = midiOutOpen(&hm, ndevs, 0, 0, CALLBACK_NULL);
1124 ok(rc==MMSYSERR_BADDEVICEID, "midiOutOpen udev>max rc=%s\n", mmsys_error(rc));
1125 if (!rc) {
1126 rc = midiOutClose(hm);
1127 ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
1129 if (!ndevs) {
1130 MIDIOUTCAPSA capsA;
1131 skip("Found no MIDI out device\n");
1133 rc = midiOutGetDevCapsA(MIDIMAPPER, &capsA, sizeof(capsA));
1134 /* GetDevCaps and Open must return compatible results */
1135 ok(rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*nt,w2k*/), "midiOutGetDevCaps MAPPER with no MIDI rc=%s\n", mmsys_error(rc));
1137 rc = midiOutOpen(&hm, MIDIMAPPER, 0, 0, CALLBACK_NULL);
1138 todo_wine_if (rc == MIDIERR_INVALIDSETUP) /* Wine without snd-seq */
1139 ok(rc == MMSYSERR_BADDEVICEID || broken(rc == MMSYSERR_NODRIVER /*w2k sound disabled*/),
1140 "midiOutOpen MAPPER with no MIDI rc=%s\n", mmsys_error(rc));
1141 if (!rc) {
1142 rc = midiOutClose(hm);
1143 ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc));
1145 return;
1147 trace("Found %d MIDI OUT devices\n", ndevs);
1149 test_midi_mci(hwnd);
1151 for (udev=0; udev < ndevs; udev++) {
1152 MIDIOUTCAPSA capsA;
1153 rc = midiOutGetDevCapsA(udev, &capsA, sizeof(capsA));
1154 if (rc || strcmp(capsA.szPname, "Creative Sound Blaster MPU-401") != 0 || ! on_vmware()) {
1155 trace("** Testing device %d\n", udev);
1156 test_midiOut_device(udev, hwnd);
1157 Sleep(800); /* Let the synth rest */
1158 test_midiStream(udev, hwnd);
1159 Sleep(800);
1161 else
1162 win_skip("Skipping this device on VMware, driver problem\n");
1164 trace("** Testing MIDI mapper\n");
1165 test_midiOut_device(MIDIMAPPER, hwnd);
1166 Sleep(800);
1167 test_midiStream(MIDIMAPPER, hwnd);
1170 START_TEST(midi)
1172 HWND hwnd = 0;
1174 CoInitialize(NULL); /* Needed for Win 10 */
1176 if (1) /* select 1 for CALLBACK_WINDOW or 0 for CALLBACK_FUNCTION */
1177 hwnd = CreateWindowExA(0, "static", "winmm midi test", WS_POPUP, 0,0,100,100,
1178 0, 0, 0, NULL);
1179 test_midi_infns(hwnd);
1180 test_midi_outfns(hwnd);
1181 if (hwnd) DestroyWindow(hwnd);
1183 CoUninitialize();