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