1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
4 * Sample MIDI Wine Driver for Open Sound System (basically Linux)
6 * Copyright 1994 Martin Ayotte
7 * Copyright 1998 Luiz Otavio L. Zorzella (init procedures)
8 * Copyright 1998/1999 Eric POUECH :
9 * 98/7 changes for making this MIDI driver work on OSS
10 * current support is limited to MIDI ports of OSS systems
11 * 98/9 rewriting MCI code for MIDI
12 * 98/11 splitted in midi.c and mcimidi.c
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 * + use better instrument definition for OPL/2 (midiPatch.c) or
31 * use existing instrument definition (from playmidi or kmid)
32 * with a .winerc option
33 * + have a look at OPL/3 ?
34 * + implement asynchronous playback of MidiHdr
35 * + implement STREAM'ed MidiHdr (question: how shall we share the
36 * code between the midiStream functions in MMSYSTEM/WINMM and
37 * the code for the low level driver)
38 * + use a more accurate read mechanism than the one of snooping on
39 * timers (like select on fd)
52 #ifdef HAVE_SYS_IOCTL_H
53 # include <sys/ioctl.h>
55 #ifdef HAVE_SYS_POLL_H
66 #include "wine/unicode.h"
67 #include "wine/debug.h"
69 WINE_DEFAULT_DEBUG_CHANNEL(midi);
73 #define MIDI_SEQ "/dev/sequencer"
76 int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
78 MIDIOPENDESC midiDesc;
82 unsigned char incoming[3];
83 unsigned char incPrev;
92 MIDIOPENDESC midiDesc;
96 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
100 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
101 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
103 /* this is the total number of MIDI out devices found (synth and port) */
104 static int MODM_NumDevs = 0;
105 /* this is the number of FM synthetizers (index from 0 to NUMFMSYNTHDEVS - 1) */
106 static int MODM_NumFMSynthDevs = 0;
107 /* the Midi ports have index from NUMFMSYNTHDEVS to NumDevs - 1 */
109 /* this is the total number of MIDI out devices found */
110 static int MIDM_NumDevs = 0;
112 static int midiSeqFD = -1;
113 static int numOpenMidiSeq = 0;
114 static int numStartedMidiIn = 0;
116 static CRITICAL_SECTION crit_sect; /* protects all MidiIn buffers queues */
117 static CRITICAL_SECTION_DEBUG critsect_debug =
120 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
121 0, 0, { 0, (DWORD)(__FILE__ ": crit_sect") }
123 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
125 static int end_thread;
126 static HANDLE hThread;
128 /*======================================================================*
129 * Low level MIDI implementation *
130 *======================================================================*/
132 static int midiOpenSeq(void);
133 static int midiCloseSeq(void);
135 /**************************************************************************
136 * MIDI_unixToWindowsDeviceType [internal]
138 * return the Windows equivalent to a Unix Device Type
141 static int MIDI_UnixToWindowsDeviceType(int type)
143 /* MOD_MIDIPORT output port
144 * MOD_SYNTH generic internal synth
145 * MOD_SQSYNTH square wave internal synth
146 * MOD_FMSYNTH FM internal synth
147 * MOD_MAPPER MIDI mapper
148 * MOD_WAVETABLE hardware watetable internal synth
149 * MOD_SWSYNTH software internal synth
152 /* FIXME Is this really the correct equivalence from UNIX to
153 Windows Sound type */
156 case SYNTH_TYPE_FM: return MOD_FMSYNTH;
157 case SYNTH_TYPE_SAMPLE: return MOD_SYNTH;
158 case SYNTH_TYPE_MIDI: return MOD_MIDIPORT;
160 ERR("Cannot determine the type of this midi device. "
161 "Assuming FM Synth\n");
166 /**************************************************************************
167 * OSS_MidiInit [internal]
169 * Initializes the MIDI devices information variables
171 BOOL OSS_MidiInit(void)
173 int i, status, numsynthdevs = 255, nummididevs = 255;
174 struct synth_info sinfo;
175 struct midi_info minfo;
176 static BOOL bInitDone = FALSE;
181 TRACE("Initializing the MIDI variables.\n");
184 /* try to open device */
185 if (midiOpenSeq() == -1) {
189 /* find how many Synth devices are there in the system */
190 status = ioctl(midiSeqFD, SNDCTL_SEQ_NRSYNTHS, &numsynthdevs);
193 ERR("ioctl for nr synth failed.\n");
198 if (numsynthdevs > MAX_MIDIOUTDRV) {
199 ERR("MAX_MIDIOUTDRV (%d) was enough for the number of devices (%d). "
200 "Some FM devices will not be available.\n",MAX_MIDIOUTDRV,numsynthdevs);
201 numsynthdevs = MAX_MIDIOUTDRV;
204 for (i = 0; i < numsynthdevs; i++) {
205 /* Manufac ID. We do not have access to this with soundcard.h
206 * Does not seem to be a problem, because in mmsystem.h only
207 * Microsoft's ID is listed.
209 MidiOutDev[i].caps.wMid = 0x00FF;
210 MidiOutDev[i].caps.wPid = 0x0001; /* FIXME Product ID */
211 /* Product Version. We simply say "1" */
212 MidiOutDev[i].caps.vDriverVersion = 0x001;
213 MidiOutDev[i].caps.wChannelMask = 0xFFFF;
215 /* FIXME Do we have this information?
216 * Assuming the soundcards can handle
217 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
218 * not MIDICAPS_CACHE.
220 MidiOutDev[i].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
223 status = ioctl(midiSeqFD, SNDCTL_SYNTH_INFO, &sinfo);
225 static const WCHAR fmt[] = {'W','i','n','e',' ','O','S','S',' ','M','i','d','i',' ','O','u','t',' ','(','#','%','d',')',' ','-',' ','d','i','s','a','b','l','e','d',0};
226 ERR("ioctl for synth info failed on %d, disabling it.\n", i);
228 wsprintfW(MidiOutDev[i].caps.szPname, fmt, i);
230 MidiOutDev[i].caps.wTechnology = MOD_MIDIPORT;
231 MidiOutDev[i].caps.wVoices = 16;
232 MidiOutDev[i].caps.wNotes = 16;
233 MidiOutDev[i].bEnabled = FALSE;
235 MultiByteToWideChar( CP_ACP, 0, sinfo.name, -1,
236 MidiOutDev[i].caps.szPname,
237 sizeof(MidiOutDev[i].caps.szPname)/sizeof(WCHAR) );
239 MidiOutDev[i].caps.wTechnology = MIDI_UnixToWindowsDeviceType(sinfo.synth_type);
240 MidiOutDev[i].caps.wVoices = sinfo.nr_voices;
242 /* FIXME Is it possible to know the maximum
243 * number of simultaneous notes of a soundcard ?
244 * I believe we don't have this information, but
245 * it's probably equal or more than wVoices
247 MidiOutDev[i].caps.wNotes = sinfo.nr_voices;
248 MidiOutDev[i].bEnabled = TRUE;
251 /* We also have the information sinfo.synth_subtype, not used here
254 if (sinfo.capabilities & SYNTH_CAP_INPUT) {
255 FIXME("Synthesizer support MIDI in. Not supported yet (please report)\n");
258 TRACE("SynthOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%ld\n"
259 "\tOSS info: synth subtype=%d capa=%lx\n",
260 i, wine_dbgstr_w(MidiOutDev[i].caps.szPname),
261 MidiOutDev[i].caps.wTechnology,
262 MidiOutDev[i].caps.wVoices, MidiOutDev[i].caps.wNotes,
263 MidiOutDev[i].caps.wChannelMask, MidiOutDev[i].caps.dwSupport,
264 sinfo.synth_subtype, (long)sinfo.capabilities);
267 /* find how many MIDI devices are there in the system */
268 status = ioctl(midiSeqFD, SNDCTL_SEQ_NRMIDIS, &nummididevs);
270 ERR("ioctl on nr midi failed.\n");
275 /* FIXME: the two restrictions below could be loosen in some cases */
276 if (numsynthdevs + nummididevs > MAX_MIDIOUTDRV) {
277 ERR("MAX_MIDIOUTDRV was not enough for the number of devices. "
278 "Some MIDI devices will not be available.\n");
279 nummididevs = MAX_MIDIOUTDRV - numsynthdevs;
282 if (nummididevs > MAX_MIDIINDRV) {
283 ERR("MAX_MIDIINDRV (%d) was not enough for the number of devices (%d). "
284 "Some MIDI devices will not be available.\n",MAX_MIDIINDRV,nummididevs);
285 nummididevs = MAX_MIDIINDRV;
288 for (i = 0; i < nummididevs; i++) {
290 status = ioctl(midiSeqFD, SNDCTL_MIDI_INFO, &minfo);
291 if (status == -1) WARN("ioctl on midi info for device %d failed.\n", i);
293 /* This whole part is somewhat obscure to me. I'll keep trying to dig
294 info about it. If you happen to know, please tell us. The very
295 descritive minfo.dev_type was not used here.
297 /* Manufac ID. We do not have access to this with soundcard.h
298 Does not seem to be a problem, because in mmsystem.h only
299 Microsoft's ID is listed */
300 MidiOutDev[numsynthdevs + i].caps.wMid = 0x00FF;
301 MidiOutDev[numsynthdevs + i].caps.wPid = 0x0001; /* FIXME Product ID */
302 /* Product Version. We simply say "1" */
303 MidiOutDev[numsynthdevs + i].caps.vDriverVersion = 0x001;
305 static const WCHAR fmt[] = {'W','i','n','e',' ','O','S','S',' ','M','i','d','i',' ','O','u','t',' ','(','#','%','d',')',' ','-',' ','d','i','s','a','b','l','e','d',0};
306 wsprintfW(MidiOutDev[numsynthdevs + i].caps.szPname, fmt, numsynthdevs + i);
307 MidiOutDev[numsynthdevs + i].bEnabled = FALSE;
309 MultiByteToWideChar(CP_ACP, 0, minfo.name, -1,
310 MidiOutDev[numsynthdevs + i].caps.szPname,
311 sizeof(MidiOutDev[numsynthdevs + i].caps.szPname) / sizeof(WCHAR));
312 MidiOutDev[numsynthdevs + i].bEnabled = TRUE;
314 MidiOutDev[numsynthdevs + i].caps.wTechnology = MOD_MIDIPORT; /* FIXME Is this right? */
315 /* Does it make any difference? */
316 MidiOutDev[numsynthdevs + i].caps.wVoices = 16;
317 /* Does it make any difference? */
318 MidiOutDev[numsynthdevs + i].caps.wNotes = 16;
319 MidiOutDev[numsynthdevs + i].caps.wChannelMask= 0xFFFF;
321 /* FIXME Does it make any difference? */
322 MidiOutDev[numsynthdevs + i].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
324 /* This whole part is somewhat obscure to me. I'll keep trying to dig
325 info about it. If you happen to know, please tell us. The very
326 descritive minfo.dev_type was not used here.
328 /* Manufac ID. We do not have access to this with soundcard.h
329 Does not seem to be a problem, because in mmsystem.h only
330 Microsoft's ID is listed */
331 MidiInDev[i].caps.wMid = 0x00FF;
332 MidiInDev[i].caps.wPid = 0x0001; /* FIXME Product ID */
333 /* Product Version. We simply say "1" */
334 MidiInDev[i].caps.vDriverVersion = 0x001;
336 static const WCHAR fmt[] = {'W','i','n','e',' ','O','S','S',' ','M','i','d','i',' ','I','n',' ','(','#','%','d',')',' ','-',' ','d','i','s','a','b','l','e','d',0};
337 wsprintfW(MidiInDev[i].caps.szPname, fmt, numsynthdevs + i);
338 MidiInDev[i].state = -1;
340 MultiByteToWideChar(CP_ACP, 0, minfo.name, -1,
341 MidiInDev[i].caps.szPname,
342 sizeof(MidiInDev[i].caps.szPname) / sizeof(WCHAR));
343 MidiInDev[i].state = 0;
345 /* FIXME : could we get better information than that ? */
346 MidiInDev[i].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
348 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%ld\n"
349 "MidiIn [%d]\tname='%s' support=%ld\n"
350 "\tOSS info: midi dev-type=%d, capa=%lx\n",
351 i, wine_dbgstr_w(MidiOutDev[numsynthdevs + i].caps.szPname),
352 MidiOutDev[numsynthdevs + i].caps.wTechnology,
353 MidiOutDev[numsynthdevs + i].caps.wVoices, MidiOutDev[numsynthdevs + i].caps.wNotes,
354 MidiOutDev[numsynthdevs + i].caps.wChannelMask, MidiOutDev[numsynthdevs + i].caps.dwSupport,
355 i, wine_dbgstr_w(MidiInDev[i].caps.szPname), MidiInDev[i].caps.dwSupport,
356 minfo.dev_type, (long)minfo.capabilities);
360 /* windows does not seem to differentiate Synth from MIDI devices */
361 MODM_NumFMSynthDevs = numsynthdevs;
362 MODM_NumDevs = numsynthdevs + nummididevs;
364 MIDM_NumDevs = nummididevs;
366 /* close file and exit */
372 /**************************************************************************
373 * MIDI_NotifyClient [internal]
375 static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg,
376 DWORD dwParam1, DWORD dwParam2)
383 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
384 wDevID, wMsg, dwParam1, dwParam2);
391 if (wDevID > MODM_NumDevs)
392 return MMSYSERR_BADDEVICEID;
394 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
395 uFlags = MidiOutDev[wDevID].wFlags;
396 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
397 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
407 if (wDevID > MIDM_NumDevs)
408 return MMSYSERR_BADDEVICEID;
410 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
411 uFlags = MidiInDev[wDevID].wFlags;
412 hDev = MidiInDev[wDevID].midiDesc.hMidi;
413 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
416 WARN("Unsupported MSW-MIDI message %u\n", wMsg);
417 return MMSYSERR_ERROR;
420 return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
424 static int midi_warn = 1;
425 /**************************************************************************
426 * midiOpenSeq [internal]
428 static int midiOpenSeq(void)
430 if (numOpenMidiSeq == 0) {
431 midiSeqFD = open(MIDI_SEQ, O_RDWR, 0);
432 if (midiSeqFD == -1) {
435 WARN("Can't open MIDI device '%s' ! (%s). If your "
436 "program needs this (probably not): %s\n",
437 MIDI_SEQ, strerror(errno),
439 "create it ! (\"man MAKEDEV\" ?)" :
441 "load MIDI sequencer kernel driver !" :
443 "grant access ! (\"man chmod\")" : ""
450 if (fcntl(midiSeqFD, F_SETFL, O_NONBLOCK) < 0) {
451 WARN("can't set sequencer fd to non-blocking, errno %d (%s)\n", errno, strerror(errno));
457 fcntl(midiSeqFD, F_SETFD, 1); /* set close on exec flag */
458 ioctl(midiSeqFD, SNDCTL_SEQ_RESET);
464 /**************************************************************************
465 * midiCloseSeq [internal]
467 static int midiCloseSeq(void)
469 if (--numOpenMidiSeq == 0) {
476 /* FIXME: this is a bad idea, it's even not static... */
479 /* FIXME: this is not reentrant, not static - because of global variable
482 /**************************************************************************
483 * seqbuf_dump [internal]
485 * Used by SEQ_DUMPBUF to flush the buffer.
488 void seqbuf_dump(void)
491 if (write(midiSeqFD, _seqbuf, _seqbufptr) == -1) {
492 WARN("Can't write data to sequencer %d, errno %d (%s)!\n",
493 midiSeqFD, errno, strerror(errno));
496 * in any case buffer is lost so that if many errors occur the buffer
503 /**************************************************************************
504 * midReceiveChar [internal]
506 static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
510 TRACE("Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen);
512 if (wDevID >= MIDM_NumDevs) {
516 if (MidiInDev[wDevID].state <= 0) {
517 TRACE("disabled or input not started, thrown away\n");
521 if (MidiInDev[wDevID].state & 2) { /* system exclusive */
525 EnterCriticalSection(&crit_sect);
526 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
527 LPBYTE lpData = lpMidiHdr->lpData;
529 lpData[lpMidiHdr->dwBytesRecorded++] = value;
530 if (lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) {
534 if (value == 0xF7) { /* then end */
535 MidiInDev[wDevID].state &= ~2;
538 if (sbfb && lpMidiHdr != NULL) {
539 lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
540 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
541 lpMidiHdr->dwFlags |= MHDR_DONE;
542 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
543 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)lpMidiHdr, dwTime) != MMSYSERR_NOERROR) {
544 WARN("Couldn't notify client\n");
547 LeaveCriticalSection(&crit_sect);
551 #define IS_CMD(_x) (((_x) & 0x80) == 0x80)
552 #define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
554 if (!IS_CMD(value) && MidiInDev[wDevID].incLen == 0) { /* try to reuse old cmd */
555 if (IS_CMD(MidiInDev[wDevID].incPrev) && !IS_SYS_CMD(MidiInDev[wDevID].incPrev)) {
556 MidiInDev[wDevID].incoming[0] = MidiInDev[wDevID].incPrev;
557 MidiInDev[wDevID].incLen = 1;
558 TRACE("Reusing old command %02xh\n", MidiInDev[wDevID].incPrev);
560 FIXME("error for midi-in, should generate MIM_ERROR notification:"
561 " prev=%02Xh, incLen=%02Xh\n",
562 MidiInDev[wDevID].incPrev, MidiInDev[wDevID].incLen);
566 MidiInDev[wDevID].incoming[(int)(MidiInDev[wDevID].incLen++)] = value;
567 if (MidiInDev[wDevID].incLen == 1 && !IS_SYS_CMD(MidiInDev[wDevID].incoming[0])) {
568 /* store new cmd, just in case */
569 MidiInDev[wDevID].incPrev = MidiInDev[wDevID].incoming[0];
575 switch (MidiInDev[wDevID].incoming[0] & 0xF0) {
578 case MIDI_KEY_PRESSURE:
579 case MIDI_CTL_CHANGE:
580 case MIDI_PITCH_BEND:
581 if (MidiInDev[wDevID].incLen == 3) {
582 toSend = (MidiInDev[wDevID].incoming[2] << 16) |
583 (MidiInDev[wDevID].incoming[1] << 8) |
584 (MidiInDev[wDevID].incoming[0] << 0);
587 case MIDI_PGM_CHANGE:
588 case MIDI_CHN_PRESSURE:
589 if (MidiInDev[wDevID].incLen == 2) {
590 toSend = (MidiInDev[wDevID].incoming[1] << 8) |
591 (MidiInDev[wDevID].incoming[0] << 0);
594 case MIDI_SYSTEM_PREFIX:
595 if (MidiInDev[wDevID].incoming[0] == 0xF0) {
596 MidiInDev[wDevID].state |= 2;
597 MidiInDev[wDevID].incLen = 0;
599 if (MidiInDev[wDevID].incLen == 1) {
600 toSend = (MidiInDev[wDevID].incoming[0] << 0);
605 WARN("This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]);
608 TRACE("Sending event %08lx\n", toSend);
609 MidiInDev[wDevID].incLen = 0;
610 dwTime -= MidiInDev[wDevID].startTime;
611 if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
612 WARN("Couldn't notify client\n");
617 static DWORD WINAPI midRecThread(LPVOID arg)
619 unsigned char buffer[256];
624 TRACE("Thread startup\n");
630 TRACE("Thread loop\n");
632 /* Check if a event is present */
633 if (poll(&pfd, 1, 250) <= 0)
636 len = read(midiSeqFD, buffer, sizeof(buffer));
637 TRACE("Reveived %d bytes\n", len);
639 if (len < 0) continue;
640 if ((len % 4) != 0) {
641 WARN("Bad length %d, errno %d (%s)\n", len, errno, strerror(errno));
645 dwTime = GetTickCount();
647 for (idx = 0; idx < len; ) {
648 if (buffer[idx] & 0x80) {
650 "Reading<8> %02x %02x %02x %02x %02x %02x %02x %02x\n",
651 buffer[idx + 0], buffer[idx + 1],
652 buffer[idx + 2], buffer[idx + 3],
653 buffer[idx + 4], buffer[idx + 5],
654 buffer[idx + 6], buffer[idx + 7]);
657 switch (buffer[idx + 0]) {
662 midReceiveChar(buffer[idx + 2], buffer[idx + 1], dwTime);
665 TRACE("Unsupported event %d\n", buffer[idx + 0]);
675 /**************************************************************************
676 * midGetDevCaps [internal]
678 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
680 TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
682 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
683 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
685 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
687 return MMSYSERR_NOERROR;
690 /**************************************************************************
693 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
695 TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
697 if (lpDesc == NULL) {
698 WARN("Invalid Parameter !\n");
699 return MMSYSERR_INVALPARAM;
703 * how to check that content of lpDesc is correct ?
705 if (wDevID >= MIDM_NumDevs) {
706 WARN("wDevID too large (%u) !\n", wDevID);
707 return MMSYSERR_BADDEVICEID;
709 if (MidiInDev[wDevID].state == -1) {
710 WARN("device disabled\n");
711 return MIDIERR_NODEVICE;
713 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
714 WARN("device already open !\n");
715 return MMSYSERR_ALLOCATED;
717 if ((dwFlags & MIDI_IO_STATUS) != 0) {
718 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
719 dwFlags &= ~MIDI_IO_STATUS;
721 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
722 FIXME("Bad dwFlags\n");
723 return MMSYSERR_INVALFLAG;
726 if (midiOpenSeq() < 0) {
727 return MMSYSERR_ERROR;
730 if (numStartedMidiIn++ == 0) {
732 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
734 numStartedMidiIn = 0;
735 WARN("Couldn't create thread for midi-in\n");
737 return MMSYSERR_ERROR;
739 TRACE("Created thread for midi-in\n");
742 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
744 MidiInDev[wDevID].lpQueueHdr = NULL;
745 MidiInDev[wDevID].dwTotalPlayed = 0;
746 MidiInDev[wDevID].bufsize = 0x3FFF;
747 MidiInDev[wDevID].midiDesc = *lpDesc;
748 MidiInDev[wDevID].state = 0;
749 MidiInDev[wDevID].incLen = 0;
750 MidiInDev[wDevID].startTime = 0;
752 if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
753 WARN("can't notify client !\n");
754 return MMSYSERR_INVALPARAM;
756 return MMSYSERR_NOERROR;
759 /**************************************************************************
760 * midClose [internal]
762 static DWORD midClose(WORD wDevID)
764 int ret = MMSYSERR_NOERROR;
766 TRACE("(%04X);\n", wDevID);
768 if (wDevID >= MIDM_NumDevs) {
769 WARN("wDevID too big (%u) !\n", wDevID);
770 return MMSYSERR_BADDEVICEID;
772 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
773 WARN("device not opened !\n");
774 return MMSYSERR_ERROR;
776 if (MidiInDev[wDevID].lpQueueHdr != 0) {
777 return MIDIERR_STILLPLAYING;
780 if (midiSeqFD == -1) {
782 return MMSYSERR_ERROR;
784 if (--numStartedMidiIn == 0) {
785 TRACE("Stopping thread for midi-in\n");
787 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
788 WARN("Thread end not signaled, force termination\n");
789 TerminateThread(hThread, 0);
791 TRACE("Stopped thread for midi-in\n");
795 MidiInDev[wDevID].bufsize = 0;
796 if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
797 WARN("can't notify client !\n");
798 ret = MMSYSERR_INVALPARAM;
800 MidiInDev[wDevID].midiDesc.hMidi = 0;
804 /**************************************************************************
805 * midAddBuffer [internal]
807 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
809 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
811 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
812 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
814 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
815 if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
816 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
817 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
818 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
820 EnterCriticalSection(&crit_sect);
821 if (MidiInDev[wDevID].lpQueueHdr == 0) {
822 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
826 for (ptr = MidiInDev[wDevID].lpQueueHdr;
828 ptr = (LPMIDIHDR)ptr->lpNext);
829 ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
831 LeaveCriticalSection(&crit_sect);
833 return MMSYSERR_NOERROR;
836 /**************************************************************************
837 * midPrepare [internal]
839 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
841 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
843 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
844 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
845 lpMidiHdr->dwBufferLength >= 0x10000ul)
846 return MMSYSERR_INVALPARAM;
848 lpMidiHdr->lpNext = 0;
849 lpMidiHdr->dwFlags |= MHDR_PREPARED;
850 lpMidiHdr->dwBytesRecorded = 0;
852 return MMSYSERR_NOERROR;
855 /**************************************************************************
856 * midUnprepare [internal]
858 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
860 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
862 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
863 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
865 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
866 lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
867 return MMSYSERR_INVALPARAM;
869 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
870 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
872 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
874 return MMSYSERR_NOERROR;
877 /**************************************************************************
878 * midReset [internal]
880 static DWORD midReset(WORD wDevID)
882 DWORD dwTime = GetTickCount();
884 TRACE("(%04X);\n", wDevID);
886 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
887 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
889 EnterCriticalSection(&crit_sect);
890 while (MidiInDev[wDevID].lpQueueHdr) {
891 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
892 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
893 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
894 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
895 (DWORD)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
896 WARN("Couldn't notify client\n");
898 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
900 LeaveCriticalSection(&crit_sect);
902 return MMSYSERR_NOERROR;
906 /**************************************************************************
907 * midStart [internal]
909 static DWORD midStart(WORD wDevID)
911 TRACE("(%04X);\n", wDevID);
913 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
914 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
916 MidiInDev[wDevID].state = 1;
917 MidiInDev[wDevID].startTime = GetTickCount();
918 return MMSYSERR_NOERROR;
921 /**************************************************************************
924 static DWORD midStop(WORD wDevID)
926 TRACE("(%04X);\n", wDevID);
928 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
929 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
931 MidiInDev[wDevID].state = 0;
932 return MMSYSERR_NOERROR;
935 /*-----------------------------------------------------------------------*/
937 typedef struct sVoice {
938 int note; /* 0 means not used */
940 unsigned cntMark : 30,
943 #define sVS_PLAYING 1
944 #define sVS_SUSTAINED 2
947 typedef struct sChannel {
953 int bank; /* CTL_BANK_SELECT */
954 int volume; /* CTL_MAIN_VOLUME */
955 int balance; /* CTL_BALANCE */
956 int expression; /* CTL_EXPRESSION */
957 int sustain; /* CTL_SUSTAIN */
959 unsigned char nrgPmtMSB; /* Non register Parameters */
960 unsigned char nrgPmtLSB;
961 unsigned char regPmtMSB; /* Non register Parameters */
962 unsigned char regPmtLSB;
965 typedef struct sFMextra {
968 sChannel channel[16]; /* MIDI has only 16 channels */
969 sVoice voice[1]; /* dyn allocated according to sound card */
970 /* do not append fields below voice[1] since the size of this structure
971 * depends on the number of available voices on the FM synth...
975 extern unsigned char midiFMInstrumentPatches[16 * 128];
976 extern unsigned char midiFMDrumsPatches [16 * 128];
978 /**************************************************************************
979 * modFMLoad [internal]
981 static int modFMLoad(int dev)
984 struct sbi_instrument sbi;
989 memset(sbi.operators + 16, 0, 16);
990 for (i = 0; i < 128; i++) {
992 memcpy(sbi.operators, midiFMInstrumentPatches + i * 16, 16);
994 if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
995 WARN("Couldn't write patch for instrument %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
999 for (i = 0; i < 128; i++) {
1000 sbi.channel = 128 + i;
1001 memcpy(sbi.operators, midiFMDrumsPatches + i * 16, 16);
1003 if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
1004 WARN("Couldn't write patch for drum %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
1011 /**************************************************************************
1012 * modFMReset [internal]
1014 static void modFMReset(WORD wDevID)
1016 sFMextra* extra = (sFMextra*)MidiOutDev[wDevID].lpExtra;
1017 sVoice* voice = extra->voice;
1018 sChannel* channel = extra->channel;
1021 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1022 if (voice[i].status != sVS_UNUSED) {
1023 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1025 SEQ_KEY_PRESSURE(wDevID, i, 127, 0);
1026 SEQ_CONTROL(wDevID, i, SEQ_VOLMODE, VOL_METHOD_LINEAR);
1028 voice[i].channel = -1;
1029 voice[i].cntMark = 0;
1030 voice[i].status = sVS_UNUSED;
1032 for (i = 0; i < 16; i++) {
1033 channel[i].program = 0;
1034 channel[i].bender = 8192;
1035 channel[i].benderRange = 2;
1036 channel[i].bank = 0;
1037 channel[i].volume = 127;
1038 channel[i].balance = 64;
1039 channel[i].expression = 0;
1040 channel[i].sustain = 0;
1043 extra->drumSetMask = 1 << 9; /* channel 10 is normally drums, sometimes 16 also */
1047 #define IS_DRUM_CHANNEL(_xtra, _chn) ((_xtra)->drumSetMask & (1 << (_chn)))
1049 /**************************************************************************
1050 * modGetDevCaps [internal]
1052 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
1054 TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
1056 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1057 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
1059 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1061 return MMSYSERR_NOERROR;
1064 /**************************************************************************
1065 * modOpen [internal]
1067 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
1069 TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1070 if (lpDesc == NULL) {
1071 WARN("Invalid Parameter !\n");
1072 return MMSYSERR_INVALPARAM;
1074 if (wDevID >= MODM_NumDevs) {
1075 TRACE("MAX_MIDIOUTDRV reached !\n");
1076 return MMSYSERR_BADDEVICEID;
1078 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
1079 WARN("device already open !\n");
1080 return MMSYSERR_ALLOCATED;
1082 if (!MidiOutDev[wDevID].bEnabled) {
1083 WARN("device disabled !\n");
1084 return MIDIERR_NODEVICE;
1086 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
1087 WARN("bad dwFlags\n");
1088 return MMSYSERR_INVALFLAG;
1090 if (!MidiOutDev[wDevID].bEnabled) {
1091 TRACE("disabled wDevID\n");
1092 return MMSYSERR_NOTENABLED;
1095 MidiOutDev[wDevID].lpExtra = 0;
1097 switch (MidiOutDev[wDevID].caps.wTechnology) {
1102 extra = HeapAlloc(GetProcessHeap(), 0,
1103 sizeof(struct sFMextra) +
1104 sizeof(struct sVoice) * (MidiOutDev[wDevID].caps.wVoices - 1));
1107 WARN("can't alloc extra data !\n");
1108 return MMSYSERR_NOMEM;
1110 MidiOutDev[wDevID].lpExtra = extra;
1111 if (midiOpenSeq() < 0) {
1112 MidiOutDev[wDevID].lpExtra = 0;
1113 HeapFree(GetProcessHeap(), 0, extra);
1114 return MMSYSERR_ERROR;
1116 if (modFMLoad(wDevID) < 0) {
1118 MidiOutDev[wDevID].lpExtra = 0;
1119 HeapFree(GetProcessHeap(), 0, extra);
1120 return MMSYSERR_ERROR;
1127 if (midiOpenSeq() < 0) {
1128 return MMSYSERR_ALLOCATED;
1132 WARN("Technology not supported (yet) %d !\n",
1133 MidiOutDev[wDevID].caps.wTechnology);
1134 return MMSYSERR_NOTENABLED;
1137 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1139 MidiOutDev[wDevID].lpQueueHdr = NULL;
1140 MidiOutDev[wDevID].dwTotalPlayed = 0;
1141 MidiOutDev[wDevID].bufsize = 0x3FFF;
1142 MidiOutDev[wDevID].midiDesc = *lpDesc;
1144 if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1145 WARN("can't notify client !\n");
1146 return MMSYSERR_INVALPARAM;
1148 TRACE("Successful !\n");
1149 return MMSYSERR_NOERROR;
1153 /**************************************************************************
1154 * modClose [internal]
1156 static DWORD modClose(WORD wDevID)
1158 int ret = MMSYSERR_NOERROR;
1160 TRACE("(%04X);\n", wDevID);
1162 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
1163 WARN("device not opened !\n");
1164 return MMSYSERR_ERROR;
1166 /* FIXME: should test that no pending buffer is still in the queue for
1169 if (midiSeqFD == -1) {
1170 WARN("can't close !\n");
1171 return MMSYSERR_ERROR;
1174 switch (MidiOutDev[wDevID].caps.wTechnology) {
1180 WARN("Technology not supported (yet) %d !\n",
1181 MidiOutDev[wDevID].caps.wTechnology);
1182 return MMSYSERR_NOTENABLED;
1185 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
1186 MidiOutDev[wDevID].lpExtra = 0;
1188 MidiOutDev[wDevID].bufsize = 0;
1189 if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1190 WARN("can't notify client !\n");
1191 ret = MMSYSERR_INVALPARAM;
1193 MidiOutDev[wDevID].midiDesc.hMidi = 0;
1197 /**************************************************************************
1198 * modData [internal]
1200 static DWORD modData(WORD wDevID, DWORD dwParam)
1202 WORD evt = LOBYTE(LOWORD(dwParam));
1203 WORD d1 = HIBYTE(LOWORD(dwParam));
1204 WORD d2 = LOBYTE(HIWORD(dwParam));
1206 TRACE("(%04X, %08lX);\n", wDevID, dwParam);
1208 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1209 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1211 if (midiSeqFD == -1) {
1212 WARN("can't play !\n");
1213 return MIDIERR_NODEVICE;
1215 switch (MidiOutDev[wDevID].caps.wTechnology) {
1218 * - chorus depth controller is not used
1221 sFMextra* extra = (sFMextra*)MidiOutDev[wDevID].lpExtra;
1222 sVoice* voice = extra->voice;
1223 sChannel* channel = extra->channel;
1224 int chn = (evt & 0x0F);
1227 switch (evt & 0xF0) {
1229 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1230 /* don't stop sustained notes */
1231 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1232 voice[i].status = sVS_UNUSED;
1233 SEQ_STOP_NOTE(wDevID, i, d1, d2);
1238 if (d2 == 0) { /* note off if velocity == 0 */
1239 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1240 /* don't stop sustained notes */
1241 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1242 voice[i].status = sVS_UNUSED;
1243 SEQ_STOP_NOTE(wDevID, i, d1, 64);
1248 /* finding out in this order :
1250 * - if replaying the same note on the same channel
1251 * - the older voice (LRU)
1253 for (i = nv = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1254 if (voice[i].status == sVS_UNUSED ||
1255 (voice[i].note == d1 && voice[i].channel == chn)) {
1259 if (voice[i].cntMark < voice[0].cntMark) {
1264 "playing on voice=%d, pgm=%d, pan=0x%02X, vol=0x%02X, "
1265 "bender=0x%02X, note=0x%02X, vel=0x%02X\n",
1266 nv, channel[chn].program,
1267 channel[chn].balance,
1268 channel[chn].volume,
1269 channel[chn].bender, d1, d2);
1271 SEQ_SET_PATCH(wDevID, nv, IS_DRUM_CHANNEL(extra, chn) ?
1272 (128 + d1) : channel[chn].program);
1273 SEQ_BENDER_RANGE(wDevID, nv, channel[chn].benderRange * 100);
1274 SEQ_BENDER(wDevID, nv, channel[chn].bender);
1275 SEQ_CONTROL(wDevID, nv, CTL_PAN, channel[chn].balance);
1276 SEQ_CONTROL(wDevID, nv, CTL_EXPRESSION, channel[chn].expression);
1278 /* FIXME: does not really seem to work on my SB card and
1279 * screws everything up... so lay it down
1281 SEQ_CONTROL(wDevID, nv, CTL_MAIN_VOLUME, channel[chn].volume);
1283 SEQ_START_NOTE(wDevID, nv, d1, d2);
1284 voice[nv].status = channel[chn].sustain ? sVS_SUSTAINED : sVS_PLAYING;
1285 voice[nv].note = d1;
1286 voice[nv].channel = chn;
1287 voice[nv].cntMark = extra->counter++;
1289 case MIDI_KEY_PRESSURE:
1290 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1291 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn && voice[i].note == d1) {
1292 SEQ_KEY_PRESSURE(wDevID, i, d1, d2);
1296 case MIDI_CTL_CHANGE:
1298 case CTL_BANK_SELECT: channel[chn].bank = d2; break;
1299 case CTL_MAIN_VOLUME: channel[chn].volume = d2; break;
1300 case CTL_PAN: channel[chn].balance = d2; break;
1301 case CTL_EXPRESSION: channel[chn].expression = d2; break;
1302 case CTL_SUSTAIN: channel[chn].sustain = d2;
1304 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1305 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1306 voice[i].status = sVS_SUSTAINED;
1310 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1311 if (voice[i].status == sVS_SUSTAINED && voice[i].channel == chn) {
1312 voice[i].status = sVS_UNUSED;
1313 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1318 case CTL_NONREG_PARM_NUM_LSB: channel[chn].nrgPmtLSB = d2; break;
1319 case CTL_NONREG_PARM_NUM_MSB: channel[chn].nrgPmtMSB = d2; break;
1320 case CTL_REGIST_PARM_NUM_LSB: channel[chn].regPmtLSB = d2; break;
1321 case CTL_REGIST_PARM_NUM_MSB: channel[chn].regPmtMSB = d2; break;
1322 case CTL_DATA_ENTRY:
1323 switch ((channel[chn].regPmtMSB << 8) | channel[chn].regPmtLSB) {
1325 if (channel[chn].benderRange != d2) {
1326 channel[chn].benderRange = d2;
1327 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1328 if (voice[i].channel == chn) {
1329 SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1336 channel[chn].benderRange = 2;
1337 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1338 if (voice[i].channel == chn) {
1339 SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1344 TRACE("Data entry: regPmt=0x%02x%02x, nrgPmt=0x%02x%02x with %x\n",
1345 channel[chn].regPmtMSB, channel[chn].regPmtLSB,
1346 channel[chn].nrgPmtMSB, channel[chn].nrgPmtLSB,
1352 case 0x78: /* all sounds off */
1353 /* FIXME: I don't know if I have to take care of the channel
1354 * for this control ?
1356 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1357 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1358 voice[i].status = sVS_UNUSED;
1359 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1363 case 0x7B: /* all notes off */
1364 /* FIXME: I don't know if I have to take care of the channel
1365 * for this control ?
1367 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1368 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1369 voice[i].status = sVS_UNUSED;
1370 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1375 TRACE("Dropping MIDI control event 0x%02x(%02x) on channel %d\n",
1380 case MIDI_PGM_CHANGE:
1381 channel[chn].program = d1;
1383 case MIDI_CHN_PRESSURE:
1384 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1385 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1386 SEQ_KEY_PRESSURE(wDevID, i, voice[i].note, d1);
1390 case MIDI_PITCH_BEND:
1391 channel[chn].bender = (d2 << 7) + d1;
1392 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1393 if (voice[i].channel == chn) {
1394 SEQ_BENDER(wDevID, i, channel[chn].bender);
1398 case MIDI_SYSTEM_PREFIX:
1399 switch (evt & 0x0F) {
1400 case 0x0F: /* Reset */
1404 WARN("Unsupported (yet) system event %02x\n", evt & 0x0F);
1408 WARN("Internal error, shouldn't happen (event=%08x)\n", evt & 0xF0);
1409 return MMSYSERR_NOTENABLED;
1415 int dev = wDevID - MODM_NumFMSynthDevs;
1417 WARN("Internal error on devID (%u) !\n", wDevID);
1418 return MIDIERR_NODEVICE;
1421 switch (evt & 0xF0) {
1424 case MIDI_KEY_PRESSURE:
1425 case MIDI_CTL_CHANGE:
1426 case MIDI_PITCH_BEND:
1427 SEQ_MIDIOUT(dev, evt);
1428 SEQ_MIDIOUT(dev, d1);
1429 SEQ_MIDIOUT(dev, d2);
1431 case MIDI_PGM_CHANGE:
1432 case MIDI_CHN_PRESSURE:
1433 SEQ_MIDIOUT(dev, evt);
1434 SEQ_MIDIOUT(dev, d1);
1436 case MIDI_SYSTEM_PREFIX:
1437 switch (evt & 0x0F) {
1438 case 0x00: /* System Exclusive, don't do it on modData,
1439 * should require modLongData*/
1440 case 0x01: /* Undefined */
1441 case 0x04: /* Undefined. */
1442 case 0x05: /* Undefined. */
1443 case 0x07: /* End of Exclusive. */
1444 case 0x09: /* Undefined. */
1445 case 0x0D: /* Undefined. */
1447 case 0x06: /* Tune Request */
1448 case 0x08: /* Timing Clock. */
1449 case 0x0A: /* Start. */
1450 case 0x0B: /* Continue */
1451 case 0x0C: /* Stop */
1452 case 0x0E: /* Active Sensing. */
1453 SEQ_MIDIOUT(dev, evt);
1455 case 0x0F: /* Reset */
1456 /* SEQ_MIDIOUT(dev, evt);
1457 this other way may be better */
1458 SEQ_MIDIOUT(dev, MIDI_SYSTEM_PREFIX);
1459 SEQ_MIDIOUT(dev, 0x7e);
1460 SEQ_MIDIOUT(dev, 0x7f);
1461 SEQ_MIDIOUT(dev, 0x09);
1462 SEQ_MIDIOUT(dev, 0x01);
1463 SEQ_MIDIOUT(dev, 0xf7);
1465 case 0x03: /* Song Select. */
1466 SEQ_MIDIOUT(dev, evt);
1467 SEQ_MIDIOUT(dev, d1);
1468 case 0x02: /* Song Position Pointer. */
1469 SEQ_MIDIOUT(dev, evt);
1470 SEQ_MIDIOUT(dev, d1);
1471 SEQ_MIDIOUT(dev, d2);
1478 WARN("Technology not supported (yet) %d !\n",
1479 MidiOutDev[wDevID].caps.wTechnology);
1480 return MMSYSERR_NOTENABLED;
1485 return MMSYSERR_NOERROR;
1488 /**************************************************************************
1489 * modLongData [internal]
1491 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1496 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1498 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
1499 * but it seems to be used only for midi input.
1500 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
1503 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1504 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1506 if (midiSeqFD == -1) {
1507 WARN("can't play !\n");
1508 return MIDIERR_NODEVICE;
1511 lpData = lpMidiHdr->lpData;
1514 return MIDIERR_UNPREPARED;
1515 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1516 return MIDIERR_UNPREPARED;
1517 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1518 return MIDIERR_STILLPLAYING;
1519 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1520 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1522 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
1523 * data, or can it also contain raw MIDI data, to be split up and sent to
1525 * If the latest is true, then the following WARNing will fire up
1527 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
1528 WARN("Alledged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
1531 TRACE("dwBufferLength=%lu !\n", lpMidiHdr->dwBufferLength);
1532 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
1533 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
1534 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
1536 switch (MidiOutDev[wDevID].caps.wTechnology) {
1538 /* FIXME: I don't think there is much to do here */
1541 if (lpData[0] != 0xF0) {
1542 /* Send end of System Exclusive */
1543 SEQ_MIDIOUT(wDevID - MODM_NumFMSynthDevs, 0xF0);
1544 WARN("Adding missing 0xF0 marker at the beginning of "
1545 "system exclusive byte stream\n");
1547 for (count = 0; count < lpMidiHdr->dwBufferLength; count++) {
1548 SEQ_MIDIOUT(wDevID - MODM_NumFMSynthDevs, lpData[count]);
1550 if (lpData[count - 1] != 0xF7) {
1551 /* Send end of System Exclusive */
1552 SEQ_MIDIOUT(wDevID - MODM_NumFMSynthDevs, 0xF7);
1553 WARN("Adding missing 0xF7 marker at the end of "
1554 "system exclusive byte stream\n");
1559 WARN("Technology not supported (yet) %d !\n",
1560 MidiOutDev[wDevID].caps.wTechnology);
1561 return MMSYSERR_NOTENABLED;
1564 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1565 lpMidiHdr->dwFlags |= MHDR_DONE;
1566 if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1567 WARN("can't notify client !\n");
1568 return MMSYSERR_INVALPARAM;
1570 return MMSYSERR_NOERROR;
1573 /**************************************************************************
1574 * modPrepare [internal]
1576 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1578 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1580 if (midiSeqFD == -1) {
1581 WARN("can't prepare !\n");
1582 return MMSYSERR_NOTENABLED;
1585 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1586 * asks to prepare MIDIHDR which dwFlags != 0.
1587 * So at least check for the inqueue flag
1589 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1590 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
1591 lpMidiHdr->dwBufferLength >= 0x10000ul) {
1592 WARN("%p %p %08lx %d/%ld\n", lpMidiHdr, lpMidiHdr->lpData,
1593 lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
1594 return MMSYSERR_INVALPARAM;
1597 lpMidiHdr->lpNext = 0;
1598 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1599 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1600 return MMSYSERR_NOERROR;
1603 /**************************************************************************
1604 * modUnprepare [internal]
1606 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1608 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1610 if (midiSeqFD == -1) {
1611 WARN("can't unprepare !\n");
1612 return MMSYSERR_NOTENABLED;
1615 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1616 return MMSYSERR_INVALPARAM;
1617 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1618 return MIDIERR_STILLPLAYING;
1619 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1620 return MMSYSERR_NOERROR;
1623 /**************************************************************************
1624 * modReset [internal]
1626 static DWORD modReset(WORD wDevID)
1630 TRACE("(%04X);\n", wDevID);
1632 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1633 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1635 /* stop all notes */
1636 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1637 * it's channel dependent...
1639 for (chn = 0; chn < 16; chn++) {
1640 /* turn off every note */
1641 modData(wDevID, 0x7800 | MIDI_CTL_CHANGE | chn);
1642 /* remove sustain on all channels */
1643 modData(wDevID, (CTL_SUSTAIN << 8) | MIDI_CTL_CHANGE | chn);
1645 /* FIXME: the LongData buffers must also be returned to the app */
1646 return MMSYSERR_NOERROR;
1649 #endif /* HAVE_OSS_MIDI */
1651 /*======================================================================*
1652 * MIDI entry points *
1653 *======================================================================*/
1655 /**************************************************************************
1656 * midMessage (WINEOSS.4)
1658 DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1659 DWORD dwParam1, DWORD dwParam2)
1661 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1662 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1664 #ifdef HAVE_OSS_MIDI
1669 /* FIXME: Pretend this is supported */
1672 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1674 return midClose(wDevID);
1675 case MIDM_ADDBUFFER:
1676 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1678 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1679 case MIDM_UNPREPARE:
1680 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1681 case MIDM_GETDEVCAPS:
1682 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1683 case MIDM_GETNUMDEVS:
1684 return MIDM_NumDevs;
1686 return midReset(wDevID);
1688 return midStart(wDevID);
1690 return midStop(wDevID);
1693 TRACE("Unsupported message\n");
1695 return MMSYSERR_NOTSUPPORTED;
1698 /**************************************************************************
1699 * modMessage (WINEOSS.5)
1701 DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1702 DWORD dwParam1, DWORD dwParam2)
1704 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1705 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1708 #ifdef HAVE_OSS_MIDI
1713 /* FIXME: Pretend this is supported */
1716 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1718 return modClose(wDevID);
1720 return modData(wDevID, dwParam1);
1722 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1724 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1725 case MODM_UNPREPARE:
1726 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1727 case MODM_GETDEVCAPS:
1728 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1729 case MODM_GETNUMDEVS:
1730 return MODM_NumDevs;
1731 case MODM_GETVOLUME:
1733 case MODM_SETVOLUME:
1736 return modReset(wDevID);
1739 TRACE("Unsupported message\n");
1741 return MMSYSERR_NOTSUPPORTED;
1744 /*-----------------------------------------------------------------------*/