Attempt to write fragments and check whether we can notify the client
[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
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "winuser.h"
26 #include "mmddk.h"
27 #include "oss.h"
28 #include "debugtools.h"
29 #include "heap.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     MIDIOPENDESC        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     MIDIOPENDESC        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 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("Synthesizer 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=%lx\n", 
225               sinfo.synth_subtype, (long)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 for device %d failed.\n", i);
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=%lx\n", 
312               minfo.dev_type, (long)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 static int midi_warn = 1;
377 /**************************************************************************
378  *                      midiOpenSeq                             [internal]
379  */
380 static int midiOpenSeq(void)
381 {
382     if (numOpenMidiSeq == 0) {
383         midiSeqFD = open(MIDI_SEQ, O_RDWR, 0);
384         if (midiSeqFD == -1) {
385             if (midi_warn)
386             {
387                 WARN("Can't open MIDI device '%s' ! (%s). If your "
388                         "program needs this (probably not): %s\n",
389                         MIDI_SEQ, strerror(errno),
390                         errno == ENOENT ?
391                         "create it ! (\"man MAKEDEV\" ?)" :
392                         errno == ENODEV ?
393                         "load MIDI sequencer kernel driver !" :
394                         errno == EACCES ?
395                         "grant access ! (\"man chmod\")" : ""
396                 );
397             }
398             midi_warn = 0;
399             return -1;
400         }
401         if (fcntl(midiSeqFD, F_SETFL, O_NONBLOCK) < 0) {
402             WARN("can't set sequencer fd to non-blocking, errno %d (%s)\n", errno, strerror(errno));
403             close(midiSeqFD);
404             midiSeqFD = -1;
405             return -1;
406         }
407         fcntl(midiSeqFD, F_SETFD, 1); /* set close on exec flag */
408         ioctl(midiSeqFD, SNDCTL_SEQ_RESET);
409     }
410     numOpenMidiSeq++;
411     return 0;
412 }
413
414 /**************************************************************************
415  *                      midiCloseSeq                            [internal]
416  */
417 static int midiCloseSeq(void)
418 {
419     if (--numOpenMidiSeq == 0) {
420         close(midiSeqFD);
421         midiSeqFD = -1;
422     }
423     return 0;
424 }
425
426 /* FIXME: this is a bad idea, it's even not static... */
427 SEQ_DEFINEBUF(1024);
428
429 /* FIXME: this is not reentrant, not static - because of global variable 
430  * _seqbuf and al. 
431  */
432 /**************************************************************************
433  *                      seqbuf_dump                             [internal]
434  */
435 void seqbuf_dump(void)
436 {
437     if (_seqbufptr) {
438         if (write(midiSeqFD, _seqbuf, _seqbufptr) == -1) {
439             WARN("Can't write data to sequencer %d, errno %d (%s)!\n", 
440                  midiSeqFD, errno, strerror(errno));
441         }
442         /* FIXME:
443          *      in any case buffer is lost so that if many errors occur the buffer 
444          * will not overrun 
445          */
446         _seqbufptr = 0;
447     }
448 }
449
450 static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
451 {
452     DWORD               toSend = 0;
453     
454     TRACE("Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen);
455     
456     if (wDevID >= MAX_MIDIINDRV) {
457         WARN("bad devID\n");
458         return;
459     }
460     if (MidiInDev[wDevID].state == 0) {
461         TRACE("input not started, thrown away\n");
462         return;
463     }
464
465     if (MidiInDev[wDevID].state & 2) { /* system exclusive */
466         LPMIDIHDR       lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
467         WORD            sbfb = FALSE;
468
469         if (lpMidiHdr) {
470             LPBYTE      lpData = lpMidiHdr->lpData;
471         
472             lpData[lpMidiHdr->dwBytesRecorded++] = value;
473             if (lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) {
474                 sbfb = TRUE;
475             } 
476         }
477         if (value == 0xF7) { /* then end */
478             MidiInDev[wDevID].state &= ~2;
479             sbfb = TRUE;
480         }
481         if (sbfb && lpMidiHdr != NULL) {
482             lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
483             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
484             lpMidiHdr->dwFlags |= MHDR_DONE;
485             MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
486             if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)lpMidiHdr, dwTime) != MMSYSERR_NOERROR) {
487                 WARN("Couldn't notify client\n");
488             }
489         }
490         return;
491     }
492     
493 #define IS_CMD(_x)      (((_x) & 0x80) == 0x80)
494 #define IS_SYS_CMD(_x)  (((_x) & 0xF0) == 0xF0)
495     
496     if (!IS_CMD(value) && MidiInDev[wDevID].incLen == 0) { /* try to reuse old cmd */
497         if (IS_CMD(MidiInDev[wDevID].incPrev) && !IS_SYS_CMD(MidiInDev[wDevID].incPrev)) {
498             MidiInDev[wDevID].incoming[0] = MidiInDev[wDevID].incPrev;
499             MidiInDev[wDevID].incLen = 1;
500             TRACE("Reusing old command %02xh\n", MidiInDev[wDevID].incPrev);
501         } else {
502             FIXME("error for midi-in, should generate MIM_ERROR notification:"
503                   " prev=%02Xh, incLen=%02Xh\n", 
504                   MidiInDev[wDevID].incPrev, MidiInDev[wDevID].incLen);
505             return;
506         }
507     }
508     MidiInDev[wDevID].incoming[(int)(MidiInDev[wDevID].incLen++)] = value;
509     if (MidiInDev[wDevID].incLen == 1 && !IS_SYS_CMD(MidiInDev[wDevID].incoming[0])) {
510         /* store new cmd, just in case */
511         MidiInDev[wDevID].incPrev = MidiInDev[wDevID].incoming[0];
512     }
513
514 #undef IS_CMD
515 #undef IS_SYS_CMD
516
517     switch (MidiInDev[wDevID].incoming[0] & 0xF0) {
518     case MIDI_NOTEOFF:
519     case MIDI_NOTEON:
520     case MIDI_KEY_PRESSURE:
521     case MIDI_CTL_CHANGE:
522     case MIDI_PITCH_BEND:
523         if (MidiInDev[wDevID].incLen == 3) {
524             toSend = (MidiInDev[wDevID].incoming[2] << 16) | 
525                 (MidiInDev[wDevID].incoming[1] <<  8) |
526                 (MidiInDev[wDevID].incoming[0] <<  0);
527         }
528         break;
529     case MIDI_PGM_CHANGE:
530     case MIDI_CHN_PRESSURE:
531         if (MidiInDev[wDevID].incLen == 2) {
532             toSend = (MidiInDev[wDevID].incoming[1] <<  8) |
533                 (MidiInDev[wDevID].incoming[0] <<  0);
534         }
535         break;
536     case MIDI_SYSTEM_PREFIX:
537         if (MidiInDev[wDevID].incoming[0] == 0xF0) {
538             MidiInDev[wDevID].state |= 2;
539             MidiInDev[wDevID].incLen = 0;
540         } else {                
541             if (MidiInDev[wDevID].incLen == 1) {
542                 toSend = (MidiInDev[wDevID].incoming[0] <<  0);
543             }
544         }
545         break;
546     default:
547         WARN("This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]);
548     }
549     if (toSend != 0) {
550         TRACE("Sending event %08lx\n", toSend);
551         MidiInDev[wDevID].incLen =      0;
552         dwTime -= MidiInDev[wDevID].startTime;
553         if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
554             WARN("Couldn't notify client\n");
555         }
556     }
557 }
558
559 static VOID WINAPI midTimeCallback(HWND hwnd, UINT msg, UINT id, DWORD dwTime)
560 {
561     unsigned    char            buffer[256];
562     int                         len, idx;
563     
564     TRACE("(%04X, %d, %d, %lu)\n", hwnd, msg, id, dwTime);
565     
566     len = read(midiSeqFD, buffer, sizeof(buffer));
567     
568     if (len < 0) return;
569     if ((len % 4) != 0) {
570         WARN("Bad length %d, errno %d (%s)\n", len, errno, strerror(errno));
571         return;
572     }
573     
574     for (idx = 0; idx < len; ) {
575         if (buffer[idx] & 0x80) {
576             TRACE(
577                   "Reading<8> %02x %02x %02x %02x %02x %02x %02x %02x\n", 
578                   buffer[idx + 0], buffer[idx + 1], 
579                   buffer[idx + 2], buffer[idx + 3], 
580                   buffer[idx + 4], buffer[idx + 5], 
581                   buffer[idx + 6], buffer[idx + 7]);
582             idx += 8;
583         } else {
584             switch (buffer[idx + 0]) {
585             case SEQ_WAIT:
586             case SEQ_ECHO:
587                 break;
588             case SEQ_MIDIPUTC:
589                 midReceiveChar(buffer[idx + 2], buffer[idx + 1], dwTime);
590                 break;
591             default:
592                 TRACE("Unsupported event %d\n", buffer[idx + 0]);
593                 break;
594             }
595             idx += 4;
596         }                               
597     }
598 }
599
600 /**************************************************************************
601  *                              midGetDevCaps                   [internal]
602  */
603 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSA lpCaps, DWORD dwSize)
604 {
605     TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
606     
607     if (wDevID >= MIDM_NUMDEVS) return MMSYSERR_BADDEVICEID;
608     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
609
610     memcpy(lpCaps, midiInDevices[wDevID], min(dwSize, sizeof(*lpCaps)));
611     
612     return MMSYSERR_NOERROR;
613 }
614
615 /**************************************************************************
616  *                      midOpen                                 [internal]
617  */
618 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
619 {
620     TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
621     
622     if (lpDesc == NULL) {
623         WARN("Invalid Parameter !\n");
624         return MMSYSERR_INVALPARAM;
625     }
626
627     /* FIXME :
628      *  how to check that content of lpDesc is correct ?
629      */
630     if (wDevID >= MAX_MIDIINDRV) {
631         WARN("wDevID too large (%u) !\n", wDevID);
632         return MMSYSERR_BADDEVICEID;
633     }
634     if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
635         WARN("device already open !\n");
636         return MMSYSERR_ALLOCATED;
637     }
638     if ((dwFlags & MIDI_IO_STATUS) != 0) { 
639         WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
640         dwFlags &= ~MIDI_IO_STATUS;
641     }
642     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) { 
643         FIXME("Bad dwFlags\n");
644         return MMSYSERR_INVALFLAG;
645     }
646     
647     if (midiOpenSeq() < 0) {
648         return MMSYSERR_ERROR;
649     }
650     
651     if (numStartedMidiIn++ == 0) {
652         midiInTimerID = SetTimer(0, 0, 250, midTimeCallback);
653         if (!midiInTimerID) {
654             numStartedMidiIn = 0;
655             WARN("Couldn't start timer for midi-in\n");
656             midiCloseSeq();
657             return MMSYSERR_ERROR;
658         }
659         TRACE("Starting timer (%u) for midi-in\n", midiInTimerID);
660     }
661     
662     MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
663
664     MidiInDev[wDevID].lpQueueHdr = NULL;
665     MidiInDev[wDevID].dwTotalPlayed = 0;
666     MidiInDev[wDevID].bufsize = 0x3FFF;
667     MidiInDev[wDevID].midiDesc = *lpDesc;
668     MidiInDev[wDevID].state = 0;
669     MidiInDev[wDevID].incLen = 0;
670     MidiInDev[wDevID].startTime = 0;
671
672     if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
673         WARN("can't notify client !\n");
674         return MMSYSERR_INVALPARAM;
675     }
676     return MMSYSERR_NOERROR;
677 }
678
679 /**************************************************************************
680  *                      midClose                                [internal]
681  */
682 static DWORD midClose(WORD wDevID)
683 {
684     int         ret = MMSYSERR_NOERROR;
685     
686     TRACE("(%04X);\n", wDevID);
687     
688     if (wDevID >= MAX_MIDIINDRV) {
689         WARN("wDevID too big (%u) !\n", wDevID);
690         return MMSYSERR_BADDEVICEID;
691     }
692     if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
693         WARN("device not opened !\n");
694         return MMSYSERR_ERROR;
695     }
696     if (MidiInDev[wDevID].lpQueueHdr != 0) {
697         return MIDIERR_STILLPLAYING;
698     }
699     
700     if (midiSeqFD == -1) {
701         WARN("ooops !\n");
702         return MMSYSERR_ERROR;
703     }
704     if (--numStartedMidiIn == 0) {
705         TRACE("Stopping timer for midi-in\n");
706         if (!KillTimer(0, midiInTimerID)) {
707             WARN("Couldn't stop timer for midi-in\n");
708         }                       
709         midiInTimerID = 0;
710     }
711     midiCloseSeq();
712
713     MidiInDev[wDevID].bufsize = 0;
714     if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
715         WARN("can't notify client !\n");
716         ret = MMSYSERR_INVALPARAM;
717     }
718     MidiInDev[wDevID].midiDesc.hMidi = 0;
719     return ret;
720 }
721
722 /**************************************************************************
723  *                              midAddBuffer                    [internal]
724  */
725 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
726 {
727     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
728     
729     if (lpMidiHdr == NULL)      return MMSYSERR_INVALPARAM;
730     if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
731     if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
732     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
733     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
734     
735     if (MidiInDev[wDevID].lpQueueHdr == 0) {
736         MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
737     } else {
738         LPMIDIHDR       ptr;
739         
740         for (ptr = MidiInDev[wDevID].lpQueueHdr; 
741              ptr->lpNext != 0; 
742              ptr = (LPMIDIHDR)ptr->lpNext);
743         ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
744     }
745     return MMSYSERR_NOERROR;
746 }
747
748 /**************************************************************************
749  *                              midPrepare                      [internal]
750  */
751 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
752 {
753     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
754     
755     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 || 
756         lpMidiHdr->lpData == 0 || lpMidiHdr->dwFlags != 0 || 
757         lpMidiHdr->dwBufferLength >= 0x10000ul)
758         return MMSYSERR_INVALPARAM;
759     
760     lpMidiHdr->lpNext = 0;
761     lpMidiHdr->dwFlags |= MHDR_PREPARED;
762     lpMidiHdr->dwBytesRecorded = 0;
763     
764     return MMSYSERR_NOERROR;
765 }
766
767 /**************************************************************************
768  *                              midUnprepare                    [internal]
769  */
770 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
771 {
772     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
773     
774     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 || 
775         lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
776         return MMSYSERR_INVALPARAM;
777     
778     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
779     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
780     
781     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
782     
783     return MMSYSERR_NOERROR;
784 }
785
786 /**************************************************************************
787  *                      midReset                                [internal]
788  */
789 static DWORD midReset(WORD wDevID)
790 {
791     DWORD               dwTime = GetTickCount();
792     
793     TRACE("(%04X);\n", wDevID);
794     
795     while (MidiInDev[wDevID].lpQueueHdr) {
796         MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
797         MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
798         /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
799         if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, 
800                               (DWORD)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
801             WARN("Couldn't notify client\n");
802         }
803         MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
804     }
805     
806     return MMSYSERR_NOERROR;
807 }
808
809
810 /**************************************************************************
811  *                      midStart                                [internal]
812  */
813 static DWORD midStart(WORD wDevID)
814 {
815     TRACE("(%04X);\n", wDevID);
816     
817     /* FIXME : should test value of wDevID */
818     
819     MidiInDev[wDevID].state = 1;
820     MidiInDev[wDevID].startTime = GetTickCount();
821     return MMSYSERR_NOERROR;
822 }
823
824 /**************************************************************************
825  *                      midStop                                 [internal]
826  */
827 static DWORD midStop(WORD wDevID)
828 {
829     TRACE("(%04X);\n", wDevID);
830     
831     /* FIXME : should test value of wDevID */
832     MidiInDev[wDevID].state = 0;
833     return MMSYSERR_NOERROR;
834 }
835
836 /*-----------------------------------------------------------------------*/
837
838 typedef struct sVoice {
839     int                 note;                   /* 0 means not used */
840     int                 channel;
841     unsigned            cntMark : 30,
842                         status : 2;
843 #define sVS_UNUSED      0
844 #define sVS_PLAYING     1
845 #define sVS_SUSTAINED   2
846 } sVoice;
847
848 typedef struct sChannel {
849     int                 program;
850     
851     int                 bender;
852     int                 benderRange;
853     /* controlers */
854     int                 bank;           /* CTL_BANK_SELECT */
855     int                 volume;         /* CTL_MAIN_VOLUME */
856     int                 balance;        /* CTL_BALANCE     */
857     int                 expression;     /* CTL_EXPRESSION  */
858     int                 sustain;        /* CTL_SUSTAIN     */
859     
860     unsigned char       nrgPmtMSB;      /* Non register Parameters */
861     unsigned char       nrgPmtLSB;
862     unsigned char       regPmtMSB;      /* Non register Parameters */
863     unsigned char       regPmtLSB;
864 } sChannel;
865
866 typedef struct sFMextra {
867     unsigned            counter;
868     int                 drumSetMask;
869     sChannel            channel[16];    /* MIDI has only 16 channels */
870     sVoice              voice[1];       /* dyn allocated according to sound card */
871     /* do not append fields below voice[1] since the size of this structure 
872      * depends on the number of available voices on the FM synth...
873      */
874 } sFMextra;
875
876 extern  unsigned char midiFMInstrumentPatches[16 * 128];
877 extern  unsigned char midiFMDrumsPatches     [16 * 128];
878
879 /**************************************************************************
880  *                      modFMLoad                               [internal]
881  */
882 static int modFMLoad(int dev)
883 {
884     int                         i;
885     struct sbi_instrument       sbi;
886     
887     sbi.device = dev;
888     sbi.key = FM_PATCH;
889     
890     memset(sbi.operators + 16, 0, 16);
891     for (i = 0; i < 128; i++) {
892         sbi.channel = i;
893         memcpy(sbi.operators, midiFMInstrumentPatches + i * 16, 16);
894         
895         if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
896             WARN("Couldn't write patch for instrument %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
897             return -1;
898         }
899     } 
900     for (i = 0; i < 128; i++) {
901         sbi.channel = 128 + i;
902         memcpy(sbi.operators, midiFMDrumsPatches + i * 16, 16);
903         
904         if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
905             WARN("Couldn't write patch for drum %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
906             return -1;
907         }
908     } 
909     return 0;
910 }
911
912 /**************************************************************************
913  *                      modFMReset                              [internal]
914  */
915 static  void modFMReset(WORD wDevID)
916 {
917     sFMextra*   extra   = (sFMextra*)MidiOutDev[wDevID].lpExtra;
918     sVoice*     voice   = extra->voice;
919     sChannel*   channel = extra->channel;
920     int         i;
921     
922     for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
923         if (voice[i].status != sVS_UNUSED) {
924             SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
925         }
926         SEQ_KEY_PRESSURE(wDevID, i, 127, 0);
927         SEQ_CONTROL(wDevID, i, SEQ_VOLMODE, VOL_METHOD_LINEAR);
928         voice[i].note = 0;
929         voice[i].channel = -1;
930         voice[i].cntMark = 0;
931         voice[i].status = sVS_UNUSED;
932     }
933     for (i = 0; i < 16; i++) {
934         channel[i].program = 0;
935         channel[i].bender = 8192;
936         channel[i].benderRange = 2;
937         channel[i].bank = 0;
938         channel[i].volume = 127;
939         channel[i].balance = 64;
940         channel[i].expression = 0;      
941         channel[i].sustain = 0; 
942     }
943     extra->counter = 0;
944     extra->drumSetMask = 1 << 9; /* channel 10 is normally drums, sometimes 16 also */
945     SEQ_DUMPBUF();
946 }
947
948 #define         IS_DRUM_CHANNEL(_xtra, _chn)    ((_xtra)->drumSetMask & (1 << (_chn)))
949
950 /**************************************************************************
951  *                              modGetDevCaps                   [internal]
952  */
953 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSA lpCaps, DWORD dwSize)
954 {
955     TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
956
957     if (wDevID >= MODM_NUMDEVS) return MMSYSERR_BADDEVICEID;
958     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
959
960     memcpy(lpCaps, midiOutDevices[wDevID], min(dwSize, sizeof(*lpCaps)));
961
962     return MMSYSERR_NOERROR;
963 }
964
965 /**************************************************************************
966  *                      modOpen                                 [internal]
967  */
968 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
969 {
970     TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
971     if (lpDesc == NULL) {
972         WARN("Invalid Parameter !\n");
973         return MMSYSERR_INVALPARAM;
974     }
975     if (wDevID >= MAX_MIDIOUTDRV) {
976         TRACE("MAX_MIDIOUTDRV reached !\n");
977         return MMSYSERR_BADDEVICEID;
978     }
979     if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
980         WARN("device already open !\n");
981         return MMSYSERR_ALLOCATED;
982     }
983     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) { 
984         WARN("bad dwFlags\n");
985         return MMSYSERR_INVALFLAG;
986     }
987     if (midiOutDevices[wDevID] == NULL) {
988         TRACE("un-allocated wDevID\n");
989         return MMSYSERR_BADDEVICEID;
990     }
991     
992     MidiOutDev[wDevID].lpExtra = 0;
993     
994     switch (midiOutDevices[wDevID]->wTechnology) {
995     case MOD_FMSYNTH:
996         {
997             void*       extra = HeapAlloc(GetProcessHeap(), 0, 
998                                           sizeof(struct sFMextra) + 
999                                           sizeof(struct sVoice) * (midiOutDevices[wDevID]->wVoices - 1));
1000             
1001             if (extra == 0) {
1002                 WARN("can't alloc extra data !\n");
1003                 return MMSYSERR_NOMEM;
1004             }
1005             MidiOutDev[wDevID].lpExtra = extra;
1006             if (midiOpenSeq() < 0) {
1007                 MidiOutDev[wDevID].lpExtra = 0;
1008                 HeapFree(GetProcessHeap(), 0, extra);
1009                 return MMSYSERR_ERROR;
1010             }
1011             if (modFMLoad(wDevID) < 0) {
1012                 midiCloseSeq();
1013                 MidiOutDev[wDevID].lpExtra = 0;
1014                 HeapFree(GetProcessHeap(), 0, extra);
1015                 return MMSYSERR_ERROR;
1016             }
1017             modFMReset(wDevID);
1018         }
1019         break;
1020     case MOD_MIDIPORT:
1021         if (midiOpenSeq() < 0) {
1022             return MMSYSERR_ALLOCATED;
1023         }
1024         break;
1025     default:
1026         WARN("Technology not supported (yet) %d !\n", 
1027              midiOutDevices[wDevID]->wTechnology);
1028         return MMSYSERR_NOTENABLED;
1029     }
1030     
1031     MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1032
1033     MidiOutDev[wDevID].lpQueueHdr = NULL;
1034     MidiOutDev[wDevID].dwTotalPlayed = 0;
1035     MidiOutDev[wDevID].bufsize = 0x3FFF;
1036     MidiOutDev[wDevID].midiDesc = *lpDesc;
1037     
1038     if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1039         WARN("can't notify client !\n");
1040         return MMSYSERR_INVALPARAM;
1041     }
1042     TRACE("Successful !\n");
1043     return MMSYSERR_NOERROR;
1044 }
1045
1046
1047 /**************************************************************************
1048  *                      modClose                                [internal]
1049  */
1050 static DWORD modClose(WORD wDevID)
1051 {
1052     int ret = MMSYSERR_NOERROR;
1053
1054     TRACE("(%04X);\n", wDevID);
1055     
1056     if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
1057         WARN("device not opened !\n");
1058         return MMSYSERR_ERROR;
1059     }
1060     /* FIXME: should test that no pending buffer is still in the queue for
1061      * playing */
1062     
1063     if (midiSeqFD == -1) {
1064         WARN("can't close !\n");
1065         return MMSYSERR_ERROR;
1066     }
1067     
1068     switch (midiOutDevices[wDevID]->wTechnology) {
1069     case MOD_FMSYNTH:
1070     case MOD_MIDIPORT:
1071         midiCloseSeq();
1072         break;
1073     default:
1074         WARN("Technology not supported (yet) %d !\n", 
1075              midiOutDevices[wDevID]->wTechnology);
1076         return MMSYSERR_NOTENABLED;
1077     }
1078     
1079     if (MidiOutDev[wDevID].lpExtra != 0) {
1080         HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
1081         MidiOutDev[wDevID].lpExtra = 0;
1082     }
1083     
1084     MidiOutDev[wDevID].bufsize = 0;
1085     if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1086         WARN("can't notify client !\n");
1087         ret = MMSYSERR_INVALPARAM;
1088     }
1089     MidiOutDev[wDevID].midiDesc.hMidi = 0;
1090     return ret;
1091 }
1092
1093 /**************************************************************************
1094  *                      modData                                 [internal]
1095  */
1096 static DWORD modData(WORD wDevID, DWORD dwParam)
1097 {
1098     WORD        evt = LOBYTE(LOWORD(dwParam));
1099     WORD        d1  = HIBYTE(LOWORD(dwParam));
1100     WORD        d2  = LOBYTE(HIWORD(dwParam));
1101     
1102     TRACE("(%04X, %08lX);\n", wDevID, dwParam);
1103     
1104     if (midiSeqFD == -1) {
1105         WARN("can't play !\n");
1106         return MIDIERR_NODEVICE;
1107     }
1108     switch (midiOutDevices[wDevID]->wTechnology) {
1109     case MOD_FMSYNTH:
1110         /* FIXME:
1111          *      - chorus depth controller is not used
1112          */
1113         {
1114             sFMextra*   extra   = (sFMextra*)MidiOutDev[wDevID].lpExtra;
1115             sVoice*     voice   = extra->voice;
1116             sChannel*   channel = extra->channel;
1117             int         chn = (evt & 0x0F);
1118             int         i, nv;
1119             
1120             switch (evt & 0xF0) {
1121             case MIDI_NOTEOFF:
1122                 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1123                                 /* don't stop sustained notes */
1124                     if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1125                         voice[i].status = sVS_UNUSED;
1126                         SEQ_STOP_NOTE(wDevID, i, d1, d2);
1127                     }
1128                 }
1129                 break;  
1130             case MIDI_NOTEON:
1131                 if (d2 == 0) { /* note off if velocity == 0 */
1132                     for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1133                         /* don't stop sustained notes */
1134                         if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1135                             voice[i].status = sVS_UNUSED;
1136                             SEQ_STOP_NOTE(wDevID, i, d1, 64);
1137                         }
1138                     }
1139                     break;
1140                 }
1141                 /* finding out in this order :
1142                  *      - an empty voice
1143                  *      - if replaying the same note on the same channel
1144                  *      - the older voice (LRU)
1145                  */
1146                 for (i = nv = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1147                     if (voice[i].status == sVS_UNUSED || 
1148                         (voice[i].note == d1 && voice[i].channel == chn)) {
1149                         nv = i;
1150                         break;
1151                     }
1152                     if (voice[i].cntMark < voice[0].cntMark) {
1153                         nv = i;
1154                     }
1155                 }
1156                 TRACE(
1157                       "playing on voice=%d, pgm=%d, pan=0x%02X, vol=0x%02X, "
1158                       "bender=0x%02X, note=0x%02X, vel=0x%02X\n", 
1159                       nv, channel[chn].program, 
1160                       channel[chn].balance, 
1161                       channel[chn].volume, 
1162                       channel[chn].bender, d1, d2);
1163                 
1164                 SEQ_SET_PATCH(wDevID, nv, IS_DRUM_CHANNEL(extra, chn) ? 
1165                               (128 + d1) : channel[chn].program);
1166                 SEQ_BENDER_RANGE(wDevID, nv, channel[chn].benderRange * 100);
1167                 SEQ_BENDER(wDevID, nv, channel[chn].bender);
1168                 SEQ_CONTROL(wDevID, nv, CTL_PAN, channel[chn].balance);
1169                 SEQ_CONTROL(wDevID, nv, CTL_EXPRESSION, channel[chn].expression);
1170 #if 0   
1171                 /* FIXME: does not really seem to work on my SB card and
1172                  * screws everything up... so lay it down
1173                  */
1174                 SEQ_CONTROL(wDevID, nv, CTL_MAIN_VOLUME, channel[chn].volume);
1175 #endif  
1176                 SEQ_START_NOTE(wDevID, nv, d1, d2);
1177                 voice[nv].status = channel[chn].sustain ? sVS_SUSTAINED : sVS_PLAYING;
1178                 voice[nv].note = d1;
1179                 voice[nv].channel = chn;
1180                 voice[nv].cntMark = extra->counter++;
1181                 break;
1182             case MIDI_KEY_PRESSURE:
1183                 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1184                     if (voice[i].status != sVS_UNUSED && voice[i].channel == chn && voice[i].note == d1) {
1185                         SEQ_KEY_PRESSURE(wDevID, i, d1, d2);
1186                     }
1187                 }
1188                 break;
1189             case MIDI_CTL_CHANGE:
1190                 switch (d1) {
1191                 case CTL_BANK_SELECT:   channel[chn].bank = d2;         break;
1192                 case CTL_MAIN_VOLUME:   channel[chn].volume = d2;       break;
1193                 case CTL_PAN:           channel[chn].balance = d2;      break;
1194                 case CTL_EXPRESSION:    channel[chn].expression = d2;   break;
1195                 case CTL_SUSTAIN:       channel[chn].sustain = d2;
1196                     if (d2) {
1197                         for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1198                             if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1199                                 voice[i].status = sVS_SUSTAINED;
1200                             }
1201                         }
1202                     } else {
1203                         for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1204                             if (voice[i].status == sVS_SUSTAINED && voice[i].channel == chn) {
1205                                 voice[i].status = sVS_UNUSED;
1206                                 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1207                             }
1208                         }
1209                     }
1210                     break;
1211                 case CTL_NONREG_PARM_NUM_LSB:   channel[chn].nrgPmtLSB = d2;    break;
1212                 case CTL_NONREG_PARM_NUM_MSB:   channel[chn].nrgPmtMSB = d2;    break;
1213                 case CTL_REGIST_PARM_NUM_LSB:   channel[chn].regPmtLSB = d2;    break;
1214                 case CTL_REGIST_PARM_NUM_MSB:   channel[chn].regPmtMSB = d2;    break;              
1215                 case CTL_DATA_ENTRY:
1216                     switch ((channel[chn].regPmtMSB << 8) | channel[chn].regPmtLSB) {
1217                     case 0x0000: 
1218                         if (channel[chn].benderRange != d2) {
1219                             channel[chn].benderRange = d2;
1220                             for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1221                                 if (voice[i].channel == chn) {
1222                                     SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1223                                 }
1224                             }
1225                         }
1226                         break;
1227                         
1228                     case 0x7F7F:
1229                         channel[chn].benderRange = 2;
1230                         for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1231                             if (voice[i].channel == chn) {
1232                                 SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1233                             }
1234                         }
1235                         break;
1236                     default:
1237                         TRACE("Data entry: regPmt=0x%02x%02x, nrgPmt=0x%02x%02x with %x\n",
1238                               channel[chn].regPmtMSB, channel[chn].regPmtLSB,
1239                               channel[chn].nrgPmtMSB, channel[chn].nrgPmtLSB,
1240                               d2);
1241                         break;
1242                     }
1243                     break;
1244                     
1245                 case 0x78: /* all sounds off */
1246                     /* FIXME: I don't know if I have to take care of the channel 
1247                      * for this control ?
1248                      */
1249                     for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1250                         if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1251                             voice[i].status = sVS_UNUSED;
1252                             SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1253                         }
1254                     }
1255                     break;
1256                 case 0x7B: /* all notes off */
1257                     /* FIXME: I don't know if I have to take care of the channel 
1258                      * for this control ?
1259                      */
1260                     for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1261                         if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1262                             voice[i].status = sVS_UNUSED;
1263                             SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1264                         }
1265                     }
1266                     break;      
1267                 default:
1268                     TRACE("Dropping MIDI control event 0x%02x(%02x) on channel %d\n", 
1269                           d1, d2, chn);
1270                     break;
1271                 }
1272                 break;
1273             case MIDI_PGM_CHANGE:
1274                 channel[chn].program = d1;
1275                 break;
1276             case MIDI_CHN_PRESSURE:
1277                 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1278                     if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1279                         SEQ_KEY_PRESSURE(wDevID, i, voice[i].note, d1);
1280                     }
1281                 }
1282                 break;
1283             case MIDI_PITCH_BEND:
1284                 channel[chn].bender = (d2 << 7) + d1;
1285                 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1286                     if (voice[i].channel == chn) {
1287                         SEQ_BENDER(wDevID, i, channel[chn].bender);
1288                     }
1289                 }
1290                 break;
1291             case MIDI_SYSTEM_PREFIX:
1292                 switch (evt & 0x0F) {
1293                 case 0x0F:      /* Reset */
1294                     modFMReset(wDevID);
1295                     break; 
1296                 default:
1297                     WARN("Unsupported (yet) system event %02x\n", evt & 0x0F);
1298                 }
1299                 break;
1300             default:    
1301                 WARN("Internal error, shouldn't happen (event=%08x)\n", evt & 0xF0);
1302                 return MMSYSERR_NOTENABLED;
1303             }
1304         }
1305         break;
1306     case MOD_MIDIPORT:
1307         {
1308             int dev = wDevID - MODM_NUMFMSYNTHDEVS;
1309             if (dev < 0) {
1310                 WARN("Internal error on devID (%u) !\n", wDevID);
1311                 return MIDIERR_NODEVICE;
1312             }
1313             
1314             switch (evt & 0xF0) {
1315             case MIDI_NOTEOFF:
1316             case MIDI_NOTEON:
1317             case MIDI_KEY_PRESSURE:
1318             case MIDI_CTL_CHANGE:
1319             case MIDI_PITCH_BEND:
1320                 SEQ_MIDIOUT(dev, evt);  
1321                 SEQ_MIDIOUT(dev, d1);   
1322                 SEQ_MIDIOUT(dev, d2);   
1323                 break;
1324             case MIDI_PGM_CHANGE:
1325             case MIDI_CHN_PRESSURE:
1326                 SEQ_MIDIOUT(dev, evt);  
1327                 SEQ_MIDIOUT(dev, d1);                                                                           
1328                 break;  
1329             case MIDI_SYSTEM_PREFIX:
1330                 switch (evt & 0x0F) {
1331                 case 0x00:      /* System Exclusive, don't do it on modData, 
1332                                  * should require modLongData*/
1333                 case 0x01:      /* Undefined */
1334                 case 0x04:      /* Undefined. */
1335                 case 0x05:      /* Undefined. */
1336                 case 0x07:      /* End of Exclusive. */
1337                 case 0x09:      /* Undefined. */
1338                 case 0x0D:      /* Undefined. */
1339                     break;
1340                 case 0x06:      /* Tune Request */
1341                 case 0x08:      /* Timing Clock. */
1342                 case 0x0A:      /* Start. */
1343                 case 0x0B:      /* Continue */
1344                 case 0x0C:      /* Stop */
1345                 case 0x0E:      /* Active Sensing. */
1346                     SEQ_MIDIOUT(dev, evt);      
1347                     break;
1348                 case 0x0F:      /* Reset */
1349                                 /* SEQ_MIDIOUT(dev, evt);
1350                                    this other way may be better */
1351                     SEQ_MIDIOUT(dev, MIDI_SYSTEM_PREFIX);
1352                     SEQ_MIDIOUT(dev, 0x7e);
1353                     SEQ_MIDIOUT(dev, 0x7f);
1354                     SEQ_MIDIOUT(dev, 0x09);
1355                     SEQ_MIDIOUT(dev, 0x01);
1356                     SEQ_MIDIOUT(dev, 0xf7);
1357                     break;
1358                 case 0x03:      /* Song Select. */
1359                     SEQ_MIDIOUT(dev, evt);      
1360                     SEQ_MIDIOUT(dev, d1);                                                                               
1361                 case 0x02:      /* Song Position Pointer. */
1362                     SEQ_MIDIOUT(dev, evt);      
1363                     SEQ_MIDIOUT(dev, d1);
1364                     SEQ_MIDIOUT(dev, d2);
1365                 }
1366                 break;
1367             }
1368         }
1369         break;
1370     default:
1371         WARN("Technology not supported (yet) %d !\n", 
1372              midiOutDevices[wDevID]->wTechnology);
1373         return MMSYSERR_NOTENABLED;
1374     }
1375     
1376     SEQ_DUMPBUF();
1377
1378     return MMSYSERR_NOERROR;
1379 }
1380
1381 /**************************************************************************
1382  *              modLongData                                     [internal]
1383  */
1384 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1385 {
1386     int         count;
1387     LPBYTE      lpData;
1388
1389     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1390     
1391     if (midiSeqFD == -1) {
1392         WARN("can't play !\n");
1393         return MIDIERR_NODEVICE;
1394     }
1395     
1396     lpData = lpMidiHdr->lpData;
1397
1398     if (lpData == NULL) 
1399         return MIDIERR_UNPREPARED;
1400     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) 
1401         return MIDIERR_UNPREPARED;
1402     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) 
1403         return MIDIERR_STILLPLAYING;
1404     lpMidiHdr->dwFlags &= ~MHDR_DONE;
1405     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1406
1407     /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
1408      * data, or can it also contain raw MIDI data, to be split up and sent to 
1409      * modShortData() ?
1410      * If the latest is true, then the following WARNing will fire up
1411      */
1412     if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
1413         WARN("Alledged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
1414     }
1415
1416     TRACE("dwBufferLength=%lu !\n", lpMidiHdr->dwBufferLength);
1417     TRACE("                 %02X %02X %02X ... %02X %02X %02X\n",
1418           lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3], 
1419           lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
1420
1421     switch (midiOutDevices[wDevID]->wTechnology) {
1422     case MOD_FMSYNTH:
1423         /* FIXME: I don't think there is much to do here */
1424         break;
1425     case MOD_MIDIPORT:
1426         if (lpData[0] != 0xF0) {
1427             /* Send end of System Exclusive */
1428             SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, 0xF0);
1429             WARN("Adding missing 0xF0 marker at the begining of "
1430                  "system exclusive byte stream\n");
1431         }
1432         for (count = 0; count < lpMidiHdr->dwBytesRecorded; count++) {
1433             SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, lpData[count]);   
1434         }
1435         if (lpData[count - 1] != 0xF7) {
1436             /* Send end of System Exclusive */
1437             SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, 0xF7);
1438             WARN("Adding missing 0xF7 marker at the end of "
1439                  "system exclusive byte stream\n");
1440         }
1441         SEQ_DUMPBUF();
1442         break;
1443     default:
1444         WARN("Technology not supported (yet) %d !\n", 
1445              midiOutDevices[wDevID]->wTechnology);
1446         return MMSYSERR_NOTENABLED;
1447     }
1448     
1449     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1450     lpMidiHdr->dwFlags |= MHDR_DONE;
1451     if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1452         WARN("can't notify client !\n");
1453         return MMSYSERR_INVALPARAM;
1454     }
1455     return MMSYSERR_NOERROR;
1456 }
1457
1458 /**************************************************************************
1459  *                      modPrepare                              [internal]
1460  */
1461 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1462 {
1463     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1464     
1465     if (midiSeqFD == -1) {
1466         WARN("can't prepare !\n");
1467         return MMSYSERR_NOTENABLED;
1468     }
1469
1470     /* MS doc says taht dwFlags must be set to zero, but (kinda funny) MS mciseq drivers 
1471      * asks to prepare MIDIHDR which dwFlags != 0.
1472      * So at least check for the inqueue flag
1473      */
1474     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 || 
1475         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 || 
1476         lpMidiHdr->dwBufferLength >= 0x10000ul) {
1477         WARN("%p %p %08lx %d/%ld\n", lpMidiHdr, lpMidiHdr->lpData, 
1478                    lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
1479         return MMSYSERR_INVALPARAM;
1480     }
1481     
1482     lpMidiHdr->lpNext = 0;
1483     lpMidiHdr->dwFlags |= MHDR_PREPARED;
1484     lpMidiHdr->dwFlags &= ~MHDR_DONE;
1485     return MMSYSERR_NOERROR;
1486 }
1487
1488 /**************************************************************************
1489  *                              modUnprepare                    [internal]
1490  */
1491 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1492 {
1493     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1494
1495     if (midiSeqFD == -1) {
1496         WARN("can't unprepare !\n");
1497         return MMSYSERR_NOTENABLED;
1498     }
1499
1500     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1501         return MMSYSERR_INVALPARAM;
1502     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1503         return MIDIERR_STILLPLAYING;
1504     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1505     return MMSYSERR_NOERROR;
1506 }
1507
1508 /**************************************************************************
1509  *                      modReset                                [internal]
1510  */
1511 static DWORD modReset(WORD wDevID)
1512 {
1513     unsigned chn;
1514     
1515     TRACE("(%04X);\n", wDevID);
1516
1517     /* stop all notes */
1518     /* FIXME: check if 0x78B0 is channel dependant or not. I coded it so that 
1519      * it's channel dependent...
1520      */
1521     for (chn = 0; chn < 16; chn++) {
1522         /* turn off every note */
1523         modData(wDevID, 0x7800 | MIDI_CTL_CHANGE | chn);
1524         /* remove sustain on all channels */
1525         modData(wDevID, (CTL_SUSTAIN << 8) | MIDI_CTL_CHANGE | chn);
1526     }
1527     /* FIXME: the LongData buffers must also be returned to the app */
1528     return MMSYSERR_NOERROR;
1529 }
1530
1531 #endif /* HAVE_OSS_MIDI */
1532
1533 /*======================================================================*
1534  *                          MIDI entry points                           *
1535  *======================================================================*/
1536
1537 /**************************************************************************
1538  *                      OSS_midMessage                  [sample driver]
1539  */
1540 DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser, 
1541                             DWORD dwParam1, DWORD dwParam2)
1542 {
1543     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", 
1544           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1545     switch (wMsg) {
1546 #ifdef HAVE_OSS_MIDI
1547     case DRVM_INIT:
1548     case DRVM_ENABLE:
1549     case DRVM_DISABLE:
1550         /* FIXME: Pretend this is supported */
1551         return 0;
1552     case MIDM_OPEN:
1553         return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1554     case MIDM_CLOSE:
1555         return midClose(wDevID);
1556     case MIDM_ADDBUFFER:
1557         return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1558     case MIDM_PREPARE:
1559         return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1560     case MIDM_UNPREPARE:
1561         return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1562     case MIDM_GETDEVCAPS:
1563         return midGetDevCaps(wDevID, (LPMIDIINCAPSA)dwParam1,dwParam2);
1564     case MIDM_GETNUMDEVS:
1565         return MIDM_NUMDEVS; 
1566     case MIDM_RESET:
1567         return midReset(wDevID);
1568     case MIDM_START:
1569         return midStart(wDevID);
1570     case MIDM_STOP:
1571         return midStop(wDevID);
1572 #endif
1573     default:
1574         TRACE("Unsupported message\n");
1575     }
1576     return MMSYSERR_NOTSUPPORTED;
1577 }
1578
1579 /**************************************************************************
1580  *                              OSS_modMessage          [sample driver]
1581  */
1582 DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, 
1583                             DWORD dwParam1, DWORD dwParam2)
1584 {
1585     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", 
1586           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1587
1588     switch (wMsg) {
1589 #ifdef HAVE_OSS_MIDI
1590     case DRVM_INIT:
1591     case DRVM_EXIT:
1592     case DRVM_ENABLE:
1593     case DRVM_DISABLE:
1594         /* FIXME: Pretend this is supported */
1595         return 0;
1596     case MODM_OPEN:
1597         return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1598     case MODM_CLOSE:
1599         return modClose(wDevID);
1600     case MODM_DATA:
1601         return modData(wDevID, dwParam1);
1602     case MODM_LONGDATA:
1603         return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1604     case MODM_PREPARE:
1605         return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1606     case MODM_UNPREPARE:
1607         return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1608     case MODM_GETDEVCAPS:
1609         return modGetDevCaps(wDevID, (LPMIDIOUTCAPSA)dwParam1, dwParam2);
1610     case MODM_GETNUMDEVS:
1611         return MODM_NUMDEVS;
1612     case MODM_GETVOLUME:
1613         return 0;
1614     case MODM_SETVOLUME:
1615         return 0;
1616     case MODM_RESET:
1617         return modReset(wDevID);
1618 #endif
1619     default:
1620         TRACE("Unsupported message\n");
1621     }
1622     return MMSYSERR_NOTSUPPORTED;
1623 }
1624
1625 /*-----------------------------------------------------------------------*/