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