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