📄 midi.c
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- *//* * Sample MIDI Wine Driver for Open Sound System (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 * * 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 */#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>#ifdef HAVE_SYS_IOCTL_H# include <sys/ioctl.h>#endif#include "windef.h"#include "winbase.h"#include "wingdi.h"#include "winuser.h"#include "mmddk.h"#include "oss.h"#include "wine/debug.h"WINE_DEFAULT_DEBUG_CHANNEL(midi);#ifdef HAVE_OSS_MIDI#define MIDI_SEQ "/dev/sequencer"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;} 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;} 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 number of FM synthetizers (index from 0 to NUMFMSYNTHDEVS - 1) */static int MODM_NumFMSynthDevs = 0;/* the Midi ports have index from NUMFMSYNTHDEVS to NumDevs - 1 *//* this is the total number of MIDI out devices found */static int MIDM_NumDevs = 0;static int midiSeqFD = -1;static int numOpenMidiSeq = 0;static UINT midiInTimerID = 0;static int numStartedMidiIn = 0;/*======================================================================* * Low level MIDI implementation * *======================================================================*/static int midiOpenSeq(void);static int midiCloseSeq(void);/************************************************************************** * MIDI_unixToWindowsDeviceType [internal] * * return the Windows equivalent to a Unix Device Type * */static int MIDI_UnixToWindowsDeviceType(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 UNIX to Windows Sound type */ switch (type) { case SYNTH_TYPE_FM: return MOD_FMSYNTH; case SYNTH_TYPE_SAMPLE: return MOD_SYNTH; case SYNTH_TYPE_MIDI: return MOD_MIDIPORT; default: ERR("Cannot determine the type of this midi device. " "Assuming FM Synth\n"); return MOD_FMSYNTH; } return MOD_FMSYNTH;}/************************************************************************** * OSS_MidiInit [internal] * * Initializes the MIDI devices information variables */BOOL OSS_MidiInit(void){ int i, status, numsynthdevs = 255, nummididevs = 255; struct synth_info sinfo; struct midi_info minfo; static BOOL bInitDone = FALSE; if (bInitDone) return TRUE; TRACE("Initializing the MIDI variables.\n"); bInitDone = TRUE; /* try to open device */ if (midiOpenSeq() == -1) { return TRUE; } /* find how many Synth devices are there in the system */ status = ioctl(midiSeqFD, SNDCTL_SEQ_NRSYNTHS, &numsynthdevs); if (status == -1) { ERR("ioctl for nr synth failed.\n"); midiCloseSeq(); return TRUE; } if (numsynthdevs > MAX_MIDIOUTDRV) { ERR("MAX_MIDIOUTDRV (%d) was enough for the number of devices (%d). " "Some FM devices will not be available.\n",MAX_MIDIOUTDRV,numsynthdevs); numsynthdevs = MAX_MIDIOUTDRV; } for (i = 0; i < numsynthdevs; i++) { /* Manufac ID. We do not have access to this with soundcard.h * Does not seem to be a problem, because in mmsystem.h only * Microsoft's ID is listed. */ MidiOutDev[i].caps.wMid = 0x00FF; MidiOutDev[i].caps.wPid = 0x0001; /* FIXME Product ID */ /* Product Version. We simply say "1" */ MidiOutDev[i].caps.vDriverVersion = 0x001; MidiOutDev[i].caps.wChannelMask = 0xFFFF; /* FIXME Do we have this information? * Assuming the soundcards can handle * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but * not MIDICAPS_CACHE. */ MidiOutDev[i].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME; sinfo.device = i; status = ioctl(midiSeqFD, SNDCTL_SYNTH_INFO, &sinfo); if (status == -1) { ERR("ioctl for synth info failed on %d, disabling it.\n", i); sprintf(MidiOutDev[i].caps.szPname, "Wine OSS Midi Out (#%d) - disabled", i); MidiOutDev[i].caps.wTechnology = MOD_MIDIPORT; MidiOutDev[i].caps.wVoices = 16; MidiOutDev[i].caps.wNotes = 16; MidiOutDev[i].bEnabled = FALSE; } else { strcpy(MidiOutDev[i].caps.szPname, sinfo.name); MidiOutDev[i].caps.wTechnology = MIDI_UnixToWindowsDeviceType(sinfo.synth_type); MidiOutDev[i].caps.wVoices = sinfo.nr_voices; /* FIXME Is it possible to know the maximum * number of simultaneous notes of a soundcard ? * I believe we don't have this information, but * it's probably equal or more than wVoices */ MidiOutDev[i].caps.wNotes = sinfo.nr_voices; MidiOutDev[i].bEnabled = TRUE; } /* We also have the information sinfo.synth_subtype, not used here */ if (sinfo.capabilities & SYNTH_CAP_INPUT) { FIXME("Synthesizer support MIDI in. Not supported yet (please report)\n"); } TRACE("SynthOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%ld\n" "\tOSS info: synth subtype=%d capa=%lx\n", i, MidiOutDev[i].caps.szPname, MidiOutDev[i].caps.wTechnology, MidiOutDev[i].caps.wVoices, MidiOutDev[i].caps.wNotes, MidiOutDev[i].caps.wChannelMask, MidiOutDev[i].caps.dwSupport, sinfo.synth_subtype, (long)sinfo.capabilities); } /* find how many MIDI devices are there in the system */ status = ioctl(midiSeqFD, SNDCTL_SEQ_NRMIDIS, &nummididevs); if (status == -1) { ERR("ioctl on nr midi failed.\n"); nummididevs = 0; goto wrapup; } /* FIXME: the two restrictions below could be loosen in some cases */ if (numsynthdevs + nummididevs > MAX_MIDIOUTDRV) { ERR("MAX_MIDIOUTDRV was not enough for the number of devices. " "Some MIDI devices will not be available.\n"); nummididevs = MAX_MIDIOUTDRV - numsynthdevs; } if (nummididevs > MAX_MIDIINDRV) { ERR("MAX_MIDIINDRV (%d) was not enough for the number of devices (%d). " "Some MIDI devices will not be available.\n",MAX_MIDIINDRV,nummididevs); nummididevs = MAX_MIDIINDRV; } for (i = 0; i < nummididevs; i++) { minfo.device = i; status = ioctl(midiSeqFD, SNDCTL_MIDI_INFO, &minfo); if (status == -1) WARN("ioctl on midi info for device %d failed.\n", i); /* This whole part is somewhat obscure to me. I'll keep trying to dig info about it. If you happen to know, please tell us. The very descritive minfo.dev_type was not used here. */ /* Manufac ID. We do not have access to this with soundcard.h Does not seem to be a problem, because in mmsystem.h only Microsoft's ID is listed */ MidiOutDev[numsynthdevs + i].caps.wMid = 0x00FF; MidiOutDev[numsynthdevs + i].caps.wPid = 0x0001; /* FIXME Product ID */ /* Product Version. We simply say "1" */ MidiOutDev[numsynthdevs + i].caps.vDriverVersion = 0x001; if (status == -1) { sprintf(MidiOutDev[numsynthdevs + i].caps.szPname, "Wine OSS Midi Out (#%d) - disabled", numsynthdevs + i); MidiOutDev[numsynthdevs + i].bEnabled = FALSE; } else { strcpy(MidiOutDev[numsynthdevs + i].caps.szPname, minfo.name); MidiOutDev[numsynthdevs + i].bEnabled = TRUE; } MidiOutDev[numsynthdevs + i].caps.wTechnology = MOD_MIDIPORT; /* FIXME Is this right? */ /* Does it make any difference? */ MidiOutDev[numsynthdevs + i].caps.wVoices = 16; /* Does it make any difference? */ MidiOutDev[numsynthdevs + i].caps.wNotes = 16; MidiOutDev[numsynthdevs + i].caps.wChannelMask= 0xFFFF; /* FIXME Does it make any difference? */ MidiOutDev[numsynthdevs + i].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME; /* This whole part is somewhat obscure to me. I'll keep trying to dig info about it. If you happen to know, please tell us. The very descritive minfo.dev_type was not used here. */ /* Manufac ID. We do not have access to this with soundcard.h Does not seem to be a problem, because in mmsystem.h only Microsoft's ID is listed */ MidiInDev[i].caps.wMid = 0x00FF; MidiInDev[i].caps.wPid = 0x0001; /* FIXME Product ID */ /* Product Version. We simply say "1" */ MidiInDev[i].caps.vDriverVersion = 0x001; if (status == -1) { sprintf(MidiInDev[i].caps.szPname, "Wine OSS Midi In (#%d) - disabled", numsynthdevs + i); MidiInDev[i].state = -1; } else { strcpy(MidiInDev[i].caps.szPname, minfo.name); MidiInDev[i].state = 0; } /* FIXME : could we get better information than that ? */ MidiInDev[i].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME; TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%ld\n" "MidiIn [%d]\tname='%s' support=%ld\n" "\tOSS info: midi dev-type=%d, capa=%lx\n", i, MidiOutDev[numsynthdevs + i].caps.szPname, MidiOutDev[numsynthdevs + i].caps.wTechnology, MidiOutDev[numsynthdevs + i].caps.wVoices, MidiOutDev[numsynthdevs + i].caps.wNotes, MidiOutDev[numsynthdevs + i].caps.wChannelMask, MidiOutDev[numsynthdevs + i].caps.dwSupport, i, MidiInDev[i].caps.szPname, MidiInDev[i].caps.dwSupport, minfo.dev_type, (long)minfo.capabilities); } wrapup: /* windows does not seem to differentiate Synth from MIDI devices */ MODM_NumFMSynthDevs = numsynthdevs; MODM_NumDevs = numsynthdevs + nummididevs; MIDM_NumDevs = nummididevs; /* close file and exit */ midiCloseSeq(); return TRUE;}/************************************************************************** * 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(void){ if (numOpenMidiSeq == 0) { midiSeqFD = open(MIDI_SEQ, O_RDWR, 0); if (midiSeqFD == -1) { if (midi_warn) { WARN("Can't open MIDI device '%s' ! (%s). If your " "program needs this (probably not): %s\n", MIDI_SEQ, strerror(errno), errno == ENOENT ? "create it ! (\"man MAKEDEV\" ?)" : errno == ENODEV ? "load MIDI sequencer kernel driver !" : errno == EACCES ? "grant access ! (\"man chmod\")" : "" ); } midi_warn = 0; return -1; } if (fcntl(midiSeqFD, F_SETFL, O_NONBLOCK) < 0) { WARN("can't set sequencer fd to non-blocking, errno %d (%s)\n", errno, strerror(errno)); close(midiSeqFD); midiSeqFD = -1; return -1; } fcntl(midiSeqFD, F_SETFD, 1); /* set close on exec flag */ ioctl(midiSeqFD, SNDCTL_SEQ_RESET); } numOpenMidiSeq++; return 0;}/**************************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -