📄 midi.c
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- *//* * Sample MIDI Wine Driver for ALSA (basically Linux) * * Copyright 1994 Martin Ayotte * Copyright 1998 Luiz Otavio L. Zorzella (init procedures) * Copyright 1998/1999 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 * Copyright 2003 Christian Costa : * ALSA port * * 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 * * TODO: Finish midi record * */#include "config.h"#include <string.h>#include <stdarg.h>#include <stdio.h>#ifdef HAVE_UNISTD_H# include <unistd.h>#endif#include <fcntl.h>#include <errno.h>#include "windef.h"#include "winbase.h"#include "wingdi.h"#include "winuser.h"#include "mmddk.h"#ifdef HAVE_ALSA# include "alsa.h"#endif#include "wine/debug.h"WINE_DEFAULT_DEBUG_CHANNEL(midi);#if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)typedef struct { int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */ DWORD bufsize; MIDIOPENDESC midiDesc; WORD wFlags; LPMIDIHDR lpQueueHdr; DWORD dwTotalPlayed; unsigned char incoming[3]; unsigned char incPrev; char incLen; DWORD startTime; MIDIINCAPSA caps; snd_seq_addr_t addr;} WINE_MIDIIN;typedef struct { BOOL bEnabled; DWORD bufsize; MIDIOPENDESC midiDesc; WORD wFlags; LPMIDIHDR lpQueueHdr; DWORD dwTotalPlayed; void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */ MIDIOUTCAPSA caps; snd_seq_addr_t addr;} WINE_MIDIOUT;static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];/* this is the total number of MIDI out devices found (synth and port) */static int MODM_NumDevs = 0;/* this is the total number of MIDI out devices found */static int MIDM_NumDevs = 0;static snd_seq_t* midiSeq = NULL;static int numOpenMidiSeq = 0;static UINT midiInTimerID = 0;static int numStartedMidiIn = 0;int port_in;int port_out;/*======================================================================* * Low level MIDI implementation * *======================================================================*/static int midiOpenSeq(int);static int midiCloseSeq(void);#if 0 /* Debug Purpose */static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...){ va_list arg; if (err == ENOENT) return; va_start(arg, fmt); fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function); vfprintf(stderr, fmt, arg); if (err) fprintf(stderr, ": %s", snd_strerror(err)); putc('\n', stderr); va_end(arg);}#endif/************************************************************************** * MIDI_unixToWindowsDeviceType [internal] * * return the Windows equivalent to a Unix Device Type * */static int MIDI_AlsaToWindowsDeviceType(int type){ /* MOD_MIDIPORT output port * MOD_SYNTH generic internal synth * MOD_SQSYNTH square wave internal synth * MOD_FMSYNTH FM internal synth * MOD_MAPPER MIDI mapper * MOD_WAVETABLE hardware watetable internal synth * MOD_SWSYNTH software internal synth */ /* FIXME Is this really the correct equivalence from ALSA to Windows Sound type */ if (type & SND_SEQ_PORT_TYPE_SYNTH) return MOD_FMSYNTH; if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE)) return MOD_SYNTH; if (type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) return MOD_MIDIPORT; ERR("Cannot determine the type of this midi device. Assuming FM Synth\n"); return MOD_FMSYNTH;}/************************************************************************** * MIDI_NotifyClient [internal] */static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1, DWORD dwParam2){ DWORD dwCallBack; UINT uFlags; HANDLE hDev; DWORD dwInstance; TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n", wDevID, wMsg, dwParam1, dwParam2); switch (wMsg) { case MOM_OPEN: case MOM_CLOSE: case MOM_DONE: if (wDevID > MODM_NumDevs) return MMSYSERR_BADDEVICEID; dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback; uFlags = MidiOutDev[wDevID].wFlags; hDev = MidiOutDev[wDevID].midiDesc.hMidi; dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance; break; case MIM_OPEN: case MIM_CLOSE: case MIM_DATA: case MIM_ERROR: if (wDevID > MIDM_NumDevs) return MMSYSERR_BADDEVICEID; dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback; uFlags = MidiInDev[wDevID].wFlags; hDev = MidiInDev[wDevID].midiDesc.hMidi; dwInstance = MidiInDev[wDevID].midiDesc.dwInstance; break; default: WARN("Unsupported MSW-MIDI message %u\n", wMsg); return MMSYSERR_ERROR; } return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ? 0 : MMSYSERR_ERROR;}static int midi_warn = 1;/************************************************************************** * midiOpenSeq [internal] */static int midiOpenSeq(int create_client){ if (numOpenMidiSeq == 0) { if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { if (midi_warn) { WARN("Error opening ALSA sequencer.\n"); } midi_warn = 0; return -1; } if (create_client) { /* Setting the client name is the only init to do */ snd_seq_set_client_name(midiSeq, "WINE midi driver");#if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */ port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE, SND_SEQ_PORT_TYPE_APPLICATION); if (port_out < 0) TRACE("Unable to create output port\n"); else TRACE("Outport port created successfully (%d)\n", port_out);#else port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_APPLICATION); if (port_out < 0) TRACE("Unable to create output port\n"); else TRACE("Outport port created successfully (%d)\n", port_out); port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE, SND_SEQ_PORT_TYPE_APPLICATION); if (port_in < 0) TRACE("Unable to create input port\n"); else TRACE("Input port created successfully (%d)\n", port_in);#endif } } numOpenMidiSeq++; return 0;}/************************************************************************** * midiCloseSeq [internal] */static int midiCloseSeq(void){ if (--numOpenMidiSeq == 0) { snd_seq_delete_simple_port(midiSeq, port_out); snd_seq_delete_simple_port(midiSeq, port_in); snd_seq_close(midiSeq); midiSeq = NULL; } return 0;}static VOID WINAPI midTimeCallback(HWND hwnd, UINT msg, UINT id, DWORD dwTime){ TRACE("(%p, %d, %d, %lu)\n", hwnd, msg, id, dwTime); while(snd_seq_event_input_pending(midiSeq, 0) > 0) { snd_seq_event_t* ev; TRACE("An event is pending\n"); snd_seq_event_input(midiSeq, &ev); TRACE("Event received, type = %d\n", ev->type); snd_seq_free_event(ev); }}/************************************************************************** * midGetDevCaps [internal] */static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSA lpCaps, DWORD dwSize){ TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize); if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID; if (lpCaps == NULL) return MMSYSERR_INVALPARAM; memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps))); return MMSYSERR_NOERROR;}/************************************************************************** * midOpen [internal] */static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags){ TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags); if (lpDesc == NULL) { WARN("Invalid Parameter !\n"); return MMSYSERR_INVALPARAM; } /* FIXME : * how to check that content of lpDesc is correct ? */ if (wDevID >= MIDM_NumDevs) { WARN("wDevID too large (%u) !\n", wDevID); return MMSYSERR_BADDEVICEID; } if (MidiInDev[wDevID].state == -1) { WARN("device disabled\n"); return MIDIERR_NODEVICE; } if (MidiInDev[wDevID].midiDesc.hMidi != 0) { WARN("device already open !\n"); return MMSYSERR_ALLOCATED; } if ((dwFlags & MIDI_IO_STATUS) != 0) { WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n"); dwFlags &= ~MIDI_IO_STATUS; } if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) { FIXME("Bad dwFlags\n"); return MMSYSERR_INVALFLAG; } if (midiOpenSeq(1) < 0) { return MMSYSERR_ERROR; } /* Connect our app port to the device port */ if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0) return MMSYSERR_NOTENABLED; TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port); if (numStartedMidiIn++ == 0) { midiInTimerID = SetTimer(0, 0, 250, midTimeCallback); if (!midiInTimerID) { numStartedMidiIn = 0; WARN("Couldn't start timer for midi-in\n"); midiCloseSeq(); return MMSYSERR_ERROR; } TRACE("Starting timer (%u) for midi-in\n", midiInTimerID); } MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); MidiInDev[wDevID].lpQueueHdr = NULL; MidiInDev[wDevID].dwTotalPlayed = 0; MidiInDev[wDevID].bufsize = 0x3FFF; MidiInDev[wDevID].midiDesc = *lpDesc; MidiInDev[wDevID].state = 0; MidiInDev[wDevID].incLen = 0; MidiInDev[wDevID].startTime = 0; if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) { WARN("can't notify client !\n"); return MMSYSERR_INVALPARAM; } return MMSYSERR_NOERROR;}/************************************************************************** * midClose [internal] */static DWORD midClose(WORD wDevID){ int ret = MMSYSERR_NOERROR; TRACE("(%04X);\n", wDevID); if (wDevID >= MIDM_NumDevs) { WARN("wDevID too big (%u) !\n", wDevID); return MMSYSERR_BADDEVICEID; } if (MidiInDev[wDevID].midiDesc.hMidi == 0) { WARN("device not opened !\n"); return MMSYSERR_ERROR; } if (MidiInDev[wDevID].lpQueueHdr != 0) { return MIDIERR_STILLPLAYING; } if (midiSeq == NULL) { WARN("ooops !\n"); return MMSYSERR_ERROR; } if (--numStartedMidiIn == 0) { TRACE("Stopping timer for midi-in\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -