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