Do not check for non NULL pointer before HeapFree'ing it. It's
[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
44 #include <string.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #ifdef HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif
50 #include <fcntl.h>
51 #include <errno.h>
52 #ifdef HAVE_SYS_IOCTL_H
53 # include <sys/ioctl.h>
54 #endif
55 #ifdef HAVE_SYS_POLL_H
56 #include <sys/poll.h>
57 #endif
58
59 #include "windef.h"
60 #include "winbase.h"
61 #include "wingdi.h"
62 #include "winuser.h"
63 #include "winnls.h"
64 #include "mmddk.h"
65 #include "oss.h"
66 #include "wine/unicode.h"
67 #include "wine/debug.h"
68
69 WINE_DEFAULT_DEBUG_CHANNEL(midi);
70
71 #ifdef HAVE_OSS_MIDI
72
73 #define MIDI_SEQ "/dev/sequencer"
74
75 typedef struct {
76     int                 state;                  /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
77     DWORD               bufsize;
78     MIDIOPENDESC        midiDesc;
79     WORD                wFlags;
80     LPMIDIHDR           lpQueueHdr;
81     DWORD               dwTotalPlayed;
82     unsigned char       incoming[3];
83     unsigned char       incPrev;
84     char                incLen;
85     DWORD               startTime;
86     MIDIINCAPSW         caps;
87 } WINE_MIDIIN;
88
89 typedef struct {
90     BOOL                bEnabled;
91     DWORD               bufsize;
92     MIDIOPENDESC        midiDesc;
93     WORD                wFlags;
94     LPMIDIHDR           lpQueueHdr;
95     DWORD               dwTotalPlayed;
96     void*               lpExtra;                /* according to port type (MIDI, FM...), extra data when needed */
97     MIDIOUTCAPSW        caps;
98 } WINE_MIDIOUT;
99
100 static WINE_MIDIIN      MidiInDev [MAX_MIDIINDRV ];
101 static WINE_MIDIOUT     MidiOutDev[MAX_MIDIOUTDRV];
102
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 */
108
109 /* this is the total number of MIDI out devices found */
110 static  int             MIDM_NumDevs = 0;
111
112 static  int             midiSeqFD = -1;
113 static  int             numOpenMidiSeq = 0;
114 static  int             numStartedMidiIn = 0;
115
116 static CRITICAL_SECTION crit_sect;   /* protects all MidiIn buffers queues */
117 static CRITICAL_SECTION_DEBUG critsect_debug =
118 {
119     0, 0, &crit_sect,
120     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
121       0, 0, { 0, (DWORD)(__FILE__ ": crit_sect") }
122 };
123 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
124
125 static int end_thread;
126 static HANDLE hThread;
127
128 /*======================================================================*
129  *                  Low level MIDI implementation                       *
130  *======================================================================*/
131
132 static int midiOpenSeq(void);
133 static int midiCloseSeq(void);
134
135 /**************************************************************************
136  *                      MIDI_unixToWindowsDeviceType            [internal]
137  *
138  * return the Windows equivalent to a Unix Device Type
139  *
140  */
141 static  int     MIDI_UnixToWindowsDeviceType(int type)
142 {
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
150      */
151
152     /* FIXME Is this really the correct equivalence from UNIX to
153        Windows Sound type */
154
155     switch (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;
159     default:
160         ERR("Cannot determine the type of this midi device. "
161             "Assuming FM Synth\n");
162         return MOD_FMSYNTH;
163     }
164 }
165
166 /**************************************************************************
167  *                      OSS_MidiInit                            [internal]
168  *
169  * Initializes the MIDI devices information variables
170  */
171 BOOL OSS_MidiInit(void)
172 {
173     int                 i, status, numsynthdevs = 255, nummididevs = 255;
174     struct synth_info   sinfo;
175     struct midi_info    minfo;
176     static      BOOL    bInitDone = FALSE;
177
178     if (bInitDone)
179         return TRUE;
180
181     TRACE("Initializing the MIDI variables.\n");
182     bInitDone = TRUE;
183
184     /* try to open device */
185     if (midiOpenSeq() == -1) {
186         return TRUE;
187     }
188
189     /* find how many Synth devices are there in the system */
190     status = ioctl(midiSeqFD, SNDCTL_SEQ_NRSYNTHS, &numsynthdevs);
191
192     if (status == -1) {
193         ERR("ioctl for nr synth failed.\n");
194         midiCloseSeq();
195         return TRUE;
196     }
197
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;
202     }
203
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.
208          */
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;
214
215         /* FIXME Do we have this information?
216          * Assuming the soundcards can handle
217          * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
218          * not MIDICAPS_CACHE.
219          */
220         MidiOutDev[i].caps.dwSupport      = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
221
222         sinfo.device = i;
223         status = ioctl(midiSeqFD, SNDCTL_SYNTH_INFO, &sinfo);
224         if (status == -1) {
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);
227
228             wsprintfW(MidiOutDev[i].caps.szPname, fmt, i);
229
230             MidiOutDev[i].caps.wTechnology = MOD_MIDIPORT;
231             MidiOutDev[i].caps.wVoices     = 16;
232             MidiOutDev[i].caps.wNotes      = 16;
233             MidiOutDev[i].bEnabled = FALSE;
234         } else {
235             MultiByteToWideChar( CP_ACP, 0, sinfo.name, -1,
236                                  MidiOutDev[i].caps.szPname,
237                                  sizeof(MidiOutDev[i].caps.szPname)/sizeof(WCHAR) );
238
239             MidiOutDev[i].caps.wTechnology = MIDI_UnixToWindowsDeviceType(sinfo.synth_type);
240             MidiOutDev[i].caps.wVoices     = sinfo.nr_voices;
241
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
246              */
247             MidiOutDev[i].caps.wNotes      = sinfo.nr_voices;
248             MidiOutDev[i].bEnabled = TRUE;
249         }
250
251         /* We also have the information sinfo.synth_subtype, not used here
252          */
253
254         if (sinfo.capabilities & SYNTH_CAP_INPUT) {
255             FIXME("Synthesizer support MIDI in. Not supported yet (please report)\n");
256         }
257
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);
265     }
266
267     /* find how many MIDI devices are there in the system */
268     status = ioctl(midiSeqFD, SNDCTL_SEQ_NRMIDIS, &nummididevs);
269     if (status == -1) {
270         ERR("ioctl on nr midi failed.\n");
271         nummididevs = 0;
272         goto wrapup;
273     }
274
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;
280     }
281
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;
286     }
287
288     for (i = 0; i < nummididevs; i++) {
289         minfo.device = i;
290         status = ioctl(midiSeqFD, SNDCTL_MIDI_INFO, &minfo);
291         if (status == -1) WARN("ioctl on midi info for device %d failed.\n", i);
292
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.
296         */
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;
304         if (status == -1) {
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;
308         } else {
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;
313         }
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;
320
321         /* FIXME Does it make any difference? */
322         MidiOutDev[numsynthdevs + i].caps.dwSupport   = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
323
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.
327         */
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;
335         if (status == -1) {
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;
339         } else {
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;
344         }
345         /* FIXME : could we get better information than that ? */
346         MidiInDev[i].caps.dwSupport   = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
347
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);
357     }
358
359  wrapup:
360     /* windows does not seem to differentiate Synth from MIDI devices */
361     MODM_NumFMSynthDevs = numsynthdevs;
362     MODM_NumDevs        = numsynthdevs + nummididevs;
363
364     MIDM_NumDevs        = nummididevs;
365
366     /* close file and exit */
367     midiCloseSeq();
368
369     return TRUE;
370 }
371
372 /**************************************************************************
373  *                      MIDI_NotifyClient                       [internal]
374  */
375 static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg,
376                                DWORD dwParam1, DWORD dwParam2)
377 {
378     DWORD               dwCallBack;
379     UINT                uFlags;
380     HANDLE              hDev;
381     DWORD               dwInstance;
382
383     TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
384           wDevID, wMsg, dwParam1, dwParam2);
385
386     switch (wMsg) {
387     case MOM_OPEN:
388     case MOM_CLOSE:
389     case MOM_DONE:
390     case MOM_POSITIONCB:
391         if (wDevID > MODM_NumDevs)
392             return MMSYSERR_BADDEVICEID;
393
394         dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
395         uFlags = MidiOutDev[wDevID].wFlags;
396         hDev = MidiOutDev[wDevID].midiDesc.hMidi;
397         dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
398         break;
399
400     case MIM_OPEN:
401     case MIM_CLOSE:
402     case MIM_DATA:
403     case MIM_LONGDATA:
404     case MIM_ERROR:
405     case MIM_LONGERROR:
406     case MIM_MOREDATA:
407         if (wDevID > MIDM_NumDevs)
408             return MMSYSERR_BADDEVICEID;
409
410         dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
411         uFlags = MidiInDev[wDevID].wFlags;
412         hDev = MidiInDev[wDevID].midiDesc.hMidi;
413         dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
414         break;
415     default:
416         WARN("Unsupported MSW-MIDI message %u\n", wMsg);
417         return MMSYSERR_ERROR;
418     }
419
420     return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
421         0 : MMSYSERR_ERROR;
422 }
423
424 static int midi_warn = 1;
425 /**************************************************************************
426  *                      midiOpenSeq                             [internal]
427  */
428 static int midiOpenSeq(void)
429 {
430     if (numOpenMidiSeq == 0) {
431         midiSeqFD = open(MIDI_SEQ, O_RDWR, 0);
432         if (midiSeqFD == -1) {
433             if (midi_warn)
434             {
435                 WARN("Can't open MIDI device '%s' ! (%s). If your "
436                         "program needs this (probably not): %s\n",
437                         MIDI_SEQ, strerror(errno),
438                         errno == ENOENT ?
439                         "create it ! (\"man MAKEDEV\" ?)" :
440                         errno == ENODEV ?
441                         "load MIDI sequencer kernel driver !" :
442                         errno == EACCES ?
443                         "grant access ! (\"man chmod\")" : ""
444                 );
445             }
446             midi_warn = 0;
447             return -1;
448         }
449 #if 0
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));
452             close(midiSeqFD);
453             midiSeqFD = -1;
454             return -1;
455         }
456 #endif
457         fcntl(midiSeqFD, F_SETFD, 1); /* set close on exec flag */
458         ioctl(midiSeqFD, SNDCTL_SEQ_RESET);
459     }
460     numOpenMidiSeq++;
461     return 0;
462 }
463
464 /**************************************************************************
465  *                      midiCloseSeq                            [internal]
466  */
467 static int midiCloseSeq(void)
468 {
469     if (--numOpenMidiSeq == 0) {
470         close(midiSeqFD);
471         midiSeqFD = -1;
472     }
473     return 0;
474 }
475
476 /* FIXME: this is a bad idea, it's even not static... */
477 SEQ_DEFINEBUF(1024);
478
479 /* FIXME: this is not reentrant, not static - because of global variable
480  * _seqbuf and al.
481  */
482 /**************************************************************************
483  *                      seqbuf_dump                             [internal]
484  *
485  * Used by SEQ_DUMPBUF to flush the buffer.
486  *
487  */
488 void seqbuf_dump(void)
489 {
490     if (_seqbufptr) {
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));
494         }
495         /* FIXME:
496          *      in any case buffer is lost so that if many errors occur the buffer
497          * will not overrun
498          */
499         _seqbufptr = 0;
500     }
501 }
502
503 /**************************************************************************
504  *                      midReceiveChar                          [internal]
505  */
506 static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
507 {
508     DWORD               toSend = 0;
509
510     TRACE("Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen);
511
512     if (wDevID >= MIDM_NumDevs) {
513         WARN("bad devID\n");
514         return;
515     }
516     if (MidiInDev[wDevID].state <= 0) {
517         TRACE("disabled or input not started, thrown away\n");
518         return;
519     }
520
521     if (MidiInDev[wDevID].state & 2) { /* system exclusive */
522         LPMIDIHDR       lpMidiHdr;
523         WORD            sbfb = FALSE;
524
525         EnterCriticalSection(&crit_sect);
526         if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
527             LPBYTE      lpData = lpMidiHdr->lpData;
528
529             lpData[lpMidiHdr->dwBytesRecorded++] = value;
530             if (lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) {
531                 sbfb = TRUE;
532             }
533         }
534         if (value == 0xF7) { /* then end */
535             MidiInDev[wDevID].state &= ~2;
536             sbfb = TRUE;
537         }
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");
545             }
546         }
547         LeaveCriticalSection(&crit_sect);
548         return;
549     }
550
551 #define IS_CMD(_x)      (((_x) & 0x80) == 0x80)
552 #define IS_SYS_CMD(_x)  (((_x) & 0xF0) == 0xF0)
553
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);
559         } else {
560             FIXME("error for midi-in, should generate MIM_ERROR notification:"
561                   " prev=%02Xh, incLen=%02Xh\n",
562                   MidiInDev[wDevID].incPrev, MidiInDev[wDevID].incLen);
563             return;
564         }
565     }
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];
570     }
571
572 #undef IS_CMD
573 #undef IS_SYS_CMD
574
575     switch (MidiInDev[wDevID].incoming[0] & 0xF0) {
576     case MIDI_NOTEOFF:
577     case MIDI_NOTEON:
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);
585         }
586         break;
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);
592         }
593         break;
594     case MIDI_SYSTEM_PREFIX:
595         if (MidiInDev[wDevID].incoming[0] == 0xF0) {
596             MidiInDev[wDevID].state |= 2;
597             MidiInDev[wDevID].incLen = 0;
598         } else {
599             if (MidiInDev[wDevID].incLen == 1) {
600                 toSend = (MidiInDev[wDevID].incoming[0] <<  0);
601             }
602         }
603         break;
604     default:
605         WARN("This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]);
606     }
607     if (toSend != 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");
613         }
614     }
615 }
616
617 static DWORD WINAPI midRecThread(LPVOID arg)
618 {
619     unsigned char buffer[256];
620     int len, idx;
621     DWORD dwTime;
622     struct pollfd pfd;
623
624     TRACE("Thread startup\n");
625
626     pfd.fd = midiSeqFD;
627     pfd.fd = POLLIN;
628     
629     while(!end_thread) {
630         TRACE("Thread loop\n");
631
632         /* Check if a event is present */
633         if (poll(&pfd, 1, 250) <= 0)
634             continue;
635         
636         len = read(midiSeqFD, buffer, sizeof(buffer));
637         TRACE("Reveived %d bytes\n", len);
638
639         if (len < 0) continue;
640         if ((len % 4) != 0) {
641             WARN("Bad length %d, errno %d (%s)\n", len, errno, strerror(errno));
642             continue;
643         }
644
645         dwTime = GetTickCount();
646         
647         for (idx = 0; idx < len; ) {
648             if (buffer[idx] & 0x80) {
649                 TRACE(
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]);
655                       idx += 8;
656             } else {
657                 switch (buffer[idx + 0]) {
658                 case SEQ_WAIT:
659                 case SEQ_ECHO:
660                     break;
661                 case SEQ_MIDIPUTC:
662                     midReceiveChar(buffer[idx + 2], buffer[idx + 1], dwTime);
663                     break;
664                 default:
665                     TRACE("Unsupported event %d\n", buffer[idx + 0]);
666                     break;
667                 }
668                 idx += 4;
669             }
670         }
671     }
672     return 0;
673 }
674
675 /**************************************************************************
676  *                              midGetDevCaps                   [internal]
677  */
678 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
679 {
680     TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
681
682     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
683     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
684
685     memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
686
687     return MMSYSERR_NOERROR;
688 }
689
690 /**************************************************************************
691  *                      midOpen                                 [internal]
692  */
693 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
694 {
695     TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
696
697     if (lpDesc == NULL) {
698         WARN("Invalid Parameter !\n");
699         return MMSYSERR_INVALPARAM;
700     }
701
702     /* FIXME :
703      *  how to check that content of lpDesc is correct ?
704      */
705     if (wDevID >= MIDM_NumDevs) {
706         WARN("wDevID too large (%u) !\n", wDevID);
707         return MMSYSERR_BADDEVICEID;
708     }
709     if (MidiInDev[wDevID].state == -1) {        
710         WARN("device disabled\n");
711         return MIDIERR_NODEVICE;
712     }
713     if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
714         WARN("device already open !\n");
715         return MMSYSERR_ALLOCATED;
716     }
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;
720     }
721     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
722         FIXME("Bad dwFlags\n");
723         return MMSYSERR_INVALFLAG;
724     }
725
726     if (midiOpenSeq() < 0) {
727         return MMSYSERR_ERROR;
728     }
729
730     if (numStartedMidiIn++ == 0) {
731         end_thread = 0;
732         hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
733         if (!hThread) {
734             numStartedMidiIn = 0;
735             WARN("Couldn't create thread for midi-in\n");
736             midiCloseSeq();
737             return MMSYSERR_ERROR;
738         }
739         TRACE("Created thread for midi-in\n");
740     }
741
742     MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
743
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;
751
752     if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
753         WARN("can't notify client !\n");
754         return MMSYSERR_INVALPARAM;
755     }
756     return MMSYSERR_NOERROR;
757 }
758
759 /**************************************************************************
760  *                      midClose                                [internal]
761  */
762 static DWORD midClose(WORD wDevID)
763 {
764     int         ret = MMSYSERR_NOERROR;
765
766     TRACE("(%04X);\n", wDevID);
767
768     if (wDevID >= MIDM_NumDevs) {
769         WARN("wDevID too big (%u) !\n", wDevID);
770         return MMSYSERR_BADDEVICEID;
771     }
772     if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
773         WARN("device not opened !\n");
774         return MMSYSERR_ERROR;
775     }
776     if (MidiInDev[wDevID].lpQueueHdr != 0) {
777         return MIDIERR_STILLPLAYING;
778     }
779
780     if (midiSeqFD == -1) {
781         WARN("ooops !\n");
782         return MMSYSERR_ERROR;
783     }
784     if (--numStartedMidiIn == 0) {
785         TRACE("Stopping thread for midi-in\n");
786         end_thread = 1;
787         if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
788             WARN("Thread end not signaled, force termination\n");
789             TerminateThread(hThread, 0);
790         }
791         TRACE("Stopped thread for midi-in\n");
792     }
793     midiCloseSeq();
794
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;
799     }
800     MidiInDev[wDevID].midiDesc.hMidi = 0;
801     return ret;
802 }
803
804 /**************************************************************************
805  *                              midAddBuffer                    [internal]
806  */
807 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
808 {
809     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
810
811     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
812     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
813
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;
819
820     EnterCriticalSection(&crit_sect);
821     if (MidiInDev[wDevID].lpQueueHdr == 0) {
822         MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
823     } else {
824         LPMIDIHDR       ptr;
825
826         for (ptr = MidiInDev[wDevID].lpQueueHdr;
827              ptr->lpNext != 0;
828              ptr = (LPMIDIHDR)ptr->lpNext);
829         ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
830     }
831     LeaveCriticalSection(&crit_sect);
832
833     return MMSYSERR_NOERROR;
834 }
835
836 /**************************************************************************
837  *                              midPrepare                      [internal]
838  */
839 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
840 {
841     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
842
843     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
844         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
845         lpMidiHdr->dwBufferLength >= 0x10000ul)
846         return MMSYSERR_INVALPARAM;
847
848     lpMidiHdr->lpNext = 0;
849     lpMidiHdr->dwFlags |= MHDR_PREPARED;
850     lpMidiHdr->dwBytesRecorded = 0;
851
852     return MMSYSERR_NOERROR;
853 }
854
855 /**************************************************************************
856  *                              midUnprepare                    [internal]
857  */
858 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
859 {
860     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
861
862     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
863     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
864
865     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
866         lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
867         return MMSYSERR_INVALPARAM;
868
869     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
870     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
871
872     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
873
874     return MMSYSERR_NOERROR;
875 }
876
877 /**************************************************************************
878  *                      midReset                                [internal]
879  */
880 static DWORD midReset(WORD wDevID)
881 {
882     DWORD               dwTime = GetTickCount();
883
884     TRACE("(%04X);\n", wDevID);
885
886     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
887     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
888
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");
897         }
898         MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
899     }
900     LeaveCriticalSection(&crit_sect);
901
902     return MMSYSERR_NOERROR;
903 }
904
905
906 /**************************************************************************
907  *                      midStart                                [internal]
908  */
909 static DWORD midStart(WORD wDevID)
910 {
911     TRACE("(%04X);\n", wDevID);
912
913     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
914     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
915
916     MidiInDev[wDevID].state = 1;
917     MidiInDev[wDevID].startTime = GetTickCount();
918     return MMSYSERR_NOERROR;
919 }
920
921 /**************************************************************************
922  *                      midStop                                 [internal]
923  */
924 static DWORD midStop(WORD wDevID)
925 {
926     TRACE("(%04X);\n", wDevID);
927
928     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
929     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
930
931     MidiInDev[wDevID].state = 0;
932     return MMSYSERR_NOERROR;
933 }
934
935 /*-----------------------------------------------------------------------*/
936
937 typedef struct sVoice {
938     int                 note;                   /* 0 means not used */
939     int                 channel;
940     unsigned            cntMark : 30,
941                         status : 2;
942 #define sVS_UNUSED      0
943 #define sVS_PLAYING     1
944 #define sVS_SUSTAINED   2
945 } sVoice;
946
947 typedef struct sChannel {
948     int                 program;
949
950     int                 bender;
951     int                 benderRange;
952     /* controlers */
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     */
958
959     unsigned char       nrgPmtMSB;      /* Non register Parameters */
960     unsigned char       nrgPmtLSB;
961     unsigned char       regPmtMSB;      /* Non register Parameters */
962     unsigned char       regPmtLSB;
963 } sChannel;
964
965 typedef struct sFMextra {
966     unsigned            counter;
967     int                 drumSetMask;
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...
972      */
973 } sFMextra;
974
975 extern  unsigned char midiFMInstrumentPatches[16 * 128];
976 extern  unsigned char midiFMDrumsPatches     [16 * 128];
977
978 /**************************************************************************
979  *                      modFMLoad                               [internal]
980  */
981 static int modFMLoad(int dev)
982 {
983     int                         i;
984     struct sbi_instrument       sbi;
985
986     sbi.device = dev;
987     sbi.key = FM_PATCH;
988
989     memset(sbi.operators + 16, 0, 16);
990     for (i = 0; i < 128; i++) {
991         sbi.channel = i;
992         memcpy(sbi.operators, midiFMInstrumentPatches + i * 16, 16);
993
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));
996             return -1;
997         }
998     }
999     for (i = 0; i < 128; i++) {
1000         sbi.channel = 128 + i;
1001         memcpy(sbi.operators, midiFMDrumsPatches + i * 16, 16);
1002
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));
1005             return -1;
1006         }
1007     }
1008     return 0;
1009 }
1010
1011 /**************************************************************************
1012  *                      modFMReset                              [internal]
1013  */
1014 static  void modFMReset(WORD wDevID)
1015 {
1016     sFMextra*   extra   = (sFMextra*)MidiOutDev[wDevID].lpExtra;
1017     sVoice*     voice   = extra->voice;
1018     sChannel*   channel = extra->channel;
1019     int         i;
1020
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);
1024         }
1025         SEQ_KEY_PRESSURE(wDevID, i, 127, 0);
1026         SEQ_CONTROL(wDevID, i, SEQ_VOLMODE, VOL_METHOD_LINEAR);
1027         voice[i].note = 0;
1028         voice[i].channel = -1;
1029         voice[i].cntMark = 0;
1030         voice[i].status = sVS_UNUSED;
1031     }
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;
1041     }
1042     extra->counter = 0;
1043     extra->drumSetMask = 1 << 9; /* channel 10 is normally drums, sometimes 16 also */
1044     SEQ_DUMPBUF();
1045 }
1046
1047 #define         IS_DRUM_CHANNEL(_xtra, _chn)    ((_xtra)->drumSetMask & (1 << (_chn)))
1048
1049 /**************************************************************************
1050  *                              modGetDevCaps                   [internal]
1051  */
1052 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
1053 {
1054     TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
1055
1056     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1057     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
1058
1059     memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1060
1061     return MMSYSERR_NOERROR;
1062 }
1063
1064 /**************************************************************************
1065  *                      modOpen                                 [internal]
1066  */
1067 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
1068 {
1069     TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1070     if (lpDesc == NULL) {
1071         WARN("Invalid Parameter !\n");
1072         return MMSYSERR_INVALPARAM;
1073     }
1074     if (wDevID >= MODM_NumDevs) {
1075         TRACE("MAX_MIDIOUTDRV reached !\n");
1076         return MMSYSERR_BADDEVICEID;
1077     }
1078     if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
1079         WARN("device already open !\n");
1080         return MMSYSERR_ALLOCATED;
1081     }
1082     if (!MidiOutDev[wDevID].bEnabled) {
1083         WARN("device disabled !\n");
1084         return MIDIERR_NODEVICE;
1085     }
1086     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
1087         WARN("bad dwFlags\n");
1088         return MMSYSERR_INVALFLAG;
1089     }
1090     if (!MidiOutDev[wDevID].bEnabled) {
1091         TRACE("disabled wDevID\n");
1092         return MMSYSERR_NOTENABLED;
1093     }
1094
1095     MidiOutDev[wDevID].lpExtra = 0;
1096
1097     switch (MidiOutDev[wDevID].caps.wTechnology) {
1098     case MOD_FMSYNTH:
1099         {
1100             void*       extra;
1101
1102             extra = HeapAlloc(GetProcessHeap(), 0,
1103                               sizeof(struct sFMextra) +
1104                               sizeof(struct sVoice) * (MidiOutDev[wDevID].caps.wVoices - 1));
1105
1106             if (extra == 0) {
1107                 WARN("can't alloc extra data !\n");
1108                 return MMSYSERR_NOMEM;
1109             }
1110             MidiOutDev[wDevID].lpExtra = extra;
1111             if (midiOpenSeq() < 0) {
1112                 MidiOutDev[wDevID].lpExtra = 0;
1113                 HeapFree(GetProcessHeap(), 0, extra);
1114                 return MMSYSERR_ERROR;
1115             }
1116             if (modFMLoad(wDevID) < 0) {
1117                 midiCloseSeq();
1118                 MidiOutDev[wDevID].lpExtra = 0;
1119                 HeapFree(GetProcessHeap(), 0, extra);
1120                 return MMSYSERR_ERROR;
1121             }
1122             modFMReset(wDevID);
1123         }
1124         break;
1125     case MOD_MIDIPORT:
1126     case MOD_SYNTH:
1127         if (midiOpenSeq() < 0) {
1128             return MMSYSERR_ALLOCATED;
1129         }
1130         break;
1131     default:
1132         WARN("Technology not supported (yet) %d !\n",
1133              MidiOutDev[wDevID].caps.wTechnology);
1134         return MMSYSERR_NOTENABLED;
1135     }
1136
1137     MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1138
1139     MidiOutDev[wDevID].lpQueueHdr = NULL;
1140     MidiOutDev[wDevID].dwTotalPlayed = 0;
1141     MidiOutDev[wDevID].bufsize = 0x3FFF;
1142     MidiOutDev[wDevID].midiDesc = *lpDesc;
1143
1144     if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1145         WARN("can't notify client !\n");
1146         return MMSYSERR_INVALPARAM;
1147     }
1148     TRACE("Successful !\n");
1149     return MMSYSERR_NOERROR;
1150 }
1151
1152
1153 /**************************************************************************
1154  *                      modClose                                [internal]
1155  */
1156 static DWORD modClose(WORD wDevID)
1157 {
1158     int ret = MMSYSERR_NOERROR;
1159
1160     TRACE("(%04X);\n", wDevID);
1161
1162     if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
1163         WARN("device not opened !\n");
1164         return MMSYSERR_ERROR;
1165     }
1166     /* FIXME: should test that no pending buffer is still in the queue for
1167      * playing */
1168
1169     if (midiSeqFD == -1) {
1170         WARN("can't close !\n");
1171         return MMSYSERR_ERROR;
1172     }
1173
1174     switch (MidiOutDev[wDevID].caps.wTechnology) {
1175     case MOD_FMSYNTH:
1176     case MOD_MIDIPORT:
1177         midiCloseSeq();
1178         break;
1179     default:
1180         WARN("Technology not supported (yet) %d !\n",
1181              MidiOutDev[wDevID].caps.wTechnology);
1182         return MMSYSERR_NOTENABLED;
1183     }
1184
1185     HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
1186     MidiOutDev[wDevID].lpExtra = 0;
1187
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;
1192     }
1193     MidiOutDev[wDevID].midiDesc.hMidi = 0;
1194     return ret;
1195 }
1196
1197 /**************************************************************************
1198  *                      modData                                 [internal]
1199  */
1200 static DWORD modData(WORD wDevID, DWORD dwParam)
1201 {
1202     WORD        evt = LOBYTE(LOWORD(dwParam));
1203     WORD        d1  = HIBYTE(LOWORD(dwParam));
1204     WORD        d2  = LOBYTE(HIWORD(dwParam));
1205
1206     TRACE("(%04X, %08lX);\n", wDevID, dwParam);
1207
1208     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1209     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1210
1211     if (midiSeqFD == -1) {
1212         WARN("can't play !\n");
1213         return MIDIERR_NODEVICE;
1214     }
1215     switch (MidiOutDev[wDevID].caps.wTechnology) {
1216     case MOD_FMSYNTH:
1217         /* FIXME:
1218          *      - chorus depth controller is not used
1219          */
1220         {
1221             sFMextra*   extra   = (sFMextra*)MidiOutDev[wDevID].lpExtra;
1222             sVoice*     voice   = extra->voice;
1223             sChannel*   channel = extra->channel;
1224             int         chn = (evt & 0x0F);
1225             int         i, nv;
1226
1227             switch (evt & 0xF0) {
1228             case MIDI_NOTEOFF:
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);
1234                     }
1235                 }
1236                 break;
1237             case MIDI_NOTEON:
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);
1244                         }
1245                     }
1246                     break;
1247                 }
1248                 /* finding out in this order :
1249                  *      - an empty voice
1250                  *      - if replaying the same note on the same channel
1251                  *      - the older voice (LRU)
1252                  */
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)) {
1256                         nv = i;
1257                         break;
1258                     }
1259                     if (voice[i].cntMark < voice[0].cntMark) {
1260                         nv = i;
1261                     }
1262                 }
1263                 TRACE(
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);
1270
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);
1277 #if 0
1278                 /* FIXME: does not really seem to work on my SB card and
1279                  * screws everything up... so lay it down
1280                  */
1281                 SEQ_CONTROL(wDevID, nv, CTL_MAIN_VOLUME, channel[chn].volume);
1282 #endif
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++;
1288                 break;
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);
1293                     }
1294                 }
1295                 break;
1296             case MIDI_CTL_CHANGE:
1297                 switch (d1) {
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;
1303                     if (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;
1307                             }
1308                         }
1309                     } else {
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);
1314                             }
1315                         }
1316                     }
1317                     break;
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) {
1324                     case 0x0000:
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);
1330                                 }
1331                             }
1332                         }
1333                         break;
1334
1335                     case 0x7F7F:
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);
1340                             }
1341                         }
1342                         break;
1343                     default:
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,
1347                               d2);
1348                         break;
1349                     }
1350                     break;
1351
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 ?
1355                      */
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);
1360                         }
1361                     }
1362                     break;
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 ?
1366                      */
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);
1371                         }
1372                     }
1373                     break;
1374                 default:
1375                     TRACE("Dropping MIDI control event 0x%02x(%02x) on channel %d\n",
1376                           d1, d2, chn);
1377                     break;
1378                 }
1379                 break;
1380             case MIDI_PGM_CHANGE:
1381                 channel[chn].program = d1;
1382                 break;
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);
1387                     }
1388                 }
1389                 break;
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);
1395                     }
1396                 }
1397                 break;
1398             case MIDI_SYSTEM_PREFIX:
1399                 switch (evt & 0x0F) {
1400                 case 0x0F:      /* Reset */
1401                     modFMReset(wDevID);
1402                     break;
1403                 default:
1404                     WARN("Unsupported (yet) system event %02x\n", evt & 0x0F);
1405                 }
1406                 break;
1407             default:
1408                 WARN("Internal error, shouldn't happen (event=%08x)\n", evt & 0xF0);
1409                 return MMSYSERR_NOTENABLED;
1410             }
1411         }
1412         break;
1413     case MOD_MIDIPORT:
1414         {
1415             int dev = wDevID - MODM_NumFMSynthDevs;
1416             if (dev < 0) {
1417                 WARN("Internal error on devID (%u) !\n", wDevID);
1418                 return MIDIERR_NODEVICE;
1419             }
1420
1421             switch (evt & 0xF0) {
1422             case MIDI_NOTEOFF:
1423             case MIDI_NOTEON:
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);
1430                 break;
1431             case MIDI_PGM_CHANGE:
1432             case MIDI_CHN_PRESSURE:
1433                 SEQ_MIDIOUT(dev, evt);
1434                 SEQ_MIDIOUT(dev, d1);
1435                 break;
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. */
1446                     break;
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);
1454                     break;
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);
1464                     break;
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);
1472                 }
1473                 break;
1474             }
1475         }
1476         break;
1477     default:
1478         WARN("Technology not supported (yet) %d !\n",
1479              MidiOutDev[wDevID].caps.wTechnology);
1480         return MMSYSERR_NOTENABLED;
1481     }
1482
1483     SEQ_DUMPBUF();
1484
1485     return MMSYSERR_NOERROR;
1486 }
1487
1488 /**************************************************************************
1489  *              modLongData                                     [internal]
1490  */
1491 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1492 {
1493     int         count;
1494     LPBYTE      lpData;
1495
1496     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1497
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.
1501      */
1502     
1503     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1504     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1505
1506     if (midiSeqFD == -1) {
1507         WARN("can't play !\n");
1508         return MIDIERR_NODEVICE;
1509     }
1510
1511     lpData = lpMidiHdr->lpData;
1512
1513     if (lpData == NULL)
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;
1521
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
1524      * modShortData() ?
1525      * If the latest is true, then the following WARNing will fire up
1526      */
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");
1529     }
1530
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]);
1535
1536     switch (MidiOutDev[wDevID].caps.wTechnology) {
1537     case MOD_FMSYNTH:
1538         /* FIXME: I don't think there is much to do here */
1539         break;
1540     case MOD_MIDIPORT:
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");
1546         }
1547         for (count = 0; count < lpMidiHdr->dwBufferLength; count++) {
1548             SEQ_MIDIOUT(wDevID - MODM_NumFMSynthDevs, lpData[count]);
1549         }
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");
1555         }
1556         SEQ_DUMPBUF();
1557         break;
1558     default:
1559         WARN("Technology not supported (yet) %d !\n",
1560              MidiOutDev[wDevID].caps.wTechnology);
1561         return MMSYSERR_NOTENABLED;
1562     }
1563
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;
1569     }
1570     return MMSYSERR_NOERROR;
1571 }
1572
1573 /**************************************************************************
1574  *                      modPrepare                              [internal]
1575  */
1576 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1577 {
1578     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1579
1580     if (midiSeqFD == -1) {
1581         WARN("can't prepare !\n");
1582         return MMSYSERR_NOTENABLED;
1583     }
1584
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
1588      */
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;
1595     }
1596
1597     lpMidiHdr->lpNext = 0;
1598     lpMidiHdr->dwFlags |= MHDR_PREPARED;
1599     lpMidiHdr->dwFlags &= ~MHDR_DONE;
1600     return MMSYSERR_NOERROR;
1601 }
1602
1603 /**************************************************************************
1604  *                              modUnprepare                    [internal]
1605  */
1606 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1607 {
1608     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1609
1610     if (midiSeqFD == -1) {
1611         WARN("can't unprepare !\n");
1612         return MMSYSERR_NOTENABLED;
1613     }
1614
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;
1621 }
1622
1623 /**************************************************************************
1624  *                      modReset                                [internal]
1625  */
1626 static DWORD modReset(WORD wDevID)
1627 {
1628     unsigned chn;
1629
1630     TRACE("(%04X);\n", wDevID);
1631
1632     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1633     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1634
1635     /* stop all notes */
1636     /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1637      * it's channel dependent...
1638      */
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);
1644     }
1645     /* FIXME: the LongData buffers must also be returned to the app */
1646     return MMSYSERR_NOERROR;
1647 }
1648
1649 #endif /* HAVE_OSS_MIDI */
1650
1651 /*======================================================================*
1652  *                          MIDI entry points                           *
1653  *======================================================================*/
1654
1655 /**************************************************************************
1656  *                      midMessage (WINEOSS.4)
1657  */
1658 DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1659                             DWORD dwParam1, DWORD dwParam2)
1660 {
1661     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1662           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1663     switch (wMsg) {
1664 #ifdef HAVE_OSS_MIDI
1665     case DRVM_INIT:
1666     case DRVM_EXIT:
1667     case DRVM_ENABLE:
1668     case DRVM_DISABLE:
1669         /* FIXME: Pretend this is supported */
1670         return 0;
1671     case MIDM_OPEN:
1672         return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1673     case MIDM_CLOSE:
1674         return midClose(wDevID);
1675     case MIDM_ADDBUFFER:
1676         return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1677     case MIDM_PREPARE:
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;
1685     case MIDM_RESET:
1686         return midReset(wDevID);
1687     case MIDM_START:
1688         return midStart(wDevID);
1689     case MIDM_STOP:
1690         return midStop(wDevID);
1691 #endif
1692     default:
1693         TRACE("Unsupported message\n");
1694     }
1695     return MMSYSERR_NOTSUPPORTED;
1696 }
1697
1698 /**************************************************************************
1699  *                              modMessage (WINEOSS.5)
1700  */
1701 DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1702                             DWORD dwParam1, DWORD dwParam2)
1703 {
1704     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1705           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1706
1707     switch (wMsg) {
1708 #ifdef HAVE_OSS_MIDI
1709     case DRVM_INIT:
1710     case DRVM_EXIT:
1711     case DRVM_ENABLE:
1712     case DRVM_DISABLE:
1713         /* FIXME: Pretend this is supported */
1714         return 0;
1715     case MODM_OPEN:
1716         return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1717     case MODM_CLOSE:
1718         return modClose(wDevID);
1719     case MODM_DATA:
1720         return modData(wDevID, dwParam1);
1721     case MODM_LONGDATA:
1722         return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1723     case MODM_PREPARE:
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:
1732         return 0;
1733     case MODM_SETVOLUME:
1734         return 0;
1735     case MODM_RESET:
1736         return modReset(wDevID);
1737 #endif
1738     default:
1739         TRACE("Unsupported message\n");
1740     }
1741     return MMSYSERR_NOTSUPPORTED;
1742 }
1743
1744 /*-----------------------------------------------------------------------*/