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