📄 winmm.c
字号:
GetMessageA(&msg, 0, 0, 0);
do {
if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
goto the_end;
} while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE));
lpData = 0;
continue;
}
if (!lpData)
lpData = lpMidiHdr->lpData;
me = (LPMIDIEVENT)(lpData + lpMidiHdr->dwOffset);
/* do we have to wait ? */
if (me->dwDeltaTime) {
lpMidiStrm->dwPositionMS += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);
lpMidiStrm->dwPulses += me->dwDeltaTime;
dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->dwPositionMS;
TRACE("%ld/%ld/%ld\n", dwToGo, GetTickCount(), me->dwDeltaTime);
while ((dwCurrTC = GetTickCount()) < dwToGo) {
if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {
/* got a message, handle it */
while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
goto the_end;
}
lpData = 0;
} else {
/* timeout, so me->dwDeltaTime is elapsed, can break the while loop */
break;
}
}
}
switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {
case MEVT_COMMENT:
FIXME("NIY: MEVT_COMMENT\n");
/* do nothing, skip bytes */
break;
case MEVT_LONGMSG:
FIXME("NIY: MEVT_LONGMSG, aka sending Sysex event\n");
break;
case MEVT_NOP:
break;
case MEVT_SHORTMSG:
midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));
break;
case MEVT_TEMPO:
lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);
break;
case MEVT_VERSION:
break;
default:
FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));
break;
}
if (me->dwEvent & MEVT_F_CALLBACK) {
DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
(HDRVR)lpMidiStrm->hDevice, MM_MOM_POSITIONCB,
lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);
}
lpMidiHdr->dwOffset += sizeof(MIDIEVENT) - sizeof(me->dwParms);
if (me->dwEvent & MEVT_F_LONG)
lpMidiHdr->dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;
if (lpMidiHdr->dwOffset >= lpMidiHdr->dwBufferLength) {
/* done with this header */
lpMidiHdr->dwFlags |= MHDR_DONE;
lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
lpMidiStrm->lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
(HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
lpData = 0;
}
}
the_end:
TRACE("End of thread\n");
ExitThread(0);
return 0; /* for removing the warning, never executed */
}
/**************************************************************************
* MMSYSTEM_MidiStream_PostMessage [internal]
*/
static BOOL MMSYSTEM_MidiStream_PostMessage(WINE_MIDIStream* lpMidiStrm, WORD msg, DWORD pmt1, DWORD pmt2)
{
if (PostThreadMessageA(lpMidiStrm->dwThreadID, msg, pmt1, pmt2)) {
DWORD count;
if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
} else {
WARN("bad PostThreadMessageA\n");
return FALSE;
}
return TRUE;
}
/**************************************************************************
* midiStreamClose [WINMM.@]
*/
MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)
{
WINE_MIDIStream* lpMidiStrm;
TRACE("(%p)!\n", hMidiStrm);
if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
return MMSYSERR_INVALHANDLE;
midiStreamStop(hMidiStrm);
MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WM_QUIT, 0, 0);
HeapFree(GetProcessHeap(), 0, lpMidiStrm);
CloseHandle(lpMidiStrm->hEvent);
return midiOutClose((HMIDIOUT)hMidiStrm);
}
/**************************************************************************
* MMSYSTEM_MidiStream_Open [internal]
*/
MMRESULT MIDI_StreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID, DWORD cMidi,
DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen,
BOOL bFrom32)
{
WINE_MIDIStream* lpMidiStrm;
MMRESULT ret;
MIDIOPENSTRMID mosm;
LPWINE_MIDI lpwm;
HMIDIOUT hMidiOut;
TRACE("(%p, %p, %ld, 0x%08lx, 0x%08lx, 0x%08lx)!\n",
lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
return MMSYSERR_INVALPARAM;
lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));
if (!lpMidiStrm)
return MMSYSERR_NOMEM;
lpMidiStrm->dwTempo = 500000;
lpMidiStrm->dwTimeDiv = 480; /* 480 is 120 quater notes per minute *//* FIXME ??*/
lpMidiStrm->dwPositionMS = 0;
mosm.dwStreamID = (DWORD)lpMidiStrm;
/* FIXME: the correct value is not allocated yet for MAPPER */
mosm.wDeviceID = *lpuDeviceID;
lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm, bFrom32);
lpMidiStrm->hDevice = hMidiOut;
if (lphMidiStrm)
*lphMidiStrm = (HMIDISTRM)hMidiOut;
lpwm->mld.uDeviceID = *lpuDeviceID;
ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD)&lpwm->mod, fdwOpen);
lpMidiStrm->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
lpMidiStrm->wFlags = HIWORD(fdwOpen);
lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,
lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));
if (!lpMidiStrm->hThread) {
midiStreamClose((HMIDISTRM)hMidiOut);
return MMSYSERR_NOMEM;
}
SetThreadPriority(lpMidiStrm->hThread, THREAD_PRIORITY_TIME_CRITICAL);
/* wait for thread to have started, and for its queue to be created */
{
DWORD count;
/* (Release|Restore)ThunkLock() is needed when this method is called from 16 bit code,
* (meaning the Win16Lock is set), so that it's released and the 32 bit thread running
* MMSYSTEM_MidiStreamPlayer can acquire Win16Lock to create its queue.
*/
if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
}
TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n",
*lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);
return ret;
}
/**************************************************************************
* midiStreamOpen [WINMM.@]
*/
MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
DWORD cMidi, DWORD_PTR dwCallback,
DWORD_PTR dwInstance, DWORD fdwOpen)
{
return MIDI_StreamOpen(lphMidiStrm, lpuDeviceID, cMidi, dwCallback,
dwInstance, fdwOpen, TRUE);
}
/**************************************************************************
* midiStreamOut [WINMM.@]
*/
MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,
UINT cbMidiHdr)
{
WINE_MIDIStream* lpMidiStrm;
DWORD ret = MMSYSERR_NOERROR;
TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
ret = MMSYSERR_INVALHANDLE;
} else if (!lpMidiHdr) {
ret = MMSYSERR_INVALPARAM;
} else {
if (!PostThreadMessageA(lpMidiStrm->dwThreadID,
WINE_MSM_HEADER, cbMidiHdr,
(DWORD)lpMidiHdr)) {
WARN("bad PostThreadMessageA\n");
ret = MMSYSERR_ERROR;
}
}
return ret;
}
/**************************************************************************
* midiStreamPause [WINMM.@]
*/
MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)
{
WINE_MIDIStream* lpMidiStrm;
DWORD ret = MMSYSERR_NOERROR;
TRACE("(%p)!\n", hMidiStrm);
if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
ret = MMSYSERR_INVALHANDLE;
} else {
if (SuspendThread(lpMidiStrm->hThread) == 0xFFFFFFFF) {
WARN("bad Suspend (%ld)\n", GetLastError());
ret = MMSYSERR_ERROR;
}
}
return ret;
}
/**************************************************************************
* midiStreamPosition [WINMM.@]
*/
MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)
{
WINE_MIDIStream* lpMidiStrm;
DWORD ret = MMSYSERR_NOERROR;
TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);
if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
ret = MMSYSERR_INVALHANDLE;
} else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {
ret = MMSYSERR_INVALPARAM;
} else {
switch (lpMMT->wType) {
case TIME_MS:
lpMMT->u.ms = lpMidiStrm->dwPositionMS;
TRACE("=> %ld ms\n", lpMMT->u.ms);
break;
case TIME_TICKS:
lpMMT->u.ticks = lpMidiStrm->dwPulses;
TRACE("=> %ld ticks\n", lpMMT->u.ticks);
break;
default:
WARN("Unsupported time type %d\n", lpMMT->wType);
lpMMT->wType = TIME_MS;
ret = MMSYSERR_INVALPARAM;
break;
}
}
return ret;
}
/**************************************************************************
* midiStreamProperty [WINMM.@]
*/
MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
{
WINE_MIDIStream* lpMidiStrm;
MMRESULT ret = MMSYSERR_NOERROR;
TRACE("(%p, %p, %lx)\n", hMidiStrm, lpPropData, dwProperty);
if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
ret = MMSYSERR_INVALHANDLE;
} else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {
ret = MMSYSERR_INVALPARAM;
} else if (dwProperty & MIDIPROP_TEMPO) {
MIDIPROPTEMPO* mpt = (MIDIPROPTEMPO*)lpPropData;
if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {
ret = MMSYSERR_INVALPARAM;
} else if (dwProperty & MIDIPROP_SET) {
lpMidiStrm->dwTempo = mpt->dwTempo;
TRACE("Setting tempo to %ld\n", mpt->dwTempo);
} else if (dwProperty & MIDIPROP_GET) {
mpt->dwTempo = lpMidiStrm->dwTempo;
TRACE("Getting tempo <= %ld\n", mpt->dwTempo);
}
} else if (dwProperty & MIDIPROP_TIMEDIV) {
MIDIPROPTIMEDIV* mptd = (MIDIPROPTIMEDIV*)lpPropData;
if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {
ret = MMSYSERR_INVALPARAM;
} else if (dwProperty & MIDIPROP_SET) {
lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;
TRACE("Setting time div to %ld\n", mptd->dwTimeDiv);
} else if (dwProperty & MIDIPROP_GET) {
mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;
TRACE("Getting time div <= %ld\n", mptd->dwTimeDiv);
}
} else {
ret = MMSYSERR_INVALPARAM;
}
return ret;
}
/**************************************************************************
* midiStreamRestart [WINMM.@]
*/
MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)
{
WINE_MIDIStream* lpMidiStrm;
MMRESULT ret = MMSYSERR_NOERROR;
TRACE("(%p)!\n", hMidiStrm);
if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
ret = MMSYSERR_INVALHANDLE;
} else {
DWORD ret;
/* since we increase the thread suspend count on each midiStreamPause
* there may be a need for several midiStreamResume
*/
do {
ret = ResumeThread(lpMidiStrm->hThread);
} while (ret != 0xFFFFFFFF && ret != 0);
if (ret == 0xFFFFFFFF) {
WARN("bad Resume (%ld)\n", GetLastError());
ret = MMSYSERR_ERROR;
} else {
lpMidiStrm->dwStartTicks = GetTickCount() - lpMidiStrm->dwPositionMS;
}
}
return ret;
}
/**************************************************************************
* midiStreamStop [WINMM.@]
*/
MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)
{
WINE_MIDIStream* lpMidiStrm;
MMRESULT ret = MMSYSERR_NOERROR;
TRACE("(%p)!\n", hMidiStrm);
if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
ret = MMSYSERR_INVALHANDLE;
} else {
/* in case stream has been paused... FIXME is the current state correct ? */
midiStreamRestart(hMidiStrm);
MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WINE_MSM_STOP, 0, 0);
}
return ret;
}
UINT WAVE_Open(HANDLE* lphndl, UINT uDeviceID, UINT uType,
LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)
{
HANDLE handle;
LPWINE_MLD wmld;
DWORD dwRet = MMSYSERR_NOERROR;
WAVEOPENDESC wod;
TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08lX, %d);\n",
lphndl, (int)uDeviceID, (uType==MMDRV_WAVEOUT)?"Out":"In", lpFormat, dwCallback,
dwInstance, dwFlags, bFrom32?32:16);
if (dwFlags & WAVE_FORMAT_QUERY)
TRACE("WAVE_FORMAT_QUERY requested !\n");
if (lpFormat == NULL) {
WARN("bad format\n");
return WAVERR_BADFORMAT;
}
if ((dwFlags & WAVE_MAPPED) && (uDeviceID == (UINT)-1)) {
WARN("invalid parameter\n");
return MMSYSERR_INVALPARAM;
}
/* may have a PCMWAVEFORMAT rather than a WAVEFORMATEX so don't read cbSize */
TRACE("wFormatTag=
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -