📄 mcimidi.c
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- *//* * Sample MIDI Wine Driver for Linux * * Copyright 1994 Martin Ayotte * 1999 Eric Pouech * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * Eric POUECH : * 98/7 changes for making this MIDI driver work on OSS * current support is limited to MIDI ports of OSS systems * 98/9 rewriting MCI code for MIDI * 98/11 splitted in midi.c and mcimidi.c */#include <stdlib.h>#include <stdarg.h>#include <stdio.h>#include <string.h>#include "windef.h"#include "winbase.h"#include "wingdi.h"#include "winuser.h"#include "wownt32.h"#include "mmddk.h"#include "wine/debug.h"WINE_DEFAULT_DEBUG_CHANNEL(mcimidi);#define MIDI_NOTEOFF 0x80#define MIDI_NOTEON 0x90typedef struct { DWORD dwFirst; /* offset in file of track */ DWORD dwLast; /* number of bytes in file of track */ DWORD dwIndex; /* current index in file (dwFirst <= dwIndex < dwLast) */ DWORD dwLength; /* number of pulses in this track */ DWORD dwEventPulse; /* current pulse # (event) pointed by dwIndex */ DWORD dwEventData; /* current data (event) pointed by dwIndex */ WORD wEventLength; /* current length (event) pointed by dwIndex */ WORD wStatus : 1, /* 1 : playing, 0 : done */ wTrackNr : 7, wLastCommand : 8; /* last MIDI command on track */} MCI_MIDITRACK;typedef struct tagWINE_MCIMIDI { UINT wDevID; /* the MCI one */ HMIDI hMidi; int nUseCount; /* Incremented for each shared open */ WORD wNotifyDeviceID; /* MCI device ID with a pending notification */ HANDLE hCallback; /* Callback handle for pending notification */ HMMIO hFile; /* mmio file handle open as Element */ LPSTR lpstrElementName; /* Name of file */ LPSTR lpstrCopyright; LPSTR lpstrName; WORD dwStatus; /* one from MCI_MODE_xxxx */ DWORD dwMciTimeFormat; /* One of the supported MCI_FORMAT_xxxx */ WORD wFormat; /* Format of MIDI hFile (0, 1 or 2) */ WORD nTracks; /* Number of tracks in hFile */ WORD nDivision; /* Number of division in hFile PPQN or SMPTE */ WORD wStartedPlaying; DWORD dwTempo; /* Tempo (# of 1/4 note per second */ MCI_MIDITRACK* tracks; /* Content of each track */ DWORD dwPulse; DWORD dwPositionMS; DWORD dwStartTicks;} WINE_MCIMIDI;/* =================================================================== * =================================================================== * FIXME: should be using the new mmThreadXXXX functions from WINMM * instead of those * it would require to add a wine internal flag to mmThreadCreate * in order to pass a 32 bit function instead of a 16 bit * =================================================================== * =================================================================== */struct SCA { UINT wDevID; UINT wMsg; DWORD dwParam1; DWORD dwParam2;};/* EPP DWORD WINAPI mciSendCommandA(UINT wDevID, UINT wMsg, DWORD dwParam1, DWORD dwParam2); *//************************************************************************** * MCI_SCAStarter [internal] */static DWORD CALLBACK MCI_SCAStarter(LPVOID arg){ struct SCA* sca = (struct SCA*)arg; DWORD ret; TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n", sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2); ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2); TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n", sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2); HeapFree(GetProcessHeap(), 0, sca); ExitThread(ret); WARN("Should not happen ? what's wrong \n"); /* should not go after this point */ return ret;}/************************************************************************** * MCI_SendCommandAsync [internal] */static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1, DWORD dwParam2, UINT size){ struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size); if (sca == 0) return MCIERR_OUT_OF_MEMORY; sca->wDevID = wDevID; sca->wMsg = wMsg; sca->dwParam1 = dwParam1; if (size && dwParam2) { sca->dwParam2 = (DWORD)sca + sizeof(struct SCA); /* copy structure passed by program in dwParam2 to be sure * we can still use it whatever the program does */ memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size); } else { sca->dwParam2 = dwParam2; } if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) { WARN("Couldn't allocate thread for async command handling, sending synchonously\n"); return MCI_SCAStarter(&sca); } return 0;}/*======================================================================* * MCI MIDI implemantation * *======================================================================*/static DWORD MIDI_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);/************************************************************************** * MIDI_drvOpen [internal] */static DWORD MIDI_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp){ WINE_MCIMIDI* wmm; if (!modp) return 0xFFFFFFFF; wmm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIMIDI)); if (!wmm) return 0; wmm->wDevID = modp->wDeviceID; mciSetDriverData(wmm->wDevID, (DWORD)wmm); modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE; modp->wType = MCI_DEVTYPE_SEQUENCER; return modp->wDeviceID;}/************************************************************************** * MCIMIDI_drvClose [internal] */static DWORD MIDI_drvClose(DWORD dwDevID){ WINE_MCIMIDI* wmm = (WINE_MCIMIDI*)mciGetDriverData(dwDevID); if (wmm) { HeapFree(GetProcessHeap(), 0, wmm); mciSetDriverData(dwDevID, 0); return 1; } return (dwDevID == 0xFFFFFFFF) ? 1 : 0;}/************************************************************************** * MIDI_mciGetOpenDev [internal] */static WINE_MCIMIDI* MIDI_mciGetOpenDev(UINT wDevID){ WINE_MCIMIDI* wmm = (WINE_MCIMIDI*)mciGetDriverData(wDevID); if (wmm == NULL || wmm->nUseCount == 0) { WARN("Invalid wDevID=%u\n", wDevID); return 0; } return wmm;}/************************************************************************** * MIDI_mciReadByte [internal] */static DWORD MIDI_mciReadByte(WINE_MCIMIDI* wmm, BYTE *lpbyt){ DWORD ret = 0; if (lpbyt == NULL || mmioRead(wmm->hFile, (HPSTR)lpbyt, sizeof(BYTE)) != (long)sizeof(BYTE)) { WARN("Error reading wmm=%p\n", wmm); ret = MCIERR_INVALID_FILE; } return ret;}/************************************************************************** * MIDI_mciReadWord [internal] */static DWORD MIDI_mciReadWord(WINE_MCIMIDI* wmm, LPWORD lpw){ BYTE hibyte, lobyte; DWORD ret = MCIERR_INVALID_FILE; if (lpw != NULL && MIDI_mciReadByte(wmm, &hibyte) == 0 && MIDI_mciReadByte(wmm, &lobyte) == 0) { *lpw = ((WORD)hibyte << 8) + lobyte; ret = 0; } return ret;}/************************************************************************** * MIDI_mciReadLong [internal] */static DWORD MIDI_mciReadLong(WINE_MCIMIDI* wmm, LPDWORD lpdw){ WORD hiword, loword; DWORD ret = MCIERR_INVALID_FILE; if (lpdw != NULL && MIDI_mciReadWord(wmm, &hiword) == 0 && MIDI_mciReadWord(wmm, &loword) == 0) { *lpdw = MAKELONG(loword, hiword); ret = 0; } return ret;}/************************************************************************** * MIDI_mciReadVaryLen [internal] */static WORD MIDI_mciReadVaryLen(WINE_MCIMIDI* wmm, LPDWORD lpdw){ BYTE byte; DWORD value = 0; WORD ret = 0; if (lpdw == NULL) { ret = MCIERR_INVALID_FILE; } else { do { if (MIDI_mciReadByte(wmm, &byte) != 0) { return 0; } value = (value << 7) + (byte & 0x7F); ret++; } while (byte & 0x80); *lpdw = value; /* TRACE("val=%08lX \n", value); */ } return ret;}/************************************************************************** * MIDI_mciReadNextEvent [internal] */static DWORD MIDI_mciReadNextEvent(WINE_MCIMIDI* wmm, MCI_MIDITRACK* mmt){ BYTE b1, b2 = 0, b3; WORD hw = 0; DWORD evtPulse; DWORD evtLength; DWORD tmp; if (mmioSeek(wmm->hFile, mmt->dwIndex, SEEK_SET) != mmt->dwIndex) { WARN("Can't seek at %08lX \n", mmt->dwIndex); return MCIERR_INVALID_FILE; } evtLength = MIDI_mciReadVaryLen(wmm, &evtPulse) + 1; /* > 0 */ MIDI_mciReadByte(wmm, &b1); switch (b1) { case 0xF0: case 0xF7: evtLength += MIDI_mciReadVaryLen(wmm, &tmp); evtLength += tmp; break; case 0xFF: MIDI_mciReadByte(wmm, &b2); evtLength++; evtLength += MIDI_mciReadVaryLen(wmm, &tmp); if (evtLength >= 0x10000u) { /* this limitation shouldn't be a problem */ WARN("Ouch !! Implementation limitation to 64k bytes for a MIDI event is overflowed\n"); hw = 0xFFFF; } else { hw = LOWORD(evtLength); } evtLength += tmp; break; default: if (b1 & 0x80) { /* use running status ? */ mmt->wLastCommand = b1; MIDI_mciReadByte(wmm, &b2); evtLength++; } else { b2 = b1; b1 = mmt->wLastCommand; } switch ((b1 >> 4) & 0x07) { case 0: case 1: case 2: case 3: case 6: MIDI_mciReadByte(wmm, &b3); evtLength++; hw = b3; break; case 4: case 5: break; case 7: WARN("Strange indeed b1=0x%02x\n", b1); } break; } if (mmt->dwIndex + evtLength > mmt->dwLast) return MCIERR_INTERNAL; mmt->dwEventPulse += evtPulse; mmt->dwEventData = (hw << 16) + (b2 << 8) + b1; mmt->wEventLength = evtLength; /* TRACE("[%u] => pulse=%08lx(%08lx), data=%08lx, length=%u\n", mmt->wTrackNr, mmt->dwEventPulse, evtPulse, mmt->dwEventData, mmt->wEventLength); */ return 0;}/************************************************************************** * MIDI_mciReadMTrk [internal]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -