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