📄 winmm.c
字号:
return MMSYSERR_NOMEM;
lpwm->mod.hMidi = (HMIDI) hMidiIn;
lpwm->mod.dwCallback = dwCallback;
lpwm->mod.dwInstance = dwInstance;
lpwm->mld.uDeviceID = uDeviceID;
dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD)&lpwm->mod, dwFlags);
if (dwRet != MMSYSERR_NOERROR) {
MMDRV_Free(hMidiIn, &lpwm->mld);
hMidiIn = 0;
}
if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;
TRACE("=> %ld hMidi=%p\n", dwRet, hMidiIn);
return dwRet;
}
/**************************************************************************
* midiInOpen [WINMM.@]
*/
UINT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID,
DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
{
return MIDI_InOpen(lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags, TRUE);
}
/**************************************************************************
* midiInClose [WINMM.@]
*/
UINT WINAPI midiInClose(HMIDIIN hMidiIn)
{
LPWINE_MLD wmld;
DWORD dwRet;
TRACE("(%p)\n", hMidiIn);
if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
return MMSYSERR_INVALHANDLE;
dwRet = MMDRV_Close(wmld, MIDM_CLOSE);
MMDRV_Free(hMidiIn, wmld);
return dwRet;
}
/**************************************************************************
* midiInPrepareHeader [WINMM.@]
*/
UINT WINAPI midiInPrepareHeader(HMIDIIN hMidiIn,
MIDIHDR* lpMidiInHdr, UINT uSize)
{
LPWINE_MLD wmld;
TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
if (lpMidiInHdr == NULL || uSize < sizeof (MIDIHDR))
return MMSYSERR_INVALPARAM;
if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
return MMSYSERR_INVALHANDLE;
return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
}
/**************************************************************************
* midiInUnprepareHeader [WINMM.@]
*/
UINT WINAPI midiInUnprepareHeader(HMIDIIN hMidiIn,
MIDIHDR* lpMidiInHdr, UINT uSize)
{
LPWINE_MLD wmld;
TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
if (lpMidiInHdr == NULL || uSize < sizeof (MIDIHDR))
return MMSYSERR_INVALPARAM;
if (!(lpMidiInHdr->dwFlags & MHDR_PREPARED)) {
return MMSYSERR_NOERROR;
}
if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
return MMSYSERR_INVALHANDLE;
return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
}
/**************************************************************************
* midiInAddBuffer [WINMM.@]
*/
UINT WINAPI midiInAddBuffer(HMIDIIN hMidiIn,
MIDIHDR* lpMidiInHdr, UINT uSize)
{
LPWINE_MLD wmld;
TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
return MMSYSERR_INVALHANDLE;
return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
}
/**************************************************************************
* midiInStart [WINMM.@]
*/
UINT WINAPI midiInStart(HMIDIIN hMidiIn)
{
LPWINE_MLD wmld;
TRACE("(%p)\n", hMidiIn);
if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
return MMSYSERR_INVALHANDLE;
return MMDRV_Message(wmld, MIDM_START, 0L, 0L, TRUE);
}
/**************************************************************************
* midiInStop [WINMM.@]
*/
UINT WINAPI midiInStop(HMIDIIN hMidiIn)
{
LPWINE_MLD wmld;
TRACE("(%p)\n", hMidiIn);
if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
return MMSYSERR_INVALHANDLE;
return MMDRV_Message(wmld, MIDM_STOP, 0L, 0L, TRUE);
}
/**************************************************************************
* midiInReset [WINMM.@]
*/
UINT WINAPI midiInReset(HMIDIIN hMidiIn)
{
LPWINE_MLD wmld;
TRACE("(%p)\n", hMidiIn);
if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
return MMSYSERR_INVALHANDLE;
return MMDRV_Message(wmld, MIDM_RESET, 0L, 0L, TRUE);
}
/**************************************************************************
* midiInGetID [WINMM.@]
*/
UINT WINAPI midiInGetID(HMIDIIN hMidiIn, UINT* lpuDeviceID)
{
LPWINE_MLD wmld;
TRACE("(%p, %p)\n", hMidiIn, lpuDeviceID);
if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)
return MMSYSERR_INVALHANDLE;
*lpuDeviceID = wmld->uDeviceID;
return MMSYSERR_NOERROR;
}
/**************************************************************************
* midiInMessage [WINMM.@]
*/
UINT WINAPI midiInMessage(HMIDIIN hMidiIn, UINT uMessage,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
LPWINE_MLD wmld;
TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiIn, uMessage, dwParam1, dwParam2);
if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
return MMSYSERR_INVALHANDLE;
switch (uMessage) {
case MIDM_OPEN:
case MIDM_CLOSE:
FIXME("can't handle OPEN or CLOSE message!\n");
return MMSYSERR_NOTSUPPORTED;
}
return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
}
typedef struct WINE_MIDIStream {
HMIDIOUT hDevice;
HANDLE hThread;
DWORD dwThreadID;
DWORD dwTempo;
DWORD dwTimeDiv;
DWORD dwPositionMS;
DWORD dwPulses;
DWORD dwStartTicks;
WORD wFlags;
HANDLE hEvent;
LPMIDIHDR lpMidiHdr;
} WINE_MIDIStream;
#define WINE_MSM_HEADER (WM_USER+0)
#define WINE_MSM_STOP (WM_USER+1)
/**************************************************************************
* MMSYSTEM_GetMidiStream [internal]
*/
static BOOL MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)
{
WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);
if (lplpwm)
*lplpwm = lpwm;
if (lpwm == NULL) {
return FALSE;
}
*lpMidiStrm = (WINE_MIDIStream*)lpwm->mod.rgIds.dwStreamID;
return *lpMidiStrm != NULL;
}
/**************************************************************************
* MMSYSTEM_MidiStream_Convert [internal]
*/
static DWORD MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)
{
DWORD ret = 0;
if (lpMidiStrm->dwTimeDiv == 0) {
FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");
} else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */
int nf = -(char)HIBYTE(lpMidiStrm->dwTimeDiv); /* number of frames */
int nsf = LOBYTE(lpMidiStrm->dwTimeDiv); /* number of sub-frames */
ret = (pulse * 1000) / (nf * nsf);
} else {
ret = (DWORD)((double)pulse * ((double)lpMidiStrm->dwTempo / 1000) /
(double)lpMidiStrm->dwTimeDiv);
}
return ret;
}
/**************************************************************************
* MMSYSTEM_MidiStream_MessageHandler [internal]
*/
static BOOL MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)
{
LPMIDIHDR lpMidiHdr;
LPMIDIHDR* lpmh;
LPBYTE lpData;
switch (msg->message) {
case WM_QUIT:
SetEvent(lpMidiStrm->hEvent);
return FALSE;
case WINE_MSM_STOP:
TRACE("STOP\n");
/* this is not quite what MS doc says... */
midiOutReset(lpMidiStrm->hDevice);
/* empty list of already submitted buffers */
for (lpMidiHdr = lpMidiStrm->lpMidiHdr; lpMidiHdr; lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext) {
lpMidiHdr->dwFlags |= MHDR_DONE;
lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
(HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
}
lpMidiStrm->lpMidiHdr = 0;
SetEvent(lpMidiStrm->hEvent);
break;
case WINE_MSM_HEADER:
/* sets initial tick count for first MIDIHDR */
if (!lpMidiStrm->dwStartTicks)
lpMidiStrm->dwStartTicks = GetTickCount();
/* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent
* by native mcimidi, it doesn't look like a correct one".
* this trick allows to throw it away... but I don't like it.
* It looks like part of the file I'm trying to play and definitively looks
* like raw midi content
* I'd really like to understand why native mcimidi sends it. Perhaps a bad
* synchronization issue where native mcimidi is still processing raw MIDI
* content before generating MIDIEVENTs ?
*
* 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..
* 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..
* 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.
* 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.
* 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^
* 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b
* 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#
* 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..
* 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..
* 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.
* 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.
* 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F
* 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H
* 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;
* 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;
* 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.
* 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.
*/
lpMidiHdr = (LPMIDIHDR)msg->lParam;
lpData = lpMidiHdr->lpData;
TRACE("Adding %s lpMidiHdr=%p [lpData=0x%08lx dwBufferLength=%lu/%lu dwFlags=0x%08lx size=%u]\n",
(lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,
(DWORD)lpMidiHdr, lpMidiHdr->dwBufferLength, lpMidiHdr->dwBytesRecorded,
lpMidiHdr->dwFlags, msg->wParam);
#if 0
/* dumps content of lpMidiHdr->lpData
* FIXME: there should be a debug routine somewhere that already does this
* I hate spreading this type of shit all around the code
*/
for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {
DWORD i;
BYTE ch;
for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)
printf("%02x ", lpData[dwToGo + i]);
for (; i < 16; i++)
printf(" ");
for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {
ch = lpData[dwToGo + i];
printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');
}
printf("\n");
}
#endif
if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&
((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&
((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)lpMidiStrm) {
FIXME("Dropping bad %s lpMidiHdr (streamID=%08lx)\n",
(lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",
((LPMIDIEVENT)lpData)->dwStreamID);
lpMidiHdr->dwFlags |= MHDR_DONE;
lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
(HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
break;
}
for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = (LPMIDIHDR*)&((*lpmh)->lpNext));
*lpmh = lpMidiHdr;
lpMidiHdr = (LPMIDIHDR)msg->lParam;
lpMidiHdr->lpNext = 0;
lpMidiHdr->dwFlags |= MHDR_INQUEUE;
lpMidiHdr->dwFlags &= ~MHDR_DONE;
lpMidiHdr->dwOffset = 0;
break;
default:
FIXME("Unknown message %d\n", msg->message);
break;
}
return TRUE;
}
/**************************************************************************
* MMSYSTEM_MidiStream_Player [internal]
*/
static DWORD CALLBACK MMSYSTEM_MidiStream_Player(LPVOID pmt)
{
WINE_MIDIStream* lpMidiStrm = pmt;
WINE_MIDI* lpwm;
MSG msg;
DWORD dwToGo;
DWORD dwCurrTC;
LPMIDIHDR lpMidiHdr;
LPMIDIEVENT me;
LPBYTE lpData = 0;
TRACE("(%p)!\n", lpMidiStrm);
if (!lpMidiStrm ||
(lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)
goto the_end;
/* force thread's queue creation */
/* Used to be InitThreadInput16(0, 5); */
/* but following works also with hack in midiStreamOpen */
PeekMessageA(&msg, 0, 0, 0, 0);
/* FIXME: this next line must be called before midiStreamOut or midiStreamRestart are called */
SetEvent(lpMidiStrm->hEvent);
TRACE("Ready to go 1\n");
/* thread is started in paused mode */
SuspendThread(lpMidiStrm->hThread);
TRACE("Ready to go 2\n");
lpMidiStrm->dwStartTicks = 0;
lpMidiStrm->dwPulses = 0;
lpMidiStrm->lpMidiHdr = 0;
for (;;) {
lpMidiHdr = lpMidiStrm->lpMidiHdr;
if (!lpMidiHdr) {
/* for first message, block until one arrives, then process all that are available */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -