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