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