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