static void test_midiIn_device(UINT udev, HWND hwnd) { HMIDIIN hm; MMRESULT rc; MIDIINCAPSA capsA; MIDIHDR mhdr; rc = midiInGetDevCapsA(udev, &capsA, sizeof(capsA)); ok((MIDIMAPPER==udev) ? (rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*nt,w2k*/)) : rc==0, "midiInGetDevCaps(dev=%d) rc=%s\n", udev, mmsys_error(rc)); if (!rc) { /* MIDI IN capsA.dwSupport may contain garbage, absent in old MS-Windows */ trace("* %s: manufacturer=%d, product=%d, support=%X\n", capsA.szPname, capsA.wMid, capsA.wPid, capsA.dwSupport); } if (hwnd) rc = midiInOpen(&hm, udev, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW); else rc = midiInOpen(&hm, udev, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION); ok((MIDIMAPPER!=udev) ? rc==0 : (rc==MMSYSERR_BADDEVICEID || broken(rc==MMSYSERR_NODRIVER /*nt,w2k*/)), "midiInOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc)); if (rc) return; test_notification(hwnd, "midiInOpen", MIM_OPEN, 0); memset(&mhdr, 0, sizeof(mhdr)); mhdr.dwFlags = 0; mhdr.dwUser = 0x56FA552C; mhdr.dwBufferLength = 70000; /* > 64KB! */ mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength); ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength); if (mhdr.lpData) { rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1); ok(rc==MMSYSERR_INVALPARAM, "midiInPrepare tiny rc=%s\n", mmsys_error(rc)); rc = midiInPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiInPrepare old size rc=%s\n", mmsys_error(rc)); rc = midiInPrepareHeader(hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiInPrepare rc=%s\n", mmsys_error(rc)); rc = midiInUnprepareHeader(hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiInUnprepare rc=%s\n", mmsys_error(rc)); trace("MIDIHDR flags=%x when unsent\n", mhdr.dwFlags); HeapFree(GetProcessHeap(), 0, mhdr.lpData); } ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser); rc = midiInReset(hm); /* Return any pending buffer */ ok(!rc, "midiInReset rc=%s\n", mmsys_error(rc)); rc = midiInClose(hm); ok(!rc, "midiInClose rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "midiInClose", MIM_CLOSE, 0); test_notification(hwnd, "midiIn over", 0, WHATEVER); }
static int test_rotation_completed_notification( struct lttng_notification_channel *notification_channel, struct session *session) { return test_notification(notification_channel, session, "rotation completed", LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED); }
static int test_rotation_ongoing_notification( struct lttng_notification_channel *notification_channel, struct session *session) { return test_notification(notification_channel, session, "rotation ongoing", LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING); }
static void test_play(HWND hwnd) { MCIDEVICEID wDeviceID; MCI_PARMS_UNION parm; MCIERROR err, ok_hw; DWORD numtracks, track, duration; DWORD factor = winetest_interactive ? 3 : 1; char buf[1024]; memset(buf, 0, sizeof(buf)); parm.gen.dwCallback = (DWORD_PTR)hwnd; /* once to rule them all */ err = mciSendString("open cdaudio alias c notify shareable", buf, sizeof(buf), hwnd); ok(!err || err == MCIERR_CANNOT_LOAD_DRIVER || err == MCIERR_MUST_USE_SHAREABLE, "mci open cdaudio notify returned %s\n", dbg_mcierr(err)); test_notification(hwnd, "open alias notify", err ? 0 : MCI_NOTIFY_SUCCESSFUL); /* Native returns MUST_USE_SHAREABLE when there's trouble with the hardware * (e.g. unreadable disk) or when Media Player already has the device open, * yet adding that flag does not help get past this error. */ if(err) { skip("Cannot open any cdaudio device, %s.\n", dbg_mcierr(err)); return; } wDeviceID = atoi(buf); ok(!strcmp(buf,"1"), "mci open deviceId: %s, expected 1\n", buf); err = mciSendString("capability c has video notify", buf, sizeof(buf), hwnd); ok(!err, "capability video: %s\n", dbg_mcierr(err)); if(!err) ok(!strcmp(buf, "false"), "capability video is %s\n", buf); test_notification(hwnd, "capability notify", MCI_NOTIFY_SUCCESSFUL); err = mciSendString("capability c can play", buf, sizeof(buf), hwnd); ok(!err, "capability video: %s\n", dbg_mcierr(err)); if(!err) ok(!strcmp(buf, "true"), "capability play is %s\n", buf); err = mciSendString("capability c", buf, sizeof(buf), NULL); ok(err == MCIERR_MISSING_PARAMETER, "capability nokeyword: %s\n", dbg_mcierr(err)); parm.caps.dwItem = 0x4001; parm.caps.dwReturn = 0xFEEDABAD; err = mciSendCommand(wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&parm); ok(err == MCIERR_UNSUPPORTED_FUNCTION, "GETDEVCAPS %x: %s\n", parm.caps.dwItem, dbg_mcierr(err)); parm.caps.dwItem = MCI_GETDEVCAPS_DEVICE_TYPE; err = mciSendCommand(wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&parm); ok(!err, "GETDEVCAPS device type: %s\n", dbg_mcierr(err)); if(!err) ok( parm.caps.dwReturn == MCI_DEVTYPE_CD_AUDIO, "getdevcaps device type: %u\n", parm.caps.dwReturn); err = mciSendCommand(wDeviceID, MCI_RECORD, 0, (DWORD_PTR)&parm); ok(err == MCIERR_UNSUPPORTED_FUNCTION, "MCI_RECORD: %s\n", dbg_mcierr(err)); /* Wine's MCI_MapMsgAtoW crashes on MCI_SAVE without parm->lpfilename */ parm.save.lpfilename = "foo"; err = mciSendCommand(wDeviceID, MCI_SAVE, 0, (DWORD_PTR)&parm); ok(err == MCIERR_UNSUPPORTED_FUNCTION, "MCI_SAVE: %s\n", dbg_mcierr(err)); /* commands from the core set are UNSUPPORTED, others UNRECOGNIZED */ err = mciSendCommand(wDeviceID, MCI_STEP, 0, (DWORD_PTR)&parm); ok(err == MCIERR_UNRECOGNIZED_COMMAND, "MCI_STEP: %s\n", dbg_mcierr(err)); parm.status.dwItem = MCI_STATUS_TIME_FORMAT; parm.status.dwReturn = 0xFEEDABAD; err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm); ok(!err, "STATUS time format: %s\n", dbg_mcierr(err)); if(!err) ok(parm.status.dwReturn == MCI_FORMAT_MSF, "status time default format: %ld\n", parm.status.dwReturn); /* "CD-Audio" */ err = mciSendString("info c product wait notify", buf, sizeof(buf), hwnd); ok(!err, "info product: %s\n", dbg_mcierr(err)); test_notification(hwnd, "info notify", err ? 0 : MCI_NOTIFY_SUCCESSFUL); parm.status.dwItem = MCI_STATUS_MEDIA_PRESENT; parm.status.dwReturn = 0xFEEDABAD; err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm); ok(err || parm.status.dwReturn == TRUE || parm.status.dwReturn == FALSE, "STATUS media present: %s\n", dbg_mcierr(err)); if (parm.status.dwReturn != TRUE) { skip("No CD-ROM in drive.\n"); return; } parm.status.dwItem = MCI_STATUS_MODE; err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm); ok(!err, "STATUS mode: %s\n", dbg_mcierr(err)); switch(parm.status.dwReturn) { case MCI_MODE_NOT_READY: skip("CD-ROM mode not ready (DVD in drive?)\n"); return; case MCI_MODE_OPEN: /* should not happen with MEDIA_PRESENT */ skip("CD-ROM drive is open\n"); /* set door closed may not work. */ return; default: /* play/record/seek/pause */ ok(parm.status.dwReturn==MCI_MODE_STOP, "STATUS mode is %lx\n", parm.status.dwReturn); /* fall through */ case MCI_MODE_STOP: /* normal */ break; } /* Initial mode is "stopped" with a CD in drive */ err = mciSendString("status c mode", buf, sizeof(buf), hwnd); ok(!err, "status mode: %s\n", dbg_mcierr(err)); if(!err) ok(!strcmp(buf, "stopped"), "status mode is initially %s\n", buf); err = mciSendString("status c ready", buf, sizeof(buf), hwnd); ok(!err, "status ready: %s\n", dbg_mcierr(err)); if(!err) ok(!strcmp(buf, "true"), "status ready with media is %s\n", buf); err = mciSendString("info c product identity", buf, sizeof(buf), hwnd); ok(!err, "info 2flags: %s\n", dbg_mcierr(err)); /* not MCIERR_FLAGS_NOT_COMPATIBLE */ /* Precedence rule p>u>i verified experimentally, not tested here. */ err = mciSendString("info c identity", buf, sizeof(buf), hwnd); ok(!err || err == MCIERR_HARDWARE, "info identity: %s\n", dbg_mcierr(err)); /* a blank disk causes MCIERR_HARDWARE and other commands to fail likewise. */ ok_hw = err; err = mciSendString("info c upc", buf, sizeof(buf), hwnd); ok(err == ok_hw || err == MCIERR_NO_IDENTITY, "info upc: %s\n", dbg_mcierr(err)); parm.status.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; parm.status.dwReturn = 0; err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm); ok(err == ok_hw, "STATUS number of tracks: %s\n", dbg_mcierr(err)); numtracks = parm.status.dwReturn; /* cf. MAXIMUM_NUMBER_TRACKS */ ok(0 < numtracks && numtracks <= 99, "number of tracks=%ld\n", parm.status.dwReturn); err = mciSendString("status c length", buf, sizeof(buf), hwnd); ok(err == ok_hw, "status length: %s\n", dbg_mcierr(err)); if(!err) trace("CD length %s\n", buf); if(err) { /* MCIERR_HARDWARE when given a blank disk */ skip("status length %s (blank disk?)\n", dbg_mcierr(ok_hw)); return; } /* Linux leaves the drive at some random position, * native initialises to the start position below. */ err = mciSendString("status c position", buf, sizeof(buf), hwnd); ok(!err, "status position: %s\n", dbg_mcierr(err)); if(!err) todo_wine ok(!strcmp(buf, "00:02:00") || !strcmp(buf, "00:02:33") || !strcmp(buf, "00:03:00"), "status position initially %s\n", buf); /* 2 seconds is the initial position even with data tracks. */ err = mciSendString("status c position start notify", buf, sizeof(buf), hwnd); ok(err == ok_hw, "status position start: %s\n", dbg_mcierr(err)); if(!err) ok(!strcmp(buf, "00:02:00") || !strcmp(buf, "00:02:33") || !strcmp(buf, "00:03:00"), "status position start %s\n", buf); test_notification(hwnd, "status notify", err ? 0 : MCI_NOTIFY_SUCCESSFUL); err = mciSendString("status c position start track 1 notify", buf, sizeof(buf), hwnd); ok(err == MCIERR_FLAGS_NOT_COMPATIBLE, "status position start: %s\n", dbg_mcierr(err)); test_notification(hwnd, "status 2flags", err ? 0 : MCI_NOTIFY_SUCCESSFUL); err = mciSendString("play c from 00:02:00 to 00:01:00 notify", buf, sizeof(buf), hwnd); todo_wine ok(err == MCIERR_OUTOFRANGE, "play 2s to 1s: %s\n", dbg_mcierr(err)); test_notification(hwnd, "play 2s to 1s", err ? 0 : MCI_NOTIFY_SUCCESSFUL); err = mciSendString("resume c", buf, sizeof(buf), hwnd); ok(err == MCIERR_HARDWARE || /* Win9x */ err == MCIERR_UNSUPPORTED_FUNCTION, "resume without play: %s\n", dbg_mcierr(err)); /* not NONAPPLICABLE_FUNCTION */ /* vmware with a .iso (data-only) yields no error on NT/w2k */ err = mciSendString("seek c wait", buf, sizeof(buf), hwnd); ok(err == MCIERR_MISSING_PARAMETER, "seek noflag: %s\n", dbg_mcierr(err)); err = mciSendString("seek c to start to end", buf, sizeof(buf), hwnd); ok(err == MCIERR_FLAGS_NOT_COMPATIBLE || broken(!err), "seek to start+end: %s\n", dbg_mcierr(err)); /* Win9x only errors out with Seek to start to <position> */ /* set Wine to a defined position before play */ err = mciSendString("seek c to start notify", buf, sizeof(buf), hwnd); ok(!err, "seek to start: %s\n", dbg_mcierr(err)); test_notification(hwnd, "seek to start", err ? 0 : MCI_NOTIFY_SUCCESSFUL); /* Win9X Status position / current track then sometimes report the end position / track! */ err = mciSendString("status c mode", buf, sizeof(buf), hwnd); ok(!err, "status mode: %s\n", dbg_mcierr(err)); if(!err) ok(!strcmp(buf, "stopped"), "status mode after seek is %s\n", buf); /* MCICDA ignores MCI_SET_VIDEO * One xp machine ignored SET_AUDIO, one w2k and one w7 machine honoured it * and simultaneously toggled the mute button in the mixer control panel. * Or does it only depend on the HW, not the OS? */ err = mciSendString("set c video audio all on", buf, sizeof(buf), hwnd); ok(!err, "set video/audio: %s\n", dbg_mcierr(err)); err = mciSendString("set c time format ms", buf, sizeof(buf), hwnd); ok(!err, "set time format ms: %s\n", dbg_mcierr(err)); memset(buf, 0, sizeof(buf)); err = mciSendString("status c position start", buf, sizeof(buf), hwnd); ok(!err, "status position start (ms): %s\n", dbg_mcierr(err)); duration = atoi(buf); if(!err) ok(duration > 2000, "status position initially %sms\n", buf); /* 00:02:00 corresponds to 2001 ms, 02:33 -> 2441 etc. */ err = mciSendString("status c position start track 1", buf, sizeof(buf), hwnd); ok(err == MCIERR_FLAGS_NOT_COMPATIBLE, "status position start+track: %s\n", dbg_mcierr(err)); err = mciSendString("status c notify wait", buf, sizeof(buf), hwnd); ok(err == MCIERR_MISSING_PARAMETER, "status noflag: %s\n", dbg_mcierr(err)); err = mciSendString("status c length track 1", buf, sizeof(buf), hwnd); ok(!err, "status length (ms): %s\n", dbg_mcierr(err)); if(!err) { trace("track #1 length %sms\n", buf); duration = atoi(buf); } else duration = 2001; /* for the position test below */ if (0) { /* causes some native systems to return Seek and Play with MCIERR_HARDWARE */ /* depending on capability can eject only? */ err = mciSendString("set c door closed notify", buf, sizeof(buf), hwnd); ok(!err, "set door closed: %s\n", dbg_mcierr(err)); test_notification(hwnd, "door closed", err ? 0 : MCI_NOTIFY_SUCCESSFUL); } /* Changing the disk while the MCI device is open causes the Status * command to report stale data. Native obviously caches the TOC. */ /* status type track is localised, strcmp("audio|other") may fail. */ parm.status.dwItem = MCI_CDA_STATUS_TYPE_TRACK; parm.status.dwTrack = 1; parm.status.dwReturn = 0xFEEDABAD; err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK, (DWORD_PTR)&parm); ok(!err, "STATUS type track 1: %s\n", dbg_mcierr(err)); ok(parm.status.dwReturn==MCI_CDA_TRACK_OTHER || parm.status.dwReturn==MCI_CDA_TRACK_AUDIO, "unknown track type %lx\n", parm.status.dwReturn); if (parm.status.dwReturn == MCI_CDA_TRACK_OTHER) { /* Find an audio track */ parm.status.dwItem = MCI_CDA_STATUS_TYPE_TRACK; parm.status.dwTrack = numtracks; parm.status.dwReturn = 0xFEEDABAD; err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK, (DWORD_PTR)&parm); ok(!err, "STATUS type track %u: %s\n", numtracks, dbg_mcierr(err)); ok(parm.status.dwReturn == MCI_CDA_TRACK_OTHER || parm.status.dwReturn == MCI_CDA_TRACK_AUDIO, "unknown track type %lx\n", parm.status.dwReturn); track = (!err && parm.status.dwReturn == MCI_CDA_TRACK_AUDIO) ? numtracks : 0; /* Seek to start (above) skips over data tracks * In case of a data only CD, it seeks to the end of disk, however * another Status position a few seconds later yields MCIERR_HARDWARE. */ parm.status.dwItem = MCI_STATUS_POSITION; parm.status.dwReturn = 2000; err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm); ok(!err || broken(err == MCIERR_HARDWARE), "STATUS position: %s\n", dbg_mcierr(err)); if(!err && track) ok(parm.status.dwReturn > duration, "Seek did not skip data tracks, position %lums\n", parm.status.dwReturn); /* dwReturn > start + length(#1) may fail because of small position report fluctuation. * On some native systems, status position fluctuates around the target position; * Successive calls return varying positions! */ err = mciSendString("set c time format msf", buf, sizeof(buf), hwnd); ok(!err, "set time format msf: %s\n", dbg_mcierr(err)); parm.status.dwItem = MCI_STATUS_LENGTH; parm.status.dwTrack = 1; parm.status.dwReturn = 0xFEEDABAD; err = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK, (DWORD_PTR)&parm); ok(!err, "STATUS length track %u: %s\n", parm.status.dwTrack, dbg_mcierr(err)); duration = parm.status.dwReturn; trace("track #1 length: %02um:%02us:%02uframes\n", MCI_MSF_MINUTE(duration), MCI_MSF_SECOND(duration), MCI_MSF_FRAME(duration)); ok(duration>>24==0, "CD length high bits %08X\n", duration); /* TODO only with mixed CDs? */ /* play track 1 to length silently works with data tracks */ parm.play.dwFrom = MCI_MAKE_MSF(0,2,0); parm.play.dwTo = duration; /* omitting 2 seconds from end */ err = mciSendCommand(wDeviceID, MCI_PLAY, MCI_FROM|MCI_TO, (DWORD_PTR)&parm); ok(!err, "PLAY data to %08X: %s\n", duration, dbg_mcierr(err)); Sleep(1500*factor); /* Time to spin up, hopefully less than track length */ err = mciSendString("status c mode", buf, sizeof(buf), hwnd); ok(!err, "status mode: %s\n", dbg_mcierr(err)); if(!err) ok(!strcmp(buf, "stopped"), "status mode on data is %s\n", buf); } else if (parm.status.dwReturn == MCI_CDA_TRACK_AUDIO) {
static void test_midiStream(UINT udev, HWND hwnd) { HMIDISTRM hm; MMRESULT rc, rc2; MIDIHDR mhdr; union { MIDIPROPTEMPO tempo; MIDIPROPTIMEDIV tdiv; } midiprop; if (hwnd) rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW); else rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION); if (rc == MMSYSERR_NOTSUPPORTED) { skip( "MIDI stream not supported\n" ); return; } ok(!rc, "midiStreamOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc)); if (rc) return; test_notification(hwnd, "midiStreamOpen", MOM_OPEN, 0); 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(midiprop.tempo.dwTempo==500000, "default stream tempo %u microsec per quarter note\n", midiprop.tempo.dwTempo); midiprop.tdiv.cbStruct = sizeof(midiprop.tdiv); rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TIMEDIV); ok(!rc, "midiStreamProperty TIMEDIV rc=%s\n", mmsys_error(rc)); todo_wine ok(24==LOWORD(midiprop.tdiv.dwTimeDiv), "default stream time division %u\n", midiprop.tdiv.dwTimeDiv); memset(&mhdr, 0, sizeof(mhdr)); mhdr.dwFlags = 0; mhdr.dwUser = 0x56FA552C; mhdr.dwOffset = 1234567890; mhdr.dwBufferLength = sizeof(strmEvents); mhdr.dwBytesRecorded = mhdr.dwBufferLength; mhdr.lpData = (LPSTR)&strmEvents[0]; if (mhdr.lpData) { rc = midiOutLongMsg((HMIDIOUT)hm, &mhdr, sizeof(mhdr)); ok(rc==MIDIERR_UNPREPARED, "midiOutLongMsg unprepared rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER); rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1); ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc)); rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiOutPrepare old size rc=%s\n", mmsys_error(rc)); ok(mhdr.dwFlags & MHDR_PREPARED, "MHDR.dwFlags when prepared %x\n", mhdr.dwFlags); /* The device is still in paused mode and should queue the message. */ rc = midiStreamOut(hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiStreamOut old size rc=%s\n", mmsys_error(rc)); rc2 = rc; trace("MIDIHDR flags=%x when submitted\n", mhdr.dwFlags); /* w9X/me does not set MHDR_ISSTRM when StreamOut exits, * but it will be set on all systems after the job is finished. */ Sleep(90); /* Wine <1.1.39 started playing immediately */ test_notification(hwnd, "midiStream still paused", 0, WHATEVER); /* MSDN asks to use midiStreamRestart prior to midiStreamOut() * because the starting state is 'pause', but some apps seem to * work with the inverse order: queue everything, then play. */ rc = midiStreamRestart(hm); ok(!rc, "midiStreamRestart rc=%s\n", mmsys_error(rc)); if (!rc2) while(mhdr.dwFlags & MHDR_INQUEUE) { trace("async MIDI still queued\n"); Sleep(100); } /* Checking INQUEUE is not the recommended way to wait for the end of a job, but we're testing. */ /* MHDR_ISSTRM is not necessarily set when midiStreamOut returns * rather than when the queue is eventually processed. */ ok(mhdr.dwFlags & MHDR_ISSTRM, "MHDR.dwFlags %x no ISSTRM when out of queue\n", mhdr.dwFlags); if (!rc2) while(!(mhdr.dwFlags & MHDR_DONE)) { /* Never to be seen except perhaps on multicore */ trace("async MIDI still not done\n"); Sleep(100); } ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags %x not DONE when out of queue\n", mhdr.dwFlags); test_notification(hwnd, "midiStream callback", MOM_POSITIONCB, (DWORD_PTR)&mhdr); test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr); /* Native fills dwOffset regardless of the cbMidiHdr size argument to midiStreamOut */ ok(1234567890!=mhdr.dwOffset, "play left MIDIHDR.dwOffset at %u\n", mhdr.dwOffset); rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc)); rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiOutUnprepare #2 rc=%s\n", mmsys_error(rc)); trace("MIDIHDR stream flags=%x when finished\n", mhdr.dwFlags); ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags when done %x\n", mhdr.dwFlags); test_position(hm, TIME_MS, TIME_MS); test_position(hm, TIME_TICKS, TIME_TICKS); todo_wine test_position(hm, TIME_MIDI, TIME_MIDI); test_position(hm, TIME_SMPTE, TIME_MS); test_position(hm, TIME_SAMPLES, TIME_MS); test_position(hm, TIME_BYTES, TIME_MS); Sleep(400); /* Hear note */ 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; /* 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)); rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiOutUnprepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc)); rc = midiStreamRestart(hm); ok(!rc, "midiStreamRestart #3 rc=%s\n", mmsys_error(rc)); } ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser); 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); trace("buffer: %u\n", mhdr.dwBufferLength); mhdr.dwBytesRecorded = 0; mhdr.lpData = (LPSTR)&strmNops[0]; 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)); rc = playStream(hm, &mhdr); ok(!rc, "midiStreamOut 0 bytes recorded rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr); test_notification(hwnd, "0 bytes recorded", 0, WHATEVER); /* FIXME: check dwOffset within callback * instead of the unspecified value afterwards */ ok(1234123123==mhdr.dwOffset || broken(0==mhdr.dwOffset), "play 0 set MIDIHDR.dwOffset to %u\n", mhdr.dwOffset); /* w2k and later only set dwOffset when processing MEVT_T_CALLBACK, * while w9X/me/nt always sets it. Have Wine behave like w2k because the * dwOffset slot does not exist in the small size MIDIHDR. */ mhdr.dwOffset = 1234123123; mhdr.dwBytesRecorded = 1*sizeof(MIDISHORTEVENT); rc = playStream(hm, &mhdr); ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "1 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr); test_notification(hwnd, "1 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr); test_notification(hwnd, "1 of 2 events", 0, WHATEVER); ok(0==mhdr.dwOffset, "MIDIHDR.dwOffset 1/2 changed to %u\n", mhdr.dwOffset); mhdr.dwOffset = 1234123123; mhdr.dwBytesRecorded = 2*sizeof(MIDISHORTEVENT); rc = playStream(hm, &mhdr); ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr); test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr); test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr); test_notification(hwnd, "2 of 2 events", 0, WHATEVER); 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); 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)); 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(sizeof(MIDISHORTEVENT)==mhdr.dwOffset), "MIDIHDR.dwOffset nocb changed to %u\n", mhdr.dwOffset); mhdr.dwBytesRecorded = mhdr.dwBufferLength-1; rc = playStream(hm, &mhdr); 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); } mhdr.dwBytesRecorded = mhdr.dwBufferLength+1; rc = playStream(hm, &mhdr); ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBufferLength<dwBytesRecorded rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "past MIDIHDR tests", 0, WHATEVER); rc = midiStreamStop(hm); ok(!rc, "midiStreamStop rc=%s\n", mmsys_error(rc)); ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser); rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc)); 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); ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength); if (mhdr.lpData) { mhdr.dwFlags = 0; /* PrepareHeader detects the too large buffer is for a stream. */ rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr)); todo_wine ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare stream too large rc=%s\n", mmsys_error(rc)); rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc)); HeapFree(GetProcessHeap(), 0, mhdr.lpData); } rc = midiStreamClose(hm); ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "midiStreamClose", MOM_CLOSE, 0); test_notification(hwnd, "midiStream over", 0, WHATEVER); 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_midiOut_device(UINT udev, HWND hwnd) { HMIDIOUT hm; 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); 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) rc = midiOutOpen(&hm, udev, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW); else rc = midiOutOpen(&hm, udev, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION); if (rc == MMSYSERR_NOTSUPPORTED) { skip( "MIDI out not supported\n" ); return; } ok(!rc, "midiOutOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc)); if (rc) return; test_notification(hwnd, "midiOutOpen", MOM_OPEN, 0); rc = midiOutGetVolume(hm, &ovolume); ok((capsA.dwSupport & MIDICAPS_VOLUME) ? rc==MMSYSERR_NOERROR : rc==MMSYSERR_NOTSUPPORTED, "midiOutGetVolume rc=%s\n", mmsys_error(rc)); /* The native mapper responds with FFFFFFFF initially, * real devices with the volume GUI SW-synth settings. */ if (!rc) trace("Current volume %x on device %d\n", ovolume, udev); /* The W95 ESFM Synthesis device reports NOTENABLED although * GetVolume by handle works and music plays. */ 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 * not adequate to restore the value prior to tests. */ if (winetest_interactive && (capsA.dwSupport & MIDICAPS_VOLUME)) { DWORD volume2 = (ovolume < 0x80000000) ? 0xC000C000 : 0x40004000; rc = midiOutSetVolume(hm, volume2); ok(!rc, "midiOutSetVolume rc=%s\n", mmsys_error(rc)); if (!rc) { DWORD volume3; rc = midiOutGetVolume(hm, &volume3); ok(!rc, "midiOutGetVolume new rc=%s\n", mmsys_error(rc)); if (!rc) trace("New volume %x on device %d\n", volume3, udev); todo_wine ok(volume2==volume3, "volume Set %x = Get %x\n", volume2, volume3); rc = midiOutSetVolume(hm, ovolume); ok(!rc, "midiOutSetVolume restore rc=%s\n", mmsys_error(rc)); } } rc = midiOutGetDevCapsA((UINT_PTR)hm, &capsA, sizeof(capsA)); ok(!rc, "midiOutGetDevCaps(dev=%d) by handle rc=%s\n", udev, mmsys_error(rc)); rc = midiInGetDevCapsA((UINT_PTR)hm, (LPMIDIINCAPSA)&capsA, sizeof(DWORD)); ok(rc==MMSYSERR_BADDEVICEID, "midiInGetDevCaps(dev=%d) by out handle rc=%s\n", udev, mmsys_error(rc)); { DWORD e = 0x006F4893; /* velocity, note (#69 would be 440Hz) channel */ trace("ShortMsg type %x\n", LOBYTE(LOWORD(e))); rc = midiOutShortMsg(hm, e); ok(!rc, "midiOutShortMsg rc=%s\n", mmsys_error(rc)); if (!rc) Sleep(400); /* Hear note */ } memset(&mhdr, 0, sizeof(mhdr)); mhdr.dwFlags = 0; mhdr.dwUser = 0x56FA552C; mhdr.dwOffset = 0xDEADBEEF; mhdr.dwBufferLength = 70000; /* > 64KB! */ mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength); ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength); if (mhdr.lpData) { rc = midiOutLongMsg(hm, &mhdr, sizeof(mhdr)); ok(rc==MIDIERR_UNPREPARED, "midiOutLongMsg unprepared rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER); rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1); ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc)); rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiOutPrepare old size rc=%s\n", mmsys_error(rc)); rc = midiOutPrepareHeader(hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc)); rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc)); trace("MIDIHDR flags=%x when unsent\n", mhdr.dwFlags); HeapFree(GetProcessHeap(), 0, mhdr.lpData); } 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)); rc = midiOutClose(hm); ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "midiOutClose", MOM_CLOSE, 0); test_notification(hwnd, "midiOut over", 0, WHATEVER); }