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