Use a more portable scheme for storing the name of a critical
[wine] / dlls / winmm / wineoss / midi.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * Sample MIDI Wine Driver for Open Sound System (basically Linux)
5  *
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
13  *
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.
18  *
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.
23  *
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
27  */
28
29 /* TODO:
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)
40  */
41
42 #include "config.h"
43 #include "wine/port.h"
44
45 #include <string.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48 #ifdef HAVE_UNISTD_H
49 # include <unistd.h>
50 #endif
51 #include <fcntl.h>
52 #include <errno.h>
53 #ifdef HAVE_SYS_IOCTL_H
54 # include <sys/ioctl.h>
55 #endif
56 #ifdef HAVE_POLL_H
57 #include <poll.h>
58 #endif
59 #ifdef HAVE_SYS_POLL_H
60 #include <sys/poll.h>
61 #endif
62
63 #include "windef.h"
64 #include "winbase.h"
65 #include "wingdi.h"
66 #include "winuser.h"
67 #include "winnls.h"
68 #include "mmddk.h"
69 #include "oss.h"
70 #include "wine/unicode.h"
71 #include "wine/debug.h"
72
73 WINE_DEFAULT_DEBUG_CHANNEL(midi);
74
75 #ifdef SNDCTL_SEQ_NRMIDIS
76 #define HAVE_OSS_MIDI
77 #endif
78
79 #ifdef HAVE_OSS_MIDI
80
81 #define MIDI_SEQ "/dev/sequencer"
82
83 typedef struct {
84     int                 state;                  /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
85     DWORD               bufsize;
86     MIDIOPENDESC        midiDesc;
87     WORD                wFlags;
88     LPMIDIHDR           lpQueueHdr;
89     DWORD               dwTotalPlayed;
90     unsigned char       incoming[3];
91     unsigned char       incPrev;
92     char                incLen;
93     DWORD               startTime;
94     MIDIINCAPSW         caps;
95 } WINE_MIDIIN;
96
97 typedef struct {
98     BOOL                bEnabled;
99     DWORD               bufsize;
100     MIDIOPENDESC        midiDesc;
101     WORD                wFlags;
102     LPMIDIHDR           lpQueueHdr;
103     DWORD               dwTotalPlayed;
104     void*               lpExtra;                /* according to port type (MIDI, FM...), extra data when needed */
105     MIDIOUTCAPSW        caps;
106 } WINE_MIDIOUT;
107
108 static WINE_MIDIIN      MidiInDev [MAX_MIDIINDRV ];
109 static WINE_MIDIOUT     MidiOutDev[MAX_MIDIOUTDRV];
110
111 /* this is the total number of MIDI out devices found (synth and port) */
112 static  int             MODM_NumDevs = 0;
113 /* this is the number of FM synthetizers (index from 0 to NUMFMSYNTHDEVS - 1) */
114 static  int             MODM_NumFMSynthDevs = 0;
115 /* the Midi ports have index from NUMFMSYNTHDEVS to NumDevs - 1 */
116
117 /* this is the total number of MIDI out devices found */
118 static  int             MIDM_NumDevs = 0;
119
120 static  int             midiSeqFD = -1;
121 static  int             numOpenMidiSeq = 0;
122 static  int             numStartedMidiIn = 0;
123
124 static CRITICAL_SECTION crit_sect;   /* protects all MidiIn buffers queues */
125 static CRITICAL_SECTION_DEBUG critsect_debug =
126 {
127     0, 0, &crit_sect,
128     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
129       0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
130 };
131 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
132
133 static int end_thread;
134 static HANDLE hThread;
135
136 /*======================================================================*
137  *                  Low level MIDI implementation                       *
138  *======================================================================*/
139
140 static int midiOpenSeq(void);
141 static int midiCloseSeq(void);
142
143 /**************************************************************************
144  *                      MIDI_unixToWindowsDeviceType            [internal]
145  *
146  * return the Windows equivalent to a Unix Device Type
147  *
148  */
149 static  int     MIDI_UnixToWindowsDeviceType(int type)
150 {
151     /* MOD_MIDIPORT     output port
152      * MOD_SYNTH        generic internal synth
153      * MOD_SQSYNTH      square wave internal synth
154      * MOD_FMSYNTH      FM internal synth
155      * MOD_MAPPER       MIDI mapper
156      * MOD_WAVETABLE    hardware watetable internal synth
157      * MOD_SWSYNTH      software internal synth
158      */
159
160     /* FIXME Is this really the correct equivalence from UNIX to
161        Windows Sound type */
162
163     switch (type) {
164     case SYNTH_TYPE_FM:     return MOD_FMSYNTH;
165     case SYNTH_TYPE_SAMPLE: return MOD_SYNTH;
166     case SYNTH_TYPE_MIDI:   return MOD_MIDIPORT;
167     default:
168         ERR("Cannot determine the type of this midi device. "
169             "Assuming FM Synth\n");
170         return MOD_FMSYNTH;
171     }
172 }
173
174 /**************************************************************************
175  *                      OSS_MidiInit                            [internal]
176  *
177  * Initializes the MIDI devices information variables
178  */
179 BOOL OSS_MidiInit(void)
180 {
181     int                 i, status, numsynthdevs = 255, nummididevs = 255;
182     struct synth_info   sinfo;
183     struct midi_info    minfo;
184     static      BOOL    bInitDone = FALSE;
185
186     if (bInitDone)
187         return TRUE;
188
189     TRACE("Initializing the MIDI variables.\n");
190     bInitDone = TRUE;
191
192     /* try to open device */
193     if (midiOpenSeq() == -1) {
194         return TRUE;
195     }
196
197     /* find how many Synth devices are there in the system */
198     status = ioctl(midiSeqFD, SNDCTL_SEQ_NRSYNTHS, &numsynthdevs);
199
200     if (status == -1) {
201         ERR("ioctl for nr synth failed.\n");
202         midiCloseSeq();
203         return TRUE;
204     }
205
206     if (numsynthdevs > MAX_MIDIOUTDRV) {
207         ERR("MAX_MIDIOUTDRV (%d) was enough for the number of devices (%d). "
208             "Some FM devices will not be available.\n",MAX_MIDIOUTDRV,numsynthdevs);
209         numsynthdevs = MAX_MIDIOUTDRV;
210     }
211
212     for (i = 0; i < numsynthdevs; i++) {
213         /* Manufac ID. We do not have access to this with soundcard.h
214          * Does not seem to be a problem, because in mmsystem.h only
215          * Microsoft's ID is listed.
216          */
217         MidiOutDev[i].caps.wMid = 0x00FF;
218         MidiOutDev[i].caps.wPid = 0x0001;       /* FIXME Product ID  */
219         /* Product Version. We simply say "1" */
220         MidiOutDev[i].caps.vDriverVersion = 0x001;
221         MidiOutDev[i].caps.wChannelMask   = 0xFFFF;
222
223         /* FIXME Do we have this information?
224          * Assuming the soundcards can handle
225          * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
226          * not MIDICAPS_CACHE.
227          */
228         MidiOutDev[i].caps.dwSupport      = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
229
230         sinfo.device = i;
231         status = ioctl(midiSeqFD, SNDCTL_SYNTH_INFO, &sinfo);
232         if (status == -1) {
233             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};
234             ERR("ioctl for synth info failed on %d, disabling it.\n", i);
235
236             wsprintfW(MidiOutDev[i].caps.szPname, fmt, i);
237
238             MidiOutDev[i].caps.wTechnology = MOD_MIDIPORT;
239             MidiOutDev[i].caps.wVoices     = 16;
240             MidiOutDev[i].caps.wNotes      = 16;
241             MidiOutDev[i].bEnabled = FALSE;
242         } else {
243             MultiByteToWideChar( CP_ACP, 0, sinfo.name, -1,
244                                  MidiOutDev[i].caps.szPname,
245                                  sizeof(MidiOutDev[i].caps.szPname)/sizeof(WCHAR) );
246
247             MidiOutDev[i].caps.wTechnology = MIDI_UnixToWindowsDeviceType(sinfo.synth_type);
248             MidiOutDev[i].caps.wVoices     = sinfo.nr_voices;
249
250             /* FIXME Is it possible to know the maximum
251              * number of simultaneous notes of a soundcard ?
252              * I believe we don't have this information, but
253              * it's probably equal or more than wVoices
254              */
255             MidiOutDev[i].caps.wNotes      = sinfo.nr_voices;
256             MidiOutDev[i].bEnabled = TRUE;
257         }
258
259         /* We also have the information sinfo.synth_subtype, not used here
260          */
261
262         if (sinfo.capabilities & SYNTH_CAP_INPUT) {
263             FIXME("Synthesizer support MIDI in. Not supported yet (please report)\n");
264         }
265
266         TRACE("SynthOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%ld\n"
267               "\tOSS info: synth subtype=%d capa=%lx\n",
268               i, wine_dbgstr_w(MidiOutDev[i].caps.szPname), 
269               MidiOutDev[i].caps.wTechnology, 
270               MidiOutDev[i].caps.wVoices, MidiOutDev[i].caps.wNotes, 
271               MidiOutDev[i].caps.wChannelMask, MidiOutDev[i].caps.dwSupport,
272               sinfo.synth_subtype, (long)sinfo.capabilities);
273     }
274
275     /* find how many MIDI devices are there in the system */
276     status = ioctl(midiSeqFD, SNDCTL_SEQ_NRMIDIS, &nummididevs);
277     if (status == -1) {
278         ERR("ioctl on nr midi failed.\n");
279         nummididevs = 0;
280         goto wrapup;
281     }
282
283     /* FIXME: the two restrictions below could be loosened in some cases */
284     if (numsynthdevs + nummididevs > MAX_MIDIOUTDRV) {
285         ERR("MAX_MIDIOUTDRV was not enough for the number of devices. "
286             "Some MIDI devices will not be available.\n");
287         nummididevs = MAX_MIDIOUTDRV - numsynthdevs;
288     }
289
290     if (nummididevs > MAX_MIDIINDRV) {
291         ERR("MAX_MIDIINDRV (%d) was not enough for the number of devices (%d). "
292             "Some MIDI devices will not be available.\n",MAX_MIDIINDRV,nummididevs);
293         nummididevs = MAX_MIDIINDRV;
294     }
295
296     for (i = 0; i < nummididevs; i++) {
297         minfo.device = i;
298         status = ioctl(midiSeqFD, SNDCTL_MIDI_INFO, &minfo);
299         if (status == -1) WARN("ioctl on midi info for device %d failed.\n", i);
300
301         /* This whole part is somewhat obscure to me. I'll keep trying to dig
302            info about it. If you happen to know, please tell us. The very
303            descritive minfo.dev_type was not used here.
304         */
305         /* Manufac ID. We do not have access to this with soundcard.h
306            Does not seem to be a problem, because in mmsystem.h only
307            Microsoft's ID is listed */
308         MidiOutDev[numsynthdevs + i].caps.wMid = 0x00FF;
309         MidiOutDev[numsynthdevs + i].caps.wPid = 0x0001;        /* FIXME Product ID */
310         /* Product Version. We simply say "1" */
311         MidiOutDev[numsynthdevs + i].caps.vDriverVersion = 0x001;
312         if (status == -1) {
313             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};
314             wsprintfW(MidiOutDev[numsynthdevs + i].caps.szPname, fmt, numsynthdevs + i);
315             MidiOutDev[numsynthdevs + i].bEnabled = FALSE;
316         } else {
317             MultiByteToWideChar(CP_ACP, 0, minfo.name, -1, 
318                                 MidiOutDev[numsynthdevs + i].caps.szPname,
319                                 sizeof(MidiOutDev[numsynthdevs + i].caps.szPname) / sizeof(WCHAR));
320             MidiOutDev[numsynthdevs + i].bEnabled = TRUE;
321         }
322         MidiOutDev[numsynthdevs + i].caps.wTechnology = MOD_MIDIPORT; /* FIXME Is this right? */
323         /* Does it make any difference? */
324         MidiOutDev[numsynthdevs + i].caps.wVoices     = 16;
325         /* Does it make any difference? */
326         MidiOutDev[numsynthdevs + i].caps.wNotes      = 16;
327         MidiOutDev[numsynthdevs + i].caps.wChannelMask= 0xFFFF;
328
329         /* FIXME Does it make any difference? */
330         MidiOutDev[numsynthdevs + i].caps.dwSupport   = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
331
332         /* This whole part is somewhat obscure to me. I'll keep trying to dig
333            info about it. If you happen to know, please tell us. The very
334            descritive minfo.dev_type was not used here.
335         */
336         /* Manufac ID. We do not have access to this with soundcard.h
337            Does not seem to be a problem, because in mmsystem.h only
338            Microsoft's ID is listed */
339         MidiInDev[i].caps.wMid = 0x00FF;
340         MidiInDev[i].caps.wPid = 0x0001;        /* FIXME Product ID */
341         /* Product Version. We simply say "1" */
342         MidiInDev[i].caps.vDriverVersion = 0x001;
343         if (status == -1) {
344             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};
345             wsprintfW(MidiInDev[i].caps.szPname, fmt, numsynthdevs + i);
346             MidiInDev[i].state = -1;
347         } else {
348             MultiByteToWideChar(CP_ACP, 0, minfo.name, -1, 
349                                 MidiInDev[i].caps.szPname,
350                                 sizeof(MidiInDev[i].caps.szPname) / sizeof(WCHAR));
351             MidiInDev[i].state = 0;
352         }
353         /* FIXME : could we get better information than that ? */
354         MidiInDev[i].caps.dwSupport   = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
355
356         TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%ld\n"
357               "MidiIn [%d]\tname='%s' support=%ld\n"
358               "\tOSS info: midi dev-type=%d, capa=%lx\n",
359               i, wine_dbgstr_w(MidiOutDev[numsynthdevs + i].caps.szPname), 
360               MidiOutDev[numsynthdevs + i].caps.wTechnology,
361               MidiOutDev[numsynthdevs + i].caps.wVoices, MidiOutDev[numsynthdevs + i].caps.wNotes,
362               MidiOutDev[numsynthdevs + i].caps.wChannelMask, MidiOutDev[numsynthdevs + i].caps.dwSupport,
363               i, wine_dbgstr_w(MidiInDev[i].caps.szPname), MidiInDev[i].caps.dwSupport,
364               minfo.dev_type, (long)minfo.capabilities);
365     }
366
367  wrapup:
368     /* windows does not seem to differentiate Synth from MIDI devices */
369     MODM_NumFMSynthDevs = numsynthdevs;
370     MODM_NumDevs        = numsynthdevs + nummididevs;
371
372     MIDM_NumDevs        = nummididevs;
373
374     /* close file and exit */
375     midiCloseSeq();
376
377     return TRUE;
378 }
379
380 /**************************************************************************
381  *                      MIDI_NotifyClient                       [internal]
382  */
383 static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg,
384                                DWORD dwParam1, DWORD dwParam2)
385 {
386     DWORD               dwCallBack;
387     UINT                uFlags;
388     HANDLE              hDev;
389     DWORD               dwInstance;
390
391     TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
392           wDevID, wMsg, dwParam1, dwParam2);
393
394     switch (wMsg) {
395     case MOM_OPEN:
396     case MOM_CLOSE:
397     case MOM_DONE:
398     case MOM_POSITIONCB:
399         if (wDevID > MODM_NumDevs)
400             return MMSYSERR_BADDEVICEID;
401
402         dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
403         uFlags = MidiOutDev[wDevID].wFlags;
404         hDev = MidiOutDev[wDevID].midiDesc.hMidi;
405         dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
406         break;
407
408     case MIM_OPEN:
409     case MIM_CLOSE:
410     case MIM_DATA:
411     case MIM_LONGDATA:
412     case MIM_ERROR:
413     case MIM_LONGERROR:
414     case MIM_MOREDATA:
415         if (wDevID > MIDM_NumDevs)
416             return MMSYSERR_BADDEVICEID;
417
418         dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
419         uFlags = MidiInDev[wDevID].wFlags;
420         hDev = MidiInDev[wDevID].midiDesc.hMidi;
421         dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
422         break;
423     default:
424         WARN("Unsupported MSW-MIDI message %u\n", wMsg);
425         return MMSYSERR_ERROR;
426     }
427
428     return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
429         0 : MMSYSERR_ERROR;
430 }
431
432 static int midi_warn = 1;
433 /**************************************************************************
434  *                      midiOpenSeq                             [internal]
435  */
436 static int midiOpenSeq(void)
437 {
438     if (numOpenMidiSeq == 0) {
439         midiSeqFD = open(MIDI_SEQ, O_RDWR, 0);
440         if (midiSeqFD == -1) {
441             if (midi_warn)
442             {
443                 WARN("Can't open MIDI device '%s' ! (%s). If your "
444                         "program needs this (probably not): %s\n",
445                         MIDI_SEQ, strerror(errno),
446                         errno == ENOENT ?
447                         "create it ! (\"man MAKEDEV\" ?)" :
448                         errno == ENODEV ?
449                         "load MIDI sequencer kernel driver !" :
450                         errno == EACCES ?
451                         "grant access ! (\"man chmod\")" : ""
452                 );
453             }
454             midi_warn = 0;
455             return -1;
456         }
457 #if 0
458         if (fcntl(midiSeqFD, F_SETFL, O_NONBLOCK) < 0) {
459             WARN("can't set sequencer fd to non-blocking, errno %d (%s)\n", errno, strerror(errno));
460             close(midiSeqFD);
461             midiSeqFD = -1;
462             return -1;
463         }
464 #endif
465         fcntl(midiSeqFD, F_SETFD, 1); /* set close on exec flag */
466         ioctl(midiSeqFD, SNDCTL_SEQ_RESET);
467     }
468     numOpenMidiSeq++;
469     return 0;
470 }
471
472 /**************************************************************************
473  *                      midiCloseSeq                            [internal]
474  */
475 static int midiCloseSeq(void)
476 {
477     if (--numOpenMidiSeq == 0) {
478         close(midiSeqFD);
479         midiSeqFD = -1;
480     }
481     return 0;
482 }
483
484 /* FIXME: this is a bad idea, it's even not static... */
485 SEQ_DEFINEBUF(1024);
486
487 /* FIXME: this is not reentrant, not static - because of global variable
488  * _seqbuf and al.
489  */
490 /**************************************************************************
491  *                      seqbuf_dump                             [internal]
492  *
493  * Used by SEQ_DUMPBUF to flush the buffer.
494  *
495  */
496 void seqbuf_dump(void)
497 {
498     if (_seqbufptr) {
499         if (write(midiSeqFD, _seqbuf, _seqbufptr) == -1) {
500             WARN("Can't write data to sequencer %d, errno %d (%s)!\n",
501                  midiSeqFD, errno, strerror(errno));
502         }
503         /* FIXME:
504          *      in any case buffer is lost so that if many errors occur the buffer
505          * will not overrun
506          */
507         _seqbufptr = 0;
508     }
509 }
510
511 /**************************************************************************
512  *                      midReceiveChar                          [internal]
513  */
514 static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
515 {
516     DWORD               toSend = 0;
517
518     TRACE("Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen);
519
520     if (wDevID >= MIDM_NumDevs) {
521         WARN("bad devID\n");
522         return;
523     }
524     if (MidiInDev[wDevID].state <= 0) {
525         TRACE("disabled or input not started, thrown away\n");
526         return;
527     }
528
529     if (MidiInDev[wDevID].state & 2) { /* system exclusive */
530         LPMIDIHDR       lpMidiHdr;
531         WORD            sbfb = FALSE;
532
533         EnterCriticalSection(&crit_sect);
534         if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
535             LPBYTE      lpData = (LPBYTE) lpMidiHdr->lpData;
536
537             lpData[lpMidiHdr->dwBytesRecorded++] = value;
538             if (lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) {
539                 sbfb = TRUE;
540             }
541         }
542         if (value == 0xF7) { /* then end */
543             MidiInDev[wDevID].state &= ~2;
544             sbfb = TRUE;
545         }
546         if (sbfb && lpMidiHdr != NULL) {
547             lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
548             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
549             lpMidiHdr->dwFlags |= MHDR_DONE;
550             MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
551             if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)lpMidiHdr, dwTime) != MMSYSERR_NOERROR) {
552                 WARN("Couldn't notify client\n");
553             }
554         }
555         LeaveCriticalSection(&crit_sect);
556         return;
557     }
558
559 #define IS_CMD(_x)      (((_x) & 0x80) == 0x80)
560 #define IS_SYS_CMD(_x)  (((_x) & 0xF0) == 0xF0)
561
562     if (!IS_CMD(value) && MidiInDev[wDevID].incLen == 0) { /* try to reuse old cmd */
563         if (IS_CMD(MidiInDev[wDevID].incPrev) && !IS_SYS_CMD(MidiInDev[wDevID].incPrev)) {
564             MidiInDev[wDevID].incoming[0] = MidiInDev[wDevID].incPrev;
565             MidiInDev[wDevID].incLen = 1;
566             TRACE("Reusing old command %02xh\n", MidiInDev[wDevID].incPrev);
567         } else {
568             FIXME("error for midi-in, should generate MIM_ERROR notification:"
569                   " prev=%02Xh, incLen=%02Xh\n",
570                   MidiInDev[wDevID].incPrev, MidiInDev[wDevID].incLen);
571             return;
572         }
573     }
574     MidiInDev[wDevID].incoming[(int)(MidiInDev[wDevID].incLen++)] = value;
575     if (MidiInDev[wDevID].incLen == 1 && !IS_SYS_CMD(MidiInDev[wDevID].incoming[0])) {
576         /* store new cmd, just in case */
577         MidiInDev[wDevID].incPrev = MidiInDev[wDevID].incoming[0];
578     }
579
580 #undef IS_CMD
581 #undef IS_SYS_CMD
582
583     switch (MidiInDev[wDevID].incoming[0] & 0xF0) {
584     case MIDI_NOTEOFF:
585     case MIDI_NOTEON:
586     case MIDI_KEY_PRESSURE:
587     case MIDI_CTL_CHANGE:
588     case MIDI_PITCH_BEND:
589         if (MidiInDev[wDevID].incLen == 3) {
590             toSend = (MidiInDev[wDevID].incoming[2] << 16) |
591                 (MidiInDev[wDevID].incoming[1] <<  8) |
592                 (MidiInDev[wDevID].incoming[0] <<  0);
593         }
594         break;
595     case MIDI_PGM_CHANGE:
596     case MIDI_CHN_PRESSURE:
597         if (MidiInDev[wDevID].incLen == 2) {
598             toSend = (MidiInDev[wDevID].incoming[1] <<  8) |
599                 (MidiInDev[wDevID].incoming[0] <<  0);
600         }
601         break;
602     case MIDI_SYSTEM_PREFIX:
603         if (MidiInDev[wDevID].incoming[0] == 0xF0) {
604             MidiInDev[wDevID].state |= 2;
605             MidiInDev[wDevID].incLen = 0;
606         } else {
607             if (MidiInDev[wDevID].incLen == 1) {
608                 toSend = (MidiInDev[wDevID].incoming[0] <<  0);
609             }
610         }
611         break;
612     default:
613         WARN("This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]);
614     }
615     if (toSend != 0) {
616         TRACE("Sending event %08lx\n", toSend);
617         MidiInDev[wDevID].incLen =      0;
618         dwTime -= MidiInDev[wDevID].startTime;
619         if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
620             WARN("Couldn't notify client\n");
621         }
622     }
623 }
624
625 static DWORD WINAPI midRecThread(LPVOID arg)
626 {
627     unsigned char buffer[256];
628     int len, idx;
629     DWORD dwTime;
630     struct pollfd pfd;
631
632     TRACE("Thread startup\n");
633
634     pfd.fd = midiSeqFD;
635     pfd.fd = POLLIN;
636     
637     while(!end_thread) {
638         TRACE("Thread loop\n");
639
640         /* Check if an event is present */
641         if (poll(&pfd, 1, 250) <= 0)
642             continue;
643         
644         len = read(midiSeqFD, buffer, sizeof(buffer));
645         TRACE("Reveived %d bytes\n", len);
646
647         if (len < 0) continue;
648         if ((len % 4) != 0) {
649             WARN("Bad length %d, errno %d (%s)\n", len, errno, strerror(errno));
650             continue;
651         }
652
653         dwTime = GetTickCount();
654         
655         for (idx = 0; idx < len; ) {
656             if (buffer[idx] & 0x80) {
657                 TRACE(
658                       "Reading<8> %02x %02x %02x %02x %02x %02x %02x %02x\n",
659                       buffer[idx + 0], buffer[idx + 1],
660                       buffer[idx + 2], buffer[idx + 3],
661                       buffer[idx + 4], buffer[idx + 5],
662                       buffer[idx + 6], buffer[idx + 7]);
663                       idx += 8;
664             } else {
665                 switch (buffer[idx + 0]) {
666                 case SEQ_WAIT:
667                 case SEQ_ECHO:
668                     break;
669                 case SEQ_MIDIPUTC:
670                     midReceiveChar(buffer[idx + 2], buffer[idx + 1], dwTime);
671                     break;
672                 default:
673                     TRACE("Unsupported event %d\n", buffer[idx + 0]);
674                     break;
675                 }
676                 idx += 4;
677             }
678         }
679     }
680     return 0;
681 }
682
683 /**************************************************************************
684  *                              midGetDevCaps                   [internal]
685  */
686 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
687 {
688     TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
689
690     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
691     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
692
693     memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
694
695     return MMSYSERR_NOERROR;
696 }
697
698 /**************************************************************************
699  *                      midOpen                                 [internal]
700  */
701 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
702 {
703     TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
704
705     if (lpDesc == NULL) {
706         WARN("Invalid Parameter !\n");
707         return MMSYSERR_INVALPARAM;
708     }
709
710     /* FIXME :
711      *  how to check that content of lpDesc is correct ?
712      */
713     if (wDevID >= MIDM_NumDevs) {
714         WARN("wDevID too large (%u) !\n", wDevID);
715         return MMSYSERR_BADDEVICEID;
716     }
717     if (MidiInDev[wDevID].state == -1) {        
718         WARN("device disabled\n");
719         return MIDIERR_NODEVICE;
720     }
721     if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
722         WARN("device already open !\n");
723         return MMSYSERR_ALLOCATED;
724     }
725     if ((dwFlags & MIDI_IO_STATUS) != 0) {
726         WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
727         dwFlags &= ~MIDI_IO_STATUS;
728     }
729     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
730         FIXME("Bad dwFlags\n");
731         return MMSYSERR_INVALFLAG;
732     }
733
734     if (midiOpenSeq() < 0) {
735         return MMSYSERR_ERROR;
736     }
737
738     if (numStartedMidiIn++ == 0) {
739         end_thread = 0;
740         hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
741         if (!hThread) {
742             numStartedMidiIn = 0;
743             WARN("Couldn't create thread for midi-in\n");
744             midiCloseSeq();
745             return MMSYSERR_ERROR;
746         }
747         SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
748         TRACE("Created thread for midi-in\n");
749     }
750
751     MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
752
753     MidiInDev[wDevID].lpQueueHdr = NULL;
754     MidiInDev[wDevID].dwTotalPlayed = 0;
755     MidiInDev[wDevID].bufsize = 0x3FFF;
756     MidiInDev[wDevID].midiDesc = *lpDesc;
757     MidiInDev[wDevID].state = 0;
758     MidiInDev[wDevID].incLen = 0;
759     MidiInDev[wDevID].startTime = 0;
760
761     if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
762         WARN("can't notify client !\n");
763         return MMSYSERR_INVALPARAM;
764     }
765     return MMSYSERR_NOERROR;
766 }
767
768 /**************************************************************************
769  *                      midClose                                [internal]
770  */
771 static DWORD midClose(WORD wDevID)
772 {
773     int         ret = MMSYSERR_NOERROR;
774
775     TRACE("(%04X);\n", wDevID);
776
777     if (wDevID >= MIDM_NumDevs) {
778         WARN("wDevID too big (%u) !\n", wDevID);
779         return MMSYSERR_BADDEVICEID;
780     }
781     if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
782         WARN("device not opened !\n");
783         return MMSYSERR_ERROR;
784     }
785     if (MidiInDev[wDevID].lpQueueHdr != 0) {
786         return MIDIERR_STILLPLAYING;
787     }
788
789     if (midiSeqFD == -1) {
790         WARN("ooops !\n");
791         return MMSYSERR_ERROR;
792     }
793     if (--numStartedMidiIn == 0) {
794         TRACE("Stopping thread for midi-in\n");
795         end_thread = 1;
796         if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
797             WARN("Thread end not signaled, force termination\n");
798             TerminateThread(hThread, 0);
799         }
800         TRACE("Stopped thread for midi-in\n");
801     }
802     midiCloseSeq();
803
804     MidiInDev[wDevID].bufsize = 0;
805     if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
806         WARN("can't notify client !\n");
807         ret = MMSYSERR_INVALPARAM;
808     }
809     MidiInDev[wDevID].midiDesc.hMidi = 0;
810     return ret;
811 }
812
813 /**************************************************************************
814  *                              midAddBuffer                    [internal]
815  */
816 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
817 {
818     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
819
820     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
821     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
822
823     if (lpMidiHdr == NULL)      return MMSYSERR_INVALPARAM;
824     if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
825     if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
826     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
827     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
828
829     EnterCriticalSection(&crit_sect);
830     if (MidiInDev[wDevID].lpQueueHdr == 0) {
831         MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
832     } else {
833         LPMIDIHDR       ptr;
834
835         for (ptr = MidiInDev[wDevID].lpQueueHdr;
836              ptr->lpNext != 0;
837              ptr = (LPMIDIHDR)ptr->lpNext);
838         ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
839     }
840     LeaveCriticalSection(&crit_sect);
841
842     return MMSYSERR_NOERROR;
843 }
844
845 /**************************************************************************
846  *                              midPrepare                      [internal]
847  */
848 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
849 {
850     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
851
852     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
853         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
854         lpMidiHdr->dwBufferLength >= 0x10000ul)
855         return MMSYSERR_INVALPARAM;
856
857     lpMidiHdr->lpNext = 0;
858     lpMidiHdr->dwFlags |= MHDR_PREPARED;
859     lpMidiHdr->dwBytesRecorded = 0;
860
861     return MMSYSERR_NOERROR;
862 }
863
864 /**************************************************************************
865  *                              midUnprepare                    [internal]
866  */
867 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
868 {
869     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
870
871     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
872     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
873
874     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
875         lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
876         return MMSYSERR_INVALPARAM;
877
878     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
879     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
880
881     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
882
883     return MMSYSERR_NOERROR;
884 }
885
886 /**************************************************************************
887  *                      midReset                                [internal]
888  */
889 static DWORD midReset(WORD wDevID)
890 {
891     DWORD               dwTime = GetTickCount();
892
893     TRACE("(%04X);\n", wDevID);
894
895     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
896     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
897
898     EnterCriticalSection(&crit_sect);
899     while (MidiInDev[wDevID].lpQueueHdr) {
900         MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
901         MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
902         /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
903         if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
904                               (DWORD)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
905             WARN("Couldn't notify client\n");
906         }
907         MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
908     }
909     LeaveCriticalSection(&crit_sect);
910
911     return MMSYSERR_NOERROR;
912 }
913
914
915 /**************************************************************************
916  *                      midStart                                [internal]
917  */
918 static DWORD midStart(WORD wDevID)
919 {
920     TRACE("(%04X);\n", wDevID);
921
922     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
923     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
924
925     MidiInDev[wDevID].state = 1;
926     MidiInDev[wDevID].startTime = GetTickCount();
927     return MMSYSERR_NOERROR;
928 }
929
930 /**************************************************************************
931  *                      midStop                                 [internal]
932  */
933 static DWORD midStop(WORD wDevID)
934 {
935     TRACE("(%04X);\n", wDevID);
936
937     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
938     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
939
940     MidiInDev[wDevID].state = 0;
941     return MMSYSERR_NOERROR;
942 }
943
944 /*-----------------------------------------------------------------------*/
945
946 typedef struct sVoice {
947     int                 note;                   /* 0 means not used */
948     int                 channel;
949     unsigned            cntMark : 30,
950                         status : 2;
951 #define sVS_UNUSED      0
952 #define sVS_PLAYING     1
953 #define sVS_SUSTAINED   2
954 } sVoice;
955
956 typedef struct sChannel {
957     int                 program;
958
959     int                 bender;
960     int                 benderRange;
961     /* controlers */
962     int                 bank;           /* CTL_BANK_SELECT */
963     int                 volume;         /* CTL_MAIN_VOLUME */
964     int                 balance;        /* CTL_BALANCE     */
965     int                 expression;     /* CTL_EXPRESSION  */
966     int                 sustain;        /* CTL_SUSTAIN     */
967
968     unsigned char       nrgPmtMSB;      /* Non register Parameters */
969     unsigned char       nrgPmtLSB;
970     unsigned char       regPmtMSB;      /* Non register Parameters */
971     unsigned char       regPmtLSB;
972 } sChannel;
973
974 typedef struct sFMextra {
975     unsigned            counter;
976     int                 drumSetMask;
977     sChannel            channel[16];    /* MIDI has only 16 channels */
978     sVoice              voice[1];       /* dyn allocated according to sound card */
979     /* do not append fields below voice[1] since the size of this structure
980      * depends on the number of available voices on the FM synth...
981      */
982 } sFMextra;
983
984 extern  unsigned char midiFMInstrumentPatches[16 * 128];
985 extern  unsigned char midiFMDrumsPatches     [16 * 128];
986
987 /**************************************************************************
988  *                      modFMLoad                               [internal]
989  */
990 static int modFMLoad(int dev)
991 {
992     int                         i;
993     struct sbi_instrument       sbi;
994
995     sbi.device = dev;
996     sbi.key = FM_PATCH;
997
998     memset(sbi.operators + 16, 0, 16);
999     for (i = 0; i < 128; i++) {
1000         sbi.channel = i;
1001         memcpy(sbi.operators, midiFMInstrumentPatches + i * 16, 16);
1002
1003         if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
1004             WARN("Couldn't write patch for instrument %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
1005             return -1;
1006         }
1007     }
1008     for (i = 0; i < 128; i++) {
1009         sbi.channel = 128 + i;
1010         memcpy(sbi.operators, midiFMDrumsPatches + i * 16, 16);
1011
1012         if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
1013             WARN("Couldn't write patch for drum %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
1014             return -1;
1015         }
1016     }
1017     return 0;
1018 }
1019
1020 /**************************************************************************
1021  *                      modFMReset                              [internal]
1022  */
1023 static  void modFMReset(WORD wDevID)
1024 {
1025     sFMextra*   extra   = (sFMextra*)MidiOutDev[wDevID].lpExtra;
1026     sVoice*     voice   = extra->voice;
1027     sChannel*   channel = extra->channel;
1028     int         i;
1029
1030     for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1031         if (voice[i].status != sVS_UNUSED) {
1032             SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1033         }
1034         SEQ_KEY_PRESSURE(wDevID, i, 127, 0);
1035         SEQ_CONTROL(wDevID, i, SEQ_VOLMODE, VOL_METHOD_LINEAR);
1036         voice[i].note = 0;
1037         voice[i].channel = -1;
1038         voice[i].cntMark = 0;
1039         voice[i].status = sVS_UNUSED;
1040     }
1041     for (i = 0; i < 16; i++) {
1042         channel[i].program = 0;
1043         channel[i].bender = 8192;
1044         channel[i].benderRange = 2;
1045         channel[i].bank = 0;
1046         channel[i].volume = 127;
1047         channel[i].balance = 64;
1048         channel[i].expression = 0;
1049         channel[i].sustain = 0;
1050     }
1051     extra->counter = 0;
1052     extra->drumSetMask = 1 << 9; /* channel 10 is normally drums, sometimes 16 also */
1053     SEQ_DUMPBUF();
1054 }
1055
1056 #define         IS_DRUM_CHANNEL(_xtra, _chn)    ((_xtra)->drumSetMask & (1 << (_chn)))
1057
1058 /**************************************************************************
1059  *                              modGetDevCaps                   [internal]
1060  */
1061 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
1062 {
1063     TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
1064
1065     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1066     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
1067
1068     memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1069
1070     return MMSYSERR_NOERROR;
1071 }
1072
1073 /**************************************************************************
1074  *                      modOpen                                 [internal]
1075  */
1076 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
1077 {
1078     TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1079     if (lpDesc == NULL) {
1080         WARN("Invalid Parameter !\n");
1081         return MMSYSERR_INVALPARAM;
1082     }
1083     if (wDevID >= MODM_NumDevs) {
1084         TRACE("MAX_MIDIOUTDRV reached !\n");
1085         return MMSYSERR_BADDEVICEID;
1086     }
1087     if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
1088         WARN("device already open !\n");
1089         return MMSYSERR_ALLOCATED;
1090     }
1091     if (!MidiOutDev[wDevID].bEnabled) {
1092         WARN("device disabled !\n");
1093         return MIDIERR_NODEVICE;
1094     }
1095     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
1096         WARN("bad dwFlags\n");
1097         return MMSYSERR_INVALFLAG;
1098     }
1099     if (!MidiOutDev[wDevID].bEnabled) {
1100         TRACE("disabled wDevID\n");
1101         return MMSYSERR_NOTENABLED;
1102     }
1103
1104     MidiOutDev[wDevID].lpExtra = 0;
1105
1106     switch (MidiOutDev[wDevID].caps.wTechnology) {
1107     case MOD_FMSYNTH:
1108         {
1109             void*       extra;
1110
1111             extra = HeapAlloc(GetProcessHeap(), 0,
1112                               sizeof(struct sFMextra) +
1113                               sizeof(struct sVoice) * (MidiOutDev[wDevID].caps.wVoices - 1));
1114
1115             if (extra == 0) {
1116                 WARN("can't alloc extra data !\n");
1117                 return MMSYSERR_NOMEM;
1118             }
1119             MidiOutDev[wDevID].lpExtra = extra;
1120             if (midiOpenSeq() < 0) {
1121                 MidiOutDev[wDevID].lpExtra = 0;
1122                 HeapFree(GetProcessHeap(), 0, extra);
1123                 return MMSYSERR_ERROR;
1124             }
1125             if (modFMLoad(wDevID) < 0) {
1126                 midiCloseSeq();
1127                 MidiOutDev[wDevID].lpExtra = 0;
1128                 HeapFree(GetProcessHeap(), 0, extra);
1129                 return MMSYSERR_ERROR;
1130             }
1131             modFMReset(wDevID);
1132         }
1133         break;
1134     case MOD_MIDIPORT:
1135     case MOD_SYNTH:
1136         if (midiOpenSeq() < 0) {
1137             return MMSYSERR_ALLOCATED;
1138         }
1139         break;
1140     default:
1141         WARN("Technology not supported (yet) %d !\n",
1142              MidiOutDev[wDevID].caps.wTechnology);
1143         return MMSYSERR_NOTENABLED;
1144     }
1145
1146     MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1147
1148     MidiOutDev[wDevID].lpQueueHdr = NULL;
1149     MidiOutDev[wDevID].dwTotalPlayed = 0;
1150     MidiOutDev[wDevID].bufsize = 0x3FFF;
1151     MidiOutDev[wDevID].midiDesc = *lpDesc;
1152
1153     if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1154         WARN("can't notify client !\n");
1155         return MMSYSERR_INVALPARAM;
1156     }
1157     TRACE("Successful !\n");
1158     return MMSYSERR_NOERROR;
1159 }
1160
1161
1162 /**************************************************************************
1163  *                      modClose                                [internal]
1164  */
1165 static DWORD modClose(WORD wDevID)
1166 {
1167     int ret = MMSYSERR_NOERROR;
1168
1169     TRACE("(%04X);\n", wDevID);
1170
1171     if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
1172         WARN("device not opened !\n");
1173         return MMSYSERR_ERROR;
1174     }
1175     /* FIXME: should test that no pending buffer is still in the queue for
1176      * playing */
1177
1178     if (midiSeqFD == -1) {
1179         WARN("can't close !\n");
1180         return MMSYSERR_ERROR;
1181     }
1182
1183     switch (MidiOutDev[wDevID].caps.wTechnology) {
1184     case MOD_FMSYNTH:
1185     case MOD_MIDIPORT:
1186         midiCloseSeq();
1187         break;
1188     default:
1189         WARN("Technology not supported (yet) %d !\n",
1190              MidiOutDev[wDevID].caps.wTechnology);
1191         return MMSYSERR_NOTENABLED;
1192     }
1193
1194     HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
1195     MidiOutDev[wDevID].lpExtra = 0;
1196
1197     MidiOutDev[wDevID].bufsize = 0;
1198     if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1199         WARN("can't notify client !\n");
1200         ret = MMSYSERR_INVALPARAM;
1201     }
1202     MidiOutDev[wDevID].midiDesc.hMidi = 0;
1203     return ret;
1204 }
1205
1206 /**************************************************************************
1207  *                      modData                                 [internal]
1208  */
1209 static DWORD modData(WORD wDevID, DWORD dwParam)
1210 {
1211     WORD        evt = LOBYTE(LOWORD(dwParam));
1212     WORD        d1  = HIBYTE(LOWORD(dwParam));
1213     WORD        d2  = LOBYTE(HIWORD(dwParam));
1214
1215     TRACE("(%04X, %08lX);\n", wDevID, dwParam);
1216
1217     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1218     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1219
1220     if (midiSeqFD == -1) {
1221         WARN("can't play !\n");
1222         return MIDIERR_NODEVICE;
1223     }
1224     switch (MidiOutDev[wDevID].caps.wTechnology) {
1225     case MOD_FMSYNTH:
1226         /* FIXME:
1227          *      - chorus depth controller is not used
1228          */
1229         {
1230             sFMextra*   extra   = (sFMextra*)MidiOutDev[wDevID].lpExtra;
1231             sVoice*     voice   = extra->voice;
1232             sChannel*   channel = extra->channel;
1233             int         chn = (evt & 0x0F);
1234             int         i, nv;
1235
1236             switch (evt & 0xF0) {
1237             case MIDI_NOTEOFF:
1238                 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1239                                 /* don't stop sustained notes */
1240                     if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1241                         voice[i].status = sVS_UNUSED;
1242                         SEQ_STOP_NOTE(wDevID, i, d1, d2);
1243                     }
1244                 }
1245                 break;
1246             case MIDI_NOTEON:
1247                 if (d2 == 0) { /* note off if velocity == 0 */
1248                     for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1249                         /* don't stop sustained notes */
1250                         if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1251                             voice[i].status = sVS_UNUSED;
1252                             SEQ_STOP_NOTE(wDevID, i, d1, 64);
1253                         }
1254                     }
1255                     break;
1256                 }
1257                 /* finding out in this order :
1258                  *      - an empty voice
1259                  *      - if replaying the same note on the same channel
1260                  *      - the older voice (LRU)
1261                  */
1262                 for (i = nv = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1263                     if (voice[i].status == sVS_UNUSED ||
1264                         (voice[i].note == d1 && voice[i].channel == chn)) {
1265                         nv = i;
1266                         break;
1267                     }
1268                     if (voice[i].cntMark < voice[0].cntMark) {
1269                         nv = i;
1270                     }
1271                 }
1272                 TRACE(
1273                       "playing on voice=%d, pgm=%d, pan=0x%02X, vol=0x%02X, "
1274                       "bender=0x%02X, note=0x%02X, vel=0x%02X\n",
1275                       nv, channel[chn].program,
1276                       channel[chn].balance,
1277                       channel[chn].volume,
1278                       channel[chn].bender, d1, d2);
1279
1280                 SEQ_SET_PATCH(wDevID, nv, IS_DRUM_CHANNEL(extra, chn) ?
1281                               (128 + d1) : channel[chn].program);
1282                 SEQ_BENDER_RANGE(wDevID, nv, channel[chn].benderRange * 100);
1283                 SEQ_BENDER(wDevID, nv, channel[chn].bender);
1284                 SEQ_CONTROL(wDevID, nv, CTL_PAN, channel[chn].balance);
1285                 SEQ_CONTROL(wDevID, nv, CTL_EXPRESSION, channel[chn].expression);
1286 #if 0
1287                 /* FIXME: does not really seem to work on my SB card and
1288                  * screws everything up... so lay it down
1289                  */
1290                 SEQ_CONTROL(wDevID, nv, CTL_MAIN_VOLUME, channel[chn].volume);
1291 #endif
1292                 SEQ_START_NOTE(wDevID, nv, d1, d2);
1293                 voice[nv].status = channel[chn].sustain ? sVS_SUSTAINED : sVS_PLAYING;
1294                 voice[nv].note = d1;
1295                 voice[nv].channel = chn;
1296                 voice[nv].cntMark = extra->counter++;
1297                 break;
1298             case MIDI_KEY_PRESSURE:
1299                 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1300                     if (voice[i].status != sVS_UNUSED && voice[i].channel == chn && voice[i].note == d1) {
1301                         SEQ_KEY_PRESSURE(wDevID, i, d1, d2);
1302                     }
1303                 }
1304                 break;
1305             case MIDI_CTL_CHANGE:
1306                 switch (d1) {
1307                 case CTL_BANK_SELECT:   channel[chn].bank = d2;         break;
1308                 case CTL_MAIN_VOLUME:   channel[chn].volume = d2;       break;
1309                 case CTL_PAN:           channel[chn].balance = d2;      break;
1310                 case CTL_EXPRESSION:    channel[chn].expression = d2;   break;
1311                 case CTL_SUSTAIN:       channel[chn].sustain = d2;
1312                     if (d2) {
1313                         for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1314                             if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1315                                 voice[i].status = sVS_SUSTAINED;
1316                             }
1317                         }
1318                     } else {
1319                         for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1320                             if (voice[i].status == sVS_SUSTAINED && voice[i].channel == chn) {
1321                                 voice[i].status = sVS_UNUSED;
1322                                 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1323                             }
1324                         }
1325                     }
1326                     break;
1327                 case CTL_NONREG_PARM_NUM_LSB:   channel[chn].nrgPmtLSB = d2;    break;
1328                 case CTL_NONREG_PARM_NUM_MSB:   channel[chn].nrgPmtMSB = d2;    break;
1329                 case CTL_REGIST_PARM_NUM_LSB:   channel[chn].regPmtLSB = d2;    break;
1330                 case CTL_REGIST_PARM_NUM_MSB:   channel[chn].regPmtMSB = d2;    break;
1331                 case CTL_DATA_ENTRY:
1332                     switch ((channel[chn].regPmtMSB << 8) | channel[chn].regPmtLSB) {
1333                     case 0x0000:
1334                         if (channel[chn].benderRange != d2) {
1335                             channel[chn].benderRange = d2;
1336                             for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1337                                 if (voice[i].channel == chn) {
1338                                     SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1339                                 }
1340                             }
1341                         }
1342                         break;
1343
1344                     case 0x7F7F:
1345                         channel[chn].benderRange = 2;
1346                         for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1347                             if (voice[i].channel == chn) {
1348                                 SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1349                             }
1350                         }
1351                         break;
1352                     default:
1353                         TRACE("Data entry: regPmt=0x%02x%02x, nrgPmt=0x%02x%02x with %x\n",
1354                               channel[chn].regPmtMSB, channel[chn].regPmtLSB,
1355                               channel[chn].nrgPmtMSB, channel[chn].nrgPmtLSB,
1356                               d2);
1357                         break;
1358                     }
1359                     break;
1360
1361                 case 0x78: /* all sounds off */
1362                     /* FIXME: I don't know if I have to take care of the channel
1363                      * for this control ?
1364                      */
1365                     for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1366                         if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1367                             voice[i].status = sVS_UNUSED;
1368                             SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1369                         }
1370                     }
1371                     break;
1372                 case 0x7B: /* all notes off */
1373                     /* FIXME: I don't know if I have to take care of the channel
1374                      * for this control ?
1375                      */
1376                     for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1377                         if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1378                             voice[i].status = sVS_UNUSED;
1379                             SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1380                         }
1381                     }
1382                     break;
1383                 default:
1384                     TRACE("Dropping MIDI control event 0x%02x(%02x) on channel %d\n",
1385                           d1, d2, chn);
1386                     break;
1387                 }
1388                 break;
1389             case MIDI_PGM_CHANGE:
1390                 channel[chn].program = d1;
1391                 break;
1392             case MIDI_CHN_PRESSURE:
1393                 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1394                     if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1395                         SEQ_KEY_PRESSURE(wDevID, i, voice[i].note, d1);
1396                     }
1397                 }
1398                 break;
1399             case MIDI_PITCH_BEND:
1400                 channel[chn].bender = (d2 << 7) + d1;
1401                 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1402                     if (voice[i].channel == chn) {
1403                         SEQ_BENDER(wDevID, i, channel[chn].bender);
1404                     }
1405                 }
1406                 break;
1407             case MIDI_SYSTEM_PREFIX:
1408                 switch (evt & 0x0F) {
1409                 case 0x0F:      /* Reset */
1410                     modFMReset(wDevID);
1411                     break;
1412                 default:
1413                     WARN("Unsupported (yet) system event %02x\n", evt & 0x0F);
1414                 }
1415                 break;
1416             default:
1417                 WARN("Internal error, shouldn't happen (event=%08x)\n", evt & 0xF0);
1418                 return MMSYSERR_NOTENABLED;
1419             }
1420         }
1421         break;
1422     case MOD_MIDIPORT:
1423         {
1424             int dev = wDevID - MODM_NumFMSynthDevs;
1425             if (dev < 0) {
1426                 WARN("Internal error on devID (%u) !\n", wDevID);
1427                 return MIDIERR_NODEVICE;
1428             }
1429
1430             switch (evt & 0xF0) {
1431             case MIDI_NOTEOFF:
1432             case MIDI_NOTEON:
1433             case MIDI_KEY_PRESSURE:
1434             case MIDI_CTL_CHANGE:
1435             case MIDI_PITCH_BEND:
1436                 SEQ_MIDIOUT(dev, evt);
1437                 SEQ_MIDIOUT(dev, d1);
1438                 SEQ_MIDIOUT(dev, d2);
1439                 break;
1440             case MIDI_PGM_CHANGE:
1441             case MIDI_CHN_PRESSURE:
1442                 SEQ_MIDIOUT(dev, evt);
1443                 SEQ_MIDIOUT(dev, d1);
1444                 break;
1445             case MIDI_SYSTEM_PREFIX:
1446                 switch (evt & 0x0F) {
1447                 case 0x00:      /* System Exclusive, don't do it on modData,
1448                                  * should require modLongData*/
1449                 case 0x01:      /* Undefined */
1450                 case 0x04:      /* Undefined. */
1451                 case 0x05:      /* Undefined. */
1452                 case 0x07:      /* End of Exclusive. */
1453                 case 0x09:      /* Undefined. */
1454                 case 0x0D:      /* Undefined. */
1455                     break;
1456                 case 0x06:      /* Tune Request */
1457                 case 0x08:      /* Timing Clock. */
1458                 case 0x0A:      /* Start. */
1459                 case 0x0B:      /* Continue */
1460                 case 0x0C:      /* Stop */
1461                 case 0x0E:      /* Active Sensing. */
1462                     SEQ_MIDIOUT(dev, evt);
1463                     break;
1464                 case 0x0F:      /* Reset */
1465                                 /* SEQ_MIDIOUT(dev, evt);
1466                                    this other way may be better */
1467                     SEQ_MIDIOUT(dev, MIDI_SYSTEM_PREFIX);
1468                     SEQ_MIDIOUT(dev, 0x7e);
1469                     SEQ_MIDIOUT(dev, 0x7f);
1470                     SEQ_MIDIOUT(dev, 0x09);
1471                     SEQ_MIDIOUT(dev, 0x01);
1472                     SEQ_MIDIOUT(dev, 0xf7);
1473                     break;
1474                 case 0x03:      /* Song Select. */
1475                     SEQ_MIDIOUT(dev, evt);
1476                     SEQ_MIDIOUT(dev, d1);
1477                 case 0x02:      /* Song Position Pointer. */
1478                     SEQ_MIDIOUT(dev, evt);
1479                     SEQ_MIDIOUT(dev, d1);
1480                     SEQ_MIDIOUT(dev, d2);
1481                 }
1482                 break;
1483             }
1484         }
1485         break;
1486     default:
1487         WARN("Technology not supported (yet) %d !\n",
1488              MidiOutDev[wDevID].caps.wTechnology);
1489         return MMSYSERR_NOTENABLED;
1490     }
1491
1492     SEQ_DUMPBUF();
1493
1494     return MMSYSERR_NOERROR;
1495 }
1496
1497 /**************************************************************************
1498  *              modLongData                                     [internal]
1499  */
1500 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1501 {
1502     int         count;
1503     LPBYTE      lpData;
1504
1505     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1506
1507     /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
1508      * but it seems to be used only for midi input.
1509      * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
1510      */
1511     
1512     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1513     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1514
1515     if (midiSeqFD == -1) {
1516         WARN("can't play !\n");
1517         return MIDIERR_NODEVICE;
1518     }
1519
1520     lpData = (LPBYTE) lpMidiHdr->lpData;
1521
1522     if (lpData == NULL)
1523         return MIDIERR_UNPREPARED;
1524     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1525         return MIDIERR_UNPREPARED;
1526     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1527         return MIDIERR_STILLPLAYING;
1528     lpMidiHdr->dwFlags &= ~MHDR_DONE;
1529     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1530
1531     /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
1532      * data, or can it also contain raw MIDI data, to be split up and sent to
1533      * modShortData() ?
1534      * If the latest is true, then the following WARNing will fire up
1535      */
1536     if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
1537         WARN("Alledged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
1538     }
1539
1540     TRACE("dwBufferLength=%lu !\n", lpMidiHdr->dwBufferLength);
1541     TRACE("                 %02X %02X %02X ... %02X %02X %02X\n",
1542           lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
1543           lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
1544
1545     switch (MidiOutDev[wDevID].caps.wTechnology) {
1546     case MOD_FMSYNTH:
1547         /* FIXME: I don't think there is much to do here */
1548         break;
1549     case MOD_MIDIPORT:
1550         if (lpData[0] != 0xF0) {
1551             /* Send end of System Exclusive */
1552             SEQ_MIDIOUT(wDevID - MODM_NumFMSynthDevs, 0xF0);
1553             WARN("Adding missing 0xF0 marker at the beginning of "
1554                  "system exclusive byte stream\n");
1555         }
1556         for (count = 0; count < lpMidiHdr->dwBufferLength; count++) {
1557             SEQ_MIDIOUT(wDevID - MODM_NumFMSynthDevs, lpData[count]);
1558         }
1559         if (lpData[count - 1] != 0xF7) {
1560             /* Send end of System Exclusive */
1561             SEQ_MIDIOUT(wDevID - MODM_NumFMSynthDevs, 0xF7);
1562             WARN("Adding missing 0xF7 marker at the end of "
1563                  "system exclusive byte stream\n");
1564         }
1565         SEQ_DUMPBUF();
1566         break;
1567     default:
1568         WARN("Technology not supported (yet) %d !\n",
1569              MidiOutDev[wDevID].caps.wTechnology);
1570         return MMSYSERR_NOTENABLED;
1571     }
1572
1573     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1574     lpMidiHdr->dwFlags |= MHDR_DONE;
1575     if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1576         WARN("can't notify client !\n");
1577         return MMSYSERR_INVALPARAM;
1578     }
1579     return MMSYSERR_NOERROR;
1580 }
1581
1582 /**************************************************************************
1583  *                      modPrepare                              [internal]
1584  */
1585 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1586 {
1587     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1588
1589     if (midiSeqFD == -1) {
1590         WARN("can't prepare !\n");
1591         return MMSYSERR_NOTENABLED;
1592     }
1593
1594     /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1595      * asks to prepare MIDIHDR which dwFlags != 0.
1596      * So at least check for the inqueue flag
1597      */
1598     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1599         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
1600         lpMidiHdr->dwBufferLength >= 0x10000ul) {
1601         WARN("%p %p %08lx %d/%ld\n", lpMidiHdr, lpMidiHdr->lpData,
1602                    lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
1603         return MMSYSERR_INVALPARAM;
1604     }
1605
1606     lpMidiHdr->lpNext = 0;
1607     lpMidiHdr->dwFlags |= MHDR_PREPARED;
1608     lpMidiHdr->dwFlags &= ~MHDR_DONE;
1609     return MMSYSERR_NOERROR;
1610 }
1611
1612 /**************************************************************************
1613  *                              modUnprepare                    [internal]
1614  */
1615 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1616 {
1617     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1618
1619     if (midiSeqFD == -1) {
1620         WARN("can't unprepare !\n");
1621         return MMSYSERR_NOTENABLED;
1622     }
1623
1624     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1625         return MMSYSERR_INVALPARAM;
1626     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1627         return MIDIERR_STILLPLAYING;
1628     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1629     return MMSYSERR_NOERROR;
1630 }
1631
1632 /**************************************************************************
1633  *                      modReset                                [internal]
1634  */
1635 static DWORD modReset(WORD wDevID)
1636 {
1637     unsigned chn;
1638
1639     TRACE("(%04X);\n", wDevID);
1640
1641     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1642     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1643
1644     /* stop all notes */
1645     /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1646      * it's channel dependent...
1647      */
1648     for (chn = 0; chn < 16; chn++) {
1649         /* turn off every note */
1650         modData(wDevID, 0x7800 | MIDI_CTL_CHANGE | chn);
1651         /* remove sustain on all channels */
1652         modData(wDevID, (CTL_SUSTAIN << 8) | MIDI_CTL_CHANGE | chn);
1653     }
1654     /* FIXME: the LongData buffers must also be returned to the app */
1655     return MMSYSERR_NOERROR;
1656 }
1657
1658 #else /* HAVE_OSS_MIDI */
1659
1660 BOOL OSS_MidiInit(void)
1661 {
1662     return FALSE;
1663 }
1664
1665 #endif /* HAVE_OSS_MIDI */
1666
1667 /*======================================================================*
1668  *                          MIDI entry points                           *
1669  *======================================================================*/
1670
1671 /**************************************************************************
1672  *                      midMessage (WINEOSS.4)
1673  */
1674 DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1675                             DWORD dwParam1, DWORD dwParam2)
1676 {
1677     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1678           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1679     switch (wMsg) {
1680 #ifdef HAVE_OSS_MIDI
1681     case DRVM_INIT:
1682     case DRVM_EXIT:
1683     case DRVM_ENABLE:
1684     case DRVM_DISABLE:
1685         /* FIXME: Pretend this is supported */
1686         return 0;
1687     case MIDM_OPEN:
1688         return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1689     case MIDM_CLOSE:
1690         return midClose(wDevID);
1691     case MIDM_ADDBUFFER:
1692         return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1693     case MIDM_PREPARE:
1694         return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1695     case MIDM_UNPREPARE:
1696         return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1697     case MIDM_GETDEVCAPS:
1698         return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1699     case MIDM_GETNUMDEVS:
1700         return MIDM_NumDevs;
1701     case MIDM_RESET:
1702         return midReset(wDevID);
1703     case MIDM_START:
1704         return midStart(wDevID);
1705     case MIDM_STOP:
1706         return midStop(wDevID);
1707 #endif
1708     default:
1709         TRACE("Unsupported message\n");
1710     }
1711     return MMSYSERR_NOTSUPPORTED;
1712 }
1713
1714 /**************************************************************************
1715  *                              modMessage (WINEOSS.5)
1716  */
1717 DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1718                             DWORD dwParam1, DWORD dwParam2)
1719 {
1720     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1721           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1722
1723     switch (wMsg) {
1724 #ifdef HAVE_OSS_MIDI
1725     case DRVM_INIT:
1726     case DRVM_EXIT:
1727     case DRVM_ENABLE:
1728     case DRVM_DISABLE:
1729         /* FIXME: Pretend this is supported */
1730         return 0;
1731     case MODM_OPEN:
1732         return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1733     case MODM_CLOSE:
1734         return modClose(wDevID);
1735     case MODM_DATA:
1736         return modData(wDevID, dwParam1);
1737     case MODM_LONGDATA:
1738         return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1739     case MODM_PREPARE:
1740         return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1741     case MODM_UNPREPARE:
1742         return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1743     case MODM_GETDEVCAPS:
1744         return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1745     case MODM_GETNUMDEVS:
1746         return MODM_NumDevs;
1747     case MODM_GETVOLUME:
1748         return 0;
1749     case MODM_SETVOLUME:
1750         return 0;
1751     case MODM_RESET:
1752         return modReset(wDevID);
1753 #endif
1754     default:
1755         TRACE("Unsupported message\n");
1756     }
1757     return MMSYSERR_NOTSUPPORTED;
1758 }
1759
1760 /*-----------------------------------------------------------------------*/