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