winmm: More MIDI boundary tests & fixes.
diff --git a/dlls/winmm/tests/midi.c b/dlls/winmm/tests/midi.c
index 4ceec33..891bf70 100644
--- a/dlls/winmm/tests/midi.c
+++ b/dlls/winmm/tests/midi.c
@@ -207,13 +207,20 @@
MMRESULT rc;
MIDIOUTCAPSA capsA;
DWORD ovolume;
+ UINT udevid;
MIDIHDR mhdr;
rc = midiOutGetDevCapsA(udev, &capsA, sizeof(capsA));
ok(!rc, "midiOutGetDevCaps(dev=%d) rc=%s\n", udev, mmsys_error(rc));
if (!rc) {
- trace("* %s: manufacturer=%d, product=%d, tech=%d, support=%X: %d voices, %d notes\n",
- capsA.szPname, capsA.wMid, capsA.wPid, capsA.wTechnology, capsA.dwSupport, capsA.wVoices, capsA.wNotes);
+ trace("* %s: manufacturer=%d, product=%d, tech=%d, support=%X: %d voices, %d notes\n",
+ capsA.szPname, capsA.wMid, capsA.wPid, capsA.wTechnology, capsA.dwSupport, capsA.wVoices, capsA.wNotes);
+ ok(!((MIDIMAPPER==udev) ^ (MOD_MAPPER==capsA.wTechnology)), "technology %d on device %d\n", capsA.wTechnology, udev);
+ if (MOD_MIDIPORT == capsA.wTechnology) {
+ ok(capsA.wVoices == 0 && capsA.wNotes == 0, "external device with notes or voices\n");
+ ok(capsA.wChannelMask == 0xFFFF, "external device channel mask %x\n", capsA.wChannelMask);
+ ok(!(capsA.dwSupport & (MIDICAPS_VOLUME|MIDICAPS_LRVOLUME|MIDICAPS_CACHE)), "external device support=%X\n", capsA.dwSupport);
+ }
}
if (hwnd)
@@ -236,6 +243,9 @@
rc = midiOutGetVolume(UlongToHandle(udev), &ovolume);
ok((capsA.dwSupport & MIDICAPS_VOLUME) ? rc==MMSYSERR_NOERROR || broken(rc==MMSYSERR_NOTENABLED) : rc==MMSYSERR_NOTSUPPORTED, "midiOutGetVolume(dev=%d) rc=%s\n", udev, mmsys_error(rc));
+ rc = midiOutGetVolume(hm, NULL);
+ ok(rc==MMSYSERR_INVALPARAM, "midiOutGetVolume NULL rc=%s\n", mmsys_error(rc));
+
/* Tests with midiOutSetvolume show that the midi mapper forwards
* the value to the real device, but Get initially always reports
* FFFFFFFF. Therefore, a Get+SetVolume pair with the mapper is
@@ -294,6 +304,10 @@
ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
ok(mhdr.dwOffset==0xDEADBEEF, "MIDIHDR.dwOffset changed to %x\n", mhdr.dwOffset);
+ rc = midiOutGetID(hm, &udevid);
+ ok(!rc, "midiOutGetID rc=%s\n", mmsys_error(rc));
+ if(!rc) ok(udevid==udev, "midiOutGetID gives %d, expect %d\n", udevid, udev);
+
rc = midiOutReset(hm); /* Quiet everything */
ok(!rc, "midiOutReset rc=%s\n", mmsys_error(rc));
@@ -324,19 +338,25 @@
}
}
+typedef struct midishortevent_tag { /* ideal size for MEVT_F_SHORT event type */
+ DWORD dwDeltaTime;
+ DWORD dwStreamID;
+ DWORD dwEvent;
+} MIDISHORTEVENT;
+
/* Native crashes on a second run with the const qualifier set on this data! */
static BYTE strmEvents[] = { /* A set of variable-sized MIDIEVENT structs */
0, 0, 0, 0, 0, 0, 0, 0, /* dwDeltaTime and dwStreamID */
0, 0, 0, MEVT_NOP | 0x40, /* with MEVT_F_CALLBACK */
- 0, 0, 0, 0, 0, 0, 0, 0, /* dwDeltaTime and dwStreamID */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0xE0, 0x93, 0x04, MEVT_TEMPO, /* 0493E0 == 300000 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
0x93, 0x48, 0x6F, MEVT_SHORTMSG,
};
-static BYTE strmNops[] = { /* Test callback + dwOffset */
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, MEVT_NOP | 0x40, /* with MEVT_F_CALLBACK */
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, MEVT_NOP | 0x40, /* with MEVT_F_CALLBACK */
+static MIDISHORTEVENT strmNops[] = { /* Test callback + dwOffset */
+ { 0, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
+ { 0, 0, (MEVT_NOP <<24)| MEVT_F_CALLBACK },
};
static MMRESULT playStream(HMIDISTRM hm, LPMIDIHDR lpMidiHdr)
@@ -357,8 +377,6 @@
MIDIPROPTEMPO tempo;
MIDIPROPTIMEDIV tdiv;
} midiprop;
- BYTE * const evt1 = &strmNops[1*offsetof(MIDIEVENT,dwParms)-1];
- BYTE * const evt2 = &strmNops[2*offsetof(MIDIEVENT,dwParms)-1];
if (hwnd)
rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
@@ -382,7 +400,7 @@
mhdr.dwFlags = 0;
mhdr.dwUser = 0x56FA552C;
mhdr.dwOffset = 1234567890;
- mhdr.dwBufferLength = sizeof(strmEvents) * sizeof(strmEvents[0]);
+ mhdr.dwBufferLength = sizeof(strmEvents);
mhdr.dwBytesRecorded = mhdr.dwBufferLength;
mhdr.lpData = (LPSTR)&strmEvents[0];
if (mhdr.lpData) {
@@ -410,7 +428,7 @@
/* MSDN asks to use midiStreamRestart prior to midiStreamOut()
* because the starting state is 'pause', but some apps seem to
- * work with the inverse order.
+ * work with the inverse order: queue everything, then play.
*/
rc = midiStreamRestart(hm);
@@ -452,10 +470,15 @@
Sleep(400); /* Hear note */
+ midiprop.tempo.cbStruct = sizeof(midiprop.tempo);
+ rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TEMPO);
+ ok(!rc, "midiStreamProperty TEMPO rc=%s\n", mmsys_error(rc));
+ ok(0x0493E0==midiprop.tempo.dwTempo, "stream set tempo %u\n", midiprop.tdiv.dwTimeDiv);
+
rc = midiStreamRestart(hm);
ok(!rc, "midiStreamRestart #2 rc=%s\n", mmsys_error(rc));
- mhdr.dwFlags |= MHDR_ISSTRM; /* just in case */
+ mhdr.dwFlags |= MHDR_ISSTRM;
/* Preset flags (e.g. MHDR_ISSTRM) do not disturb. */
rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
ok(!rc, "midiOutPrepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc));
@@ -466,16 +489,17 @@
ok(!rc, "midiStreamRestart #3 rc=%s\n", mmsys_error(rc));
}
ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
- trace("dwStreamID set to %x\n", ((LPMIDIEVENT)&strmEvents[0])->dwStreamID);
+ ok(0==((MIDISHORTEVENT*)&strmEvents)[0].dwStreamID, "dwStreamID set to %x\n", ((LPMIDIEVENT)&strmEvents[0])->dwStreamID);
/* dwBytesRecorded controls how much is played, not dwBufferLength
* allowing to immediately forward packets from midiIn to midiOut */
mhdr.dwOffset = 1234123123;
- mhdr.dwBufferLength = sizeof(strmNops) * sizeof(strmNops[0]);
+ mhdr.dwBufferLength = sizeof(strmNops);
+ trace("buffer: %u\n", mhdr.dwBufferLength);
mhdr.dwBytesRecorded = 0;
mhdr.lpData = (LPSTR)&strmNops[0];
- *evt1 |= 0x40; /* MEVT_CALLBACK flag */
- *evt2 |= 0x40;
+ strmNops[0].dwEvent |= MEVT_F_CALLBACK;
+ strmNops[1].dwEvent |= MEVT_F_CALLBACK;
rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc));
@@ -494,7 +518,7 @@
* dwOffset slot does not exist in the small size MIDIHDR. */
mhdr.dwOffset = 1234123123;
- mhdr.dwBytesRecorded = offsetof(MIDIEVENT,dwParms);
+ mhdr.dwBytesRecorded = 1*sizeof(MIDISHORTEVENT);
rc = playStream(hm, &mhdr);
ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
@@ -503,10 +527,9 @@
test_notification(hwnd, "1 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
test_notification(hwnd, "1 of 2 events", 0, WHATEVER);
ok(0==mhdr.dwOffset, "MIDIHDR.dwOffset 1/2 changed to %u\n", mhdr.dwOffset);
- trace("MIDIHDR.dwOffset left at %u\n", mhdr.dwOffset);
mhdr.dwOffset = 1234123123;
- mhdr.dwBytesRecorded = 2*offsetof(MIDIEVENT,dwParms);
+ mhdr.dwBytesRecorded = 2*sizeof(MIDISHORTEVENT);
rc = playStream(hm, &mhdr);
ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
@@ -515,11 +538,11 @@
test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
test_notification(hwnd, "2 of 2 events", 0, WHATEVER);
- ok(3*sizeof(DWORD)==mhdr.dwOffset, "MIDIHDR.dwOffset 2/2 changed to %u\n", mhdr.dwOffset);
- trace("MIDIHDR.dwOffset left at %u\n", mhdr.dwOffset);
+ ok(sizeof(MIDISHORTEVENT)==mhdr.dwOffset, "MIDIHDR.dwOffset 2/2 changed to %u\n", mhdr.dwOffset);
+ ok(mhdr.dwBytesRecorded == 2*sizeof(MIDISHORTEVENT), "dwBytesRecorded changed to %u\n", mhdr.dwBytesRecorded);
- *evt1 &= ~0x40; /* MEVT_CALLBACK flag */
- *evt2 &= ~0x40;
+ strmNops[0].dwEvent &= ~MEVT_F_CALLBACK;
+ strmNops[1].dwEvent &= ~MEVT_F_CALLBACK;
mhdr.dwOffset = 1234123123;
rc = playStream(hm, &mhdr);
ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));
@@ -527,11 +550,11 @@
test_notification(hwnd, "0 CB in 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
test_notification(hwnd, "0 CB in 2 events", 0, WHATEVER);
/* w9X/me/nt set dwOffset to the position played last */
- ok(1234123123==mhdr.dwOffset || broken(3*sizeof(DWORD)==mhdr.dwOffset), "MIDIHDR.dwOffset nocb changed to %u\n", mhdr.dwOffset);
+ ok(1234123123==mhdr.dwOffset || broken(sizeof(MIDISHORTEVENT)==mhdr.dwOffset), "MIDIHDR.dwOffset nocb changed to %u\n", mhdr.dwOffset);
mhdr.dwBytesRecorded = mhdr.dwBufferLength-1;
rc = playStream(hm, &mhdr);
- todo_wine ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBytesRecorded/MIDIEVENT rc=%s\n", mmsys_error(rc));
+ ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBytesRecorded modulo MIDIEVENT rc=%s\n", mmsys_error(rc));
if (!rc) {
test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
}
@@ -547,6 +570,8 @@
rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
+ ok(0==strmNops[0].dwStreamID, "dwStreamID[0] set to %x\n", strmNops[0].dwStreamID);
+ ok(0==strmNops[1].dwStreamID, "dwStreamID[1] set to %x\n", strmNops[1].dwStreamID);
mhdr.dwBufferLength = 70000; /* > 64KB! */
mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength);
@@ -567,6 +592,21 @@
ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
test_notification(hwnd, "midiStreamClose", MOM_CLOSE, 0);
test_notification(hwnd, "midiStream over", 0, WHATEVER);
+
+ rc = midiStreamOpen(&hm, &udev, 1, 0, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
+ ok(!rc /*w2k*/|| rc==MMSYSERR_INVALPARAM/*w98*/, "midiStreamOpen NULL function rc=%s\n", mmsys_error(rc));
+ if (!rc) {
+ trace("Device %d accepts NULL CALLBACK_FUNCTION\n", udev);
+ rc = midiStreamClose(hm);
+ ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
+ }
+
+ rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)0xDEADBEEF, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
+ ok(rc==MMSYSERR_INVALPARAM, "midiStreamOpen bad window rc=%s\n", mmsys_error(rc));
+ if (!rc) {
+ rc = midiStreamClose(hm);
+ ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
+ }
}
static void test_midi_outfns(HWND hwnd)
@@ -620,7 +660,7 @@
START_TEST(midi)
{
- HWND hwnd;
+ HWND hwnd = 0;
if (1) /* select 1 for CALLBACK_WINDOW or 0 for CALLBACK_FUNCTION */
hwnd = CreateWindowExA(0, "static", "winmm midi test", WS_POPUP, 0,0,100,100,
0, 0, 0, NULL);
diff --git a/dlls/winmm/winmm.c b/dlls/winmm/winmm.c
index 1c6a13e..029a1c9 100644
--- a/dlls/winmm/winmm.c
+++ b/dlls/winmm/winmm.c
@@ -911,6 +911,13 @@
if (lphMidiOut != NULL) *lphMidiOut = 0;
+ switch (dwFlags & CALLBACK_TYPEMASK) {
+ case CALLBACK_WINDOW:
+ if (dwCallback && !IsWindow((HWND)dwCallback))
+ return MMSYSERR_INVALPARAM;
+ break;
+ }
+
lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &dwFlags, 0, NULL);
if (lpwm == NULL)
@@ -1754,6 +1761,13 @@
if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
return MMSYSERR_INVALPARAM;
+ switch (fdwOpen & CALLBACK_TYPEMASK) {
+ case CALLBACK_WINDOW:
+ if (dwCallback && !IsWindow((HWND)dwCallback))
+ return MMSYSERR_INVALPARAM;
+ break;
+ }
+
lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));
if (!lpMidiStrm)
return MMSYSERR_NOMEM;
@@ -1816,7 +1830,8 @@
TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
if (cbMidiHdr < offsetof(MIDIHDR,dwOffset) || !lpMidiHdr || !lpMidiHdr->lpData
- || lpMidiHdr->dwBufferLength < lpMidiHdr->dwBytesRecorded)
+ || lpMidiHdr->dwBufferLength < lpMidiHdr->dwBytesRecorded
+ || lpMidiHdr->dwBytesRecorded % 4 /* player expects DWORD padding */)
return MMSYSERR_INVALPARAM;
/* FIXME: Native additionally checks if the MIDIEVENTs in lpData
* exactly fit dwBytesRecorded. */